目次

グラフィック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の端末ソフトから
 コマンドを与えます。

 コマンドは、次のように決めました。

 コマンド'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以上使うのは、他の処理ができなく
 なることがあるため、シフトレジスタを入れます。

 ピン割当ては、次のようにしました。

 シフトレジスタにデータ転送する関数を定義します。

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ピンの処理さえ合わせてやれば、これまで
 作ったファームウエアを流用できるので乗換え
 ます。


目次

inserted by FC2 system