ArduinoにLCDキャラクタ・ディスプレイ・モジュールを接続する(9)Wire ライブラリ
■I2CインターフェースのLCDキャラクタ・モジュールをArduinoで使う(2)Wire ライブラリを使用してLCD処理の関数を作る
まず、Wireライブラリを利用してLCDモジュールの初期化、文字の表示、表示位置の制御などを行うことのできる関数を作ります。
●Wireライブラリを利用するための準備
Wireライブラリを利用するために、まずWireライブラリを読み込みます。Wireライブラリのヘッダ・ファイルWire.hをインクルードするために、次の記述を最初に書き込みます。
#include <Wire.h> |
次にLCDモジュールのI2Cのアドレスを定義します。LCDモジュールのI2Cのスレーブアドレスは、データシートより0x50です。
byte lcd_address = 0x50; void setup() { |
setup()関数の中にWire.begin(); を記述すると、具体的な通信の命令を書いたり、I2Cの処理の関数が作れるようになります。
●Wireライブラリでスレーブにデータを書き込む
スレーブ・デバイスへの書き込みは、最初にWire.beginTransmission(address)を実行します。この命令でArduinoからaddressで指定しているスレーブ・デバイスへの書き込み処理を開始します。具体的なデータの書き込みはWire.write(value)でバイト単位のデータ伝送を行います。Wire.write(vlue)関数で必要なデータ伝送を行った後は、Wire.endTransmission()で伝送を終わります。0x00のデータをデバイスに送信するには、次のようになります。
Wire.beginTransmission(lcd_address); Wire.write(0x00); Wire.endTransmission(); |
LCDモジュールとの通信は、LCDへの書き込み処理だけ済ますこともできます。今回はこの書き込み処理をもとに必要な関数を作ります。
●LCDモジュールへのコマンド、データの書き込みの関数を作る
ここでは、I2Cのインターフェース経由でコマンドとデータを書き込みます。この送信メッセージは3バイトのメッセージで行います。
- 1番目 アドレスのバイト
- 2番目 コマンドかデータかを示すバイト
- 3番目 コマンドまたはデータのバイト
◆最初のバイト
最初のバイトはスレーブ・アドレスで、I2CLCDのアドレスで0x50を指定します。このメッセージは、Wire.beginTransmission(address)の関数で生成されます。引数で指定されたアドレスをもとに、WireライブラリがR/Wのビットを追加して実際の送信バイトを構成します。
◆2バイト目
2バイト目は、3バイト目の値がコマンド/データのどちらかを示すバイトで、コマンドの場合は0x00、データの場合は0x80となります。
◆3バイト目
3バイト目はコマンドまたは送信データをセットします。
I2Cからのデータを受信してモジュール内のPICは2バイト目のデータをもとにR/Sの信号を制御し、次に3バイト目のデータを書き込みます。
コマンドの書き込みはi2cwritecmd()関数として、具体的なコマンドを引数とする関数を作りました。スレーブ・アドレスに読み書きのビットを追加し、I2Cのフォーマットに整えI2Cのプロトコールに従った制御はWireライブラリが行ってくれるので、次の4行メッセージの送信が行えます。
◆コマンド送信関数
int i2cwritecmd(byte cmd){ Wire.beginTransmission(lcd_address); Wire.write(0x00); Wire.write(cmd); return Wire.endTransmission(); } |
Wire.endTransmission(); には戻り値があり、通信の成否がわかります。
0 通信が成功 1 バッファ・オーバフロー 2 アドレス送信時にNAKが戻る 3 データ送信時にNAKが戻る 4 その他のエラー |
◆データ送信関数
2バイト目を、コマンドの場合の0x00からデータの場合の0x80に変更して、データ送信用の関数i2cwritedata()を用意しました。
int i2cwritedata(byte data){ Wire.beginTransmission(lcd_address); Wire.write(0x80); Wire.write(data); return Wire.endTransmission(); } |
この二つ関数を組み合せて、実際のLCDの初期化、書き込み処理の関数を作ります。
●初期化の関数
初期化の処理は最初に、電源投入時の過渡期が過ぎ安定化するのを待つための関数delay(45)が用意されています。LCDモジュールの制御、データの受け渡しを8ビットとするためのコマンドを書き込みます。前段の状況によっては最初のコマンドがコマンドとして受け取られなかった可能性もあるので、再度データ・フォーマットを8ビットとする0x38のコマンドを送信します。次のデータ表示と2行表示を行うコマンド0x0Cを送信しています。
最後にLCDモジュールの表示をクリアするコマンド0x01を送信して、初期化を終えています。実際のコードは次のようになります。
void init_lcd(){ |
以上の関数を作成したあと、setup()関数でWireライブラリの初期化を行い、LCDモジュールの初期化を行い、1、2、5の数字を表示します。その後、9、7、6と数字の表示を繰り返すプログラムを次のように作成しました。
●このプログラムのコンパイル
マイコン・ボードをArduino Leonardoに設定すると、問題なくコンパイルできマイコン・ボードに書き込むことができました。マイコン・ボードをCPUがARMコアのArduino Zeroに設定すると、エラーとなりコンパイルできませんでした。
ツール>ボード>ボードマネージャー
を選択し、ボードマネージャーを起動し各ボードの対応プログラムのバージョンを確認しました。
Arduino M0のためのArduino SAM Boardのバージョンが1.6.1でした。最新の1.6.6をインストールしました。
バージョン1.6.6にバージョンアップ後はエラーなくコンパイルできました。しかしArduino M0はArduino.orgの製品で、Arduino IDE1.6.9ではボードに書き込めませんでした。
●Arduino.orgの最新のIDEでコンパイルする
Arduino M0はArduino.orgの製品でArduino IDE1.7.10が対応しています。しかし、今回のプログラムは次に示すようにLeonardoではエラーなくコンパイルできます。
同じプログラムを、ボードの設定をArduino M0に設定してコンパイルすると、次に示すように早い段階でエラーとなりコンパイルもできません。
Arduino.ccのArduino IDEのようなボードマネージャーもなく、対応に困ってしまいました。
そのため、秋月電子通商から販売されている、I2C電圧レベル変換モジュールAE-PCA9306を利用し、5V電源のArduino Leonardoと3.3V電源のI2CインターフェースLCDモジュールを接続しました。
次に示すのは、Arduino Leonardoに、
- AE-PCA9603モジュール
- 電流8A対応のソリッド・ステート・リレー
- K型熱電対温度センサ・モジュール・キット(MAX31855)
を利用して、電熱で加熱する鍋の温度をコントロール・システムの表示装置を利用しました。
●テスト・プログラムをアップロードする
今回作成したテスト・プログラムardlcd7020.insをアップロードした結果を、次に示します。最初の125は、setup()関数の中で表示されています。その後、976をloop()関数の中で繰り返して表示されます。
今回ブレッドボードにセットしたLCDモジュールとも接続して、次に示すように同様な結果が得られました。
手元の3.3V電源のArduino M0を利用したI2Cのプログラムを、2016/5/10日現在アップロードすることができません。
一刻も早く、Arduinoの開発環境が一本化されることを願っています。
次回からは、3.3V/5V両方に対応している「I2C接続小型キャラクタLCDモジュール(16×2行・3.3V/5V)ピッチ変換キット」や「I2Cバス用双方向電圧レベル変換モジュール(PCA9306)」を利用して5V電源のArduino Uno、Leonardoなどを使ってテストを続けます。
(2016/6/10 V1.0)
(2016/6/10 V1.1)LCDのアドレスの変数名に2通りあったので、lcd_addressに統一した。
<神崎康宏>
バックグラウンド
3.3V/5V;Arduino UnoとArduino LeonardoはCPUが5V、I/Oも5V対応です。Arduino M0のCPUは3.3Vで、I/OはUSBを除いて3.3Vで動きます。