Project_OKI’s diary

エンジニアの勉強ブログ

STM32マイコン25(LCD:ACM1602のI2C制御方法)

STM32 (I2C通信でLCD制御)

1.本日の内容

 (1) STM32マイコン(nucleof401re)を使用して、

   LCD(ACM1602)にHellow worldを表示する。

  プログラム動作確認用に、LEDを点灯する。

 

・目次

 

2.内容

(1) やる内容の詳細

 ・CubeMXでI2C通信の設定を行う。

 ・nucleof401REとLCD(ACM1602)を接続する。

 ・プログラム起動時に、I2C通信を行い、LCD

  1行目にHellow world、2行目にThank youを表示する。

 

(2) 使用部品
個数 部品名 型番
1 接続ケーブル  ジャンパーワイヤ​​
1 mbed(マイコンボード) ​​ nucleo f401RE
1 USBケーブル USBミニBタイプ
1 ブレッドボード ​​ ​​EIC104j
1 LCD ACM1602NI-FLW-FBW-M01
2 カーボン抵抗(10kΩ) RD25S
1 多回転半固定ボリューム(1kΩ) 3296W-1-102LF

 

LCD(ACM1602)データシート:

https://akizukidenshi.com/download/ds/xiamen/ACM1602NI-FLW-FBW-M01_20190219.pdf

 

(3) 接続
nucleof401re側 LCD
pin番号 役割 pin番号 役割
GND GND 1 GND
3.3V 3.3V電源 2 VDD
3.3V(可変抵抗接続) 3.3V電源-可変抵抗 3 Vo
D15(PB8) I2C_SCL 4 I2C_SCL
D14(PB9) I2C_SDA 5 I2C_SDA
3.3V 3.3V電源 6 BL+(バックライト)
GND GND 7 BL-(バックライトGND)

※Voは、LCDのコンストラクタの為、可変抵抗で入力する電圧を変化させ、調整する。

 

 ・LCDのデータシートより

 

 接続は下記記事を参考にしました。 

ArduinoでI2C制御LCD ACM1602を使う(1) | wsnakのブログ

・上記記事では、SCDとSDAに5Vが接続されているが、3.3Vを使用。

 

(2) CubeMXの設定

 ・File→New ProjectでSTM32F401を選択し、新しいプロジェクトを作成する。

  (a) Pin設定画面の下記ピンを設定する。

    ・PB9をI2C1_SDA   (LCD制御用 I2C_SDA)

    ・PB8をI2C1_SCL          (LCD制御用 I2C_SCL)

    ・PA5をGPIO_Output     (LED点灯用)

    ・PC13をGPIO_EXTI13 (今回は使わないが、デバック用にあると良い)

  (b) それぞれのピンの名前を右クリック、Enter User Labelで

    画像の名前に変更する。(変更しなくてもよい。)

 

 

  (b) ConnectivityのI2C1を選択し、LCDの仕様に合わせて、下記設定を行う。

Mater Feature
  I2C Speed Mode Standard Mode
  I2C Clock Speed 100000
Slave Features
      Clock No Stretch Mode Disable
 Primary Address Length selection 7-bit
 Dual Address Acknowledged Disable
 Primary slave address 0
 Generala Call address detection Disabled

 

   下記に簡単に、それぞれの設定の意味を示す。

設定項目 説明
Master Features I2C通信のマスター側の設定項目
I2C Speed Mode

I2Cのスピードモード設定。

標準モード、Fastモードが選択可能

※今回はそこまで速度が必要ないので標準。

I2C Clock Speed

I2Cのクロック速度設定。Hz単位で設定可能

※データシートより100kHzに設定

Slave Features I2C通信のスレーブ側の設定項目
Clock No Stretch Mode スレーブデバイスによるクロックのストレッチを禁止するか選択可能。
Primary Address Length selection

プライマリアドレスのビット数を7ビット

または10ビットで設定可能。

Dual Address Acknowledged スレーブデバイスが2つのアドレスを認識するかどうか選択可能。
Primary slave address スレーブデバイスのプライマリアドレスを設定可能。
General Call address detection ジェネラルコールアドレスの受信を有効にするか選択可能。

※仕様上、LCDのI2Cクロックスピードは100kHzまで可能ですが。

線の長さなどによっては、そのスピードだと通信できない可能性があります。

その場合は、70kHzなどクロックスピードを落とすと通信出来るようになります。

 

 

 

  (c)NVICを設定

 

  (d) 割り込みの優先順位を下げる。

 

  (e) プロジェクト名を付け、Toolchain/IEDをTrueSTUDIOに設定

 

 

 (f)GENERATE CODEでコードを生成し、AtricTrueStdioを起動する。

  ※Open PROJECTをクリックすると、AtricTrueStdioが自動的に起動される。

 

 LED及びボタン設定に関しては下記記事を参照 

 ・STM32マイコン_6(スイッチ入力(INT割り込み)とLED出力) - Project_OKI’s diary

 

