最近、あまり作っていないのでグラフィック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を読み書きする場合はメインメモリ上にバッファを持って一括(ブロック)転送した方が速い
【参考】
・電子工作の実験室
→グラフィック液晶表示器用ライブラリ

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言語のソース形式で出力できます。

#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を読み書きする場合はメインメモリ上にバッファを持って一括(ブロック)転送した方が速い
【参考】
・電子工作の実験室
→グラフィック液晶表示器用ライブラリ