Raspberry Pi 3でCentOS7からのキャラクタLCDへのアクセス方法をまとめました。
秋月電子で扱っているI2Cインタフェースかつ3.3V品ということで、AE-AQM1602A(KIT)を購入してみました。
結果的には、まさかこんなものでというぐらい、大変苦労することになりました。
キャラクタディスプレイといえば、この系統が主流ですね。往年の68系バスに接続するインタフェース仕様です。このチップのインタフェースをI2Cに変換するものが、世の中にいくつかあります。
今回は、Sitronix ST7032というチップのものです。パラレル、I2C、SPI何でも来い!的な石です。
電源はRaspberryPi?の3.3Vを使用します。PINのラッチアップ等を避けるため、同一電源とします。
Raspberry Pi LCD Signal Pin# Signal Pin# 3V3 01,17 3V3 01 GND 34,39 GND 04 I2C SDA.1 03 SDA 03 I2C SCL.1 05 SCL 02
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 ); }
以下でコンパイルして、実行します。
# gcc -o i2c_lcd_test_st7032 i2c_lcd_test_st7032.c -l bcm2835 -lrt
※-l rt はreal-time extensionsです。(RTカーネルではないので、意味ないかもしれませんが)
結果はまったく動きません。NACKが返ります。
実にこまりました。
といことで調査をします。この世界では、有名なi2cdetectでスキャンしてみましょう
BCM2835用のコードがないか、調べます。ありました。
https://github.com/vanvught/OpenILDA/blob/master/bw_i2cdetect/src/i2cdetect.c
早速コンパイルして実行します。# ./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には厳しいです。
ということで、抵抗をはずしましょう。でも、抵抗が違うようです。
一応、回路図を調べると、R23,R24です。コネクタの近くのA面にありますので、退場してもらいます。
動きました!
LCD
せっかくなので、コマンドラインより文字を表示できるツールに仕立てます。
lcd_st7032 [-i] [-h] <message> [<line#>]
引数: message : 半角カタカナ(SJIS)を含むASCII文字列 line# : 行番号(0または1)、デフォルトは0 オプション: -i : LCDを初期化、最初の1回実施すればOK
お疲れ様でした。