/******************************************************************* * プロジェクトファイル名:SD_ADD_LCD_TEST.ino * * (1) 動作仕様 *   @ SDカードをスロットに挿入し、ESP32S3ボードの電源を入れる。 *   A SDカードのルートディレクトリを開く。 *   B オープン失敗の場合:シリアルモニターに * "ルートディレクトリが開けません"を表示 *   C オープン成功の場合:液晶の1画面に * SDカードのルートディレクトリに有るファイルリストを表示 * * コントローラ:ESP32S3 Dev Module * 内蔵MPU:ESP32-WROOM-1:動作クロック周波数=240MHz(max) * * 2026/2/16 N.Ishii *********************************************************************/ #include #include "soc/spi_reg.h" // レジスタ操作に必要 #include "soc/soc.h" #include "FS.h" #include "SD.h" #include "ASCII12dot.h" /*********** ピン定義(GPIO番号を指定)*****************/ // SDカード #define SD_CS 10 #define SD_MISO 13 // SDカードの読込み // 液晶モジュール #define D_CS 18 #define D_RST 17 // SDカード・液晶モジュール共通 #define MOSI 11 // SDカード・液晶モジュールへの書込み #define SCK 12 /*********** 液晶側の定義 *****************/ // SPI2(FSPI)の名前を「SPI2」に統一して定義 SPIClass SPI2(FSPI); // SPI2のMODE設定をグローバルに定義(液晶描画全関数で使用可能) SPISettings settings(10000000, MSBFIRST, SPI_MODE3); /******************************************* * 16bit Color Difinition *******************************************/ #define WHITE 0xFFFF #define BLACK 0x0000 #define RED 0xF800 #define GREEN 0x07E0 #define BLUE 0x001F #define CYAN 0x07FF #define MAGENTA 0xF81F #define YELLOW 0xFFE0 #define BROWN 0x8000 #define ORANGE 0xFC00 #define PERPLE 0x8010 #define COBALT 0x041F //============================================== #define ENDCOL 320 // X #define ENDROW 240 // Y #define ENDPAGE 239 // Y #define XChar (int)((ENDCOL+1) / 12) #define YLine (int)((ENDPAGE+1) / 14) ///// プロトタイプ宣言 // 液晶側 void WriteData(unsigned int data); void WriteCommand(unsigned int cmd); void LCD_Init(); void LCD_Fill(unsigned int color); void LCD_DrawPixel(int x, int y, unsigned int color); void LCD_DrawChar(int colum, int line, unsigned char letter, unsigned int color1, unsigned int color2); void LCD_DrawString(int colum, int line, const char *s, unsigned int color1, unsigned int color2); ///// SDカード側 void listRootFiles(); ///// Main void setup() { Serial.begin(115200); while (!Serial); // シリアルモニターが開くまで待機 /// ピンモード設定 pinMode(D_CS, OUTPUT); pinMode(D_RST, OUTPUT); /// CSピン・RSTピンの初期レベルを非アクティブとする digitalWrite(D_CS, HIGH); digitalWrite(D_RST, HIGH); ////////// 液晶のSPI2初期化 // 一度確実に終了させてから開始(初期化の安定化) SPI2.end(); /// CS,RSTは手動制御なので設定が必要 /// SCK, MOSIはbeginに任せる(pinModeは不要) /// これで SPI.transfer() を呼び出した際に、ESP32のハードウェアが自動的に /// SCKピンからロック信号を出力してくれる SPI2.begin(SCK, MISO, MOSI, -1); // 最後の引数:CSはbegin内では指定せず-1でOK ///// 最初は液晶画面のクリアから /// クリア前に、液晶のみCSをアクティブにする。 digitalWrite(D_CS, LOW); LCD_Init(); LCD_Fill(BLACK); // 全画面クリア ///// 次にSDカードにアクセスしてマウントする /// マウント前に、液晶側のD_CSをノンアクティブにする。 digitalWrite(D_CS, HIGH); Serial.println("\nInitializing SD card..."); // 初期化中のメッセージをモニター出力 ///// SDカードの初期化時にCSピンを指定しマウントする。 if (!SD.begin(SD_CS, SPI2, 40000000)) { // SPI2を共有バスとして渡す Serial.println("Card Mount Failed"); } else { Serial.println("Card Mounted Successfully"); } ///// SDカードのルートディレクトリにあるファイルを読込み液晶の1画面分に、ファイル名リストを表示する。 listRootFiles(); } void loop() { // 特に実行することはない } ///// 液晶側の初期化・1文字・文字列表示関数類 void WriteData(unsigned int data) { SPI2.beginTransaction(settings); digitalWrite(D_CS, LOW); SPI2.transfer(0x72); // データ識別(スタートバイト:これからデータを送ります) SPI2.transfer(data >> 8); // 上位バイトを送信 SPI2.transfer(data & 0xFF); // 下位バイトを送信 digitalWrite(D_CS, HIGH); SPI2.endTransaction(); } void WriteCommand(unsigned int cmd) { SPI2.beginTransaction(settings); digitalWrite(D_CS, LOW); SPI2.transfer(0x70); // コマンド識別(スタートバイト:これからコマンドを送ります) SPI2.transfer(cmd >> 8); // 上位バイトを送信 SPI2.transfer(cmd & 0xFF); // 下位バイトを送信 digitalWrite(D_CS, HIGH); SPI2.endTransaction(); } void LCD_Init() { // ハードリセット digitalWrite(D_RST, LOW); delay(20); digitalWrite(D_RST, HIGH); delay(120); WriteCommand(0x00E5); WriteData(0x78F0); // set SRAM internal timing WriteCommand(0x0001); WriteData(0x0000); // set SS and SM bit WriteCommand(0x0002); WriteData(0x0400); // set 1 line inversion WriteCommand(0x0003); WriteData(0x1030); // BGR=1 / ORG=0 / ID[1:0]=11 / AM=0 (AM=0とID[1:0]=11の組合せで横表示+水平・垂直ともインクリメント) //WriteCommand(0x0003); WriteData(0x1038); // BGR=1 / ORG=0 / ID[1:0]=11 / AM=1 WriteCommand(0x0004); WriteData(0x0000); // Resize register WriteCommand(0x0008); WriteData(0x0202); // set the back porch and front porch WriteCommand(0x0009); WriteData(0x0000); // set non-display area refresh cycle ISC[3:0] WriteCommand(0x000A); WriteData(0x0000); // RGB interface setting WriteCommand(0x000D); WriteData(0x0000); // Frame marker Position WriteCommand(0x000F); WriteData(0x0000); // RGB interface polarity // Power On sequence WriteCommand(0x0010); WriteData(0x0000); // SAP, BT[3:0], AP, DSTB, SLP, STB WriteCommand(0x0011); WriteData(0x0007); // DC1[2:0], DC0[2:0], VC[2:0] WriteCommand(0x0012); WriteData(0x0000); // VREG1OUT voltage WriteCommand(0x0013); WriteData(0x0000); // VDV[4:0] for VCOM amplitude WriteCommand(0x0007); WriteData(0x0001); // Dis-charge capacitor power voltage delay(200); WriteCommand(0x0010); WriteData(0x1690); // SAP, BT[3:0], AP, DSTB, SLP, STB WriteCommand(0x0011); WriteData(0x0227); // Set DC1[2:0], DC0[2:0], VC[2:0] delay(50); WriteCommand(0x0012); WriteData(0x008C); // External reference voltage= Vci; delay(50); WriteCommand(0x0013); WriteData(0x1500); // VDV[4:0] for VCOM amplitude 0X1000 WriteCommand(0x0029); WriteData(0x0004); // VCM[5:0] for VCOMH 0X0009 WriteCommand(0x002B); WriteData(0x000D); // Set Frame Rate delay(50); WriteCommand(0x0020); WriteData(0x0000); // GRAM horizontal Address WriteCommand(0x0021); WriteData(0x0000); // GRAM Vertical Address // Gamma WriteCommand(0x0030); WriteData(0x0000); WriteCommand(0x0031); WriteData(0x0607); WriteCommand(0x0032); WriteData(0x0305); WriteCommand(0x0035); WriteData(0x0000); WriteCommand(0x0036); WriteData(0x1604); WriteCommand(0x0037); WriteData(0x0204); WriteCommand(0x0038); WriteData(0x0001); WriteCommand(0x0039); WriteData(0x0707); WriteCommand(0x003C); WriteData(0x0000); WriteCommand(0x003D); WriteData(0x000F); // GRAM area WriteCommand(0x0050); WriteData(0x0000); // Horizontal GRAM Start Address //WriteCommand(0x0051); WriteData(0x013F); // Horizontal GRAM End Address (横置き表示設定) WriteCommand(0x0051); WriteData(0x00EF); // ここは変えない WriteCommand(0x0052); WriteData(0x0000); // Vertical GRAM Start Address //WriteCommand(0x0053); WriteData(0x00EF); // Vertical GRAM End Address (横置き表示設定) WriteCommand(0x0053); WriteData(0x013F); // ここは変えない WriteCommand(0x0060); WriteData(0xA700); // Gate Scan Line WriteCommand(0x0061); WriteData(0x0001); // NDL,VLE, REV WriteCommand(0x006A); WriteData(0x0000); // set scrolling line // Partial Display WriteCommand(0x0080); WriteData(0x0000); WriteCommand(0x0081); WriteData(0x0000); WriteCommand(0x0082); WriteData(0x0000); WriteCommand(0x0083); WriteData(0x0000); WriteCommand(0x0084); WriteData(0x0000); WriteCommand(0x0085); WriteData(0x0000); // Panel Control WriteCommand(0x0090); WriteData(0x0010); WriteCommand(0x0092); WriteData(0x0600); WriteCommand(0x0007); WriteData(0x0133); // 262K color and display ON } /***************************************** * 全画面を指定色で塗潰す ******************************************/ void LCD_Fill(unsigned int color) { WriteCommand(0x0020); WriteData(0x0000); // 本来はX指定だが、AM=1時はY(0-239)を制御 WriteCommand(0x0021); WriteData(0x0000); // Vertical Start Address 本来はY指定だが、AM=1時はX(0-319)を制御 WriteCommand(0x0022); // GRAM write /// 全画面塗りつぶし用(高速化のため一気に送る) SPI2.beginTransaction(settings); digitalWrite(D_CS, LOW); /// 開始指示 SPI2.transfer(0x72); // データ識別:Start Byte /// 320x240ピクセル分をループ for (int i = 0; i < ENDCOL * ENDROW; i++) { SPI2.transfer16(color); // 16bitを一気に送る } digitalWrite(D_CS, HIGH); SPI2.endTransaction(); } /*********************************** * 1ピクセル表示関数 * 座標は(0,0)-(319,239) ***********************************/ void LCD_DrawPixel(int x, int y, unsigned int color){ WriteCommand(0x0020); WriteData(y); // 本来はX開始アドレス指定だが、AM=1時はY(0-239)を制御なので、Yを指定 WriteCommand(0x0021); WriteData(x); // 本来はY開始アドレス指定だが、AM=1時はX(0-319)を制御なので、Xを指定 WriteCommand(0x0022); // GRAM write WriteData(color); // 1dot描画 } /***************************************** * ANK文字表示関数 12x12ドット * 320/12=26文字/行 240/14=17行 * (0, 0) - (25, 16)の範囲 ******************************************/ void LCD_DrawChar(int colum, int line, unsigned char letter, unsigned int color1, unsigned int color2){ int j, i, Mask; if((colum < XChar) && (line < YLine)){ // 範囲チェック // ANK表示出力 3バイトの2ラインずつを6回繰り返す for(j=0; j<6; j++){ // 8ドット連続部の表示 Mask = 0x80; for(i=0; i<8; i++){ // 1ライン目前半8ドット表示 if((ANKFont[letter][j*3] & Mask) != 0) LCD_DrawPixel(colum*12+i+4, line*14+j*2+2, color1); else LCD_DrawPixel(colum*12+i+4, line*14+j*2+2, color2); //背景色 // 2ライン目後半8ドット表示 if((ANKFont[letter][j*3+2] & Mask) != 0) LCD_DrawPixel(colum*12+i+8, line*14+j*2+3, color1); else LCD_DrawPixel(colum*12+i+8, line*14+j*2+3, color2); //背景色 Mask = Mask >> 1; } // 分割部4ドットずつ表示 Mask = 0x80; // 1ライン目後半4ドット表示 for(i=0; i<4; i++){ if((ANKFont[letter][j*3+1] & Mask) != 0) LCD_DrawPixel(colum*12+i+12, line*14+j*2+2, color1); else LCD_DrawPixel(colum*12+i+12, line*14+j*2+2, color2); //背景色 Mask = Mask >> 1; } // 2ライン目前半4ドット表示 for(i=4; i<8; i++){ if((ANKFont[letter][j*3+1] & Mask) != 0) LCD_DrawPixel(colum*12+i, line*14+j*2+3, color1); else LCD_DrawPixel(colum*12+i, line*14+j*2+3, color2); //背景色 Mask = Mask >> 1; } } } } /****************************** * 文字列描画関数 * 16文字x20行で指定 ******************************/ void LCD_DrawString(int colum, int line, const char *s, unsigned int color1, unsigned int color2) { while (*s){ LCD_DrawChar(colum++, line, *s++, color1, color2); if(colum >= XChar){ line++; colum = 0; if(line >= YLine) line = 0; } } } /******************* SDカード側のサブ関数(これをsetuo()で呼び、loop()には何も書かない。1回実行で終了) ****************/ /******************************************************** * SDカードのルートディレクトリにあるファイルを読込み * 液晶の1画面にファイル名リストを表示する。 *********************************************************/ void listRootFiles() { // 1. ルートディレクトリを開く File root = SD.open("/"); if (!root) { Serial.println("ルートディレクトリが開けません"); // シリアルモニタに出力 return; } if (!root.isDirectory()) { Serial.println("ディレクトリではありません"); // シリアルモニタに出力 return; } // 2. ループで次々とファイルを取得 File file = root.openNextFile(); int y = 0; while (file) { /// ファイル名取得 const char* fileName = file.name(); /// ファイル名の文字フォントデータを液晶表示関数に渡す。 /// 12x12フォントなので、y座標を1つずつずらしていく。 /// 液晶の1画面にファイル名リストを表示:1画面:26文字/行 * 17文字/列=442キャラクタ digitalWrite(D_CS, LOW); digitalWrite(SD_CS,HIGH); LCD_DrawString(0, y, fileName, WHITE, BLACK); // 液晶にファイル名を表示 y++; digitalWrite(D_CS, HIGH); digitalWrite(SD_CS, LOW); /// 次のファイルへ file = root.openNextFile(); /// 液晶の表示範囲を超えないためのガード(17行まで) if (y >= 16) break; } // end while root.close(); }