/*********************************** * ILI9341 display driver souse file * 2023/10/12 N.Ishii ************************************/ #include "p24FJ64GA002.h" #include "ILI9341.h" // 初期化コマンドのマクロ定義等ヘッダファイル  ポート定義も含む #include "skSPIlib.h" // skSPIlib.c - SPI通信を行う関数ライブラリ(16ビットMCU専用)のヘッダファイル by きむ茶工房 #include "ASCII12dot.h" //ASCII 12x12dot 231010 #include "ImageData.h" // 231012 /********************************************************************************* * 単一のコマンドバイトをILI9341に書き込みます。 * チップセレクトとトランザクションは事前に設定されている必要があります * デバイスを COMMAND モードにし、コマンド1バイトを発行します。 * 関数 -- ILI9341_SPI_Write() を使用するだけです。 * ILI9341_SPI_Write(cmd)は、きむ茶工房のライブラリから、 *  unsigned char SPI2_transfer(char dt)を使用 * * 引数説明: * cmd 8-bit:書込むコマンドデータ 1バイトを指定 **********************************************************************************/ void writeCommand(unsigned char cmd) { TFT_DC = 0; // DCピンをLoにする(コマンド書込みモード) ILI9341_SPI_Write(cmd); // #define ILI9341_SPI_Write(x) SPI2_transfer(x) -> ILI9341.hにて定義されている // SPI2_transfer(x)は、きむ茶工房のライブラリを使用 TFT_DC = 1; } void writeData(unsigned char dat) { ILI9341_SPI_Write(dat); } /************************************************************* * SPI 経由で ILI9341 に接続し、初期化手順コマンドを送信 * * 230720:マクロ記述無し単純化 整理 * 230723:設定追加 * 230725:ガンマ設定追加 * 230805:MADCTL: Memory Access Control設定を最初から0xE8 * (横表示・原点:左上隅・w= 320,h= 240)に変更 * 230810:MADCTL: Memory Access Control設定値を0x28に変更 * 0xE8に対しリバースモードになる **************************************************************/ void tft_begin(void) { /// 100mS幅のリセットパルスを出力 TFT_RST = 0; // delay_ms(20); delay_ms(100); TFT_RST = 1; // delay_ms(20); delay_ms(200); /* PORTAbits.RA4=0; // GREEN LED ON/OFF PULSE FOR TRG 231006 delay_us(200); PORTAbits.RA4=1; */ TFT_CS = 0; // start write // TFT_DC = 1; // DCピンをHiにする -> 既にmainにて初期化しているのでコメントアウト 231006 //// --- ILI9341 動作設定 /// 拡張コマンド:230724 writeCommand(0xCB); // Power control A(デフォルト値と同じ) writeData(0x39); writeData(0x2C); writeData(0x00); writeData(0x34); writeData(0x02); writeCommand(0xCF); // Power control B(デフォルト値は、00h, A2h, FOh) writeData(0x00); writeData(0xC1); writeData(0x30); writeCommand(0xE8); // Driver timing control A(デフォルト値は、84h, 11h, 7Ah) writeData(0x85); writeData(0x00); writeData(0x78); writeCommand(0xEA); // Driver timing control B(デフォルト値は、66h, 00h) writeData(0x00); writeData(0x00); writeCommand(0xED); // Power on sequence control(デフォルト値は、55h, 01h, 23h, 01h) writeData(0x64); writeData(0x03); writeData(0x12); writeData(0x81); writeCommand(0xF7); // Pump ratio control(デフォルト値と同じ) writeData(0x20); // Ratio[1:0]bit= 10 /// 標準コマンド writeCommand(0xC0); //Power control-1:PWCTR1 writeData(0x23); //VRH[5:0]:Set the GVDD level writeCommand(0xC1); //Power control-2:PWCTR2 writeData(0x10); //SAP[2:0];BT[3:0]:Sets the factor used in the step-up circuits. writeCommand(0xC5); //VCM control-1:VMCTR1 writeData(0x3E); //Contrast writeData(0x28); writeCommand(0xC7); //VCM control-2:VMCTR2 writeData(0x86); //-- /* writeCommand(0x01); // SOFTWARE RESET delay_ms(150); // 無くても大丈夫かも? writeCommand(0x13); // Normal Display Mode ON(全画面表示) */ writeCommand(0x36); // MADCTL: Memory Access Control writeData(0x28); // 0xE8に対しリバースモード GBR= ON(正規横表示・原点:左上隅・w= 320,h= 240) writeCommand(0x3A); //COLMOD: Pixel Format Set writeData(0X55); //RGB 16 bits / pixel, MCU 16 bits / pixel(65kカラーモードのビット転送) /// 230723 追加 writeCommand(0xB1); // FRMCTR1:Frame Rate Control (In Normal Mode/Full Colors) writeData(0x00); // Frame Rate= fosc / Clocks per line x Division ratio x (Lines VBP VFP) writeData(0x18); /// 230723 追加 writeCommand(0xB6); // Display Function Control:DFUNCTR writeData(0x08); writeData(0x82); writeData(0x27); /// ガンマ設定:230725 追加 writeCommand(0xF2); // 3Gamma Function Disable writeData(0x00); writeCommand(0x26); //Gamma curve selected writeData(0x01); writeCommand(0xE0); //Set Gamma writeData(0x0F); writeData(0x31); writeData(0x2B); writeData(0x0C); writeData(0x0E); writeData(0x08); writeData(0x4E); writeData(0xF1); writeData(0x37); writeData(0x07); writeData(0x10); writeData(0x03); writeData(0x0E); writeData(0x09); writeData(0x00); writeCommand(0xE1); //Set Gamma writeData(0x00); writeData(0x0E); writeData(0x14); writeData(0x03); writeData(0x11); writeData(0x07); writeData(0x31); writeData(0xC1); writeData(0x48); writeData(0x08); writeData(0x0F); writeData(0x0C); writeData(0x31); writeData(0x36); writeData(0x0F); writeCommand(0x11); //Sleep OUT // delay_ms(60); // このdelayは絶対に必要:電源が不安定な場合は長めに(120ms位に)する delay_ms(150); writeCommand(0x29); //Display ON delay_ms(150); // 230721 追加 TFT_CS = 1; // end write } /**************************************************************************************** * 「アドレス ウィンドウ」を設定。 * これは、SPI データ書込みの次のチャンク(塊?)で RAM に書き込む四角形です。 * ILI9341 は、各行が埋められるとデータを自動的にラップ(包む?)します。 * * 230720:引数無し・マクロ記述無し等、単純化 * 230802:WIDTHとHIGHTのテレコ修正(横置き表示) * 231008:全画面専用になっているが引数を用意してユニバーサルに対応 * 引数説明: * x1:TFT メモリ「x」の原点(RAM開始アドレス?) * y1:TFT メモリ「y」の原点 * w :長方形の幅 * h :長方形の高さ *****************************************************************************************/ void setAddrWindow(int x1, int y1, int w, int h) { int x2 = (x1 + w - 1), y2 = (y1 + h - 1); writeCommand(0x2A); // Column address set writeData((unsigned char)((x1 >> 8) & 0x00FF)); writeData((unsigned char)(x1 & 0x00FF)); writeData((unsigned char)((x2 >> 8) & 0x00FF)); writeData((unsigned char)(x2 & 0x00FF)); writeCommand(0x2B); // Row(Page) address set writeData((unsigned char)((y1 >> 8) & 0x00FF)); writeData((unsigned char)(y1 & 0x00FF)); writeData((unsigned char)((y2 >> 8) & 0x00FF)); writeData((unsigned char)(y2 & 0x00FF)); writeCommand(0x2C); // Write to RAM /* void setAddrWindow(void) { writeCommand(0x2A); // Column address set writeData(0); // 開始カラム:0(16bit転送= 2byte) writeData(0); writeData(0b00000001); // 終了カラム:319(16bit転送= 2byte) writeData(0b00111111); // 256 + 63 = 319 writeCommand(0x2B); // Row(Page) address set writeData(0); // 開始ページ:0(16bit転送= 2byte) writeData(0); writeData(0); // 終了ページ:239(16bit転送= 2byte) writeData(239); writeCommand(0x2C); // Write to RAM */ } /************************************************************************** * 長方形を1つの色で完全に塗りつぶす関数 * * 引数説明 * x:左上隅のx座標 * y:左上隅のy座標 * w:幅(ピクセル単位) * h:高さ(ピクセル単位) * color:16 ビット 5-6-5形式の 塗りつぶす色を指定 * * <変更履歴> * 230727 * ・320と240の固定値をテレコに変更 * 231006 * ・340 -> 320 * ・塗り潰し範囲内の画素総数:pxを直(320x240= 76800ドット)に指定 * 231007 * ・76800固定値指定では本来の関数の意味がなくなる。 *  積結果のキャスト記述を変更してみた *  整理して、全ての変数を極性無し型にし、積結果の()を無くし、longでキャストしたら上手く行った。 * px = (long)w * h; * 231008 * ・setAddrWindow() -> setAddrWindow(x, y, w, h)に変更 ***************************************************************************/ void fillRect(int x, int y, int w, int h, unsigned int color) { // unsigned long px; long px; /// 16bitカラーコードを上位8bit/ 下位8bitに変換(RGB565変換) unsigned char hi,lo; hi = (unsigned char)((color >> 8) & 0x00FF); lo = (unsigned char)(color & 0x00FF); if( (x < 320) && (y < 240) && w && h) { // 塗り潰し範囲が範囲内か?(ゼロ以外の幅と高さになっているか?) // if((x + w - 1) >= 340) w = 340 - x; if((x + w - 1) >= 320) w = 320 - x; // 231006 if((y + h - 1) >= 240) h = 240 - y; // PORTAbits.RA3=0; // RED LED ON(デバッグ・ロジアナトリガ用)230708 TFT_CS = 0; // start write // setAddrWindow(); setAddrWindow(x, y, w, h); // 231008 // px = (unsigned long)(w * h); // 塗り潰し範囲内の画素総数 px = (long)w * h; // px = 76800; // 231006 /// 塗り潰し範囲内の画素総数(320x240= 76800ドット)だけSPI書込みし指定色で塗潰す while (px--) { writeData(hi); writeData(lo); } TFT_CS = 1; // end write } } /**************************************************************************************** * 1Dot描画関数 * * 引数説明 * x:dot描画のx座標位置 * y:dot描画のy座標位置 * color:16 ビット 5-6-5形式の色を指定 * * 231009: *****************************************************************************************/ void drawPixel(int x, int y, unsigned int color) { if (x < 0 || x >= 320 || y < 0 || y >= 240) return ; // dot描画位置が範囲外の時は以下の処理パス /// 16bitカラーコードを上位8bit/ 下位8bitに変換(RGB565変換) unsigned char hi,lo; hi = (unsigned char)((color >> 8) & 0x00FF); lo = (unsigned char)(color & 0x00FF); int xe = x+1 ; int ye = y+1 ; TFT_CS = 0; // start write writeCommand(0x2A); // Column address set writeData((unsigned char)((x >> 8) & 0x00FF)); writeData((unsigned char)(x & 0x00FF)); writeData((unsigned char)((xe >> 8) & 0x00FF)); writeData((unsigned char)(xe & 0x00FF)); writeCommand(0x2B); // Row(Page) address set writeData((unsigned char)((y >> 8) & 0x00FF)); writeData((unsigned char)(y & 0x00FF)); writeData((unsigned char)((ye >> 8) & 0x00FF)); writeData((unsigned char)(ye & 0x00FF)); writeCommand(0x2C); // Write to RAM /// SPI書込みし指定色でdot描画 writeData(hi); writeData(lo); TFT_CS = 1; // end write } /******************************************************************* * 直線描画関数 * * 引数説明 * x0:始点のx座標位置 * y0:始点のy座標位置 * x1:終点のx座標位置 * y1:終点のy座標位置 * color:16 ビット 5-6-5形式の色を指定 * * 231009 ********************************************************************/ #define abs(a) (((a)>0) ? (a) : -(a)) void drawLine(int x0, int y0, int x1, int y1, unsigned short color) { int steep, t; int deltax, deltay, error; int x, y; int ystep; y0=240-y0 -1; // Y座標反転 y1=240-y1 -1; /// 差分の大きいほうを求める steep = (abs(y1 - y0) > abs(x1 - x0)); /// x、yの入れ替え if(steep){ t = x0; x0 = y0; y0 = t; t = x1; x1 = y1; y1 = t; } if(x0 > x1) { t = x0; x0 = x1; x1 = t; t = y0; y0 = y1; y1 = t; } deltax = x1 - x0; // 傾き計算 deltay = abs(y1 - y0); error = 0; y = y0; /// 傾きでステップの正負を切り替え if(y0 < y1) ystep = 1; else ystep = -1; /// 直線を点で描画 for(x=x0; x<=x1; x++) { if(steep) drawPixel(y,x,color); else drawPixel(x,y,color); error += deltay; if((error << 1) >= deltax) { y += ystep; error -= deltax; } } } /************************************* * 円を描く関数 * 中心点と半径を指定 * (Fussyさんのアルゴリズムを使用) * * 引数説明 * x0:中心のx座標位置 * y0:中心のy座標位置 * r :半径 * color:16 ビット 5-6-5形式の色を指定 * * 231009 **************************************/ void drawCircle(int x0, int y0, int r, unsigned int color) { int x = r; int y = 0; int F = -2 * r + 3; while(x >= y){ drawPixel(x0+x, y0+y, color); drawPixel(x0-x, y0+y, color); drawPixel(x0+x, y0-y, color); drawPixel(x0-x, y0-y, color); drawPixel(x0+y, y0+x, color); drawPixel(x0-y, y0+x, color); drawPixel(x0+y, y0-x, color); drawPixel(x0-y, y0-x, color); if(F >= 0){ x--; F -= 4 * x; } y++; F += 4 * y + 2; } } /******************************************************** * 16bitBMPカラーイメージ表示関数 * 100x100dot対応 * * 231012 *********************************************************/ void drawImage(void) { int Xpos, Ypos; int ptr; /// BMPフォーマットのデータは、左下から右上に向かって配列されているので /// この順で描画すると、上下逆様の画像表示になってしまう。 /// 右上の画素(ptr=(20138/2)-1)=10068, Xpos= 99, Ypos= 0)から下に向かって描画する必要がある。 ptr= 10068; for(Ypos=0; Ypos < 100; Ypos++){ for(Xpos=99; Xpos > -1; Xpos--){ drawPixel(Xpos, Ypos, ImageData[ptr--]); } } } /***************************************** * ANK文字表示関数 12x12ドット * 320/12=26文字/行 240/14=17行 * (0, 0) - (25, 16)の範囲 * * 231010 ******************************************/ void drawChar(char colum, char line, unsigned char letter, unsigned int color1, unsigned int color2){ unsigned char j, i, Mask; #define ENDCOL 320 // X #define ENDROW 240 // Y #define XChar (int)((ENDCOL) / 12) #define YLine (int)((ENDROW) / 14) 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) drawPixel(colum*12+i+4, line*14+j*2+2, color1); else drawPixel(colum*12+i+4, line*14+j*2+2, color2); //背景色 // 2ライン目後半8ドット表示 if((ANKFont[letter][j*3+2] & Mask) != 0) drawPixel(colum*12+i+8, line*14+j*2+3, color1); else 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) drawPixel(colum*12+i+12, line*14+j*2+2, color1); else 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) drawPixel(colum*12+i, line*14+j*2+3, color1); else drawPixel(colum*12+i, line*14+j*2+3, color2); //背景色 Mask = Mask >> 1; } } } } /****************************** * 文字列描画関数 * 16文字x20行で指定 ******************************/ void drawStr(char colum, char line, char *s, unsigned int color1, unsigned int color2) { while (*s){ drawChar(colum++, line, *s++, color1, color2); if(colum >= XChar){ line++; colum = 0; if(line >= YLine) line = 0; } } } //////// usec単位ディレイ関数 void delay_us(int usec){ usec = (int)(CLOCK*usec)/ 10; while(usec) { asm("NOP"); asm("NOP"); asm("NOP"); asm("NOP"); asm("NOP"); usec--; } } /////// msec単位ディレイ関数 void delay_ms(int msec){ int i; for(i=0; i< msec; i++) delay_us(1000); }