皆さんこんにちは、グレキチです。
新年明けましておめでとうございます。2026年になりました。
21世紀に入って、すでに四半世紀が過ぎました。私個人は、生まれて半世紀が過ぎました😆
近年では人生100年時代と言われていますが、誰しもいつお迎えが来るかわかりませんので、今出来ることに精一杯取り組んで、後悔の無い人生にしたいですね‼️
私の今年の抱負は、そろそろ真面目に収入について考えよう、ですかね😆
ブログで扱っているようなデバイスを使った組み込み開発や、アプリ開発などのお手伝いが出来ればいいんですがねー🤔
はい、前置きはこれ位にして。
さて今回は早速なんですが、前回実装を断念したLCDモニターのI2C通信について紹介します。年末年始は実家に帰省していたのですが、この件がズーッと頭の片隅に残っていて、これを解決しないとなんか次に進めないなーと思っていたので、自宅に戻ってきてから躍起になって取り組んでいたら、想定外に早く解決出来ちゃいました。
しかしその原因は、思い込みは禁物!っていう内容だった😅ので、その辺りの反省ポイントについても書いてみたいと思います。
デバイス紹介
使用したLCDは、秋月電子通商から購入したキャラクター液晶ディスプレイモジュールキット(型番:AE-AQM0802+PCA9515)です。(下図参照)

RaspberryPi 3とか4のピンヘッドに、直接刺して使えるように作られているようでした。代表的な仕様は下表の通りです。
| 項目 | 仕様 |
|---|---|
| ディスプレイフォーマット | 8 × 2 キャラクター |
| 電源電圧 | 3.3 V (最大) |
| 消費電流 | 1 mA (最大) |
| コントロールIC | ST7032i-0D or compatible |
| インターフェイス | I2C |
| SCL クロック周波数 | 400 kHz (最大) |
| モジュールサイズ(W×H×D) | 30 × 19.5 × 5.5 mm |
| 動作温度 | – 20 〜 70 ℃ |
| バックライト機能 | 有り |
表示出来るキャラクター数が8文字×2行(8文字×1行も可)なので、今回の温度と湿度表示のように、表示したい内容が2個までや、8文字以内の短い文字列を表示したいものなどに使えます。なお、ディスプレイなので、データの書き込みコマンドしか受け付けません。したがって、AQM0802を制御するためのコード内関数はwrite関数のみになります。(read関数は使わない)
それで書き込みのプロセスですが、処理の流れは下図の通りです。
(※以下の例は、2コマンドを連続で書き込む場合で記入されています)

基本的にはSHT31と同様で、スレーブアドレスのインプットに続いて、コントロールバイトとデータバイトを各々8bitずつ入力して指示を出す方式です。連続でコマンドを入力したい場合は、Coの部分(“control byte”の第7bit)に”1″を指定するといった感じです。また、使用可能なキャラクターはマニュアルに記載のあるもの(250文字)だけになるので、表示したいキャラクターがマニュアルに記載されているか確認して、バイナリデータも上位4bitと下位4bitの記載ミスが無い様に指示を出す必要があります。このキャラクタパターンの対応表は、基本的にはASCII表に従ったものになっているようでしたが、特殊文字などは若干バイナリ指示が異なっていたので、使用の際は注意が必要です。
それと最後に、初期設定の方法についてですが、推奨の設定例がマニュアルに書いてあるので、まずはそれに則ってコードを作成すれば良いと思います。細かな設定を把握して実施したい場合には、やはりマニュアルをしっかり確認して取り組む必要があります。
前回の不具合原因の追加調査
ここでは、前回LCD表示がうまくいかなかった原因を深掘りしたことについて紹介します。
前回は初期化処理が正常に行われなかったことまでは調べてわかっていましたが、それ以上のことは手持ちのツールでは分かりませんでした。色々調べていたら、デジタル回路の内部信号を可視化出来るロジックアナライザー(以下、ロジアナ)というものがあることを知りました。
そこで、ロジアナを購入して、プログラムの実行状態を確認してみようと考えました。信頼のあるメーカーのものだと通常は数万円以上はするようで、最初は購入を躊躇ったのですが、色々検索してみると中華製の安いもの(2,000円以内で購入可)を買って使っている人が結構いて、値段の割には普通に使えるとの意見が多かったので、まずは私もそれを試してみることにしました。
購入したのはAmazonで売っていたDAOKAIというメーカーの価格1,700円位のものでした。(色々ある中のコンパチ品と思われる)
説明書などは付属していませんでしたが、実際に使ったことのある方々がネットに挙げている情報などを参考にしながら進めたら、すんなりと使うことが出来ました。なお、使用にあたってはソフトウェアが必要ですが、皆さんが紹介してくれていた“Pulseview”というオープンソースのものをインストールして使いました。ちなみに、公式?サイトはこちらになります。
ソフトウェアのインストールにあたっては、私の場合例の如く、Arm64版のUbuntuというちょっと特殊な環境設定を使っているので、 また面倒かもなと一瞬想像しましたが、“sudo apt install pulseview”で簡単にインストール出来たので、ちょっと拍子抜けしました😳
PulseViewの使用方法はユーザーマニュアルなどを読んで概ね習得できましたが、一つだけ注意点をお伝えしておくと、データキャプチャのサンプルレートを結構上げないと正確に検知してくれませんでした。私の場合は購入デバイス最大値の24MHzで設定したら、アドレスなどをきちんと正確な値で拾ってくれるようになりました。
そして、ロジアナで確認した所、動作NG時の信号検出結果は下記のようになってました。

