● 実験テーマ166

QVGAカラー液晶_ILI9328(ESP32S3 Dev ModuleにSPI接続)を試す_その2」    
 (ANK文字表示+文字列表示+グラフィック描画関連の動作確認を行いました。かなり難航した) 

■ 2026.01.30
  ・今後の実験は横置き表示(自分は横置き表示で使う場合が多い)で行う。
   今迄やってきた実験は、以下のようにGRAM領域の初期設定が縦置き表示向けになっている。
   (今思うと、こう思ったのが難航の前兆だった。)

#define ROW 320
#define COL 240

// GRAM area
WriteCommand(0x0050); WriteData(0x0000); // Horizontal GRAM Start Address
WriteCommand(0x0051); WriteData(0x00EF); // Horizontal GRAM End Address
WriteCommand(0x0052); WriteData(0x0000); // Vertical GRAM Start Address
WriteCommand(0x0053); WriteData(0x013F); // Vertical GRAM End Address

// Init()内
WriteCommand(0x0003); WriteData(0x1090); // set GRAM write direction and BGR=1.

<Copilotに聞いてみた>
Q1:ILI9328 縦置き表示から、横置き表示に変更する設定は?

A1:ILI9328液晶ドライバで画面を横置き(Landscape)に変更するには、
主に「Entry Mode (R03h)」レジスタの設定を変更します。

  1. レジスタ設定の変更
   ・横置き表示にするには、レジスタ 0x0003 (Entry Mode) の 
    AMビット と ID[1:0]ビット を操作して、データの書き込み方向を制御します。 

   ・レジスタ: 0x0003
   ・設定値の例:
     ① 横置き (Landscape): 0x1038 (AM=1、ID[1:0]=11)などの値を設定します。
      ・AM = 1: アドレス更新方向を「垂直方向(横置き時の水平)」に設定します。
      ・D[1:0]: 描画の開始点と進む方向(右・左、上・下)を決定します。

・とりあえず横方向描画で塗潰しを試してみる。_test1


■ 2026.01.31
  ・ILI9328のデータシート:53-54pageを見てみた。以下Google翻訳した内容を示す。

<Entry Mode R03h ビットマップの注釈>

 bit15 14 13 12   | 11 10 9  8 | 7     6     5       4
       |   3  2 1 bit0
---------------------------------------------------------------------------------
 TRI   DFM 0 BGR |      0  0  0   0 | ORG 0 I/D1 I/D0 | AM 0 0 0

AM は GRAM の更新方向を制御します。
AM = “0” の場合、アドレスは水平方向に更新されます。
AM = “1” の場合、アドレスは垂直方向に更新されます。

レジスタ R50h ~ R53h でウィンドウ領域が設定されている場合、
I/D[1:0] と AM ビットの設定に基づいて、指定されたアドレスの GRAM 領域のみが更新されます。
I/D[1:0] は、1 ピクセルの表示データを更新するたびに、
アドレスカウンタ (AC) を 1 ずつ自動的に増加または減少するように制御します。
詳細は次の図を参照してください。

※ 数学的に素直な左上隅:0,0が原点で、
  左下隅:319,239が、走査の終わり(Horizontal:increment/ Vertical:incriment)
  なのは、ID[1:0]= 11



<Fig25:GRAM Accesss Direction Settingより>

ORG ウィンドウアドレス領域作成時に、ID設定に従って原点アドレスを移動します。
    この機能は、高速RAM書き込みを使用してウィンドウアドレス領域にデータを書き込む場合に有効です。
    ORG = “0”:原点アドレスは移動しません。
         この場合、ウィンドウアドレス領域内のGRAMアドレスマップに従って、
         書き込み動作を開始するアドレスを指定してください。

    ORG = “1”:元のアドレス“00000h”がI/D[1:0]設定に従って移動します。

   注:1. ORG=1の場合、RAMアドレス設定レジスタR20hおよびR21hには、原点アドレス“00000h”のみを 設定できます。
      2. RAM読み出し動作時は、必ずORG=0を設定してください。

