● 実験テーマ165

 「QVGAカラー液晶_ILI9328(ESP32S3 Dev ModuleにSPI接続)を試す」
 
(aitendo2026お楽しみBOXに入っていた液晶の動作確認(全画面塗潰し)を行いました。かなり難航した)

■ 2026.01.07
  ・今年の福BOXに入っていたQVGA液晶_ILI9328
   正式商品名:2.2インチTFT液晶モジュール★240x320★ [M022C9328SPI]

   キャリーボードに実装済で外観が紫レジストでとても綺麗。
   SDカードスロット付きなのも使い道が広がってGOOD!!

<主な仕様>
 ① 内蔵コントローラチップ:ILI9328
 ② 信号インターフェース:3/4線SPI
 ③ 接続:2.54mmピッチ9Pスルーホール x 2
 ④ SDカードソケット実装済み
 ⑤ 動作電源:3.3Vか5V(基板ジャンパーJ1/J2で設定)
  ・入力電源の電圧の設定
   J1をショット:VCC_IN=3.3V
   J1をオープン、J2をショット:VCC_IN=5V
   デフォルトはVCC_IN=3.3V

 ⑥ D_LEDをGNDに接続するとバックライトが点灯
 ⑦ Input high voltage: IOVCC=1.8~ 3.3V時、3.3Vmax

  ・Input high voltage: IOVCC=1.8~ 3.3V時、3.3Vmaxなので、ESP32S2 Div Moduleで試す。
   まずは各色で全画面塗潰テストをしてみる。最初のプロジェクトは、ソフトSPIにする。

  <参考サイト>
  ・「ものづくりとプログラミング日記」
   「aitendoさんから入手した「2.4インチ液晶モジュール(SPI) [M024C9328SPI] 」をarduinoに接続して図形とかテキストを表示します。」
  → 2.2インチではないが、ドライバは同じILI9328
 


■ 2026.01.09
  ・参考にしながらスケッチ作成後、
   コンパイル(ライブラリを使って無いので割とコンパイル早かった。)~書込み後、Run→ 動作確認OK ただ、digitalWriteで出力をON/OFFしているせいか遅い。
   1画面塗潰すのに約3秒掛かる。参考サイトの所感と同じ。
   オシロで、CS-SCKを観測した。SCKの転送速度は、約617kHzであった。

   動作中のVIDEOも取った。以下クリックで見れます。
   video001.mp3(4.20M 15秒)