結果から、ラズパイ(マスター)からスレーブアドレスは送信されているが、モニター側(スレーブ)がそれを受け取っておらず、さらに続くコマンドとデータも受け取ることが出来ていないことがわかりました。所謂、門前払い状態ということですかね😭
さて、この門前払い状況をどうやって打開するか・・・🤔すぐに相談できるとしたら・・・やはりChatGPTかな?😒
しかし、ChatGPTには前回も色々と情報を問い合わせてみましたが、結局解決できなかったので、正直もう使っても仕様が無いかと思いました。それならばということで、下馬評ではChatGPTよりも賢い!と言われていたGoogle Geminiを使ってみることにしました☝️
GeminiにもChatGPTに質問したのとほぼ同じ内容の文書を記入してみました。Geminiには、モードが3つあるようで、今回は特に急ぎでは無く、かつ、なるべく解決のヒントが多く抽出できるようにしたかったので、思考モードというものを使ってみました。この思考モードは返答まで少し時間(20秒位)が掛かったので、確かに思考しているような感じを受けました。(そういう味付けなのか?)
返答内容を確認すると、ChatGPTが返してきた内容と同じような内容ではあったものの、一つだけ違ったものがあることに気がつきました。そこにはこう記載されていました。
“AQM0802にはRESET端子が設けられていると思いますが、LCDでの信号の受信にあたってはRESET端子はHighに設定されている必要があります。そうでなければ、LCDはコントロールコマンドを受け付けません。通常、RESET端子は組合せ基盤のVCCに接続することが多いですが、そうでない場合は、接続したポートでプルアップに設定しておく必要があります。そのように設定されていますか?”
なっ、何ですと‼️😱
そんなことマニュアルに書いてあったかなー、と思いつつ、持っていた資料を今一度詳しく読んでみたら・・・、有りました😭 以下、使い方5のところです。

やってしまいました😢
マニュアルに従った回路になっていないので、そりゃまともに動くはずないですよね😩
それで、RESET端子をPicoの3V3端子に接続しなおして、サンプルコードを書き込んでみると・・・

しっかりと“A”の文字が表示されました😭 やっと出来たぁーー!!っと、報われた瞬間でした🎉
それと、ロジアナを使って実行状態の信号確認も実施しました。結果は、以下の3図の通りです。(上から順に、最初のコマンド入力部のモニター結果の拡大、初期化処理1の結果、初期化処理2の結果、になります)



初期化処理全てにおいて、エラー無くコマンドがきちんと入力されていることがわかります。
しかし、一点だけ腑に落ちないことが有りまして。
以前の回路(RESET端子はVDD接続では無く、GPIO15に設定)で、MicroPythonで実装した時は、問題なく文字が表示されたのは何故だろうか🤔という点です☝️
これについて掘り下げてみた所、以下のことがわかりました。
1. MicroPythonのコードでは、そもそもGPIO15を操作するコードは一切書かれていない
2. ラズパイPicoのGPIOポートは、初期状態ではHi-Zもしくはプルダウン抵抗付き入力状態
3. 使用したAQM0802にはプルアップ抵抗が内部に実装されていた
以上から考えられることとして、AQM0802のプルアップ抵抗によって、GPIO15ポートは自動的にHighに引き上げられていたため、(たまたま)問題なく文字が表示されていた、ということが推測されました🧐☝️ 他にも考えられることはありましたが、この内容が一番信憑性があると思った次第です。
いやー、また一つ、電子回路についての理解が深まりました☺️
と言うことで、不具合原因が特定出来たので、心置き無く次に進むことができます!
回路図および配線図
回路図は前回のSHT31温湿度センサー(以下、SHT)を実装した回路に、AQM0802(以下、AQM)を含めたものになります。下図の通り作成しました。

