180円で買ったグラフィック液晶を動かす

大阪・日本橋のデジットの店頭に1個180円で液晶モジュールが
山積みされていました。動くのか、動かせるのか、わかりませんでしたが
2個買ってみました。
こんな風に売られていました

実は、JN3XBYさんが買われていましたので私もつきあい(?)で
買ったのです。さっそくBLOGに書かれています。
http://jn3xby.sblo.jp/

うまくいきそうな気配がしたので、私は20MHzクロックのATmega168に
繋ぎ追試しました。

回路図です↓ LCDとAVRの結線はJN3XBYさんと同じにしました。
マイナス電源(約-12V)が必要なのでAVRで矩形波を出力し、コッククロフトウォルトン回路で
作っています。(コンデンサは0.1μFの積セラです)

JN3XBYさんがBASICコンパイラで書かれた千鳥のブロックパターン表示を
アセンブラで書き直しました。うまくいきました。↓


タイミングは↓でうまくいくようです。データはCPの立ち下がりエッジで取り込まれます。
(書きにくかったので省略しました)


一応のメドがついたので、実用的なソフトを考えてみました。

このLCDモジュールは横128ドット・縦64ラインのグラフィックタイプ
です。表示部分はおおよそ、60mm×30mmです。秋月電子で売って
いるキャラクタタイプの16文字2行のモジュール(SC1602B)の表示部分は
65mm×16mmですから、横幅は少し小さく、高さは2倍ほどということに
なります。

表示バッファ(VRAM)は内蔵されておらず、横1ライン(128ビット)分の
シフトレジスタに書き込むことで、1ラインだけ表示されます。64ライン全てに
表示するには、常に1ラインずつ書き込みを続ける必要があります。
ちょうどアナログテレビのスキャンと同じような感じです。
書き続ける速度が遅いと、画面がチラつきます。だいたい、1秒間に
30回以上、1画面分書くといいようです。

次に、VRAMです。VRAMはAVR側で持たなければなりません。
128ドット×64ラインですから(128÷8)×64=1024バイトあれば
いいわけです。ところがATmega168の内蔵メモリはちょうど1024
バイトしかないのです。これでは、プログラムを動かすための
ワークエリア(スタックとか変数の領域)が全く確保できません。

内蔵RAMが1024バイトよりも多いAVRはあることはありますが
64ピンの大きなパッケージのAVRになってしまいます。LCDの
制御線は4ビット、マイナス電源生成用に1ビット、外部との通信用に2ビット
あればいいので、64ピンのAVRを使うのはもったいねー!ということ
になります。

ということで、しょうがないので、LCDの64ラインのうち、最下行の
1ラインは表示しないことにしました。VRAMは、
(128÷8)×63=1008バイト でいいことになります。ワークエリア
として16バイト確保できました。ちょっとワークエリアが少ないのですが
何とかこれでやってみます。


psetルーチン(任意の場所に点を打つ)ができたので試運転。
リサージュ図形がうまく表示されました。(データはPCから送っています)
(ちょっとLCDの表面にキズが付いています---180円のジャンクですから...)

もっと悪ノリして猫を表示。(まあ、何とかわかる程度ですね)


LCDに送っているドットクロック(CP)を観測しています。
1.25MHzぐらいです。

8ビット毎にやや遅延があります。VRAMから1バイトずつLOAD
してくるからです。テレビではないので書き込み速度にバラつきが
あっても大きな問題はありません。
時間軸を延ばしてみました。
ラインとラインの間にずいぶん時間があります。
これは、LCDのスキャンは割込でやっていますので、
少し間を取って、通常処理ができるようにしてあるの
です。

ラインとラインの間隔は400μ秒、
実際の1ラインのスキャン時間は100μ秒程度ですから
AVRの処理能力の1/4をスキャンに費やしていることになります。

1ライン約400μ秒、63ラインで、約25m秒です。
1秒間に約40回、画面を更新していることになります。
これでLCDのチラつきはほとんど感じません。


表示例です。
横8dot×縦13dot

"JR3KBU"の文字間を0、1、2dotと変えてみました。
"K"を1dot下にズラしながら表示しました。
"egg" "pen" など、小文字の"g"や"p"が流麗です。

せっかくのグラフィックLCDなので5×7フォントは使わないように
しました。
このフォントで14文字4行が表示できます。
横8dot×縦13dot
横10dot×縦16dot
横12dot×縦18dot (中抜き文字)
横10dot×縦23dot (中抜き文字)
横16dot×縦20dot (中抜き文字)

を表示した後で、

横16dot×縦29dot (中抜き文字)

を重ねて表示してみました。
横16dot×縦39dot (中抜き文字)
横29dot×縦39dot (中抜き文字)


これらのフォントは「UHF」フォントといいます。
足立 裕司さんのおつくりになったフォントです。
英字の小文字が流麗だと思います。

フォントをAVRマイコンに組み込むことを快く了承して
いただきました。


ソフトウェア

 \180の液晶モジュールにAVR(ATmega168)を接続して文字や
画像を表示できるようにしたものです。既述のように、AVRのRAMを
かなり無理して使っていますので、スタックメモリの余裕がほと
んどありません。なるべくメモリを消費しないように変数として
レジスタを駆使していますので、アプリケーションプログラムを
さらに作るのは苦しいです。(メモリがあと数十バイトでもあれば
かなり楽なのですが...)

 ということでアプリケーションプログラムは他のマイコンなど
にやってもらって、本装置は、表示装置に徹することにしました。
外部からは非同期通信で表示コマンドを受け取ります。
PCと繋ぐ場合は上のようにインバータが必要ですが、マイコンと
繋ぐ場合は不要で直結できます。現状はHC04タイプのインバータ
を使うというインチキインターフェースで115.2kbsで順調に動作して
います。