(3) プログラムの作成

動作:

 ・LCDの初期化

 ・LCDのカーソル位置を左上(0,0)に設定

 ・Hellow worldを表示

 ・LCDのカーソル位置を左下(0,1)に設定

 ・Thank youを表示

 
main.c
/* USER CODE BEGIN PD */
//コマンド送信用定数
#define LCDADRESS 0xa0	//LCDのスレーブアドレス
#define CMDSEND 0x00	//コマンド送信
#define	DATASEND 0x80	//データ送信
/* USER CODE END PD */
/* USER CODE BEGIN 0 */

///コマンド送信用関数
///引数:送信コマンド
///返り値:無し
void writeCmd(uint8_t cmd)
{
	uint8_t buf[] = {CMDSEND,cmd};
	HAL_I2C_Master_Transmit(&hi2c1,LCDADRESS,buf,sizeof(buf),HAL_MAX_DELAY);
}

///データ送信用関数
///引数:送信データ
///返り値:無し
void writeData(uint8_t data)
{
	uint8_t buf[] = {DATASEND,data};
	HAL_I2C_Master_Transmit(&hi2c1 ,LCDADRESS , buf,sizeof(buf),HAL_MAX_DELAY);
}

///LCD初期化関数
///引数:無し
///返り値:無し
void Lcd_init(){
	writeCmd(0x01);		///画面クリア
	HAL_Delay(5);		///5msまち
	writeCmd(0x38);		///ファンクション設定
	HAL_Delay(5);		///5ms
	writeCmd(0x0f);		///ディスプレイON、CURSOR-ON、blinking-ON
	HAL_Delay(5);		///5msまち
	writeCmd(0x06);		///データ書き込み後アドレス加算モード設定
	HAL_Delay(5);		///5msまち
}

//LCD画面クリア
///引数:送信データ
///返り値:無し
void LCD_Clear(void){
	writeCmd(0x01);	//0x01コマンドを送信
	HAL_Delay(2);
}

///LCDのカーソル位置設定
///引数:uint8_t col:横の位置、uint8_t row:縦の位置(1段目、2段目)
///返り値:無し
void LCD_SetCursor(uint8_t col,uint8_t row)
{
	///
	uint8_t row_offsets[] = { 0x00, 0x40};
	if (row > 1) {		///2段目以上はない。
	row = 1;
	}
	writeCmd(0x80 | (col + row_offsets[row]));
}

//LCD文字出力
///引数:char str:送信する文字
///返り値:無し
void LCD_Print(char* str) { while (*str) { writeData((uint8_t)*str++); } } /* USER CODE END 0 */
  /* USER CODE BEGIN 2 */

  Lcd_init();				//LCD初期化
  LCD_SetCursor(0, 0);		//カーソル位置を0,0に設定(1行目の左端)
  LCD_Print("Hello World!");//Hellow World!を出力
  LCD_SetCursor(0, 1);		//カーソル位置を0,1に設定(2行目の左端)
  LCD_Print("Thank you"); //Thank youを出力
  /* USER CODE END 2 */
 
