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 件のコメント:
コメントを投稿
コメントをどうぞ