コマンド 引数の数  意味 働き
 C   0  CLEAR 画面を全部クリアします。
 P x y    2  PSET (x, y)の位置に点を打ちます。
 R x y   2  PRESET (x, y)の位置をクリアします。
 F f
  1
 FORMAT
fの
上位4ビットでフォントの種類、
下位4ビットで文字間のスペースを指定します。
 L x y   2  LOCATE 文字を表示する位置を指定します。
 O d   1  OUT CHARACTER  文字を1文字表示します。

 本装置はコマンドを受信後、ACK(バイナリで3)を送信します。
ホスト側では、ACKを受信してから次のコマンドを送って下さい。
本装置は受信バッファが4バイトしかないのでホストからどんどん
コマンドを送られると処理が追いつかなくなって変になります。

 xは0〜127、yは0〜62 の範囲の整数です。値のチェックはしていないので
気を付けて下さい。VRAMのすぐ後ろはスタックエリアなので変な値にすると
暴走します。

 dは、各フォントの最初の文字をゼロとして、順番に1,2,3、、、と数えます。
これも値のチェックをしていないので、変な値にすると、めちゃくちゃな文字が
表示されます。Oコマンドで1文字出力した後、次に表示する位置は自動的に
1文字分進み(右に移動し)ます。
 また特別に、d=$FFにすると、文字は表示せず、文字の位置だけ1文字分進みま
す。スペース文字に似ていますが、VRAMをスペースで埋めるわけではなく、
単に位置が右に進むだけです。透明なスペース文字と考えて下さい。

 dのどんな値がどんな文字に該当するかは、フォントの種類によります。ほんとは
ASCIIコードの文字にすればいいのですが、ROM領域に限りがあり、大きなフォント
ほど、定義している文字は少なくなっています。それを以下に示します。

フォント種類
(番号)
横ドット数 縦ドット数 文字の数          備考  
   0    8   14  94  ASCIIコードの$21〜$7Eが0〜$5Dに対応 
   1   10   16  94  同上
   2   12   18  94  同上
   3   10   23  63  ASCIIコードの$21〜$5Fが0〜$3Eに対応  
   4   16     20  10  数字のみ
   5   12   29  10  同上
   6   16   39  10  同上
   7   29   39  10  同上

 フォント種類=0、1、2はASCIIコードで表示可能な文字全部を網羅しています。
単純にASCIIコードから$21を引いた値をOコマンドで送ってやればOKです。

 !"# $%&'()*+,-./
 0123456789:;<=>?
 @ABCDEFGHIJKLMNO
 PQRSTUVWXYZ[/]^_
 `abcdefghijklmno
 pqrstuvwxyz{|}〜


 フォント種類=3は、上記から`a以降を抜いたものです。
英小文字+αが無くなったものです。

 フォント種類=4、5、6、7は数字だけです。

 これで、ATmega168の16kバイトのROMがほぼいっぱいになっています。
(コード部は1kバイト、フォントが15kバイト)

 フォントはどのようなサイズでもOKです。もちろん表示エリア(128×63)以内で。
フォントを定義するインデックス部があり、そこにフォントデータの先頭アドレス、
フォントの横、縦の大きさなどを書いておけばよいのです。最大で16種類まで。
またひとつのフォントで定義できる文字は254文字までです。($FFは透明スペースなので)

 BMPファイルを読んで、AVRのアセンブラソース(.DB文の塊)を出力する
ユーティリティも作りました。


制御の概略

 VRAMの構成を説明します。VRAMと言ってもソフトウェア的な定義ですので
どのようにしてもいいのです。ただ、スキャンをしなければならないのでスキャン
がし易いような構成にします。
上の図はVRAMをバイト単位に見たところです。横に16バイト連なったものです。
数字はVRAMのアドレスです。(実際はオフセットがあります)
ラインがひとつ下はアドレスが+16されることになります。(当たり前か)


1バイト毎に注目すると、上のようになっています。左端がLSBになっています。
1バイトで8個の点を示します。横に16バイト連なっていますから 8×16=128個の
点を示すことになります。


上はフォントデータの例です。簡単のため、横を8ドットとしました。
フォントデータはROMにあります。ROMにあるフォントデータをVRAMに転送すれば
そのフォントが表示できます。(これも当たり前か)


上の図は最も簡単な転送です。VRAMにぴったり収まる(バイトとバイトの境にならない)場合です。

 フォントデータの0番目のデータ(1バイト=8ドット) → VRAMの17番地
 フォントデータの1番目のデータ(1バイト=8ドット) → VRAMの33番地
 フォントデータの2番目のデータ(1バイト=8ドット) → VRAMの49番地
   ・
   ・
   ・

というように、フォントデータのアドレスを+1しながら、VRAMのアドレスは+16 していきます。


上の図は、VRAMのバイトの境にまたがる場合です。かなり複雑になります。
 フォントデータの0番目のデータ(1バイト=8ドット)の左2ビット → VRAMの17番地右2ビット
 フォントデータの0番目のデータ(1バイト=8ドット)の右6ビット → VRAMの18番地左6ビット
 フォントデータの1番目のデータ(1バイト=8ドット)の左2ビット → VRAMの33番地右2ビット
 フォントデータの1番目のデータ(1バイト=8ドット)の右6ビット → VRAMの34番地左6ビット
 フォントデータの2番目のデータ(1バイト=8ドット)の左2ビット → VRAMの49番地右2ビット
 フォントデータの2番目のデータ(1バイト=8ドット)の右6ビット → VRAMの50番地左6ビット
   ・
   ・
   ・
という感じです。

ソフトウェアはこちらから



まだ途中です。また続きを書きます...
ソフトももうちょっと整理してからダウンロードできるようにします...
戻る