AQMのSDAおよびSCL端子は、温湿度センサーのSDA、SCL端子と各々並列接続としました。また、AQMのLED(バックライト)端子はGPIO14、RESET端子はGPIO15と接続しました。ここで、RESET端子をVDDではなくGPIO15に相変わらず接続しているのはなぜかと言うと、VDD接続で実行テストをした際、初期化処理中にディスプレイに何かしらの文字が表示されるバグが発生したためです。よって、RESET処理はこちらが決めたタイミングで実施させることにしました。
それと、先ほどのセクションでも紹介しましたが、AQMではSDAとSCLの回路に10kΩのプルアップ抵抗が内蔵されていましたので、追加抵抗などは不要でした。
なお、今回温度と湿度の測定結果はLCDに表示するので、PicoProbeを使ったモニタリングは特に必要は無いのですが、前回のままデバッグ用に念のため残しておきました。
続いて、配線図は下図の通りです。

AQMのパーツはFritzingのライブラリには無かったので、似たようなパーツを参考に自作トライしました😀それとついでにSHTのパーツデータも、併せてきちんとしたものを作成しました。(回路図の表示も前回と違うのに気づきましたかね?)
両者とも画像データと埋め込みデータを編集するのが少々面倒でしたが、何とか作成できました。
プログラム内容
プログラムは前回のI2C記事のものに加えて、AQM0802の初期化処理と、SHT31から取得した数値データをディスプレイへ反映するためのコードを作成しました。
今回のコード説明で特筆しておきたい内容は、AQMの初期化処理に関してとコントロールコマンドの使い方です。
まず最初は、AQMの初期化処理についてです。気を付けるポイントとして、処理の所々でデータ読み込みの待ち時間を設定する必要があることです。それが下記の部分になります。
// AQM0802初期設定
PUT32(SIO_GPIO_OUT_SET, 1 << 15); // ディスプレイ RESET
DELAY(5000000); // 40ms待機 <=== ※気をつけポイント1
uart_send_str("Monitor Init 1");
uart_send_str("1-1"); // DEBUG 1
write_i2c(monitor_addr, 0x00, 0x38); // Function set -> 8bit bus, 2-line display
DELAY(5000); // 40μs待機
uart_send_str("1-2"); // DEBUG 2
write_i2c(monitor_addr, 0x00, 0x39); // Function set -> (IS tabel = 1) 拡張機能セット
DELAY(5000); // 40μs待機
uart_send_str("1-3"); // DEBUG 3
write_i2c(monitor_addr, 0x00, 0x14); // @内蔵発振器の周波数設定 -> bias: 1/5, F2=1: 183Hz(3.0Vの場合)
DELAY(5000); // 40μs待機
uart_send_str("1-4"); // DEBUG 4
write_i2c(monitor_addr, 0x00, 0x73); // コントラスト設定(Low byte)
DELAY(5000); // 40μs待機
uart_send_str("1-5"); // DEBUG 5
write_i2c(monitor_addr, 0x00, 0x56); // @Power,ICONコントロール,コントラスト設定(High byte) -> booster on
DELAY(5000); // 40μs待機
uart_send_str("1-6"); // DEBUG 6
write_i2c(monitor_addr, 0x00, 0x6C); // @フォロワー制御 -> 内部フォロワー回路on
DELAY(25000000); // 200ms待機 <=== ※気をつけポイント2
uart_send_str("Monitor Init 2");
uart_send_str("2-1"); // DEBUG 1
write_i2c(monitor_addr, 0x00, 0x38); // @Function set -> 標準セット戻し
DELAY(5000); // 40μs待機
uart_send_str("2-2"); // DEBUG 2
write_i2c(monitor_addr, 0x00, 0x0C); // @ディスプレイ制御 -> ディスプレイon
DELAY(5000); // 40μs待機
uart_send_str("2-3"); // DEBUG 3
write_i2c(monitor_addr, 0x00, 0x01); // ディスプレイクリア
DELAY(125000); // 1ms待機待ち時間設定で気を付けるタイミングは2箇所あります。(※単位にはmsとμsがあります)
まず最初は、初期化処理の開始前(1-1の前)に40ms待機が必要で、これは基盤に電源が供給されて安定するまでに必要な待ち時間の最低値とのことでした。2つ目は、フォロワー制御の後で、ここは200ms以上の待機が必要です。理由は、ここも前コマンド実行後の電力安定のためとのことでした。それ以外は各コマンド間毎に最低26.3μs待機が必要で、今回はちょっと多めの40μsで設定しました。そして全てのコマンド終了後に1ms待機が必要となります。
なお、この設定内容はAQMのマニュアルに記載されています。
次にコントロールコマンドの使い方についてです。これについては、私はマニュアルを読んで理解するまでに結構時間が掛かった内容でした😅 マニュアルの該当部分は、“デバイス紹介”のセクションでも用いた図で、以下に再掲します。図中のRS(ピンク枠部)のbit設定で行います。