■ 2026.01.10
  ・次はこれを、ハードSPI(Arduinoの標準のSPIライブラリ)を使用してどこまで高速化できるか実験しました。
   ハードSPIについてよく理解出来ないので、Copilotに聞いてみた。

   Q1. ESP32-S3-DevkitボードとILI9328液晶を、SPI接続して全面塗潰しを試しています。
      最初にライブラリを使わないで、GPIOポートのON/OFFでソフト的に制御するステッチを書き、
      動作確認したところ上手く行きましたが、全面塗り潰しまで約3秒も掛かります。
      そこでESP32のハードSPI(ライブラリ)で制御したいのですが、SPIの種類が4種もあって
      どれを使ってよいのやら迷います。
      前記のソフトSPIでの接続は以下です。
      // LCDピン定義 ESP32S3
      #define SDA    15
      #define SCK    16
      #define CS       17
      #define RST    18 
      ハードSPI(ライブラリ)でも、この4信号のみで制御したいのですが可能でしょうか?
      要は、ハードSPI(ライブラリ)でのESP32-S3とILI9328の接続が知りたいです。

    A1. もちろん可能です。しかも、ESP32‑S3 の SPI は柔軟なので、あなたが今ソフトSPIで使っている 
      15/16/17 ピンをそのままハードSPIに割り当てることもできます。
      ただし、いくつか「押さえておくべきポイント」があります。整理して説明しますね。

     (1) ESP32‑S3 の SPI 種類(4種類の正体)
       ・ESP32‑S3 には SPI が4種類ありますが、実際に ユーザーが自由に使えるのは 2つだけ です。
        その内の「SPI2」 がSPIとして使われることが多いです。

     (2) ILI9328 を SPI で使うときに必要な信号
       ・最低限以下が必要です
        SCK SPIクロック
        MOSI(SDA) データ出力
        CS チップセレクト
        DC(A0) データ/コマンド切り替え
        RST リセット

     ※ DC ピンが必須 です
        ソフトSPIで DC を使っていない場合は、内部でコマンド/データを区別する別の方法を使っていた可能性がありますが、
        ハードSPIでは DC ピンを必ず追加 してください。

       ※↑↑-- 後日の実験で、DCを使うと上手く液晶と通信出来ませんでした。(白画面のままになる)ソフトSPIの時と同じく、
           スタートバイト:0x70/0x72のみでコマンド/データの識別を液晶に伝えることで、上手く通信できました。
           (詳細はこの後の記事を参照してください。)
 

     (3) 結論:ハードSPIで 15/16/17 ピンを使うことは可能
      ・ESP32‑S3 の SPI は ピン固定ではなく、任意のGPIOに割り当て可能 です。
       つまり:
       SCK → GPIO16 
       MOSI → GPIO15 
       CS → GPIO17 
       DC → 任意の空きGPIO(例:14) 
       RST → GPIO18 

       このように自由に設定できます。

       ※↑↑-- 後でWEB検索したところ、ハードSPIの場合の、ESP32S3側の接続先(推奨GPIOピン)が載っていました。
            それによると、マルチ機能ピンの中に、FSPI**がある以下のピンを推奨していましたので、これに変更しました。

           CS: GPIO10/FSPCS0
           SDI: GPIO11/FSPIID 
           SCK:GPIO12/FSPICLK
           RST:GPIO18(これだけは、任意のピンでOK) 

           (詳細はこの後の記事を参照してください。)  


■ 2026.01.22
  ・次はこれを、ハードSPI(Arduinoの標準のSPIライブラリ)を使用してどこまで高速化できるか実験しました。
   この日まで色々試したのですが、相変わらず白画面のまま変わらずじまいでした。
   そこで、上手く行っているソフトSPIの時と同じ条件:DCピンを使わないで、スタートバイトの中にある
   RS(DC)ビットによる、コマンド/データ識別法に変更してみました。
   その他変更した点は以下です。

    ① ESP32S3のSPIは、SPIモジュールでなくSPI2モジュールを指定する。
       具体的には、SPI.transfer()→ SPI2.transfer()に変更

    ② LCD_Fill() の先頭に以下に示す0x0020 / 0x0021 のアドレス設定を追加
       WriteCommand(0x0020); WriteData(0x0000); // Horizontal Address
       WriteCommand(0x0021); WriteData(0x0000); // Vertical Address
       WriteCommand(0x0022); // GRAM write

    ③ SCKのMODE(待機時レベル・サンプルエッジ)を、MODE0(待機時:LOW・立上りサンプリング)から
       MODE3(待機時:HIGH・立上りサンプリング)に変更

       ④ SCK周波数を、10MHzから1MHzに落としてみる。(上手く行ったら徐々に上げてみる。)

  ・この修正で、ようやく各色の塗潰しが見れたが、やや色が薄い。それでも赤以外の、緑・青・白は見た目、許せる範囲なのだが、
   赤は、どう見ても真っ赤には見えない。濃いマゼンタに見える。

  ・1MHz -> ok:全画面塗潰しに掛かる時間:約2秒
   5MHz -> ok:全画面塗潰しに掛かる時間:約1秒                
   10MHz -> ok:全画面塗潰しに掛かる時間:約0.5秒
   20MHz -> ok:全画面塗潰しに掛かる時間:約0.25秒(ストップウォッチでは測定不可)
   30MHz -> ok:全画面塗潰しに掛かる時間:20MHzより速い(ストップウォッチでは測定不可)

      尚、ソフトSPI=3秒 約630kHz

      ※ 実用的な周波数 (安定動作):
      5MHz〜10MHzの範囲が、表示の安定性と速度のバランスが取れた領域です。
      10MHzを大きく超える(20MHz〜)と、信号品質が低下し、
      表示の乱れ(ノイズ)、画面の白飛び、または真っ白な表示になるなど、安定して動作しなくなる可能性が高くなります。

           ということで、10MHzにフィックスした。