TRI TRI = “1”の時、8ビットインターフェースを介して8ビット×3転送モードで内蔵RAMにデータを転送します。
     (RGB565 -> RGB666:65K Color)
     また、DFMビットと組み合わせることで、
     26万色表示を実現する転送モードで16ビットインターフェースまたはSPI経由でデータを送信することも可能です。
     これらのインターフェースモードを使用しない場合は、必ずTRI = “0”に設定してください。

DFM TRI = “1”の時、内蔵RAMへのデータ転送モードを設定します。
TRI=1, DF=0-> 80-system 16bit interface(2 transfers/pixcel) 262144 colors
TRI=1, DF=1-> 80-system 16bit interface(2 transfers/pixcel) 262144 colors

  ※ この2つは、1st Transforと2nd Transforの、2bit, 16bitの位置が上位になるか下位になるかの違いで
    分解能は同じ
------------------------------------------------------------------------------------------------------

<縦置き表示から横置き表示:ILI9328_test3の変更点>
 ① #define ROW 320
    #define COL 240

 → #define ROW 240
             #define COL 320

②  // GRAM area
   WriteCommand(0x0050); WriteData(0x0000); // Horizontal GRAM Start Address
   WriteCommand(0x0051); WriteData(0x00EF); // Horizontal GRAM End Address
   WriteCommand(0x0052); WriteData(0x0000); // Vertical GRAM Start Address
   WriteCommand(0x0053); WriteData(0x013F); // Vertical GRAM End Address

 →WriteCommand(0x0050); WriteData(0x0000); // Horizontal GRAM Start Address
   WriteCommand(0x0051); WriteData(0x013F); // Horizontal GRAM End Address
   WriteCommand(0x0052); WriteData(0x0000); // Vertical GRAM Start Address
   WriteCommand(0x0053); WriteData(0x00EF); // Vertical GRAM End Address

 ③  // Init()内
    WriteCommand(0x0003); WriteData(0x1090); // set GRAM write direction and BGR=1(test2の設定:この時はとにかく塗潰しが出来ればと思っていたので
                          // 横縦は意識してなかった。尚、0x1090は、aitendoの参考コードの値
                          // 向きを縦にして見てたから縦表示と思っていたが、データシートで追うと、
                          // BGR=1 / ORG=1 /AM=0 / I/D[1:0]=01 は、横表示ではあるが、開始点が左下・終点が右上になり
                          // 文字表示が左右逆になる。

<変更後の動作確認>
・駄目:原点は左上隅になっていて、そこから走査開始されているが、水平方向の走査が縦方向と同じ240dotで垂直方向にインクリメントされる。
    そして垂直方向上1/4位で走査が終わってしまい、結果、上部240bot x 60dotの領域しか塗潰せない。



→ WriteCommand(0x0003); WriteData(0x1038); // BGR=1 / ORG=0 / ID[1:0]=11 / AM=1(Copilotが言ってきたのは、こっち)
   にしても同じ。                // これだと、データシートの54pageの図表で見ると、縦置き表示(ただ文字が左右逆表示)になる。つまり
                            // 左上隅から走査開始されY方向:240dotの端まで来たらX方向をインクリメントと進みX方向:320dotの端まで
                            // 来たら走査完了となると理解


→ WriteCommand(0x0003); WriteData(0x1030); // BGR=1 / ORG=0 / ID[1:0]=11 / AM=0
  にしても同じ。→ これでいいはず:AM=0とID[1:0]=11の組合せで、横表示+水平・垂直ともインクリメント(水平の端まで来たら垂直方向インクリメント)
             これで、データシート54page-Fig25:GRAM Access Direction Settingの、AM=0とI/D[1:0]=11の交差点の図相当
             (原点:図のB(開始点:数学的な原点)から走査が始まって、右下の[E]で走査完了となると理解)
             またORG=0で、ウインドウアドレス領域内のGRAMアドレスマップに従って書込み動作を開始するアドレスを指定。
             ウインドウアドレス(塗潰し範囲)は可変の方が応用が利くので、こちらを使おうと考えている。
  明日に続く


