goo blog サービス終了のお知らせ 

寝ても覚めてもPSoC

電子工作とマイコンをやりはじめました。
ど素人ですので配信する情報の取り扱いには十分注意してください。

グラフィックLCD[SG12864A]について

2010年11月18日 | 電子工作
最近、あまり作っていないのでグラフィックLCD(SUNLIKE社 SG12864A)についてまとめておきます。

SG12864Aは、横128ドット、縦64ドットを表せるグラフィックLCDです。
色数は残念ながらモノクロですが、8bitマイコンで扱いやすいと言えます。
このLCDは検索すると沢山使い方がヒットするので、要点だけにします。
CS1やE、DB0等の接続端子についてはググってください。
制御コマンド等についてはこちらを見てください。

いつものようにおかしなところがあったら、各自脳内で補完してください。

【SG12864A仕様】
動作電圧: 5.0V
コントローラー: KS0107
I/F: 8bitパラレル
色数: モノクロ
解像度: 128x64

【SG12864AのVRAM構造】

※図は電子工作の実験室さんからお借りしました。(加筆修正済み)

128x64ドットと言っても、64x64ドット表示部を2つ並べています。
左右それぞれのアクセス対象をCS1、CS2で切り替えます。
縦8ドット分をPageという単位で表し、横64ドット分をColumnで表します。
従って、フォント等のイメージデータは縦8ドット分にスライスした形式で持つとアクセスが楽になります。

このへんは、電子工作の実験室さんの説明がわかりやすいので引用します。
---------------------------------------------------------------------------
このグラフィックLCDは図のように左右2つの64×64ドットの表示部として構成されています。
いずれも全く同じ機能を持っていて、CS1とCS2で選択することで別々に制御する必要があります。
それぞれの座標は下図のようになっていて、左上が原点(0, 0)で、右下端が(63, 63)と2つの表示部が独立した座標になっています。
上から8LineずつまとめてPageと呼び、Pageの内部は横方向に64本ので構成されていて、この1本の縦方向の8Line分が1バイトのデータとして制御されます。
注意が必要なのは、1バイトの上側がデータビットの0ビット目となっていることです。
場所の指定方法はCS1かCS2で左右のどちらかを選択し、さらにPageとColum Addressで特定のバイトを指定します。
---------------------------------------------------------------------------


【SG12864Aの初期化】
1.左右のLCDをONにする[setDisplay()]
2.表示開始ラインを0にする[setStartline( 0 )]

void setDisplay( void )
{
  set_E( 0 ); .....E pinをLowにする
  set_RW( 0 ); .....R/W pinをLowにする
  set_DI( 0 ); .....D/I pinをLowにする
  set_CS1( 1 );.....CS1 pinをHighにする
  set_CS2( 1 );.....CS2 pinをHighにする
  PORT_DB = 0x3F;....DB0~DB7に0x3Fを出力する(LCD ON)
  set_E( 1 ); .....E pinをHighにする
  set_E( 0 ); .....E pinをLowにする
}

void setStartline( unsigned char ln )
{
  set_E( 0 ); ......E pinをLowにする
  set_RW( 0 ); ......R/W pinをLowにする
  set_DI( 0 ); ......D/I pinをLowにする
  set_CS1( 1 );......CS1 pinをHighにする
  set_CS2( 1 );......CS2 pinをHighにする
  PORT_DB = 0xC0+(ln&0x3F);DB0~DB7に開始ラインを出力する
  set_E( 1 ); ......E pinをHighにする
  set_E( 0 ); ......E pinをLowにする
}

【VRAMにデータを書き込む】
1.書き込むLCDを指定する[setCS1()、setCS2()]
2.Pageで縦位置を指定する[setPage( page )]
 ※Pageは一度設定すると、再度設定しなおすまで値が保持されます。
3.Columnで横位置を指定する[setColumn( colm )]
 ※Columnは設定すると、データを書き込み毎に自動的に+1されます。
4.データを書き込む[writeData( dat )]
 ※書き込む単位は1バイト(8bit)単位となります。

// global
unsigned char cs1 = 0;
unsigned char cs2 = 0;

void setCS1( unsigned char fg )
{
  if( fg == 0 ){
    cs1 = 0;
  }else{
    cs1 = 1;
  }
}

void setCS2( unsigned char fg )
{
  if( fg == 0 ){
    cs2 = 0;
  }else{
    cs2 = 1;
  }
}

