LCD1602と言う表示器(下図)があるのですが、4ビットバスと制御信号で結構I/Oポートを使うのと、16桁×2行しか表示できなくて、ちょっと使い難いんですね。
そこで、Arduino Microを使って、I2C経由でデータを表示可能な「LCDサブシステム」ってものを作って、使っていたのですが、前回、肝心なMicroが壊れてしまったのです(1;_;)。
と、言う事で、急遽、LCDサブシステムを何とかしなくてはならない事態となってしまいました。
LCDサブシステムと言うのは、こんな感じの物(下図、外観)です。
概略図は以下の様になります。
※元々、システムを設計した「#3-3 I2C接続 LCD表示モジュール」でも解説してありますので、併せてご覧ください。
LCD1602の制御と、ユーザインターフェイス(U/I)としてのロータリエンコーダ制御に、Arduinoを1機丸々使った贅沢な装置です。I2Cで他の機器(I2Cマスタ)と接続して、I2C経由で受信した文字列を、LCD1602に表示します。
最大の特徴は、バックログを持っていることです。I2Cマスタから受信したデータをLCD1602に表示すると同時に、メモリが許す限り、バックログに記憶して行きます。バックログに記憶したデータは、U/I(ロータリエンコーダ)を使って、まるでマウスのホイールを操作している感覚で、上下・左右に表示領域をスクロールして閲覧することが可能となっています(下図)。
※U/Iの操作方法はこちらをご参照ください。
こうして見ると、16桁×2行の表示領域って狭すぎるでしょ?日付と時刻くらいの表示には十分だけれども、ちょっと長いセンテンスを表示しようとすると、あふれて見えなくなってしまいます。その点、バックログがあると、後から戻って閲覧できるので、便利なのです。
最大で40桁×48行のバックログを有しています。ただし、Arduino(ATmega328)のRAMは2Kバイトしかないため、40×48=1,920バイトのデータは、記憶できません。……スタックとか、自動変数とかで使用する領域も必要だからね。
つまり、行内の文字数次第で、バックログに格納可能な文字数・行数は変動します。新たな1行を受信した時点でメモリが獲得できないと、その行にはASCIIコードで0xffの文字が表示されて、受信したデータは消失してしまいます。
メモリ不足にならない限り、最新48行の受信データをバックログに記憶しており、U/Iを使ってバックログを自由に閲覧可能です。その状態で新たに1行受信すると、一番古いデータ(行)から順に消えて行きます。
※MicroのATmega32U4には、ATmega328よりも大きな2.5KバイトのRAMが搭載されているので、メモリ不足で表示できなくなる危険性は少し低かったのですが……。
I2Cスレーブとなって、LCD1602の制御を担うArduinoとして、元々、UNOとMicroでコンパイルできる様にプログラムしていたのだけど、今回使うPro Miniでは、#errorでコンパイルエラーが出るようになっていました。
#ifndef ARDUINO_AVR_UNO
#ifndef ARDUINO_AVR_LEONARDO
#error "USE_ASM needs Arduino UNO/LEONARDO board configuration."
#endif // ARDUINO_AVR_LEONARDO
#endif // ARDUINO_AVR_UNO
Pro Mini(下図)はUNOと同じATmega328を搭載しており、ほぼUNOと同じなので、#errorを出しているところだけ変更すれば、そのまま使えます。
Pro Miniのプリデファインマクロは、ARDUINO_AVR_PROですので、この#ifdefを追加しても良いのですが、もう少し根元に遡って、MCUの型番で判定する様に変更しました(以下コード)。
#ifndef __AVR_ATmega328P__
#ifndef __AVR_ATmega32U4__
#error "USE_ASM needs AVR ATmega328P or ATmega32U4."
#endif // __AVR_ATmega32U4__
#endif // __AVR_ATmega328P__
Pro Miniでのコンパイル&書き込みが可能となり、LCDサブシステムの動作を確認済みです。
逆に、Microでの動作確認は、筆者手元では不可能となってしまったため、Microで動作するかどうかは、わからなくなってしまいました。Micro用の場合分け(#ifdef)コードは、そのまま残しておきますが、そんな状況ですのでご注意ください。
さて、良い機会なので、前からちょっと「対策したいな〜」と思っていたところを改定しておきたいと思います。
そんなこんなで、I2Cスレーブ側の機能改定は解説するまでも無いので、ライブラリの整備について、もう少し詳しく解説してみます。
Serialクラスの様な、クラスライブラリを作りました。以下、LcdSubsysライブラリの仕様です。
LcdSubsysライブラリ仕様:
準備は整いましたので、Arduino Pro Miniを使った、I2Cスレーブ側のLCDサブシステムを構築していきます。回路図は「#3-3 I2C接続 LCD表示モジュール」と同じですが、再掲載しておきます(下図)。
Pro MiniはUNOとほぼ同じですので、上図のUNO側(スラッシュ(/)記号の左側)端子を使って接続します。
前節でLcdSubsysライブラリをインストールしてあれば、Arduino IDEから「ファイルメニュー ⇒ スケッチ例 ⇒ LcdSubsys ⇒ lcd_subsys.ino」と辿って、lcd_subsys.inoをPro Miniへコンパイル&書き込んでください。
これで完成ですが、LCDサブシステムは単体では特に何もしません。ただの表示器ですので。もう1機別なArduinoを起動してI2C接続(I2Cマスタ)して使用します。I2Cマスタ側のArduinoでLcdSubsysライブラリ(LcdSubsys.h)をインクルードすると、print()メソッドなどを使ってLCDに表示が可能となります。
LcdSubsysライブラリのサンプルとして、時計を表示するプログラムを作ってみました。前に、Raspberry PiとLCDサブシステムを接続して時計を作ったのだけど、そのArduino版となります。
LCD時計の仕様:
時計の実装については、「#5-9 JJYのエミュレーション」や「#9-1 喋る気温・湿度計」で解説してあります。
では、LCDサブシステムのPro Miniとは別に、もう1機Arduinoを用意して以下の様に接続しましょう。筆者はArduino UNOを使いました。
Arduino UNO | 接続 | LCDサブシステム(Pro Mini) |
5V | → | VCC |
GND | ⇔ | GND |
A5(SCL) | ⇔ | A5(SCL) |
A4(SDA) | ⇔ | A4(SDA) |
※上表では、Pro Miniの電源(5V)をUNOから供給しています。別な方法で供給する場合は接続しないでください。
プログラムは、前節でLcdSubsysライブラリをインストールしてあれば、Arduino IDEから「ファイルメニュー ⇒ スケッチ例 ⇒ LcdSubsys ⇒ lcd_clock.ino」と辿って開くことができます。lcd_clock.inoをUNOへコンパイル&書き込んでください。
以下、実験中の様子です。
実験サンプル:【 】内はシリアルモニタからの送信データ
【mset,20,04,06,23,53,50】
OK: mset,20,04,06,23,53,50
LCDサブシステムに時計が表示されましたか?以下は、Pi ZeroWで作成したLCD時計の動画です。今回のArduino版でも、ほぼ同じ表示なので、ご参考まで。
Pro MiniとLCDサブシステムまで含めて、全てUNOから電源を供給してしまったので、念のため、USB電圧・電流計(下図)でUNOを含めた全体の電流消費量を測定しておきました。結果70mAと、特に問題なしと結論しました。
逝ってしまった👼Microさんのお陰で一仕事発生してしまいました……Microさんに合掌。