(4) プログラムの説明
 (a) #define LCDADRESS 0xa0 ///LCDのスレーブアドレス
   #define CMDSEND 0x00 ///コマンド送信
        #define DATASEND 0x80 ///データ送信
  ・#defineでLCDのスレーブアドレス、データ送信、コマンド送信の値を
   名前に置き換える。
  ・設定した数値に関しては、下記LCDの使い方にて説明
 
 (b)  void writeCmd(uint8_t cmd)
 ・LCDのコマンドを送信するための関数
 
 (c) uint8_t buf = {CMDSEND,cmd};
  ・CMDSEND(0x00)と  
  ・送信するコマンドをbufに格納する。
 
 (d) HAL_I2C_Master_Transmit(&hi2c1,LCDADRESS,buf,sizeof(buf),HAL_MAX_DELAY);
   ・I2C通信でコマンド送信を行う。
   ・それぞれの引数について
    ・I2C_HandleTypeDef *hi2c :I2Cバスのハンドルを指定
    ・uint16_t DevAddress   :通信先のスレーブのアドレスを指定
                  (今回は、LCDADRESS:0xa0)
    ・uint8_t *pData       :送信するデータのポインタを指定。
    ・uint16_t Size          :送信するデータのサイズをバイト単位で指定
    ・uint32_t Timeout      :タイムアウト時間をミリ秒単位で指定   

 
 (e) void writeData(uint8_t data)
 ・writeCmdと基本同じ。
 ・LCDのデータを送信する為の関数。
 
 (f) void Lcd_init(){
 ・LCDを初期化するための関数。
 ・初期化内容は、下記LCDコマンド内容に記載
 
 (g) void LCD_Clear(void){
 ・LCDの画面をクリアする為の関数。
 ・クリアするためのコマンド(0x01)を送信する。
 
 (h) void LCD_SetCursor(uint8_t col,uint8_t row)
 ・LCDのカーソル位置を設定する為の関数
 ・colでカーソルの列指定
 ・rowでカーソルの行指定
 ・LCDカーソル位置については、下記LCDカーソル位置指定に記載
 
 (i) void LCD_Print(char* str)
 ・LCDに表示する文字列を出力するための関数
 ・引数で、文字列を指定
 ・writeData((uint8_t)*str++);で1文字ずつ送信する。
 ・whileで全ての文字の出力が終るするまで、ループする。
 ・コマンドはuin8_t型なので、char型をuin8_t型に変更している。
 
 
(5) LCDの使い方説明
 (a) スレーブアドレス、コマンド送信、データ送信について
  LCDの送信には、
  •  スレーブアドレス→ CONTROL BYTE→ COMMAND OR DATA BYTE
  の順番で送信する。
 
  送信するスレーブアドレス及び、CONTROL BYTEについて説明する。
 

 ※データシートより
 

 LCDのスレーブアドレスは、0b1010000(16進数では0x50)だが、

 SLAVE ADDRESSの表を見ると分かるように、
 R/Wの0を含めた8bitで送信する必要がある。
 
 つまり、101000000を送る。(0b1010000を左に1bitシフトする。)
 0b1010000<<1 = 0b10100000 = 0xa0
 となる。
 
 0xa0の所を、0x50<<1でも、0b1010000<<1などに変更しても送信できる。
 これをI2C通信で、最初に送る。
 
 
 次に、CONTROL BYTEを確認する。
 RSが0又は1で他は0000000となっている。
 
 つまり
 コマンド送信の時はR/S = 0で
 0b00000000 = 0x00
 
 データ送信の時はR/S = 1で
 0b10000000 = 0x80
 
 をCONTROL BYTEとして送信する。
 
 その後、コマンド又はデータバイトを送信する。
 
  それが、HALドライバでは、以下のようになる。
uint8_t buf = {DATASEND,data};
HAL_I2C_Master_Transmit(&hi2c1 ,LCDADRESS , buf,sizeof(buf),HAL_MAX_DELAY);
 
  LCDADRESSがLCDのアドレス
  buf[0]にデータ送信時は、0x80を格納
  buf[1]に送信するデータを格納している。
 
 上記内容については、下記記事を参考:
 
 
 (b) 初期化処理
  データシートの初期化処理を参考に、以下の初期化処理を行う。
 ・writeCmd(0x01); ///画面クリア
  →0b00000001
 
 ・writeCmd(0x38); ///ファンクション設定
  →0b00111000
  →Set interface data length :8bit
  →number of display line :2line
  →display font type    :5×10dot
 
 ・writeCmd(0x0f); ///ディスプレイON、CURSOR-ON、blinking-ON
  →0b00001111
  →Set display cursor and blinking of cursor ON
 
 ・writeCmd(0x06); ///データ書き込み後アドレス加算モード設定
  →0b00000110
  →Assign cursor moving direction and enable he shift of entire display.
    カーソルの移動方向を指定し、表示全体のシフトを可能にする。
 

 
 
 (c) カーソル位置の指定

void LCD_SetCursor(uint8_t col,uint8_t row) { 

  uint8_t row_offsets = { 0x00, 0x40};

  if (row > 1) { 

   row = 1;

   }

  writeCmd(0x80 | (col + row_offsets[row]));

}

 
  ・カーソルの位置を指定する。
  ・1 xxxxxxxxでカーソルの位置を指定する。
  ・一の左上(00)を指定する場合
  ・10000000 を送信する。
 


 ・1行目は、00から0F

 ・2行目は、40から4F

  その為、rowで row_offsets = {0x00,0x40}で1行目、2行目指定している。

 ・colで0からFまでを指定することにより、列を指定する。

 

 ・例えば、01(1行2列目)を指定する場合、colが1でrowを1で指定する。

  計算:0x00 + 0x01 = 0x01

     0x80 | 0x01 = 0x81 

     よって、0x81をwriteCmdで、送信する。

  演算子については、こちら:C言語基礎知識21(演算子) - Project_OKI’s diary

 

 (d) 文字列を出力する。
  文字列は、下記データシート部分に乗っている。
  これは、キーコードと同じ値になっており、
  文字列を関数に引数として渡している。
  while (*str) {
   writeData((uint8_t)*str++);
  }
  これで、全ての文字を1文字ずつwriteDataで送っている。
 

 
参考記事:
 

3.関連記事

関連記事一覧:
 
組み込みC言語