void setPage( unsigned char pg )
{
  set_E( 0 ); ......E pinをLowにする
  set_RW( 0 ); ......R/W pinをLowにする
  set_DI( 0 ); ......D/I pinをLowにする
  set_CS1( cs1 );.....cs1が1なら左側を設定
  set_CS2( cs2 );.....cs2が1なら右側を設定
  PORT_DB = 0xB8+(pg&0x07);DB0~DB7に縦(Page)位置を出力する
  set_E( 1 ); ......E pinをHighにする
  set_E( 0 ); ......E pinをLowにする
}

void setColmn( unsigned char cl )
{
  set_E( 0 ); ......E pinをLowにする
  set_RW( 0 ); ......R/W pinをLowにする
  set_DI( 0 ); ......D/I pinをLowにする
  set_CS1( cs1 );.....cs1が1なら左側を設定
  set_CS2( cs2 );.....cs2が1なら右側を設定
  PORT_DB = 0x40+(cl&0x07);DB0~DB7に横(Column)位置を出力する
  set_E( 1 ); ......E pinをHighにする
  set_E( 0 ); ......E pinをLowにする
}

void writeData( unsigned char val )
{
  set_E( 0 ); ......E pinをLowにする
  set_RW( 0 ); ......R/W pinをLowにする
  set_DI( 1 ); ......D/I pinをHighにする
  set_CS1( cs1 );.....cs1が1なら左側に書き込み
  set_CS2( cs2 );.....cs2が1なら右側に書き込み
  PORT_DB = val; .....DB0~DB7にデータを出力する
  set_E( 1 ); ......E pinをHighにする
  set_E( 0 ); ......E pinをLowにする
}

例えば、以下のようにすると画面左上に3本線が表示されます。
unsigned char x;
setCS1( 1 ); setCS2( 0 );
setPage( 0 );
setColumn( 0 ); // Xアドレス(Column)の自動+1を利用する
for( x = 0; x <64; x++ ){ 
  writeData( 0x2A );
}

表示エリアを拡大すると、以下のようになります。
(0,0)        (63,0)
□□□□□ □□□
■■■■■ ■■■
□□□□□ □□□
■■■■■~■■■
□□□□□ □□□
■■■■■ ■■■
□□□□□ □□□
□□□□□ □□□

また、画面クリアは以下のようにCS1とCS2を両方ONにして一度に書き込みます。
unsigned char x, y;
setCS1( 1 ); setCS2( 1 ); ※両画面同時に書き換える
for( y = 0; y <8; y++ ){ 
  setPage( y );
  setColumn( 0 );
  for( x = 0; x <64; x++ ){
    writeData( 0 );
  }
}

【VRAMのデータを読み込む】
1.書き込むLCDを指定する[setCS1()、setCS2()]
2.Pageで縦位置を指定する[setPage( page )]
3.Columnで横位置を指定する[setColumn( colm )]
4.データを読み込む[readData()]
 ※読み込みはダミー読み込みと確定読み込みの2回行う必要があります。
 ※読み込む単位は1バイト(8bit)単位となります。

unsigned char readData()
{
  unsigned char val;

  // ダミー読み込み
  set_E( 0 ); ......E pinをLowにする
  set_RW( 1 ); ......R/W pinをHighにする
  set_DI( 1 ); ......D/I pinをHighにする
  set_CS1( cs1 );.....cs1が1なら左側を読み込み
  set_CS2( cs2 );.....cs2が1なら右側を読み込み
  set_E( 1 ); ......E pinをHighにする
  val = PORT_DB; .....DB0~DB7からデータを受け取る
  set_E( 0 ); ......E pinをLowにする

  // 確定読み込み
  set_E( 0 ); ......E pinをLowにする
  set_RW( 1 ); ......R/W pinをHighにする
  set_DI( 1 ); ......D/I pinをHighにする
  set_CS1( cs1 );.....cs1が1なら左側を読み込み
  set_CS2( cs2 );.....cs2が1なら右側を読み込み
  set_E( 1 ); ......E pinをHighにする
  val = PORT_DB; .....DB0~DB7からデータを受け取る
  set_E( 0 ); ......E pinをLowにする

  return val;
}

※PSoCの場合、PORTのDRIVE設定(PRTxDM0、PRTxDM1、PRTxDM2)を読み込み前にHigh-Z、読み込み後にStrongにする必要があります。