■ 2026.2.1
  ・WriteCommand(0x0003); WriteData(0x1030); // BGR=1 / ORG=0 / ID[1:0]=11 / AM=0 を再度試す。

  → いくらやっても駄目。
     Copilotに聞いたら、ILI9328の横・縦表示に関する仕様が非常に素直でないことが
     手こずった要因の一部であることが分かった。

    <Copilot返答コメント>
     ・ROW、COLの値を入れ替え、開始位置のリセットも行っているとのこと、実装の手順は完璧です。
      それでも「240ドットで折り返してしまう」場合、ILI9328特有の「物理的なX/Y軸」と「論理的なWindowレジスタ」の対応関係が、
      データシートの解釈とズレている可能性があります。
  
  → ※ Copilotからの重要コメント
     ・この時代の液晶ドライバ(ILI9328やILI9325など)は、「物理的な構造」がポートレート(縦長)専用に作られているため、
      横置き設定はかなり「力技」な仕様になっています。
      今の状況を整理すると、以下の「ややこしい仕様」が牙を剥いている可能性が高いです。

      ① 物理的な壁: Horizontal(X) 方向には物理的に 239 までしか回路が繋がっていない。
      ② 論理的な回転: AM=1 にすると、「データが流れる方向」を 垂直(320ドット側) に変えるだけで、
         Windowの「枠」そのものが回転するわけではない。

 ※ 結局、GRAMマッピング初期化は、X=239, Y=319 (縦表示相当なまま)にし、
    WriteCommand(0x0003); WriteData(0x1038); // BGR=1 / ORG=0 / ID[1:0]=11 / AM=1 でAM=1にして
    横方向にローテートするイメージでOKということが分かった。

   
・横方向の塗潰しスケッチ:ILI9328_test3.ino
   ・横方向の塗潰しビデオ :video001.mp4(2.52MB・9秒)


■ 2026.2.2
  ・ILI9328_test4プロジェクト計画(ANK文字表示+文字列表示)
    ① フォントデータは、使用実績のあるASCII12dot.h(ASCIIフォントデータ 12x12ドット)を使用
    ② PIC32MXで開発(MPLAB IDE)したグラフィック描画関数を、Arduino IDEに移植する形で進める。


■ 2026.2.3
  ・スケッチ作成終了。試してみる。
   コンパイルエラーが2箇所出たが、イージーミスで直ぐ修正しOKになる。

  ・一応文字関係は一発で動いた。


■ 2026.2.4
  ・ILI9328_test5プロジェクト計画(これが最終形になる。)
    ① _test4の内容そのままに_test4に追加する形で、グラフィック描画関数を追加する。
    ② PIC32MXで作った関数を移植する形で進める。
       ただ2次元曲線描画関数は省略する。直線+円+斜め線+矩形のみ:関数としては、直線と円のみで、後は直線を組み合わせて実現する方式
       後、簡易イメージ描画関数も入れる。(イメージファイルはPIC32MXで使ったものを流用:imagedata.h)

  ・スケッチ作成終了。試してみる。
   コンパイルエラーが大量に出た。
   まとめると、以下のエラー
   Compilation error: call of overloaded 'LCD_DrawLine(unsigned char&, unsigned char&, int, unsigned char&, int)' is ambiguous


