前回まででESP32へのプログラミング環境が整ったので、今回はI2C通信を利用したLCD表示プログラムを作成する。
#LCDモジュール
今回使用するLCD(液晶ディスプレイ)は、表示文字32文字(16×2行)で3.3Vでも駆動可能な低消費電力(1mA)となっている。
I2C接続小型キャラクタLCDモジュール(16×2行・3.3V/5V)ピッチ変換キット
抵抗やコンデンサがそもそも一体型となっており、そのままESP32へピン接続するだけで使用することができる。
表示方法としては、I2C通信を利用してデータのやりとりを実施して文字を表示させる。
#そもそもI2C通信とは?
I2C通信とは、指示を出すマスター側(1台)と指示を受けるスレーブ側(複数台)で、データのタイミングを同期する「クロック信号:SCL」とデータの書き込みや読み込みを指示する「データ信号:SDA」の2本の信号でデータのやりとりを行うシリアル通信方式です。
マスターが側からスレーブのアドレスを指定して一定のデータを送ることでデータの送受信が可能な仕組みになっている。
詳細な仕組みについては、以下サイトにて紹介されているのでそちらにて確認ください。
・http://www.lapis-semi.com/jp/common/miconlp/tips/2-17-i2c%E3%81%A8%E3%81%AF/
#回路図
過去の記事で回路図について作成しているので詳細はそちらを確認ください。
ピン接続は以下の通りにして下さい。
名称 | 使用部品 | データのやり取り | ESP32への接続ピン |
---|---|---|---|
I2C接続LCDモジュール | AE-AQM1602A(KIT) | I2C通信 | SDA:21,SCL:22 |
#作成プログラムについて
今回作成するプログラム内容は、シリアル通信(115200bit/s)にて受信した文字列をそのままLCDに表示させるプログラムである。
例:USBにてPCと接続後、シリアルモニターから「1234」を送信するとLCDに「1234」が表示される。
・シリアルモニタ画面
・LCD画面
以下は作成したテストプログラムなのでそのままコピーして使用してみて下さい。
/* 作成日;2020/3/1
ファイル名;01_LCD_I2C_TEST1
プログラム内容;PCからのシリアルモニターからの入力文字をI2C通信にてLCDへ表示する
使用部品;ESP32、LCD
ピン接続(ESP32側;LCD側);(21:SDA)、(22:SCL)
*/
#include<Wire.h> //I2C通信ライブラリ
#define LCD_ADRS 0x3E //I2Cアドレス
String overflow = "Over flow of characters";
/* 初期設定 */
void setup() {
Serial.begin(115200); //シリアル通信開始(通信レート:115200bit/s)
Wire.begin(); //I2C通信開始(ESP32をマスター)
init_LCD(); //LCD初期化
lcdwriteData("Booting now..."); //LCDへ起動中の表示
/* ここに起動時に確認する項目を記載 */
delay(5000);
/* ここに起動時に確認する項目を記載 */
//「Set DDRAM Address」= 1番目:0x○○(液晶表示入りのアドレス値)、2番目:0x80(機械コード「Set DDRAM Address」)
lcdwriteCommand(0x40+0x80); //2行目へカーソル移動(0x40 = 2行目先頭文字アドレス、0x80 = DDRAMアドレスへの移動)
lcdwriteData("OK"); //起動OKの表示
delay(3000);
lcdwriteCommand(0x01); //LCD液晶文字クリア([Clear Display])
lcdwriteData("S_Data Wating"); //シリアルデータ入力待ちの表示
}
/* 初期設定 */
/* メイン */
void loop() {
if (Serial.available() > 0) { // 受信したデータが存在する時
String serial_data = ""; //格納文字列を初期化
serial_data = Serial.readStringUntil('\n'); //[\n]がくるまで受信データを格納
lcdwriteCommand(0x01); //LCD液晶文字クリア([Clear Display])
lcdwriteData("S_Data Recved"); //シリアルデータ受信の表示
lcdwriteCommand(0x40+0x80); //2行目へカーソル移動
lcdwriteData(serial_data); //受信データを表示
}
}
/* メイン */
/* データ書き込み(複数byte) */
void lcdwriteData(String t_data){
int m_count = t_data.length(); //送信データの文字数をカウント
if(m_count>16){ //データ数が16バイトを超えたら
lcdwriteCommand(0x01); //LCD液晶文字クリア([Clear Display])
//全文字を表示
for(int i=0;i<23;i++){
if(i==16){ //17文字目に到達したら
lcdwriteCommand(0x40+0x80); //2行目へカーソル移動
}
Wire.beginTransmission(LCD_ADRS); //LCDをI2Cスレーブとして通信開始([slave address])
Wire.write(0x40); //最終データ、LCDへの表示データ([control byte] = 8bit目 0:最終,1:連続、7bit目 0:命令,1:表示データ)
Wire.write(overflow[i]); //1文字をキューに格納
Wire.endTransmission(); //キューに格納されたデータを送信してI2C通信停止
}
}else{
//全文字を表示
for(int i=0;i<m_count;i++){
Wire.beginTransmission(LCD_ADRS); //LCDをI2Cスレーブとして通信開始([slave address])
Wire.write(0x40); //最終データ、LCDへの表示データ([control byte] = 8bit目 0:最終,1:連続、7bit目 0:命令,1:表示データ)
Wire.write(t_data[i]); //1文字をキューに格納
Wire.endTransmission(); //キューに格納されたデータを送信してI2C通信停止
}
}
}
/* データ書き込み(複数byte) */
/* コマンド書き込み(1byte) */
void lcdwriteCommand(byte t_command){ //(書き込み順 = [slave address] + [control byte] + [data byte] )
Wire.beginTransmission(LCD_ADRS); //LCDをI2Cスレーブとして通信開始([slave address])
Wire.write(0x00); //最終データ、LCDへの命令([control byte] = 8bit目 0:最終,1:連続、7bit目 0:命令,1:表示データ)
Wire.write(t_command); //命令文を送信([data byte])
Wire.endTransmission(); //I2C通信停止
delay(10); //10ms待つ
}
/* コマンド書き込み(1byteずつ) */
/* LCD初期化 */
void init_LCD(){
delay(100); //電源ON後40ms以上待つ
//[Function set] = 5bit目 0:4ビット,1:8ビット、4bit目 0:1行,1:2行、3bit目 フォントサイズ(0でOK)、1bit目 0:通常状態,1:以降拡張コマンド入力
lcdwriteCommand(0x38); //8ビット、2行表示、5×8ドット、通常状態([Function set])
delay(20); //26.3μs以上待つ(「lcdwriteCommand」で10ms待つためなくてもOK)
lcdwriteCommand(0x39); //8ビット、2行表示、5×8ドット、以降拡張コマンド入力([Function set])
delay(20); //26.3μs以上待つ(「lcdwriteCommand」で10ms待つためなくてもOK)
//[Internal OSC frequency] = 4bit目 0:1/5バイアス電圧,1:1/4バイアス電圧、3〜1bit フレーム周波数[Hz]
lcdwriteCommand(0x14); //1/5バイアス電圧、フレーム周波数183[Hz]([Internal OSC frequency])
delay(20); //26.3μs以上待つ(「lcdwriteCommand」で10ms待つためなくてもOK)
//[Contrast set] = 4〜1bit コントラスト64段階調整([Power/ICONcontrol/Contrast set]と併せて設定)
lcdwriteCommand(0x73); //コントラスト設定([Contrast set])
delay(20); //26.3μs以上待つ(「lcdwriteCommand」で10ms待つためなくてもOK)
//[Power/ICONcontrol/Contrast set] = 4bit目 0:アイコン無,1:アイコン有(このLCDにはアイコンがないため0で設定)、3bit目 0:ブースターON(3.3V時),1:ブースターOFF(5V時)、
// 2〜1bit コントラスト64段階調整([Contrast set]と併せて設定))
lcdwriteCommand(0x56); //アイコン無、ブースターON(3.3Vのため)、コントラスト設定(35)([Power/ICONcontrol/Contrast set])
delay(20); //26.3μs以上待つ(「lcdwriteCommand」で10ms待つためなくてもOK)
//[Follower control] = 4bit目 0:フォロア回路OFF,1:フォロア回路ON、3〜1bit 増幅率の設定(0〜7)
lcdwriteCommand(0x6C); //フォロア回路ON、増幅度6([Follower control])
delay(500); //200ms以上待つ
lcdwriteCommand(0x38); //8ビット、2行表示、5×8ドット、通常状態(拡張コマンド入力終了)([Function set])
delay(20); //26.3μs以上待つ(「lcdwriteCommand」で10ms待つためなくてもOK)
lcdwriteCommand(0x01); //LCD液晶文字クリア([Clear Display])
delay(20); //26.3μs以上待つ(「lcdwriteCommand」で10ms待つためなくてもOK)
//[Display ON/OFF] = 3bit目 0:文字表示OFF(RAM内のデータはそのまま),1:文字表示ON、2bit目 0:カーソル表示OFF,1:カーソル表示ON、
// 1bit目 0:ブランク表示OFF,1:ブランク表示ON
lcdwriteCommand(0x0F); //文字表示ON、カーソル表示ON、ブランク表示ON([Display ON/OFF])
delay(20); //26.3μs以上待つ(「lcdwriteCommand」で10ms待つためなくてもOK)
}
/* LCD初期化 */
以下から各ブロックの説明をしていきます。
①ライブラリ定義、グローバル変数設定等
#include<Wire.h> //I2C通信ライブラリ
#define LCD_ADRS 0x3E //I2Cアドレス
String overflow = "Over flow of characters";
「#include<Wire.h>」:I2C通信を実施する各関数を使用する場合に必要な定義となるので記載が必要
「#define LCD_ADRS 0x3E」:I2C通信をする際には通信したいLCDのスレーブ値を定義
「String overflow = "Over flow of characters"」:LCDに表示できる文字数は32byteのため、それ以上のデータがきた際に表示させる変数を用意
②初期設定
/* 初期設定 */
void setup() {
Serial.begin(115200); //シリアル通信開始(通信レート:115200bit/s)
Wire.begin(); //I2C通信開始(ESP32をマスター)
init_LCD(); //LCD初期化
lcdwriteData("Booting now..."); //LCDへ起動中の表示
/* ここに起動時に確認する項目を記載 */
delay(5000);
/* ここに起動時に確認する項目を記載 */
//「Set DDRAM Address」= 1番目:0x○○(液晶表示入りのアドレス値)、2番目:0x80(機械コード「Set DDRAM Address」)
lcdwriteCommand(0x40+0x80); //2行目へカーソル移動(0x40 = 2行目先頭文字アドレス、0x80 = DDRAMアドレスへの移動)
lcdwriteData("OK"); //起動OKの表示
delay(3000);
lcdwriteCommand(0x01); //LCD液晶文字クリア([Clear Display])
lcdwriteData("S_Data Wating"); //シリアルデータ入力待ちの表示
}
/* 初期設定 */
「Serial.begin(115200)」:USB経由でのPCとのシリアル通信レートを115200bit/sで通信開始
「Wire.begin()」:I2C通信をESP32をマスターとして接続 ※()内にアドレスを入力するとスレーブとして接続する。
「init_LCD()」:LCDを初期化して各表示設定を行う関数 ← 後ほど詳しく説明する
「lcdwriteData("Booting now...")」:起動中の文字をLCDへ表示させる ※lcdwriteData()は文字を表示させる関数 ← 後ほど詳しく説明する
「delay(5000)」:5秒間待機する。もし、この間にバックグラウンドで何か初期処理を実行させる場合にはここに記載する。
「lcdwriteCommand(0x40+0x80)」:LCDの2行目へカーソル移動(0x40 = 2行目先頭文字アドレス、0x80 = DDRAMアドレスへの移動) ※lcdwriteCommand()はLCDの設定データを送信する関数 ← 後ほど詳しく説明する
「lcdwriteData("OK")」:移動した2行目にOKの文字を表示
「delay(3000)」:3秒待機
「lcdwriteCommand(0x01)」:LCD液晶に表示された文字をクリア ※LCDの機械コード=「Clear Display」
「lcdwriteData("S_Data Wating")」:クリアされた液晶画面の先頭にシリアルデータ入力待ちの状態であることを表示
③メイン(ループ)
/* メイン */
void loop() {
if (Serial.available() > 0) { // 受信したデータが存在する時
String serial_data = ""; //格納文字列を初期化
serial_data = Serial.readStringUntil('\n'); //[\n]がくるまで受信データを格納
lcdwriteCommand(0x01); //LCD液晶文字クリア([Clear Display])
lcdwriteData("S_Data Recved"); //シリアルデータ受信の表示
lcdwriteCommand(0x40+0x80); //2行目へカーソル移動
lcdwriteData(serial_data); //受信データを表示
}
}
/* メイン */
「if (Serial.available() > 0)」:Serial.available()がシリアルポートに受信されたデータバイト数を返す関数なので、受信データが1バイト以上あればif文内の処理を実行
「String serial_data = ""」:受信した文字を格納する変数を初期化
「serial_data = Serial.readStringUntil('\n')」:改行(LF:\n)の入力以前までの文字を変数に格納
例:「1234」と入力した後に「送信」を押すと、ESP32に送信されるデータとしては、「1234\n」となるので上記関数を利用すると\n以前までの値「1234」を読み込む
「lcdwriteCommand(0x01)」:LCD液晶に表示された文字をクリア ※LCDの機械コード=「Clear Display」
「lcdwriteData("S_Data Recved")」:シリアルデータを受信した旨の表示
「lcdwriteCommand(0x40+0x80)」:LCDの2行目へカーソル移動
「lcdwriteData(serial_data)」:PCから入力された文字を2行目に表示
④データ書き込み(複数byte)
lcdwriteData()関数は、PCからのシリアル通信での入力文字をLCDに表示させる。
また、LCDの2行目に入力文字を表示するようにしており、16文字を超える文字数がきた場合は「Over flow of characters」、それ以下の場合はシリアル通信にて受信した文字を表示させるようにしている。
/* データ書き込み(複数byte) */
void lcdwriteData(String t_data){
int m_count = t_data.length(); //送信データの文字数をカウント
if(m_count>16){ //データ数が16バイトを超えたら
lcdwriteCommand(0x01); //LCD液晶文字クリア([Clear Display])
//全文字を表示
for(int i=0;i<23;i++){
if(i==16){ //17文字目に到達したら
lcdwriteCommand(0x40+0x80); //2行目へカーソル移動
}
Wire.beginTransmission(LCD_ADRS); //LCDをI2Cスレーブとして通信開始([slave address])
Wire.write(0x40); //最終データ、LCDへの表示データ([control byte] = 8bit目 0:最終,1:連続、7bit目 0:命令,1:表示データ)
Wire.write(overflow[i]); //1文字をキューに格納
Wire.endTransmission(); //キューに格納されたデータを送信してI2C通信停止
}
}else{
//全文字を表示
for(int i=0;i<m_count;i++){
Wire.beginTransmission(LCD_ADRS); //LCDをI2Cスレーブとして通信開始([slave address])
Wire.write(0x40); //最終データ、LCDへの表示データ([control byte] = 8bit目 0:最終,1:連続、7bit目 0:命令,1:表示データ)
Wire.write(t_data[i]); //1文字をキューに格納
Wire.endTransmission(); //キューに格納されたデータを送信してI2C通信停止
}
}
}
/* データ書き込み(複数byte) */
「int m_count = t_data.length()」:入力文字の文字数を変数に格納
「Wire.beginTransmission(LCD_ADRS)」:LCDをスレーブとして通信を開始(STARTビット送信) ※()内にスレーブ側のアドレスを入力
「Wire.write(0x40)」:スレーブ側に送信する1バイトのデータ(最終、LCDへの表示データ)をキューに格納 ※LCDの機械コード=「control byte」
「Wire.write(overflow[i] or t_data[i])」:1文字をスレーブ側に送信するキューへデータを格納
「Wire.endTransmission()」:キューに格納されたデータをスレーブ(LCD)に送信してI2C通信を停止(STOPビット送信)
⑤コマンド書き込み(1byte)
lcdwriteCommand()関数は、LCDの各種設定を行う場合に送信する機械データコマンドです。
LCDへの書き込み順はデータシートに記載されており、[slave address] + [control byte] + [data byte]の順番で送信する。
/* コマンド書き込み(1byte) */
void lcdwriteCommand(byte t_command){ //(書き込み順 = [slave address] + [control byte] + [data byte] )
Wire.beginTransmission(LCD_ADRS); //LCDをI2Cスレーブとして通信開始([slave address])
Wire.write(0x00); //最終データ、LCDへの命令([control byte] = 8bit目 0:最終,1:連続、7bit目 0:命令,1:表示データ)
Wire.write(t_command); //命令文を送信([data byte])
Wire.endTransmission(); //I2C通信停止
delay(10); //10ms待つ
}
/* コマンド書き込み(1byteずつ) */
「Wire.beginTransmission(LCD_ADRS)」:LCDをスレーブとして通信を開始(STARTビット送信) ※()内にスレーブ側のアドレスを入力
「Wire.write(0x00)」:スレーブ側に送信する1バイトのデータ(最終、LCDへの命令データ)をキューに格納 ※LCDの機械コード=「control byte」
「Wire.write(t_command)」:各種機械データコマンドをスレーブ側に送信するキューへデータを格納 ※LCDの機械コード=「control byte」
「Wire.endTransmission()」:キューに格納されたデータをスレーブ(LCD)に送信してI2C通信を停止(STOPビット送信)
「delay(10)」:命令データを送信した場合は一定時間待つ必要がある(1コマンドにつき26.3μs〜200ms) ※基本は26.3μs
⑥LCD初期化
lcd_init()関数は、LCDの初期化および初期設定を実施する関数である。
/* LCD初期化 */
void init_LCD(){
delay(100); //電源ON後40ms以上待つ
//[Function set] = 5bit目 0:4ビット,1:8ビット、4bit目 0:1行,1:2行、3bit目 フォントサイズ(0でOK)、1bit目 0:通常状態,1:以降拡張コマンド入力
lcdwriteCommand(0x38); //8ビット、2行表示、5×8ドット、通常状態([Function set])
delay(20); //26.3μs以上待つ(「lcdwriteCommand」で10ms待つためなくてもOK)
lcdwriteCommand(0x39); //8ビット、2行表示、5×8ドット、以降拡張コマンド入力([Function set])
delay(20); //26.3μs以上待つ(「lcdwriteCommand」で10ms待つためなくてもOK)
//[Internal OSC frequency] = 4bit目 0:1/5バイアス電圧,1:1/4バイアス電圧、3〜1bit フレーム周波数[Hz]
lcdwriteCommand(0x14); //1/5バイアス電圧、フレーム周波数183[Hz]([Internal OSC frequency])
delay(20); //26.3μs以上待つ(「lcdwriteCommand」で10ms待つためなくてもOK)
//[Contrast set] = 4〜1bit コントラスト64段階調整([Power/ICONcontrol/Contrast set]と併せて設定)
lcdwriteCommand(0x73); //コントラスト設定([Contrast set])
delay(20); //26.3μs以上待つ(「lcdwriteCommand」で10ms待つためなくてもOK)
//[Power/ICONcontrol/Contrast set] = 4bit目 0:アイコン無,1:アイコン有(このLCDにはアイコンがないため0で設定)、3bit目 0:ブースターON(3.3V時),1:ブースターOFF(5V時)、
// 2〜1bit コントラスト64段階調整([Contrast set]と併せて設定))
lcdwriteCommand(0x56); //アイコン無、ブースターON(3.3Vのため)、コントラスト設定(35)([Power/ICONcontrol/Contrast set])
delay(20); //26.3μs以上待つ(「lcdwriteCommand」で10ms待つためなくてもOK)
//[Follower control] = 4bit目 0:フォロア回路OFF,1:フォロア回路ON、3〜1bit 増幅率の設定(0〜7)
lcdwriteCommand(0x6C); //フォロア回路ON、増幅度6([Follower control])
delay(500); //200ms以上待つ
lcdwriteCommand(0x38); //8ビット、2行表示、5×8ドット、通常状態(拡張コマンド入力終了)([Function set])
delay(20); //26.3μs以上待つ(「lcdwriteCommand」で10ms待つためなくてもOK)
lcdwriteCommand(0x01); //LCD液晶文字クリア([Clear Display])
delay(20); //26.3μs以上待つ(「lcdwriteCommand」で10ms待つためなくてもOK)
//[Display ON/OFF] = 3bit目 0:文字表示OFF(RAM内のデータはそのまま),1:文字表示ON、2bit目 0:カーソル表示OFF,1:カーソル表示ON、
// 1bit目 0:ブランク表示OFF,1:ブランク表示ON
lcdwriteCommand(0x0F); //文字表示ON、カーソル表示ON、ブランク表示ON([Display ON/OFF])
delay(20); //26.3μs以上待つ(「lcdwriteCommand」で10ms待つためなくてもOK)
}
/* LCD初期化 */
電源ON後は40ms以上待つ必要があり、それ以降に順次設定をしていく。
各種設定順
1.「Function set」:0x38(8ビット、2行表示、5×8ドット、通常状態)
2.「Function set」:0x39(8ビット、2行表示、5×8ドット、以降拡張コマンド入力)
3.「Internal OSC frequency」:0x14(1/5バイアス電圧、クロック周波数100kbit/s(標準))
4.「Contrast set」:0x73(コントラスト設定)
5.「Power/ICON/Contrast control」:0x56(アイコン無、ブースターON(3.3Vのため)、コントラスト設定(35))
6.「Follower control」:0x6C(フォロア回路ON、増幅度6)
7.「Function set」:0x38(8ビット、2行表示、5×8ドット、通常状態(拡張コマンド入力終了))
8.「Clear Display」:0x01(LCD液晶文字クリア)
9.「Display ON/OFF control」:0x0F(文字表示ON、カーソル表示ON、ブランク表示ON)
#今回使用したLCDデータ送信コマンド一覧
コマンド名称 | 設定内容 | ビット配置 | 各ビット説明 |
---|---|---|---|
Function set | ディスプレイの各種機能 | 0,0,1,DL,N,DH,0,IS | 「DL(扱いデータ):0 4ビット,1 8ビット」,「N(表示行):0 1行,1 2行」,「DH(フォントサイズ):0 5×8ドット,1 5×16ドット(N=0) 入力禁止(N=1)」,「IS:0 通常状態,1 拡張コマンド入力待ち状態」 |
Internal OSC frequency | バイアス電圧,フレーム周波数 | 0,0,0,1,BS,F2,F1,F0 | 「BS(バイアス値):0 1/5,1 1/4」,「F2〜0:フレーム周波数[Hz] *表1」 |
Contrast set | コントラスト | 0,1,1,1,C3,C2,C1,C0 | 「C3〜0:コントラスト64段階設定([Power/ICONcontrol/Contrast set]と併せて設定)」 |
Power/ICON/Contrast control | アイコン,ブースター回路,コントラスト | 0,1,0,1,Ion,Bon,C5,C4 | 「Ion(このLCDはアイコンないため常時0に設定):0 無,1 有」,「Bon(ブースター回路):0 ON(3.3V時), 1 OFF(5V時)」,「C5〜4:コントラスト64段階設定([Contrast set]と併せて設定)」 |
Follower control | フォロア回路,増幅率 | 0,1,1,0,Fon,Rab2,Rab1,Rab0 | 「Fon(フォロア回路):0 OFF,1 ON」,「Rab2〜0(増幅率):7段階調整」 |
Clear Display | 液晶文字クリア | 0,0,0,0,0,0,0,1 | - |
Display ON/OFF control | ディスプレイ表示,カーソル表示,カーソル位置表示 | 0,0,0,0,1,D,C,B | 「D(ディスプレイ表示):0 OFF,1 ON」,「C(カーソル表示):0 OFF,1 ON」,「B(カーソル位置表示):0 OFF,1 ON」 |
Set DDRAM Address | 液晶位置移動 | 1,AC6,AC5,AC4,AC3,AC2,AC1,AC0 | AC6〜0:液晶位置 *表2 |
#次回記事予定
ESP32にてWiFi接続を実施してその状態をLCDに表示できるプログラム作成
#参考文献
・http://akizukidenshi.com/catalog/g/gK-08896/
・http://www.lapis-semi.com/jp/common/miconlp/tips/2-17-i2c%E3%81%A8%E3%81%AF/