LCD設定の命令(RS = L (=0))と表示データ書込みの命令(RS = H (=1))とで、使用するbit値が分けられています。LCD設定命令とは、初期化処理で行なったようにLCDのモード変更を行うなどの設定をするためのもので、表示データの書込み命令はLCDに表示させたい文字を指定するためのものです。
コード内で“control byte”を指示する値を上図を参考に16進数で表現すると、第6bitで使い分けるので、LCD設定の場合は0x00、表示データ書込みの場合は0x40となります。(※Coを“0”と考えた場合です)
また、実際のコード作成では、初期化処理のように、LCD設定を変更するのみの場合はwrite関数は1回で済みますが、文字表示をさせる場合には、LCD設定と表示データ書込みで2回write関数を使用することになります。以下にその例を示します。
// LCD設定の場合
write_i2c(monitor_addr, 0x00, 0x01); // 例:ディスプレイクリア
// 表示データ書込みの場合
write_i2c(monitor_addr, 0x00, (0x00 | 0x80)); // 例:データ書込み位置指定とDDRAMアドレスセット
write_i2c(monitor_addr, 0x40, 0x41); // 例:文字“A”の書込み上記の補足ですが、表示データ書込みのためのLCD設定には、DDRAMアドレスセットというものを使います。このコマンドは、7bit目を“1”に設定する必要があるので、0x80を追加設定(論理和)しています。以上の書込み命令によって、下図の左上の位置に文字“A”が表示されます。

ということで、プログラム説明については以上になります。全体コードはこちらに掲載しています。
実行結果
プログラム実行結果は、以下の動画をご覧下さい。
数字だけだと物足りないと思ったので、各単位も表示するようにしました。わかりやすい様にセンサー部に指を近づけて強制的に数値変化を促していますが、リアルタイムで値の変化を検出できているのが見て取れます。どうですか、上手く表示できてますよね?😃
これで今後、何かちょっとしたデバイスを作成する際に、情報確認用のディスプレイなどとして活用出来そうです☺️
まとめ
今回は、I2C通信のリベンジマッチでした。
前回の不具合原因はまさかの、佐々木健介ばりのポカミス💦というオチでしたが、言い訳をすると、同時に試したMicroPythonプログラムでうまくいったために、勘違いした節が大きかったと思います。
そんな失敗もあって今回ロジアナを使うきっかけになったんですが、I2Cの書き込みおよび読み出し内容のバイナリや確認応答パルスなどの詳細な実行状態を確認出来るので、組み込み開発のデバックには欠かせないものだということが良く理解出来ました。また一つ、手放せ無いツールが増えました😄
それと前回のブログで、I2Cは使い難い!というコメントをしていましたが、それは撤回しないといけないですねぇ🙇 気持ちを入れ替えて、他のI2C通信デバイスの実装も今後試してみることとします。
まあ今後製作したいものには確実にI2C通信を使わないといけないデバイスがあるので、絶対に避けては通れないんですけどね😏
ということで、今回も最後までお付き合い頂き、ありがとうございました。
それではまた次回🖐️
〆