■ 2026.01.23
  ・一応動いた(色が薄いが)ので波形評価をしてみた。
   (1) SCK-SDI:SCKの立下りトリガ
      1.jpg=500nS/div・データ送信時-> 0x72+0xF8+0xOO正しく送出している。OK
      2.jpg=100nS/div・データ送信時-> 0x72部分拡大-> 確かに0x72である。 OK
            3.jpg=100nS/div・データ送信時-> 0xF8部分拡大-> 確かに0xF8である。 OK
         4.jpg=100nS/div・データ送信時-> 0xOO部分拡大-> 確かに0x00である。 OK
                   5.jpg= 50nS/div・データ送信時-> SCK部分拡大-> 確かに10MHzである。 OK

  ・タイミングは、大方問題無いように見えるが、色が薄い? さらなる調査・追及は明日以降に続く。


■ 2026.01.25
  ・ハードSPIの10MHzで赤コードを送信した場合、マゼンタ寄りの発光になってしまうのが気になるので、
   またCopilotに聞いてみた。

   データのサンプリングタイミングに微妙な問題(マージン)があるのではと言ってきた。
   SCKのMODEを、MODE3 -> MODE2へ変更してみてくれと言われた。
   結果は、白画面のまま。NG

   これを受けてCopilotの回答は「原因は「速度(マージン)」ではなくデータの送り出しタイミングが、  
   液晶側の期待より半クロック〜1クロック分ズレている」ことが確定しましたと言ってきた。

   赤(11111000 00000000)を送ってマゼンタ(青みが混ざる)になるのは、末尾に「1」が入り込んでいる。
   つまり全体が左に1ビットずれて、次のデータ(またはゴミデータ)の先頭ビットを拾っている可能性が高いです。
   解決のために、Arduino環境で次に試すべき2つの設定を提案します。

   <対策案>
     ① MODE0 を試す(サンプリングエッジを逆にしてみる)
      ・MODE3でズレる場合、クロックの立ち上がりとデータの変化点が重なりすぎていることがあります。
       MODE0に変更することで、サンプリング(液晶がデータを読み取るタイミング)を半周期ずらすことができます。

      → 白画面のままになる。

      → Copilot回答
        ・MODE0で白画面になったということは、ILI9328がクロックの極性(待機時にLowかHighか)を厳格に見ており、待機時High(MODE2/3)でなければ
         通信が成立しないことが改めて明確になりました。

        ・現状、MODE3(待機時High)では画面は映るものの、色が「赤(11111000...)」ではなく「マゼンタ(青混じり)」になる。
         これは、ESP32-S3の出力タイミングが液晶の期待よりわずかに早く、液晶がデータを取り込む瞬間に
         次のビット(または不要なビット)が混入している可能性が非常に高いです。

        ※ ソフトSPIのような「ゆとり」をハードウェアSPI(SPI2)で再現するための、Arduino環境で使える「送信タイミング微調整」の決定版を提案します。

     ② 次に試すべき対策:送信エッジの反転(レジスタ直接操作)
      ・MODE3をベースにしつつ、ESP32-S3側の「データを送り出すタイミング」を半サイクルずらして、
       液晶側がサンプリングする瞬間にデータが完全に安定している状態を作ります。

#include <SPI.h>
#include "soc/spi_reg.h" // レジスタ操作に必要

// SPI2(FSPI)を使用する設定
SPIClass SPI2_BUS(FSPI); 
SPISettings settings(600000, MSBFIRST, SPI_MODE3); // 低速MODE3

