AQMシリーズのI2C接続LCDキャラクタ・ディスプレイを使う(1)微妙なSCLクロック
■ArduinoとRaspberry Piの両方で動くLCD
今まで、I2C接続キャラクタLCDモジュール16×2行(ACM1602NI-FLW-FBW-M0)を使用していました。しかしこのモジュールは電源電圧が3.3Vで、まだ5V電源の多いArduino と接続するときはI2Cバスの電圧レベル変換が必要です。また、Arduinoでは特に問題なく動作していますが、Raspberry PiとのI2Cでは動作できませんでした。
I2CバスのクロックSCLはマスタが出します。マスタにArduino Pro(電源は3.3V)を使った場合、I2CバスのSCL信号のLOW出力の様子を確認しました。規格ではLOWと判断されるVilは0.3Vddですから0.99Vになり、このLOWレベルの時間は5.1μsくらいです。波形の立ち上がりが緩やかなので、LOWがもっと高い電圧で判断されたら、5.1μsより長いと想定されます。
次のオシロスコープの図は、Raspberry Pi 3 model Bを使ったとき、I2Cバスの信号です。LOWレベルの期間(0.99V)は5.0μsとなっています。波形の立ち上がりは急峻なので、LOWがもっと高い電圧で判断されても5.1μs以下でしょう。
I2Cの規格ではSCLのLOW レベルの最小値は4.7μsです。どちらのマイコンも規格を満たしています。
けれど、ACM1602はSCLのLOW期間の最小値を5.2μsとしています。そのため、ACM1602はRaspberry Piからクロックが送られてきているのにもかかわらず短いと判断して無視するため、Raspberry PiのI2Cバスからの自分あてのアドレスが読み取れず、ACKが返せなく通信ができないと想像しています。Raspberry PiのI2Cクロックを、デフォルトの100kHzからほんのわずかに下げると、クロックSCLのLOWの時間が5.2μs以上になって通信できそうです。
ArduinoのほかにRaspberry Piも利用するようになったので、両方で利用できるものを探すことにしました。次に示すAQMシリーズのI2C接続キャラクタLCDモジュールを利用することにします。
●AQMシリーズのキャラクタLCDモジュール
◆3.3V、5V電源で利用できるがI2Cバスのドライブ能力が小さい
AQMシリーズのキャラクタLCDモジュールは、薄型の3.3V、5Vの電源で動作できArduinoでもRaspberry Piでも利用でき便利です。3.3V電源で1mAの低消費電力のLCDですが、そのためI2Cバスのドライブ能力が少なく、±1mAとI2Cバスのプルアップ抵抗が3kΩ以下だと、LOWのドライブができないことが生じます。
◆コントラストの調整、電源電圧の設定がコマンドによる
PCなどとのインターフェースはI2Cですが、LCDモジュールの駆動のためのコマンドなどはパラレル・接続で使ったコントローラHD44780と同じです。ただしコントラストの調整や電源電圧の設定もコマンドで行うようになっていて、そのためのコマンドの拡張が行われています。具体的には、初期化のためのプログラムを作成するところで説明します。
◆それぞれ変換基板が用意されている
AQM1602とAQM0802の本体は、9ピンのハーフ・ピッチ(1.28mm)の端子が出ています。信号などの種類と並びは同じです。しかし、ピン番号の振り方は反対になっています。そのため、ピン番号でなくピン名で確認する必要があります。
実際にブレッドボードで利用するためにはピン・ピッチの変換基板を取り付ける必要があります。変換基板からはAQM1602はSCL、SDA、プラス電源、GNDの4端子、AQM0802はリセット端子が追加された5端子となっています。
AQM0802の場合、リセットの必要な場面があまり想定されないので電源に接続して、ArduinoとはSCL、SDA、プラス電源、GNDの4ラインで結びます。
AQM1602とAQM0802の端子の並び方は、基板の裏表の関係になっていて、表示面からみると端子の並びが反対になります。配線するときにはよく確認してください。
●型番のAE
AE-AQM1602A、AQW1602、AE-AQM0802、AQM0802と型番にAEがついているものとそうでないものがあります。AEのついているものがピッチ変換基板の付加されたキットです。ブレッドボードでは変換基板が不可欠ですから、変換基板付きを準備します。
●AQM1602、AQM0802への送信データ
◆I2Cアドレス
AQM1602、AQM0802は共にI2Cのスレーブ・アドレスは0x3Eです。その他の設定を行う方法は見つかりませんでした。そのため、同一バスには1台のモジュールしか接続できません。
◆モジュールとの通信
LCDモジュールとの通信は、書き込みだけで、読み取りは行えません。基本的なコマンドはHD47780のコマンドを引き継いでいます。初期化コマンドの不定だったコマンドのD0ビットをISビットとして、コマンドの拡張のために利用しています。HD47780のコマンドのときはこのISビット0にします。拡張コマンドを利用する場合はISビットを1にして初期化コマンドを書き込み、拡張コマンドを書き込みます。
拡張コマンドでは内部回路の周波数を設定し、電源電圧、内部の電源ブースタ回路、コントラストの設定を行います。その後、拡張モードを終了するためIS=0にした初期化コマンドを送信します。
リファレンスのページにHD47780互換のコマンドのリファレンスを用意しました(制作中)。そちらも参照してください。
<1> 初期化コマンドを送信 0x38 8ビット・バス、2行表示、5×7フォント <2> 拡張設定した初期化コマンドを送信 0x39 IS=1に設定(拡張コマンド入力を行う) <3> 拡張コマンド内部周波数の設定 0x14 <4> 拡張コマンドでコントラストの設定 0x73 <5> 拡張コマンドで電源、コントラストを設定する。 0x56 <6> 拡張コマンドでフォロアをON 0x6C このコマンドを書き込んだ後200ms以上の時間を待つ <7> 初期化コマンドで拡張コマンドの処理から戻る 0x38 IS=0に設定(拡張コマンドの処理を終える) <8> ディスプレイのON/OFFコマンドで表示をON 0x0C <9> 表示クリア・コマンドを実行して初期化を終える 0x01 |
●LCDモジュールへの書き込み処理
LCDモジュールへの書き込みは、基本的には次の3バイトで行います。
アドレス・データ | 1バイト目 | アドレス+R/Wビット 0x7C | |
制御データ | 2バイト目 |
Coビット(D7) |
|
データ・バイト | 3バイト目 | コマンドまたは表示データ |
Co=1の場合、データ・コマンドの後に次の制御データを送ることができます。しかし今回はこの機能を使用しないので、制御データはコマンド0x00と表示データの0x40の2種類となります。
今回使用しているモジュールの制御チップのST7032iは、アンテナなどのアイコンの制御のための拡張コマンドが用意されています。しかし、今回のモジュールにはアイコンの機能がないので利用しません。
●Arduinoのプログラム
ArduinoのAQMシリーズのプログラムは、次の記事でACM1602のI2C接続キャラクタLCDモジュール用に作成したプログラムの一部を修正して利用しました。
「ArduinoのI2C接続キャラクタLCDモジュール用の関数を作る(1)
コントローラHD47780を操作 」
修正個所は次の3か所です。
(1) スレーブ・アドレスを 0x50を0x3E に変更
(2) コマンド・レジスタ、表示データの選択ビットがACM1602ではD7ビットで表示データを書き込む場合は0x80を書き込んでいました。AQMシリーズではこの選択はD6ビットなので、0x80の代わりに次に示すように0x40に変更します。コマンド・レジスタを選択するときはどちらも0x00なので、変更しません。
int i2cwritedata(byte data) { Wire.beginTransmission(lcd_address); Wire.write(0x40); Wire.write(data); return Wire.endTransmission(); } |
(3) 初期化処理
AQMシリーズでは、拡張コマンドで内部周波数、電源電圧、コントラストなどの設定が次のように追加されます。黄色部分が拡張命令による設定です。
void init_lcd() { delay(145); i2cwritecmd(0x38); delay(1); i2cwritecmd(0x39); delay(1); i2cwritecmd(0x14); // 内部周波数の設定でサンプルに従った delay(1); i2cwritecmd(0x73); // コントラストの下位4ビット設定値サンプルを参考 delay(1); i2cwritecmd(0x51); // Unoで5V電源で稼働しているのでブースタをOFF Bon=0 // 3.3V電源の時はオンでBon=1 0x56 delay(2); i2cwritecmd(0x6C); // Fon=1 Follower回路をONにする delay(300); i2cwritecmd(0x38); delay(1); i2cwritecmd(0x01); delay(2); i2cwritecmd(0x0C); delay(2); } |
この初期化の関数は、Unoなどの5V電源のArduinoに対応するものです。3.3Vを利用するときの値もコメント記入しておきました。
●テスト・プログラム
このテスト・プログラムは、ACM1602用に作成したものと同じですので、内容の確認はそちらも参照して確認してください。
#include <Wire.h> unsigned char lcd_address = 0x3E; int n = 0; int i2cwritecmd(byte cmd) { Wire.beginTransmission(lcd_address); Wire.write(0x00); Wire.write(cmd); return Wire.endTransmission(); } int i2cwritedata(byte data) { Wire.beginTransmission(lcd_address); Wire.write(0x40); Wire.write(data); return Wire.endTransmission(); } void lcdcu_set(int x, int y) { byte ca = (x + y * 0x40) | (0x80); i2cwritecmd(ca); } void lcdclear() { i2cwritecmd(0x01); delay(1); } void lcdhome() { i2cwritecmd(0x02); delay(1); } void dsift_l() { i2cwritecmd(0x1C); } void dsift_r() { i2cwritecmd(0x18); } void init_lcd() { delay(145); i2cwritecmd(0x38); delay(1); i2cwritecmd(0x39); delay(1); i2cwritecmd(0x14); delay(1); i2cwritecmd(0x73); delay(1); i2cwritecmd(0x51); delay(2); i2cwritecmd(0x6C); delay(300); i2cwritecmd(0x38); delay(1); i2cwritecmd(0x01); delay(2); i2cwritecmd(0x0C); delay(2); } void i2cprint( String pdata) { int n = pdata.length(); for (int i = 0; i < n; i = i + 1) { i2cwritedata(pdata.charAt(i)); delay(1); } } void setup() { Wire.begin(); init_lcd(); } void loop() { i2cprint(":"); n = n + 1; i2cprint(String(n)); delay(1000); } |
実行結果を次に示します。
同じプログラムをAQM0802をセットして実行、表示桁が8桁になっただけで同じ結果を得ました。
拡張コマンドについてはサンプルに従って設定しました。次回拡張コマンドについて確認します。
(2016/10/26 V1.0)
(2017/7/20 V1.0) 中ほどで説明している「i2cwritecmd(0x6C); // Fon=1 Follower回路をONにする 」の16進コード0x64が間違っていたので、0x6cに訂正。
<神崎康宏>
バックグラウンド
HIGH、LOWの規格値;
LOW 電源電圧3.3V ×0.3 = 0.99V 0.99V以下になると、LOWと認識します。
HIGH 電源電圧3.3V × 0.70 = 2.31V 2.31V以上になるとHIGHになります。
0.99Vから2.31Vの遷移域は、ヒステリシスをもつので前歴の状況に変わり、立ち上がりの緩やかなArduinoでは、2V以上になるまでLOWが維持されると思われます。
電源電圧は∓5%程度は変動するので、最悪な条件でも確実にLOWとHIGHを規格以内に入るようにすることで、確実な動作が期待できます。