【VRAMの指定位置にドットを書く】
void setPixel( int x, int y )
{
  unsigned char tpg; // page
  unsigned char tcol; // colmn
  BOOL tcs1; // cs1
  BOOL tcs2; // cs2
  unsigned char tx, ty;
  unsigned char bitpos;
  unsigned char val;
  unsigned char sb;
  unsigned char setbit[] = { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 };

  tx = x;
  ty = y;

  if( tx <64 ){
    tcs1 = TRUE; tcs2 = FALSE; // 左側LCDにアクセス
  }else{
    tcs1 = FALSE; tcs2 = TRUE; // 右側LCDにアクセス
    tx = tx-64; // tx = 0...63に収める
  }
  tx &= 0x3f; // X = 0...63に収める
  ty &= 0x3f; // Y = 0...63に収める

  tpg = ty/8; // Pageアドレスは y座標÷8
  tcol = tx;
  bitpos = ty%8;
  sb = setbit[bitpos]; // 書き込む値

  setCS1( tcs1 );
  setCS2( tcs2 );
  setPage( tpg );
  setColumn( tcol );
  val = readData();        // ドットデータ読み込み(縦8ドット単位)
  val |= sb;           // ドットを打つ

                   // ページアドレスは同じなので設定する必要はない
  setColumn( tcol ); // Xアドレスは増加するので再設定が必要
  writeData( v );         // データ書き込み
}

【キャラクタ文字を描画する】
扱いを簡単にするため、ここでは8x8ドットを基本としたキャラクタ座標で文字(8x8ドットを表示するようにします。
キャラクタ座標になると、横位置は0~15、縦位置は0~7で指定する事になります。つまり、横16文字縦8文字表示のキャラクタLCDになります。

まずフォントデータを作成します。フォントデータは横8ドット、縦8ドットで構成されているものとします。
このツールを使うとC言語のソース形式で出力できます。
SG12864A_MakeFont

#include "font8x8_YX.h" ...出力されたフォントデータの配列(font8x8_YX[])
void dispChrText( int col, int row, unsigned char code )
{
  int i;
  unsigned char v;
  int ofs = code << 3; ....フォント位置を算出。1文字8バイトのため8倍する
  int x = col << 3;   ....キャラクタ座標をドット座標に直す
  int y = row & 0x07;
  if( x > 63 ){ ........X座標が64以上の場合は右側LCDを有効にする
    x = x-64;
    setCS1( 0 );
    setCS2( 1 );
  }else{
    setCS1( 1 );
    setCS2( 0 );
  }
  setPage( y );
  setColumn( x );

  for( i = 0; i <8; i++ ){
    v = font8x8_YX[ofs+i]; .......フォントを描画
    writeData( v );
  }
}

上の関数は、(col,row)の位置に指定の文字コード(code)を表示します。
引数のとる値は、colは0~15、rowは0~7、codeは0~255となります。
例えば左上の位置に'A'を表示するには以下のようにします。
dispChrText( 0, 0, 'A' );
または
dispChrText( 0, 0, 0x41 );
同じ位置に違う文字を描画すると前の文字は消されます。(上書き)

任意の位置に文字を表示するには以下のようにします。
void dispChr( int sx, int sy, unsigned char code )
{
  int i, j;
  unsigned char v;
  int ofs = code << 3;
  for( i = 0; i <8; i++ ){
    v = font8x8_YX[ofs+i];
    for( j = 0; j <8; j++ ){
      if( (v&0x01) != 0 ){
        setPixel( sx+i, sy+j );
      }
      v = v >> 1;
    }
  }
}
表示位置のsxは0~127、syは0~63で指定できますが描画速度は遅いです。(setPixel関数を使わないようにすれば速く描画できますがコードが複雑になります)
状況によって使い分ける必要がありそうです。


【まとめ】
1.フォント等のイメージデータは縦方向でデータを持つ
2.Pageの値保持、Colmnの自動インクリメント機能をうまく使う。
  特に自動インクリメントはデータのブロック転送に向いています。
3.文字やイメージをキャラクタ座標(8ドット単位)で描画する(重ね合わせ無し)と速い
4.ドットの読み込みは2回必要のため処理が重くなる
5.一度(1フレーム毎)に多くのVRAMを読み書きする場合はメインメモリ上にバッファを持って一括(ブロック)転送した方が速い


【参考】
電子工作の実験室
 →グラフィック液晶表示器用ライブラリ


最新の画像もっと見る

コメントを投稿

サービス終了に伴い、10月1日にコメント投稿機能を終了させていただく予定です。
ブログ作成者から承認されるまでコメントは反映されません。