void setup() {
// ピン定義(12:SCK, 11:MOSI, 10:CS)
SPI2_BUS.begin(12, -1, 11, 10);
pinMode(10, OUTPUT);

// ★重要:ここからが対策コード
// SPI2の出力エッジを反転させるレジスタ設定です
// これにより、ソフトSPIに近いタイミングマージンが生まれます
SET_PERI_REG_MASK(SPI_MS_DL_REG(2), SPI_MS_HSPI_D_LINE_INV); 
}

void loop() {
SPI2_BUS.beginTransaction(settings);
digitalWrite(10, LOW);

// 赤(0xF800)を送信
SPI2_BUS.transfer16(0xF800); 

digitalWrite(10, HIGH);
SPI2_BUS.endTransaction();
delay(1000);
}

  ・まだマゼンタ(青混じり) → 原因は「1ビットの解釈ズレ」です。
   この場合は、MODEを0〜3まで順に切り替えて、最も色が安定するものを探します。
   まずは「MODE3 + 600kHz」で、液晶がしっかり「赤」を認識できるかだけを確認してみてください。

   結局、レジスタ操作での遅延挿入は不要だった。
     重要なのは、SPI2で送る前にSPI2.end(); で一度、確実に終了させてから開始(初期化の安定化)すること。
     これで純粋にMODE3で動いた。

     赤だけのテスト:ILI9328_test2_Rev1だが、OK確認。


■ 2026.01.28
  ・Rev1は、テスト用で赤のみだったが、各色テスト版:ILI9328_test2_Rev2にする。

  ※ #include "soc/spi_reg.h" // レジスタ操作に必要
     #include "soc/soc.h"

       上記の2つは削除すると白画面のままになる。これを追加すると、
     WRITE_PERI_REG(SPI_DIN_MODE_REG(2), 0); // 入力遅延をリセット
     が無くても、リセットされ各色正常表示したが、明示的に残しておく。
     ESP32から色データを送る時、余計な遅延なしに液晶入力させ液晶側のタイミングに合わせることが出来たと考える。
     これに加え、SPI2で送る前にSPI2.end(); で一度、確実に終了させてから開始(初期化の安定化)したら純粋にMODE3で送ることが出来た。

  ※ 修正前のILI9328_testでは、赤・緑・青・白と一応見た目、発色したが全般的に色が薄かった。薄くても赤以外は、それなりに正常と判断
     出来る程度だったが、赤はどう見ても真っ赤には見えず、マゼンタ寄りに見えた。
     この時、撮ったSCK-SDIタイミングを見ると一見今回のと同じに見えるが、若干セットアップ時間が短く見える。10MHz動作なので影響していると考えられる。

  ※ 波形評価してみた。以下に各色で発光している画像もアップしたので参照してください。
     (1) SCK-SDI:SCKの立下りトリガ
      ・1.jpg=500nS/div・コマンド送信時-> 0x70+0x00+0x2O(Horizontal Start Address)正しく送出している。ok
      ・2.jpg=100nS/div・コマンド送信時-> 0x70部分拡大-> 確かに0x70である。ok
         ・3.jpg=100nS/div・コマンド送信時-> 0x00部分拡大-> 確かに0x00である。ok
                ・4.jpg=100nS/div・コマンド送信時-> 0x20部分拡大-> 確かに0x20である。ok
      ・5.jpg= 50nS/div・コマンド送信時-> SCK部分拡大-> 確かに10MHzである。ok
         ・6.jpg=500nS/div・白データ送信時->16bit連続送信で0xFFFFが正しく送出している。ok

   動作中のVIDEOも取った。以下クリックで見れます。
   video002.mp3(2.31M 7秒)


<最終回路図>

・こちらからどうそ→ ILI9328_test1回路図 (ソフトSPI)
            ILI9328_test2_Rev2回路図 (ハードSPI)            

<最終スケッチ>

こちらから、どうぞ→ ILI9328_test1.ino (ソフトSPI)
             ILI9328_test2_Rev2.ino (ハードSPI)       


← 実験テーマ1に戻る   TOP PAGEに戻る     実験テーマ166へ →