目次
前
次
グラフィックLCD制御リニューアル
Game Boy Cameraの画像を表示するために
グラフィックLCDを利用してきました。
グラフィックLCDでは、画面をラスターで配置する
のではなく、ライン方向に配置しています。
ラスター方向に0と1の組合わせで入っている内容
をライン方向に並べ直してから、グラフィックLCDに
転送します。
2値化した画像データを、グラフィックLCD(以下GLCD)に
転送するには、8x8のブロックデータに変換することが
必要になります。
8x8のブロックデータに変換する場合、次のように
ラスター方向のデータを切り出してから、並べ替えて
いきます。(右に90度回転するのと同じです)
CUIのCコンパイラで、切り出す処理と並べ替えを定義
すると、次の関数で実現できます。
#define PIXEL_SIZE 8
void block_copy(byte *dst,byte *src)
{
byte cnt ;
byte i ;
for ( cnt = 0 ; cnt < PIXEL_SIZE ; cnt++ ) {
i = (cnt << 3) ;
*(dst+cnt) = *(src+i) ;
}
}
void arrange(byte *dst,byte *src)
{
byte i ;
byte j ;
byte k ;
byte mask ;
/* arrange block */
for ( i = 0 ; i < PIXEL_SIZE ; i++ ) {
/* generate mask pattern */
mask = (1 << (7-i));
/* clear */
*(dst+i) = 0 ;
/* reverse */
for ( j = 0 ; j < PIXEL_SIZE ; j++ ) {
/* calcualte pointer */
k = 7 - j ;
/* add bit code */
*(dst+i) |= (((*(src+k) & mask) << i) >> j);
}
}
}
void show_block(byte *ptr)
{
byte cnt ;
putchar('\n');
for ( cnt = 0 ; cnt < PIXEL_SIZE ; cnt++ ) {
binary_display( *(ptr+cnt) );
putchar('\n');
}
}
8バイトの配列を2つ用意し、一方をラスターごとに
並べたデータを転送するために使い、他方を90度の
回転した結果を格納するに利用します。
2値化した画像データが64ピクセルx64ラインあると
全体では512バイトになります。8バイトの配列を2つ
用意しても、530バイトに満たないので、SRAMの容量が
小さいマイコンでも処理できます。
2015年マシンにも利用したArduinoに、GLCDを接続して
画像を表示してみます。
GLCDへ画像表示する実験システムは、以下とします。
Arduinoの中にフレームバッファを用意し、フレームバッファ
の内容を更新するため、Personal Computerの端末ソフトから
コマンドを与えます。
コマンドは、次のように決めました。
- ? help
- P set point
- R reset point
- C clear frame buffer with '0'
- F set frame buffer with '1'
- S show frame buffer
- L draw line
コマンド'P'、'R'があれば、点を打つ、点を消すを
実現できるので、'L'を定義できます。
点を打つ、点を消すのは、フレームバッファの1バイト
を取り出してきて、指定のビットを1か0にすることで
実現できます。
これを実現する関数は、以下。
void set_point(byte x,byte y,byte which)
{
word loc ;
byte q ;
byte r ;
q = x / 8 ;
r = x % 8 ;
/* calcualte location */
loc = 8 * y + q ;
/* store */
if ( which ) { *(glcd+loc) |= (1 << (7-r) ); }
else { *(glcd+loc) <= ~(1 << (7-r) ); }
}
x、yの座標を与えておいて、フレームバッファglcdの中で
どのバイトのどのビットに対応するのかを計算しています。
該当バイトと該当ビットがわかれば、1と0を設定。
64ピクセルx64ラインを考えているので、8ピクセルで
1バイトを構成します。ラインは、0、8、16のように
8バイトごとに変化していくので、フレームバッファを
1次元配列で定義できます。
コマンド'P'と'R'を担当する関数は、次のようになります。
void pset(byte x,byte y)
{
set_point(x,y,ON);
}
void pclr(byte x,byte y)
{
set_point(x,y,OFF);
}
コマンド'P'、'R'には、x、yの座標が必要なので
端末からは、次のように入力します。
x、yは、10進数の1桁か2桁なので、受信バッファから
レコードの最後を示す'\r'までを切出して変換します。
受信バッファをsbufとして考えます。
char msg[8] ;
/* copy */
last = 0 ;
for ( i = 0 ; i < 8 ; i++ ) {
*(msg+i) = *(sbuf+i+1) ;
last = i ;
if ( *(msg+i) == ',' ) { xcloc = i ; }
if ( *(sbuf+i+1) == '\r' ) break ;
}
/* calculate x component */
xx = 0 ;
for ( i = 0 ; i < xcloc ; i++ ) {
xx = 10 * xx + get_hex( *(msg+i) );
}
/* calculate y component */
yy = 0 ;
for ( i = xcloc+1 ; i < last ; i++ ) {
yy = 10 * yy + get_hex( *(msg+i) );
}
数字を数値に変換するために、関数get_hexを
利用します。get_hexは、次のように定義。
byte get_hex(char x)
{
byte result ;
/* default */
result = 0 ;
/* convert */
if ( '0' <= x && x <= '0' ) { result = x - '0' ; }
if ( 'A' <= x && x <= 'F' ) { result = x - 'A' + 10 ; }
if ( 'a' <= x && x <= 'f' ) { result = x - 'a' + 10 ; }
return result ;
}
コマンド'C'と'F'は、フレームバッファglcdの各バイトを
16進で00にするかFFにするかだけなので、土台となる関数
を定義して対応します。
void set_frame(byte x)
{
word cnt ;
byte tmp ;
tmp = 0 ;
if ( x ) { tmp = 0xff ; }
for ( cnt = 0 ; cnt < BSIZE ; cnt++ ) {
*(glcd+cnt) = tmp ;
}
}
void clear_frame(void)
{
set_frame(OFF);
}
void fill_frame(void)
{
set_frame(ON);
}
コマンド'S'は、'P'、'R'、'C'、'F'の各コマンドで
フレームバッファの内容が変化したのかを確認する
ために利用します。
Personal Computerの端末にビットパターンを表示
できればよいので、処理は単純です。
for ( cnt = 0 ; cnt < BSIZE ; cnt++ ) {
binary_display( *(glcd+cnt) );
if ( (cnt % 8) == 7 ) crlf();
}
コマンド'L'は、操作仕様を決めてから、該当する
関数を考えます。直線を引くので始点と終点を指定
します。
始点と終点は確定しているので、点を打ちます。
次に始点と終点のx、y方向の差分を求めて
直線の式を作成します。
直線の式に始点のx座標に1を加えていきながら
y座標を求め、算出された点座標を利用して
点を打っていきます。
2点の座標を取得するには、座標の成分が最大2桁で
あることを利用します。
/* copy */
for ( i = 0 ; i < 13 ; i++ ) {
*(msg+i) = *(sbuf+i+1) ;
if ( *(sbuf+i+1) == '\r' ) break ;
}
/* get x component */
x0 = 0 ;
for ( i = 0 ; i < 3 ; i++ ) {
xx = i ;
if ( *(msg+i) == ',' ) break ;
x0 = x0 * 10 + get_hex( *(msg+i) );
}
/* get y component */
yy = xx + 1 ;
y0 = 0 ;
for ( i = yy ; i < yy + 3 ; i++ ) {
xx = i ;
if ( *(msg+i) == ',' ) break ;
y0 = y0 * 10 + get_hex( *(msg+i) );
}
/* start point */
pset(x0,y0);
/* get x component */
yy = xx + 1 ;
x1 = 0 ;
for ( i = yy ; i < yy+3 ; i++ ) {
xx = i ;
if ( *(msg+i) == ',' ) break ;
x1 = x1 * 10 + get_hex( *(msg+i) );
}
/* get y component */
yy = xx + 1 ;
y1 = 0 ;
for ( i = yy ; i < yy+3 ; i++ ) {
if ( *(msg+i) == '\r' ) break ;
y1 = y1 * 10 + get_hex( *(msg+i) );
}
/* end point */
pset(x1,y1);
2点の座標がわかれば、直線の傾きを計算して
直線に式を導出し、逐次始点から離れていく点
の座標を求めます。
/* calculate difference */
xx = x1 - x0 ;
yy = y1 - y0 ;
if ( x0 > x1 ) { last = (-1)*xx ; }
else { last = xx ; }
/* calculate next point */
for ( i = 1 ; i < last ; i++ ) {
x1 = x0 + i ;
y1 = y0 + (char)(((1.0 * yy) / xx) * 1.0 * i) ;
pset(x1,y1);
}
フレームバッファの操作が出来てしまえば、内容を
GLCDに転送するコマンドを定義します。
GLCDのインタフェースは、次の回路にしました。
Arduinoのピンを10以上使うのは、他の処理ができなく
なることがあるため、シフトレジスタを入れます。
ピン割当ては、次のようにしました。
- D7 (PD7) T164_DATA
- D6 (PD6) T164_CLK
- D5 (PD5) LCD_RST
- D4 (PD4) LCD_CS2
- D17(PC3) LCD_CS1
- D16(PC2) LCD_RS
- D15(PC1) LCD_RW
- D14(PC0) LCD_E
シフトレジスタにデータ転送する関数を定義します。
void send_t164(byte x)
{
byte i ;
byte tmp ;
/* copy */
tmp = x ;
/* loop */
for ( i = 0 ; i < 8 ; i++ ) {
/* impress data */
PORTD &= ~(1 << LCD_DAT);
if ( tmp & 0x80 ) { PORTD |= (1 << LCD_DAT); }
/* TRG : H */
PORTD |= (1 << LCD_CLK);
/* shift */
tmp <<= 1 ;
/* TRG : L */
PORTD &= ~(1 << LCD_CLK);
}
PORTD &= ~(1 << LCD_DAT);
}
LCDには、データとコマンドを与えますが、LCD_RSの値で
データかコマンドかを指定します。どちらでも操作可能
な関数を定義し、それを利用したラッパー関数を作って
おきます。
void send_lcd_primitive(byte which,byte x)
{
byte tmp ;
/* copy */
tmp = x ;
/* transfer */
send_t164( tmp ) ;
/* impress RS */
PORTC &= ~(1 << LCD_RS);
if ( which ) { PORTC |= (1 << LCD_RS); }
/* latch */
PORTC |= (1 << LCD_E);
PORTC &= ~(1 << LCD_E);
}
void put_lcd_data(byte x)
{
send_lcd_primitive(ON,x);
}
void put_lcd_func(byte x)
{
send_lcd_primitive(OFF,x);
}
ここまでの関数を利用すると、LCDを初期化できるので
初期化専用の関数を定義します。
void init_lcd(void)
{
/* send reset signal */
PORTD &= ~(1 << LCD_RST) ;
delay(1);
PORTD |= (1 << LCD_RST) ;
/* delay 30 ms */
delay( 30 ) ;
/* select first line */
put_lcd_func(0xC0);
/* display on */
put_lcd_func(0x3f);
}
利用するグラフィックLCDは、128ピクセルx64ラインですが
64ピクセルx64ラインを左右に分けて扱います。
コマンド'T'に続けて0で左、1で右にフレームバッファの
内容をグラフィックLCDに転送します。
転送用関数は、以下としました。
void display_lcd(byte which,byte *x)
{
byte line ;
byte col ;
word ptr ;
byte cnt ;
byte tmp ;
/* select plane */
select_plane(which);
/* disappear */
display(OFF);
/* transfer data */
set_start_line( 0 ) ;
for ( line = 0 ; line < 8 ; line++ ) {
set_page( line ) ;
set_address(0) ;
for ( col = 0 ; col < 8 ; col++ ) {
/* calculate pointer */
ptr = (line << 6) + col ;
/* get block */
block_copy( gtmp , x + ptr );
/* rotate */
rotate( gg , gtmp );
/* store */
for ( cnt = 0 ; cnt < 8 ; cnt++ ) { put_lcd_data( *(gg+cnt) ); }
}
}
/* appear */
display(ON);
/* disable all plane */
select_plane(SEL_NONE);
}
64ピクセルx64ラインのフレームバッファの内容を
8ピクセルx8ラインずつ分けて、LCDに転送します。
64ピクセルx8ラインを1ページとして扱うので
8ページ処理すると終了します。
どのページを扱うのかを関数set_pageで指定します。
ページごとに、どこから格納を始めるのかを関数
set_addressで設定します。この2関数と、表示を
開始するラインを指定する関数は、以下。
void set_address(byte x)
{
put_lcd_func(0x40 | x);
}
void set_page(byte x)
{
put_lcd_func(0xb8 | x);
}
void set_start_line(byte x)
{
put_lcd_func(0xc0 | x);
}
8ピクセルx8ラインにして、並べ替えするための
関数を定義して対応します。
void block_copy(byte *dst,byte *src)
{
byte j ;
byte i ;
for ( j = 0 ; j < 8 ; j++ ) {
i = (j << 3) ;
*(dst+j) = *(src+i) ;
}
}
void rotate(byte *dst,byte *src)
{
byte i ;
byte j ;
byte k ;
byte mask ;
/* arrange block */
for ( i = 0 ; i < 8 ; i++ ) {
/* generate mask pattern */
mask = (1 << (7-i));
/* clear */
*(dst+i) = 0 ;
/* reverse */
for ( j = 0 ; j < 8 ; j++ ) {
/* calcualte pointer */
k = 7 - j ;
/* add bit code */
*(dst+i) |= (((*(src+k) & mask) << i) >> j);
}
}
}
スケッチとしてまとめると、以下。
#define OFF 0
#define ON OFF+1
#define BSIZE 512
#define LCD_DAT 7
#define LCD_CLK 6
#define LCD_RST 5
#define LCD_CS2 4
#define LCD_CS1 3
#define LCD_RS 2
#define LCD_RW 1
#define LCD_E 0
#define LCD_RS_H 1
#define LCD_RS_L 0
#define SEL_LEFT 2
#define SEL_RIGHT 1
#define SEL_NONE 0
/* variables */
byte uflag ;
byte sindex ;
byte sbuf[16] ;
char cmd ;
char msg[16];
byte glcd[BSIZE];
byte gtmp[8] ;
byte gg[8] ;
/* function prototype */
byte get_hex(char x);
void rs_putchar(char x);
void rs_puts(char *ptr);
void crlf();
void show_help();
void set_point(byte x,byte y,byte which);
void pset(byte x,byte y);
void pclr(byte x,byte y);
void binary_display(byte x);
void set_frame(byte x);
void clear_frame(void);
void fill_frame(void);
void block_copy(byte *dst,byte *src);
void rotate(byte *dst,byte *src);
void send_t164(byte x);
void send_lcd_primitive(byte which,byte x);
void put_lcd_data(byte x);
void put_lcd_func(byte x);
void init_lcd(void);
void select_plane(byte x);
void display(byte x);
void set_address(byte x);
void set_page(byte x);
void set_start_line(byte x);
void display_lcd(byte which,byte *x);
void setup() {
/* initialize serial */
Serial.begin(9600);
sindex = 0 ;
uflag = OFF ;
/* initialize port values */
PORTB = 0x15 ;
PORTC = 0x22 ;
PORTD = 0x01 ;
/* initialize port direction */
DDRB = 0x2e ;
DDRC = 0xff ;
DDRD = 0xfe ;
/* clear frame buffer */
clear_frame();
init_lcd();
}
void loop()
{
byte i ;
byte last ;
char xx ;
char yy ;
byte x0 ;
byte y0 ;
byte x1 ;
byte y1 ;
/* serial handling */
if ( uflag == ON ) {
/* clear flag */
uflag = OFF ;
/* new line */
crlf();
/* get command */
cmd = *(sbuf+0) ;
/* help */
if ( cmd == '?' ) { show_help() ; }
/* set or preset point */
if ( cmd == 'P' || cmd == 'R' ) {
/* copy */
for ( i = 0 ; i < 6 ; i++ ) {
*(msg+i) = *(sbuf+i+1) ;
}
/* get x component */
x0 = 0 ;
for ( i = 0 ; i < 3 ; i++ ) {
xx = i ;
if ( *(msg+i) == ',' ) break ;
x0 = x0 * 10 + get_hex( *(msg+i) );
}
/* get y component */
yy = xx+1 ;
y0 = 0 ;
for ( i = yy ; i < yy+3 ; i++ ) {
if ( *(msg+i) == '\r' ) break ;
y0 = y0 * 10 + get_hex( *(msg+i) );
}
/* store */
if ( cmd == 'P' ) { pset(x0,y0); }
else { pclr(x0,y0); }
}
/* clear frame buffer */
if ( cmd == 'C' ) { clear_frame(); }
/* fill frame buffer */
if ( cmd == 'F' ) { fill_frame(); }
/* draw line */
if ( cmd == 'L' ) {
/* copy */
for ( i = 0 ; i < 13 ; i++ ) {
*(msg+i) = *(sbuf+i+1) ;
if ( *(sbuf+i+1) == '\r' ) break ;
}
/* get x component */
x0 = 0 ;
for ( i = 0 ; i < 3 ; i++ ) {
xx = i ;
if ( *(msg+i) == ',' ) break ;
x0 = x0 * 10 + get_hex( *(msg+i) );
}
/* get y component */
yy = xx + 1 ;
y0 = 0 ;
for ( i = yy ; i < yy + 3 ; i++ ) {
xx = i ;
if ( *(msg+i) == ',' ) break ;
y0 = y0 * 10 + get_hex( *(msg+i) );
}
/* store start point */
pset(x0,y0);
/* get x component */
yy = xx + 1 ;
x1 = 0 ;
for ( i = yy ; i < yy+3 ; i++ ) {
xx = i ;
if ( *(msg+i) == ',' ) break ;
x1 = x1 * 10 + get_hex( *(msg+i) );
}
/* get y component */
yy = xx + 1 ;
y1 = 0 ;
for ( i = yy ; i < yy+3 ; i++ ) {
if ( *(msg+i) == '\r' ) break ;
y1 = y1 * 10 + get_hex( *(msg+i) );
}
/* store end point */
pset(x1,y1);
/* calculate difference */
xx = x1 - x0 ;
yy = y1 - y0 ;
if ( x0 > x1 ) { last = (-1)*xx ; }
else { last = xx ; }
/* calculate next point */
for ( i = 1 ; i < last ; i++ ) {
x1 = x0 + i ;
y1 = y0 + (char)(((1.0 * yy) / xx) * 1.0 * i) ;
pset(x1,y1);
}
}
/* show frame buffer */
if ( cmd == 'S' ) {
for ( word cnt = 0 ; cnt < BSIZE ; cnt++ ) {
binary_display( *(glcd+cnt) );
if ( (cnt % 8) == 7 ) crlf();
}
}
/* transfer */
if ( cmd == 'T' ) {
/* get side */
last = get_hex( *(sbuf+1) ) ;
/* transfer */
display_lcd(last,glcd);
}
}
}
/* receive interrupt */
void serialEvent()
{
char ch;
if ( Serial.available() > 0 ) {
/* get 1 character */
ch = Serial.read();
/* store */
*(sbuf+sindex) = ch ;
/* increment */
sindex++ ;
/* judge */
if ( ch == '\r' ) {
sindex = 0 ;
uflag = ON ;
}
}
}
byte get_hex(char x)
{
byte result ;
/* default */
result = 0 ;
/* judge */
if ( '0' <= x && x <= '9' ) { result = x - '0' ; }
if ( 'A' <= x && x <= 'F' ) { result = x - 'A' + 10 ; }
if ( 'a' <= x && x <= 'f' ) { result = x - 'a' + 10 ; }
return result ;
}
void rs_putchar(char x)
{
Serial.write(x);
}
void rs_puts(char *ptr)
{
while ( *ptr ) {
rs_putchar( *ptr );
ptr++;
}
}
void crlf()
{
rs_putchar('\r');
rs_putchar('\n');
}
void show_help()
{
rs_puts("? help"); crlf();
rs_puts("P set point"); crlf();
rs_puts("R reset point"); crlf();
rs_puts("C clear frame buffer with 0"); crlf();
rs_puts("F set frame buffer with 1"); crlf();
rs_puts("L draw line"); crlf();
rs_puts("S show frame buffer"); crlf();
rs_puts("T transfer"); crlf();
}
void set_point(byte x,byte y,byte which)
{
word loc ;
byte q ;
byte r ;
/* */
q = (x >> 3) ;
r = x & 0x07 ;
/* calculate location */
loc = (y << 3) + q ;
/* store */
if ( which ) { *(glcd+loc) |= (1 << (7-r) ); }
else { *(glcd+loc) &= ~(1 << (7-r) ); }
}
void pset(byte x,byte y)
{
set_point(x,y,ON);
}
void pclr(byte x,byte y)
{
set_point(x,y,OFF);
}
void binary_display(byte x)
{
int i ;
for ( i = 7 ; i > -1 ; i-- ) {
rs_putchar('0' + ((x >> i) & 1));
}
}
void set_frame(byte x)
{
word cnt ;
byte tmp ;
tmp = 0 ;
if ( x ) { tmp = 0xff ; }
for ( cnt = 0 ; cnt < BSIZE ; cnt++ ) {
*(glcd+cnt) = tmp ;
}
}
void clear_frame(void)
{
set_frame(OFF);
}
void fill_frame(void)
{
set_frame(ON);
}
void block_copy(byte *dst,byte *src)
{
byte j ;
byte i ;
for ( j = 0 ; j < 8 ; j++ ) {
i = (j << 3) ;
*(dst+j) = *(src+i) ;
}
}
void rotate(byte *dst,byte *src)
{
byte i ;
byte j ;
byte k ;
byte mask ;
/* arrange block */
for ( i = 0 ; i < 8 ; i++ ) {
/* generate mask pattern */
mask = (1 << (7-i));
/* clear */
*(dst+i) = 0 ;
/* reverse */
for ( j = 0 ; j < 8 ; j++ ) {
/* calcualte pointer */
k = 7 - j ;
/* add bit code */
*(dst+i) |= (((*(src+k) & mask) << i) >> j);
}
}
}
void send_t164(byte x)
{
byte i ;
byte tmp ;
/* copy */
tmp = x ;
/* loop */
for ( i = 0 ; i < 8 ; i++ ) {
/* impress data */
PORTD &= ~(1 << LCD_DAT);
if ( tmp & 0x80 ) { PORTD |= (1 << LCD_DAT); }
/* TRG : H */
PORTD |= (1 << LCD_CLK);
/* shift */
tmp <<= 1 ;
/* TRG : L */
PORTD &= ~(1 << LCD_CLK);
}
PORTD &= ~(1 << LCD_DAT);
}
void send_lcd_primitive(byte which,byte x)
{
byte tmp ;
/* copy */
tmp = x ;
/* transfer */
send_t164( tmp ) ;
/* impress RS */
PORTC &= ~(1 << LCD_RS);
if ( which ) { PORTC |= (1 << LCD_RS); }
/* latch */
PORTC |= (1 << LCD_E);
PORTC &= ~(1 << LCD_E);
}
void put_lcd_data(byte x)
{
send_lcd_primitive(ON,x);
}
void put_lcd_func(byte x)
{
send_lcd_primitive(OFF,x);
}
void init_lcd(void)
{
/* send reset signal */
PORTD &= ~(1 << LCD_RST) ;
delay(1);
PORTD |= (1 << LCD_RST) ;
/* delay 30 ms */
delay( 30 ) ;
/* select first line */
put_lcd_func(0xC0);
/* display on */
put_lcd_func(0x3f);
}
void select_plane(byte x)
{
/* CS1 = 0 , CS2 = 0 */
PORTD &= ~(1 << LCD_CS2);
PORTC &= ~(1 << LCD_CS1);
/* set */
if ( x == SEL_LEFT ) { PORTC |= (1 << LCD_CS1); }
if ( x == SEL_RIGHT ) { PORTD |= (1 << LCD_CS2); }
}
void display(byte x)
{
put_lcd_func(0x3E | x);
}
void set_address(byte x)
{
put_lcd_func(0x40 | x);
}
void set_page(byte x)
{
put_lcd_func(0xb8 | x);
}
void set_start_line(byte x)
{
put_lcd_func(0xc0 | x);
}
void display_lcd(byte which,byte *x)
{
byte line ;
byte col ;
word ptr ;
byte cnt ;
byte tmp ;
/* select plane */
select_plane(which);
/* disappear */
display(OFF);
/* transfer data */
set_start_line( 0 ) ;
for ( line = 0 ; line < 8 ; line++ ) {
set_page( line ) ;
set_address(0) ;
for ( col = 0 ; col < 8 ; col++ ) {
/* calculate pointer */
ptr = (line << 6) + col ;
/* get block */
block_copy( gtmp , x + ptr );
/* rotate */
rotate( gg , gtmp );
/* store */
for ( cnt = 0 ; cnt < 8 ; cnt++ ) { put_lcd_data( *(gg+cnt) ); }
}
}
/* appear */
display(ON);
/* disable all plane */
select_plane(SEL_NONE);
}
利用するArduino互換基板は、以下。
先に作ったArduinoのスケッチでは、まったく動作せず
改めてデータシートを見直し、必要な部分を修正して
いきます。
利用しているLCDは、64ピクセルx64ラインを2つ使う
仕様です。初期化から、見直しました。
64ピクセルx64ラインを個別に初期化するので、CS1、CS2
を制御する関数を使い、初期化後、LCD内のRAMをクリア
します。
void init_lcd()
{
byte i ;
byte bcnt ;
byte xcnt ;
/* default */
send_t164( 0x00 );
/* send reset signal */
PORTD &= ~(1 << LCD_RST) ;
delayMicroseconds(XDELAYX);
PORTD |= (1 << LCD_RST) ;
/* delay 40 ms */
delay( 40 );
/* both initialize */
for ( i = 0 ; i < 2 ; i++ ) {
/* choose plane */
if ( i ) { select_plane(SEL_RIGHT); /* RIGHT */ }
else { select_plane(SEL_LEFT); /* LEFT */ }
/* select first line */
put_lcd_func(0xC0);
/* display on */
put_lcd_func(0x3f);
}
/* clear VRAM */
display(OFF);
for ( i = 0 ; i < 2 ; i++ ) {
/* choose plane */
if ( i ) { select_plane(SEL_RIGHT); /* RIGHT */ }
else { select_plane(SEL_LEFT); /* LEFT */ }
/* set start line */
set_start_line(0);
/* block */
for ( bcnt = 0 ; bcnt < 8 ; bcnt++ ) {
/* select page */
set_page( bcnt );
/* set start address */
set_address( 0 ) ;
/* column */
xcnt = 0 ;
while ( xcnt < 64 ) {
put_lcd_data( 0x00 );
xcnt++ ;
delayMicroseconds(XDELAYX);
}
}
}
select_plane(SEL_NONE);
display(ON);
}
ArduinoのSRAMの中に用意したフレームバッファは
固定なので、フレームバッファからのデータ切出し
回転は、128ピクセルx64ラインに拡張しても利用
可能なように、定義し直しました。64ピクセルは
8バイトですが、128ピクセルでは16バイトという
点に注目してシフト量を、選択できるようにして
おきます。
#define XSFTX 3
/* #define XSFTX 4 */
void block_copy(byte *dst,word x)
{
byte j ;
byte i ;
/* transfer */
for ( i = 0 ; i < 8 ; i++ ) {
j = (i << XSFTX);
*(dst+i) = *(glcd+x+j) ;
}
}
void rotate(byte *dst,byte *src)
{
byte i ;
byte j ;
byte k ;
byte mask ;
/* arrange block */
for ( i = 0 ; i < 8 ; i++ ) {
/* generate mask pattern */
mask = (1 << (7-i));
/* clear */
*(gg+i) = 0 ;
/* reverse */
for ( j = 0 ; j < 8 ; j++ ) {
/* calculate pointer */
k = 7 - j ;
/* add bit code */
*(dst+i) |= (((*(src+k) & mask) << i) >> j);
}
}
}
90度右回転は、8バイトのデータを並べ替えるだけ
なので、ソースとディスティネーションを指定して
わかりやすくしています。8バイトx2で16バイトで
処理できるように、メモリの使用量を抑えます。
ArduinoのSRAMは、2kバイトなので1024バイト分
のフレームバッファを確保しても、対応できます。
ArduinoのSRAM内部での動作確認をして、GLCDに接続
して初期化すると、表示がうまく時とおかしい時が
あり、悩んでいました。
Arduinoは、パワーオンすると最初の10秒くらいは
シリアル通信で新たなスケッチが来ているのかを
調べています。これがGLCD初期化によくない影響
があるようです。Arduinoの利用をやめました。
10ピンを手軽に利用できるマイコン基板がないかと
過去の基板を漁ると、H8/3052F基板が出てきました。
I/Oピンの処理さえ合わせてやれば、これまで
作ったファームウエアを流用できるので乗換え
ます。
目次
前
次