LCD表示 1 (SPI接続でQVGA LCDパネル表示)
最初に
AitendoからSPI制御のQVGA LCDパネルM-TM022-SPIを入手したので接続してみました。
このLCDはSPIで接続できるので、簡単な配線だけで表示が可能です。
またドライバICはILI9340という物ですが、この辺のドライバICはどれも似たような
コマンド体系を持っているようなので、他のドライバの資料を参考にする事が出来ます。
参考:LCD&タッチセンサ活用の素
実験を行う前にbcm2835ライブラリのインストールとSPIが使える準備をしておいてください。
材料
- 2.2インチ液晶モジュール(240x320/SPI)[M-TM022-SPI]
- 抵抗(220Ω位。私は手元に470Ωしかなかったので、それを代用)
- 26pinの2列ソケット。出来れば10pinの1列ソケットも。
配線
- 配線は簡単です。下記のソースリストを参考にしてください。
プログラム
- SPIの初期化
SPIの初期化はbcm2835ライブラリのサンプルプログラムをそのままお借りしました。 SPI access
SPIのモードは以下の様にセッティングしました。- MSBFIRST
- MODE0
- Clock divide=8
- CS=Lo
- パネルの初期化
パネルの初期化はAitendoのサンプルプログラムを流用しました。 デモコード(8051)
- RGB565フォーマット変換
このLCDの1画素あたりの色情報は16ビットであり、そのフォーマットはRGB565などと呼ばれています。
RGB565フォーマットとは赤5ビット、緑6ビット、青5ビットで色を表す仕組みで、データの並びは"RRRRRGGG GGGBBBBB"の様になっており、 2バイトで色を表現できるのでプログラムが楽になります。
但し2バイトだと色数が限られるので、現在のパソコンはは24ビット(8bit*3色)で表現する事が多い。両者を変換するルーチンを用意しておくと便利です。
詳しくは、16 ビット RGB の操作 - MSDN - Microsoft を見てください。 - ILI9340のコマンド
コマンド発行の仕方
ILI9340はコマンドを発行し、直後にデータを送信する。と言う形式で操作します。
D/CラインをLoにしてからデータ送信するとコマンドと解釈されます。
D/CラインをHighにしてからデータ送信するとデータと解釈されます。
コマンドは非常に沢山ありますが、基本的に下記の三つのコマンドだけで描画が可能です。- xアドレス(座標)セットコマンド (0x2A)
操作したいx座標をセットするには、コマンド0x2Aを発行し、その後にスタートアドレス2バイト、エンドアドレス2バイトを送信します。
1画素だけ操作したい場合はスタートアドレス=エンドアドレスとします。 - yアドレス(座標)セットコマンド (0x2B)
同様に操作したいy座標をセットするには、コマンド0x2Bを発行し、その後にスタートアドレス2バイト、エンドアドレス2バイトを送信します。
1画素だけ操作したい場合はスタートアドレス=エンドアドレスとします。 - メモリ書き込みコマンド (0x2C)
0x2Cコマンドを発行し、その後にデータ2バイト(RGB565)を送信すると上記のアドレス(1画素)にデータ(色)が書き込まれます。
更に連続してデータを送信するとエンドアドレスまで順々にデータが書き込まれます。この機能を使えば、矩形の塗りつぶし(FILL)を高速に実行できます。
- xアドレス(座標)セットコマンド (0x2A)
- 直線の描画
ドライバ標準のコマンドだけでは、点か矩形しか描画が出来ません。任意の直線を引くには計算が必要です。
ブレゼンハムのアルゴリズムが元になっているらしいですが、整数演算だけで直線描画が出来るので、非常に高速です。
どこかから2D描画ルーチンを拝借するのが一番手っ取り早いのですが、お正月で暇なので、復習を兼ねて直線描画ルーチンを書いてみます。 (これを車輪の再発明と言います)
参考にしたのは、1982年発行 ポケットコンピューティング―情報化時代の知的ツール (アスキーブックス) [単行本]乾 謙一 (著) 名機PC-1500の活用本、何十年ぶりに開きました。
動きを簡単に説明すると、座標をx方向に移動しながら、傾きの余りが残っていたらY座標を1ドット上に上げる。と言う方法で直線を描きます。 傾きが45度以上になる時は、移動方向を入れ替えます。
完成したプログラム
// LCD test program 20130103 by ImageWriter // ili9340.c // This is M-TM022-SPI(controled by ILI9340) LCD-panel test program on Raspberry Pi. // Used by bcm2835 library.(Mike McCauley) // // After installing bcm2835, you can build this // with something like: // gcc -o spi ili9340.c -l bcm2835 // sudo ./ili9340 // // [Pin connection] // M-TM022-SPI Rpi(pin) // ------------------------ // MISO------------MISO(21) // LED---220ohm----3.3V( 1) // SCK-------------SCLK(23) // MOSI------------MOSI(19) // D/C-------------IO17(11) LOW = 0 = COMMAND // RES-------------IO18(12) LOW = 0 = RESET // CS--------------CS0 (24) LOW = 0 = Chip Select // GND-------------0V ( 6) // VCC-------------3.3V( 1) // ------------------------ // // [SPI settings (spi_init())] // ORDER MSB First // MODE Mode0 // Divide 8 // CS CS0 // CS_POL LOW // // #include < bcm2835.h > #include < stdio.h > #include < stdlib.h > #define D_C 17 // GPIO17=D/C #define RES_ 18 // GPIO18=RESET int a,data; // SPI interfase initialize // MSB,mode0,clock=8,cs0=low void spi_init(void){ bcm2835_spi_begin(); bcm2835_spi_setBitOrder(BCM2835_SPI_BIT_ORDER_MSBFIRST); // The default MSBFIRST bcm2835_spi_setDataMode(BCM2835_SPI_MODE0); // The default MODE0 bcm2835_spi_setClockDivider(BCM2835_SPI_CLOCK_DIVIDER_8); // The default 65536 bcm2835_spi_chipSelect(BCM2835_SPI_CS0); // The default CS0 bcm2835_spi_setChipSelectPolarity(BCM2835_SPI_CS0, LOW); // the default LOW // Send a byte to the slave and simultaneously read a byte back from the slave // If you tie MISO to MOSI, you should read back what was sent } // SPI setup void spi_reset(void){ bcm2835_gpio_fsel(D_C,BCM2835_GPIO_FSEL_OUTP); // D/C bcm2835_gpio_fsel(RES_,BCM2835_GPIO_FSEL_OUTP); // Reset bcm2835_gpio_write(D_C, HIGH); // D/C = H bcm2835_gpio_write(RES_, LOW); // Reset bcm2835_delay(100); bcm2835_gpio_write(RES_, HIGH); // Reset off bcm2835_delay(100); } // SPI Write Command // D/C=LOW then,write command(8bit) void write_command(unsigned char c){ int data; bcm2835_gpio_write(D_C,LOW); data = bcm2835_spi_transfer(c); bcm2835_gpio_write(D_C,HIGH); // printf("COMMAND: %02X\n", c); // printf("DATA: %02X\n", data); } // SPI Write Data // D/C=HIGH then,write data(8bit) void write_data(unsigned char c){ int data; bcm2835_gpio_write(D_C,HIGH); data = bcm2835_spi_transfer(c); } // ILI9340 initialize // see M-TM022-SPI demo code(8051) void lcd_initial(void){ write_command(0xCB); write_data(0x39); write_data(0x2C); write_data(0x00); write_data(0x34); write_data(0x02); write_command(0xCF); write_data(0x00); write_data(0XC1); write_data(0X30); write_command(0xE8); write_data(0x85); write_data(0x00); write_data(0x78); write_command(0xEA); write_data(0x00); write_data(0x00); write_command(0xED); write_data(0x64); write_data(0x03); write_data(0X12); write_data(0X81); write_command(0xF7); write_data(0x20); write_command(0xC0); //Power control write_data(0x23); //VRH[5:0] write_command(0xC1); //Power control write_data(0x10); //SAP[2:0];BT[3:0] write_command(0xC5); //VCM control write_data(0x3e); // write_data(0x28); write_command(0xC7); //VCM control2 write_data(0x86); //-- write_command(0x36); // Memory Access Control // //0x48 0x68 //0x28 0xE8 write_data(0x48); // write_command(0x3A); write_data(0x55); write_command(0xB1); write_data(0x00); write_data(0x18); write_command(0xB6); // Display Function Control write_data(0x08); write_data(0x82); write_data(0x27); write_command(0xF2); // 3Gamma Function Disable write_data(0x00); write_command(0x26); //Gamma curve selected write_data(0x01); write_command(0xE0); //Set Gamma write_data(0x0F); write_data(0x31); write_data(0x2B); write_data(0x0C); write_data(0x0E); write_data(0x08); write_data(0x4E); write_data(0xF1); write_data(0x37); write_data(0x07); write_data(0x10); write_data(0x03); write_data(0x0E); write_data(0x09); write_data(0x00); write_command(0XE1); //Set Gamma write_data(0x00); write_data(0x0E); write_data(0x14); write_data(0x03); write_data(0x11); write_data(0x07); write_data(0x31); write_data(0xC1); write_data(0x48); write_data(0x08); write_data(0x0F); write_data(0x0C); write_data(0x31); write_data(0x36); write_data(0x0F); write_command(0x11); //Exit Sleep bcm2835_delay(120); write_command(0x29); //Display on write_command(0x2c); } // RGB565 conversion // RGB565 is R(5)+G(6)+B(5)=16bit color format. // Bit image "RRRRRGGGGGGBBBBB" // unsigned int rgb565_conv(unsigned int r,unsigned int g,unsigned int b){ unsigned int RR,GG,BB; RR = (r * 31 / 255) << 11; GG = (g * 63 / 255) << 5; BB = (b * 31 / 255); return(RR | GG | BB); } // Write data word // unsigned int write_data_w(unsigned int w){ unsigned char hi,lo; hi = (char) (w >> 8); lo = (char) (w & 0x00FF); write_data(hi); write_data(lo); } // Addres set (1point) // void addset(unsigned int x, unsigned int y){ write_command(0x2A); // set column(x) address write_data_w(x); write_data_w(x); write_command(0x2B); // set Page(y) address write_data_w(y); write_data_w(y); write_command(0x2C); // Memory Write } // Addres set 2 (rectangle) // void addset2(unsigned int x1, unsigned int y1, unsigned int x2, unsigned int y2 ){ write_command(0x2A); // set column(x) address write_data_w(x1); // set start x write_data_w(x2); // set end x write_command(0x2B); // set Page(y) address write_data_w(y1); // set start y write_data_w(y2); // set end y write_command(0x2C); // Memory Write } // Mem read int mem_read(unsigned int x0, unsigned int y0){ int data; write_command(0x2A); // set column(x) address write_data_w(x0); write_data_w(x0); write_command(0x2B); // set Page(y) address write_data_w(y0); write_data_w(y0); write_command(0x2E); bcm2835_gpio_write(D_C,HIGH); data = bcm2835_spi_transfer(0x00); // dummy data data = bcm2835_spi_transfer(0x00); printf("%02X",data); data = bcm2835_spi_transfer(0x00); printf("%02X:",data); } void dot_plot(unsigned int x,unsigned int y){ addset(x,y); write_data_w(rgb565_conv(255,255,255)); //plot } // line plot // // /---(x1,y1) - // /---/ | // /---/ dy // (x0,y0)---/ | // |<--------- dx --------->| - // |<--- SS --->| void line_plot(int x0,int y0,int x1,int y1){ int dx,dy, // difference x,y SS, // rest XX,YY, // plot point temp, ADDX,ADDY; // Addition X,Y if(x0>240 | y0>320 | x1>240 | y0>320) return; dx = x1-x0; // dx dy = y1-y0; // dy if(dx>0){ // plot direction x ADDX= 1; } else if(dx<0){ ADDX= -1; } else { ADDX= 0; } if(dy>=0){ // plot direction y ADDY= 1; } else if(dy<0){ ADDY= -1; } else { ADDY= 0; } if(abs(dx)>=abs(dy)){ // if angle < 45deg SS = abs(dx/2); XX = x0; YY = y0; while (XX != x1+ADDX){ //printf("XX,YY:%d,%d\n",XX,YY); addset(XX,YY); write_data_w(rgb565_conv(255,255,255)); //plot SS = SS - abs(dy); if(SS > 0){ XX = XX + ADDX; YY = YY; } else { XX = XX + ADDX; YY = YY + ADDY; SS = SS + abs(dx); } } } else { SS = abs(dx/2); XX = x0; YY = y0; while (YY != y1+ADDY){ //printf("XX,YY:%d,%d\n",XX,YY); addset(XX,YY); write_data_w(rgb565_conv(255,255,255)); //plot SS = SS - abs(dx); if(SS > 0){ XX = XX; YY = YY + ADDY; } else { XX = XX + ADDX; YY = YY + ADDY; SS = SS + abs(dy); } } } } int main(int argc, char **argv) { if (!bcm2835_init()) return 1; spi_init(); spi_reset(); lcd_initial(); int i,j; // color bar for(i=0;i<240;i++){ // x = 0 to 239 for(j=0;j<320;j++){ // y = 0 to 319 //write_data(rand()); //write_data(rand()); if(j<106){ addset(i,j); write_data_w(rgb565_conv(255,0,0)); //red } else if(j<212) { addset(i,j); write_data_w(rgb565_conv(0,255,0)); //green } else { addset(i,j); write_data_w(rgb565_conv(0,0,255)); //blue } } } // rectangle fill addset2(200,200,200+30,200+30); // x1,y1,x2,y2 for(i=0;i<30*30;i++){ write_data_w(rgb565_conv(64,128,32)); // fill } // line plot(radial) line_plot(100,100, 50, 80); line_plot(100,100, 50,120); line_plot(100,100,150, 80); line_plot(100,100,150,120); line_plot(100,100, 80, 50); line_plot(100,100,120, 50); line_plot(100,100, 80,150); line_plot(100,100,120,150); line_plot(100,100, 62, 62); line_plot(100,100, 62,138); line_plot(100,100,138, 62); line_plot(100,100,138,138); line_plot(100,100,100, 48); line_plot(100,100,100,152); line_plot(100,100,152,100); line_plot(100,100, 48,100); // line_plot(rectangle) line_plot(10,10,10,50); line_plot(10,50,50,50); line_plot(50,50,50,10); line_plot(50,10,10,10); bcm2835_delay(1000); // line_plot(random) int x0,y0,x1,y1; for(i=0;i<50;i++){ x0=(char)rand(); y0=((char)rand()*2); x1=(char)rand(); y1=((char)rand()*2); line_plot(x0,y0,x1,y1); // printf("%d,%d,%d,%d\n",x0,y0,x1,y1); } //for(i=0;i<240;i++){ // for(j=0;j<320;j++){ // //dot_plot(i,j); // //printf("%d:%d:",i,j); // mem_read(i,j); // } //} //mem_read(10,10); bcm2835_spi_end(); bcm2835_close(); return 0; }
最初に3色のカラーバーを表示し、その後、放射状の線の描画、矩形のフィルを行った後、ランダムに直線を描きます。
0 件のコメント:
コメントを投稿
コメントをどうぞ