● 実験テーマ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