*[[キャラクタLCDをアクセス]] [#m5789035] **はじめに [#e7e50907] > >Raspberry Pi 3でCentOS7からのキャラクタLCDへのアクセス方法をまとめました。~ 秋月電子で扱っているI2Cインタフェースかつ3.3V品ということで、AE-AQM1602A(KIT)を購入してみました。~ 結果的には、まさかこんなものでというぐらい、大変苦労することになりました。 < **HD44780互換IC系 その1 Sitronix ST7032 [#u0dd2208] ***概要 [#l63c5a03] > >キャラクタディスプレイといえば、この系統が主流ですね。往年の68系バスに接続するインタフェース仕様です。このチップのインタフェースをI2Cに変換するものが、世の中にいくつかあります。~ 今回は、Sitronix ST7032というチップのものです。パラレル、I2C、SPI何でも来い!的な石です。 #ref(K-08896.jpg,around,zoom) #ref(ST7032-1.jpg,zoom) < ***ハードウェア [#m86d0dca] -ピンアサイン > >電源はRaspberryPiの3.3Vを使用します。PINのラッチアップ等を避けるため、同一電源とします。 |>|Raspberry Pi| |>|LCD |h |Signal|Pin#|BGCOLOR(#663300): |Signal|Pin#| |3V3|01,17|BGCOLOR(#663300): |3V3|01| |GND|34,39|BGCOLOR(#663300): |GND|04| |I2C SDA.1|03|BGCOLOR(#663300): |SDA|03| |I2C SCL.1|05|BGCOLOR(#663300): |SCL|02| < > >> << < ***ソフトウェア [#j396b47f] -概要 > >Read/Write用の関数を作成してアクセスすることになります。~ たとえば >> >>void lcd_init( void );~ int lcd_write_command( char command );~ int lcd_write_data( char data); << >を基本として、各種のコマンドを作成することとします。上記の関数を利用して >> >>void lcd_clear( void );~ void lcd_print( int line, char *str ); << >位を用意すれば良いでしょう。 < -実装 > >テストのつもりでコーディングします。 //------------------------------------------------------------------------------ // ST7032 LCDモジュール(16 x 2) 秋月版 制御TP // このTPでは、2行表示に対して、これぞれの行に文字を表示します。 // 基本的に16文字固定表示でスクロールはしません。 // // コンパイル方法 // gcc -o i2c_lcd_test_st7032 i2c_lcd_test_st7032.c -l bcm2835 -lrt // // copyright (c) 2016 Kazuhiro WATANABE All right reserved. // mailto:jj1req@ca.mbn.or.jp // //------------------------------------------------------------------------------ #include <bcm2835.h> #include <stdio.h> #include <stdlib.h> #include <string.h> // BCM2835のI2Cクロック周波数の設定値 // BCM2835_I2C_CLOCK_DIVIDER_2500 2500 = 10us = 100 kHz(IIC spec.) // BCM2835_I2C_CLOCK_DIVIDER_626 622 = 2.504us = 399.3610 kHz // BCM2835_I2C_CLOCK_DIVIDER_15 150 = 60ns = 1.666 MHz (default at reset) // BCM2835_I2C_CLOCK_DIVIDER_148 148 = 59ns = 1.689 MHz #define CLK_DIV BCM2835_I2C_CLOCK_DIVIDER_2500 //#define CLK_DIV BCM2835_I2C_CLOCK_DIVIDER_148 //#define CLK_DIV BCM2835_I2C_CLOCK_DIVIDER_626 // // キャラクタ液晶ディスプレーへの接続 #define LCD_ADDRESS 0x3e // Write Address(Write Only) // // First Byte of I2C Data // Co RS 0 0 0 0 0 0 #define LCD_COMMAND 0x00 // Following data is command with single bye #define LCD_DATA 0x40 // Following data is data byte with single byte // // Commands // Function Set // 0 0 1 DL N DH 0 IS // DL : Interface data length control bit // 1: 8-bit bus mode with MPU. // 0: 4-bit bus mode with MPU. // When in 4-bit bus mode, it needs to transfer 4-bit data by two times. // N : Display line number control bit // 1: 2-line display mode is set. // 0: 1-line display mode. // DH : Double height font type control bit // 1: double height mode(5x16 dot),RAM address can only use 00H~27H. // 0: normal (5x8 dot). // IS : normal/extension instruction select // 1: extension instruction be selected (refer extension instruction table) // 0: normal instruction be selected (refer normal instruction table) #define LCD_FUNCTION_SET_NORM 0x38 // 8bit bus, 2 line, normal font, following normal instructions #define LCD_FUNCTION_SET_EXT 0x39 // 8bit bus, 2 line, normal font, following extension instructions // // Bias selection/Internal OSC frequency adjust, BS will be invalid when external bias resistors are used // 0 0 0 1 BS F2 F1 F0 // BS: bias selection // 1: the bias will be 1/4 // 0: the bias will be 1/5 // F2,F1,F0 : Internal OSC frequency adjust, When CLS connect to high, that instruction can adjust OSC and Frame frequency. // F2 F1 F0 3.0V 5.0V // 0 0 0 122 120 // 0 0 1 131 133 // 0 1 0 144 149 // 0 1 1 161 167 // 1 0 0 183 192 // 1 0 1 221 227 // 1 1 0 274 277 // 1 1 1 347 347 #define LCD_OSC 0x14 // Internal OSC Frequency, BIAS=1/4, 183Hz at 3.3V 2line // // Contrast set(low byte) // 0 1 1 1 C3 C2 C1 C0 // C3,C2,C1,C0:Contrast set(low byte) #define LCD_CONTRAST_L 0x73 // Low byte of contrast // // Power/ICON control/Contrast set(high byte) // 0 1 0 1 ION BON C5 C4 // Ion: set ICON display on/off // 1: ICON display on. // 0: ICON display off. // Bon: switch booster circuit, Bon can only be set when internal follower is used (OPF1=0, OPF2=0). // 1: booster circuit is turn on. // 0: booster circuit is turn off. // C5,C4 : Contrast set(high byte) // C5,C4,C3,C2,C1,C0 can only be set when internal follower is used (OPF1=0,OPF2=0). // They can more precisely adjust the input reference voltage of V0 generator. // The details please refer to the supply voltage for LCD driver. #define LCD_CONTRAST 0x56 // Power/ICON control/Contrast // // Follower control // 0 1 1 0 FON Rab2 Rab1 Rab0 // Fon: switch follower circuit, Fon can only be set when internal follower is used // 1: internal follower circuit is turn on. // 0: internal follower circuit is turn off. // Rab2,Rab1,Rab0 : V0 generator amplified ratio, Rab2,Rab1,Rab0 can only be set when internal follower is used #define LCD_FOLLOWER 0x6c // // Clear Display // 0 0 0 0 0 0 0 1 #define LCD_CLEAR 0x01 // // Display ON/OFF // 0 0 0 0 1 D C B // D : Display ON/OFF control bit // 1: entire display is turned on. // 0: display is turned off, but display data is remained in DDRAM. // C : Cursor ON/OFF control bit // 1: cursor is turned on. // 0: cursor is disappeared in current display, but I/D register remains its data. // B : Cursor Blink ON/OFF control bit // 1: cursor blink is on, that performs alternate between all the high data and display character at the cursor position. // 0: blink is off. #define LCD_ON 0x0f #define LCD_OFF 0x08 // // Command Recovery Time //#define LCD_RECOVERY 100LL // 100us #define LCD_RECOVERY 1000LL // 1000us #define ON 1 #define OFF 0 //****************************************************************************** //* I2Cアクセス時のエラー要因を表示します。 //* //* Function Name //* void get_reaosn( bcm2835I2CReasonCodes res, char *str ) //* Arguments //* bcm2835I2CReasonCodes res error code //* char str message buffer //* Return //* none //* //****************************************************************************** void get_reaosn( bcm2835I2CReasonCodes res, char *str ) { switch( res ) { case BCM2835_I2C_REASON_OK: strcpy( str, "Success" ); break; case BCM2835_I2C_REASON_ERROR_NACK: strcpy( str, "Received a NACK" ); break; case BCM2835_I2C_REASON_ERROR_CLKT: strcpy( str, "Received Clock Stretch Timeout" ); break; case BCM2835_I2C_REASON_ERROR_DATA: strcpy( str, "Not all data is sent / received" ); break; default: strcpy( str, "Unknown error" ); } } //****************************************************************************** //* LCDモジュールにコマンドの書き込みを行います //* //* Function Name //* int lcd_write_command( char data ) //* Arguments //* char data byte data //* Return //* int res 0: normal end //* 1: error //* //****************************************************************************** int lcd_write_command( char data ) { bcm2835I2CReasonCodes res; char str[80]; char data_array[2]; data_array[0] = LCD_COMMAND; data_array[1] = data; if( res=bcm2835_i2c_write( data_array, 2 ) ) { get_reaosn( res, str ); printf( "Write error: %s[%d]\n", str, res ); bcm2835_delayMicroseconds( LCD_RECOVERY ); return( 1 ); } bcm2835_delayMicroseconds( LCD_RECOVERY ); return( 0 ); } //****************************************************************************** //* LCDモジュールにデータの書き込みを行います //* //* Function Name //* int lcd_write_data( char data ) //* Arguments //* char data byte data //* Return //* int res 0: normal end //* 1: error //* //****************************************************************************** int lcd_write_data( char data ) { bcm2835I2CReasonCodes res; char str[80]; char data_array[2]; data_array[0] = LCD_DATA; data_array[1] = data; if( res=bcm2835_i2c_write( data_array, 2 ) ) { get_reaosn( res, str ); printf( "Write error: %s[%d]\n", str, res ); bcm2835_delayMicroseconds( LCD_RECOVERY ); return( 1 ); } bcm2835_delayMicroseconds( LCD_RECOVERY ); return( 0 ); } //****************************************************************************** //* LCDモジュールに対して表示のON/OFFを行います //* //* Function Name //* void lcd_on_off( int flag ) //* Arguments //* flag 0: OFF //* 1: ON //* Return //* none //* //****************************************************************************** void lcd_on_off( int flag ) { if( flag ) lcd_write_command( LCD_ON ); else lcd_write_command( LCD_OFF ); } //****************************************************************************** //* LCDモジュールに対して表示のクリアを行います //* //* Function Name //* void lcd_clear( void ) //* Arguments //* none //* Return //* none //* //****************************************************************************** void lcd_clear( void ) { lcd_write_command( LCD_CLEAR ); bcm2835_delay( 10 ); } //****************************************************************************** //* LCDモジュールに対して初期化を行います //* //* Function Name //* void lcd_init( void ) //* Arguments //* none //* Return //* none //* //****************************************************************************** void lcd_init( void ) { // ライブラリ初期化 if( !bcm2835_init() ) { printf( "bcm2835 library initialize error!!\n" ); exit(1); } // I/O ピンの初期化 if( !bcm2835_i2c_begin() ) { printf( "I2C initialize error!!\n" ); exit(1); } // I2C設定 bcm2835_i2c_setSlaveAddress( LCD_ADDRESS ); //bcm2835_i2c_set_baudrate( 50000 ); bcm2835_i2c_setClockDivider( CLK_DIV ); bcm2835_delay( 200 ); // 初期化フェーズ 1 lcd_write_command( LCD_FUNCTION_SET_NORM ); bcm2835_delay( 200 ); // 初期化フェーズ 2 lcd_write_command( LCD_FUNCTION_SET_EXT ); // 初期化フェーズ 3 lcd_write_command( LCD_OSC ); // 初期化フェーズ 4 lcd_write_command( LCD_CONTRAST_L ); // 初期化フェーズ 5 lcd_write_command( LCD_CONTRAST ); // 初期化フェーズ 6 lcd_write_command( LCD_FOLLOWER ); bcm2835_delay( 400 ); // 初期化フェーズ 7 lcd_write_command( LCD_FUNCTION_SET_NORM ); // Display CLEAR & ON lcd_clear(); lcd_on_off( ON ); } //****************************************************************************** //* I2Cインタフェースを開放します。。 //* //* Function Name //* void lcd_close( void ) //* Arguments //* none //* Return //* none //* //****************************************************************************** void lcd_close( void ) { bcm2835_i2c_end(); bcm2835_close(); } //****************************************************************************** //* LCDモジュールに文字列を表示します。 //* //* Function Name //* void lcd_print( int line, char *str ) //* Arguments //* int line target position 0:first line, 1:second line //* char *str string data //* Return //* none //* //****************************************************************************** void lcd_print( int line, char *str ) { int i; int len; char data; data = (line * 0x40) | 0x80; lcd_write_command( data ); for( i=0; i<16; i++) { lcd_write_data( 0x20 ); } data = (line * 0x40) | 0x80; lcd_write_command( data ); len = strlen(str); if( len > 16 ) len = 16; for( i=0; i<len; i++ ) { lcd_write_data( str[i] ); } } int main(int argc, char **argv) { lcd_init(); lcd_print( 0, "Minkycute.homeip.net" ); lcd_print( 1, "* I2C LCD 7032 *" ); lcd_close(); return( 0 ); } < ***テスト [#re19c440] > >以下でコンパイルして、実行します。 # gcc -o i2c_lcd_test_st7032 i2c_lcd_test_st7032.c -l bcm2835 -lrt < > >※-l rt は[[real-time extensions>http://devlib.symbian.slions.net/belle/GUID-E21287EF-7211-590B-A1E3-E0B37069FCB2.html]]です。(RTカーネルではないので、意味ないかもしれませんが) < > >結果はまったく動きません。NACKが返ります。~ 実にこまりました。 < ***デバック開始 [#q87c5829] -汎用ツールでテスト > >といことで調査をします。この世界では、有名なi2cdetectでスキャンしてみましょう~ BCM2835用のコードがないか、調べます。ありました。~ https://github.com/vanvught/OpenILDA/blob/master/bw_i2cdetect/src/i2cdetect.c~ 早速コンパイルして実行します。 >※2019-08-10追記:i2c-toolsというrpmパッケージがありました。こちらを使用します。~ この場合、ドライバー経由になるため、/boot/config.txt に dtparam=i2c_arm=on を指定します。~ i2c_devが存在することをlsmodで確認します。 この場合、ドライバー経由になるため、/boot/config.txt に dtparam=i2c_arm=on と dtparam=i2c1=on を指定します。~ i2c_devが存在することをlsmodで確認します。(必要に応じ、/etc/modules-load.d/i2c.conf に i2c-bcm2708 と i2c-dev を追加) # ./i2cdetect 0 1 2 3 4 5 6 7 8 9 a b c d e f 00: -- -- -- -- -- -- -- -- -- -- -- -- -- 10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 70: -- -- -- -- -- -- -- -- # >やはり、ツールでもNGです。しかし時々読めます。 # ./i2cdetect 0x3E : 0x7C : Unknown device 0 1 2 3 4 5 6 7 8 9 a b c d e f 00: -- -- -- -- -- -- -- -- -- -- -- -- -- 10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- 3e -- 40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 70: -- -- -- -- -- -- -- -- # >何かのパラメータがちがうのでしょうか? +クロックを変えてみます。ボーレート指定でさらに下げたり、あげたり:NG +10Kプルアップ:NG +双方向レベル変換:NGです。 < > >Google先生に聞いてみます。ありました。~ https://strawberry-linux.com/support/27001/619011~ http://www.takunoko.com/blog/aqm1602xa-rn-gbw%E3%82%92raspberry-pi%E3%81%A7%E4%BD%BF%E3%81%86/~ といことで、なんと、LSIの内蔵PullUp/Downnoほかに、外部でご丁寧に1.8kでPullUpしているとのこと。たぶんMhz動作させる為なんでしょうね。~ 根性なしのLCDには厳しいです。~ ということで、抵抗をはずしましょう。でも、抵抗が違うようです。~ 一応、回路図を調べると、 #ref(bcm2837-1.jpg,zoom) >R23,R24です。コネクタの近くのA面にありますので、退場してもらいます。 < ***結果 [#j0b76f03] > >動きました! >LCD #ref(LCD_OK-2.jpg,zoom,20%) < ***ツール化 [#y63d016e] > >せっかくなので、コマンドラインより文字を表示できるツールに仕立てます。 < -コマンド形式 > lcd_st7032 [-a <addr>] [-i] [-h] <message> [<line#>] < -引数/オプション > パラメータ <message> ASCII文字(SJIS:カナ) <line#> 行番号: 0 or 3 オプション -a <addr>: I2C上のアドレス(デフォルト:0x3e) -i: LCDの初期化を行う、LCDを初期化、最初の1回実施すればOK -h: ヘルプ表示引数: < > >&ref(lcd_st7032.c); < **HD44780互換IC系 その2 HD44780/SC1602 LCDモジュール(16 x 2) + I2CExpander [#u0dd2208] ***I2CExpander [#jc325e57] > >Philips社よりI2C接続のパラレルポートICとして、PCF8574が製造されていますが、このICを使用しI2Cバスに接続するLCDモジュールが多数存在します。~ また、このICは5V/3.3Vに対応しています。 >ただし、残念ながら、多くのモジュールはLCD本体が5V電源のため、RaspberryPiとの接続は、3.3V/5Vレベル変換を必要とします。 < ***対象モジュール [#q1cc1398] > >今回はAmazonで「サインスマート(SainSmart) IIC/I2C/TWI 16*02 LCD液晶 モジュール For Arduino UNO MEGA R3 青発光 」を購入しました。~ Amazonに在庫があり、翌日には手に入りました。~ I2C Expander基盤がLCD基盤の金具にショートしそうなこと意外は製品は特に問題はありませんでした。(中華製なので仕方ありません) >&ref(sainsmart-01.jpg,zoom,20%); < ***3.3V/5Vレベル変換 [#k57e1337] > >秋月電子のPCA9306を使用したモジュールを使用しました。 < ***PCA9306とLCD制御LSIの接続 [#f88a3077] > >PCA9306の8ビット出力とLSIのピンは下表の接続です。 |PCA9306 パラレル入出力|LCD制御LSI信号|h |D7|D7| |D6|D6| |D5|D5| |D4|D4| |D3|BackLight| |D2|E| |D1|R_W| |D0|RS| < > >> >>> >>> >>>LCD制御ICは4bitバスモードで接続することになります。 <<< << < ***ソフト [#f1fe6d25] > >制御用にソフトを作成します。コマンドの仕様は以下のとおりです。~ 一応、後述する20x4タイプと共用できるよう、4行仕様にしてあります。 < -コマンド仕様 > # lcd_exp [-a ] [-i] [-h] <text> [<line#>] < -パラメータ・オプション等 > パラメータ <text> ASCII文字(SJIS:カナ) <line#> 行番号: 0 or 3 オプション -a <addr>: I2C上のアドレス(デフォルト:0x27) -i: LCDの初期化を行う。LCDを初期化、最初の1回実施すればOK -h: ヘルプ表示 < > >ファイル:&ref(lcd_exp.c); < ***結果 [#q389bcfa] > >動きました #ref(16x2.jpg,zoom,15%) < **HD44780互換IC系 その2 LCDモジュール(20 x 4) + I2CExpander [#content_1_10] > >20x4(4行)タイプをこのICを使用しI2Cバスに接続するLCDモジュールが多数存在します。~ 基本、「HD44780互換IC系 その2」と同じでDDRAM(テキストビデオバッファ)のみことなりますが、互換性があります。 < ***DDRAM [#idf4b1c2] > >資料がないため、推測を含みますが以下のようでした。 |LINE#|>|>|>|>|>|>|>|>|>|>|>|>|>|>|>|>|>|>|>|CENTER:ADDR|h |~|01|02|03|04|05|06|07|08|09|10|11|12|13|14|15|16|17|18|19|20|h |CENTER:1|00|01|02|03|04|05|06|07|08|09|0A|0B|0C|0D|0E|0F|10|11|12|13| |CENTER:2|40|41|42|43|44|45|46|47|48|49|4A|4B|4C|4D|4E|4F|50|51|52|53| |CENTER:3|14|15|16|17|18|19|1A|1B|1C|1D|1E|1F|20|21|22|23|24|24|26|27| |CENTER:4|54|55|56|57|58|59|5A|5B|5C|5D|5E|5F|60|61|62|63|64|65|66|67| < ***結果 [#w60fc469] -動かない! > >最初の火入れで、何も表示されず、i2cdetectの結果は以下のとおりです。 # ./i2cdetect 0x03 : 0x06 : Unknown device 0x04 : 0x08 : Unknown device 0x05 : 0x0A : Unknown device 0x06 : 0x0C : Unknown device (中略) 0x76 : 0xEC : Unknown device 0x77 : 0xEE : Unknown device 0 1 2 3 4 5 6 7 8 9 a b c d e f 00: 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10: 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20: 20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30: 30 31 32 33 34 35 36 37 38 39 3a 3b 3c 3d 3e 3f 40: 40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50: 50 51 52 53 54 55 56 57 58 59 5a 5b 5c 5d 5e 5f 60: 60 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f 70: 70 71 72 73 74 75 76 77 # >つまり、いつもACKが返ってますね。~ で調べると、Expander基盤のSDAピンがLCDモジュールの金具とショートしています。 #ref(short.jpg,zoom,40%) >とりあえず、コネクタを外し、基盤を浮かせた状態で再度半田付けします。(ピンはカットするので、再利用不可) >&ref(remove.jpg,zoom,15%); >&ref(rebuild.jpg,zoom,20%); < -再度挑戦でOK! > >今度は動きました。 #ref(2004-ok.jpg,zoom,40%) < お疲れ様でした。