■ 2026.2.5
  <Copilotに」このエラーを伝えた>
   ・エラーの主な原因は、「同じ名前で引数の型が微妙に違う関数」が2つ定義されており、
    コンパイラがどちらを使えばいいか判断できない(アンビギュアス) ことにあります。
    具体的には、以下の2つが重複しています:

    unsigned int color を取るもの(79行目)
    unsigned short color を取るもの(282行目)-> shortをintに修正

   <修正した箇所>
   ① unsigned short color を取るもの(282行目)void LCD_DrawCircle(int x0, int y0, int r, unsigned short color)-> shortをintに修正
   ② #define MAGENTA 0xF81Fの定義が抜けてた。追加

   → これで再度コンパイル
     エラー減った。

     Compilation error: invalid conversion from 'const char*' to 'const unsigned char*' [-fpermissive]

    ③ imagedata.hの中の、const char My_PIC_Scope[1024]→ imagedata.hの中の、const unsigned char My_PIC_Scope[1024]に修正

   → 再コンパイル
      エラーはなくなったようだが、最後に以下出る。

     Compilation error: exit status 1

   → リンクエラーのようだ。(型の不一致)

     <以下修正>
     void LCD_DrawPixel(int x, int y, unsigned int color);に対し
      void LCD_DrawChar(char colum, char line, unsigned char letter, unsigned int color1, unsigned int color2){
       unsigned char j, i, Mask;
       で異なっている。intで統一

  → 以下に修正
     void LCD_DrawChar(int colum, int line, unsigned char letter, unsigned int color1, unsigned int color2){
     int j, i, Mask;

     未だCompilation error: exit status 1

     ① プロトタイプの方を修正してなかった。
       void LCD_DrawChar(char colum, char line, unsigned char letter, unsigned int color1, unsigned int color2);
       以下に修正
       LCD_DrawChar(int colum, int line, unsigned char letter, unsigned int color1, unsigned int color2);

     ② void LCD_DrawString(char colum, char line, char *s, unsigned int color1, unsigned int color2)
       以下に修正
       void LCD_DrawString(int colum, int line, char *s, unsigned int color1, unsigned int color2)
       同時にプロトタイプも修正
       リンクエラー少なくなったが未だある。

       Compilation error: exit status 1
       次の行に問題がある模様
       undefined reference to `LCD_DrawPixel(int, int, unsigned int)'

     ③ プロトタイプ:void LCD_DrawPixel(int x, int y, unsigned int color);に対し、
       関数が、void LCD_DrawPixel(int x, int y, int color){なってた。
       これを、void LCD_DrawPixel(int x, int y, unsigned int color){に修正

  → これでエラー無しになった。
     最大1310720バイトのフラッシュメモリのうち、スケッチが314527バイト(23%)を使っています。
     最大327680バイトのRAMのうち、グローバル変数が20848バイト(6%)を使っていて、ローカル変数で306832バイト使うことができます。

     何か、MPLABより型の不一致判断がシビアなような気がした。もしかしたらMPLABの方は最適化してくれてたのかな?
  → 書き込み~ RUN
     大方動いてはいるが、斜め線の右側がおかしい。その他はOK

  → メイン頭の、unsigned char i, j, index; を int i,j,index;に変更
  → これでも同じ

  → PIC32MXの移植なので上手く行くはずが行かない。
     スケッチを見ると短い斜線の方は、X= 240~579, Y=0~239まで変化する。
     つまり、X=320以上オーバーラップし飽和、Yも79で止まるべきが、それ以上で飽和してしまう。
     なのであのようになる。
     何故これでPIC32MXの場合上手く行ってるのか分からない。今は追わない。


     ※ 以下のように修正後、動作確認OKになった。

/// 斜め直線描画------------------------------------------------------------------------------------------
LCD_Fill(WHITE); // クリア
for(i=0; i<240; i++){ // 斜め直線の表示: 白ベタ背景・黒線表示
LCD_DrawPixel(i,i,BLACK); // 1本目:始点(0,0)-終点(239,239)
//LCD_DrawPixel(240+i, i,BLACK); 
}
for(i=0; i<80; i++){
LCD_DrawPixel(240+i,i,BLACK); // 2本目:始点(240,0)-終点(319,79)
}

delay(5000); // 5秒待ち
LCD_Fill(BLACK); // クリア

for(i=0; i<240; i++){ // 斜め直線: 黒ベタ背景・白線表示
LCD_DrawPixel(i,i,WHITE); // 1本目:始点(0,0)-終点(239,239)
//LCD_DrawPixel(240+i, i,WHITE); 
}
for(i=0; i<80; i++){
LCD_DrawPixel(240+i,i,WHITE); // 2本目:始点(240,0)-終点(319,79)
}


<最終スケッチ>

こちらから、どうぞ→ 横方向の塗潰し:ILI9328_test3.ino
             
  ANK文字表示+文字列表示:ILI9328_test4.ino
             ANK文字表示+文字列表示+グラフィック描画:ILI9328_test5.ino(最終形)

             ASCII(ANK)文字データテーブル:ASCII12dot.h
             イメージデータ:imagedata.h          


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