2016年05月04日 | 公開。 |
AQM1248Aの画面の制御の方法は、一通り説明したので、ここで、実際に画面にメッセージを表示してみましょう。定番の"Hello, World!"を表示する事にします。
画面にメッセージを表示するといっても、AQM1248Aには、文字コードを送ると画面に文字が表示される様な、高度な機能は内蔵されていません。たとえ文字列を送るとしても、それらをビットマップ(ピクセルの集合で構成された絵)として送る必要があります。
メッセージのビットマップを作成する際に、まず文字の大きさを考える必要があります。複数のページにまたがる大きなビットマップを作ると、データの扱いがちょっと面倒になるので、縦方向は8ピクセル以内に収める方が楽です。
MGLCDという液晶表示用のライブラリを作った時に、フォントを作ったので、それをそのまま使うと、フォントを作る手間が省けます。MGLCD用のフォントは、縦方向が7ピクセルでデザインされており、行間のスペースを1ピクセル含めたとしても、1ページで収まるようになっています。(MGLCDライブラリの話は、また後で出てきます)
またMGLCDライブラリ用のフォントは、横方向が5ピクセルの固定幅になっています。文字間に1ピクセルのスペースを開けるとすると、1文字あたり、6ピクセルの幅になります。"Hello, World!"は、13文字の文字列ですから、全体で6×13=78ピクセル幅になります。
MGLCDライブラリのファイルからフォントをコピーすると楽なのですが、それではビットマップ作成の手順の説明にならないため、ここでは、フォントの形だけを拝借し、それを2進数に変換する作業などは、一からやり直すことにします。
ビットマップを作る方法は色々ありますが、ここでは、視覚的に理解しやすいExcel方眼紙を使う事にします。Excel方眼紙というと、「Excelの使い方としては間違っている」と批判が多いですが、フォントのデザインや小型のビットマップの作成などには、結構便利です。
まずExcelの新規のワークシートを開き、セルの幅を狭めて方眼状にします。
次に、A列の幅を少し広げて、A1のセルから順にD0、D1、…、D6、D7と、縦に入力します。(図49参照) これらは、各行のピクセルが、1バイト中の何ビット目かを表わしています。
それから、B列以降のセルの必要な部分を黒で塗りつぶし、表示するビットマップをデザインします。MGLCDライブラリの文字コード表を参考にしながら"Hello, World!"の文字列を塗りつぶしていきます。(図50参照)
デザインが終わったら、各列の白黒のパターンを、1バイトの16進数(16進数2桁)に変換します。(図51参照)
B列を例に挙げて説明します。D7からD0までを下から上に見ると、白、黒、黒、黒、黒、黒、黒、黒となっています。白を0、黒を1として、2進数に変換すると、01111111B(Bは2進数である事を表す記号)となります。それを16進数に変換すると7FHとなります。(図52参照) 2進数4桁が16進数1桁に対応しますから、上位4ビット(D4~D7)と下位4ビット(D0~D3)をそれぞれ16進数1桁に変換し、変換結果を並べて書けばよい事になります。
2進数から16進数への変換に慣れていない人は、図53の変換表を使うといいでしょう。
それでは、先ほどの作業で得られたビットマップをAQM1248Aの画面に表示するスケッチを作ります。
作成したスケッチをリスト9に示します。このスケッチはハードウェアSPIでAQM1248Aと通信する前提で作ってあります。また、Arduino IDE 1.0.X、1.6.X、および1.7.Xのいずれでも動作します。
#include <SPI.h>
#define DI_PIN 9
#define CS_PIN 10
#ifndef SPI_CLOCK_DIV32 // for arduino M0(怒)
#define SPI_CLOCK_DIV32 32
#endif
void LcdCommand(uint8_t cmd)
{
digitalWrite(CS_PIN,LOW);
digitalWrite(DI_PIN,LOW);
SPI.transfer(cmd);
digitalWrite(CS_PIN,HIGH);
} // LcdCommand
void LcdData(uint8_t dat)
{
digitalWrite(CS_PIN,LOW);
digitalWrite(DI_PIN,HIGH);
SPI.transfer(dat);
digitalWrite(CS_PIN,HIGH);
} // LcdCommand
void InitializeLcd()
{
LcdCommand(0xae);
LcdCommand(0xa0);
LcdCommand(0xc8);
LcdCommand(0xa3);
LcdCommand(0x2c);
delay(2);
LcdCommand(0x2e);
delay(2);
LcdCommand(0x2f);
LcdCommand(0x23);
LcdCommand(0x81);
LcdCommand(0x1c);
LcdCommand(0xa4);
LcdCommand(0x40);
LcdCommand(0xa6);
LcdCommand(0xaf);
} // InitializeLcd
void ClearScreen()
{
for(int page=0; page<6; page++) {
LcdCommand(0xb0+page); // Page address set
LcdCommand(0x10); // Column address set upper bit
LcdCommand(0x00); // Column address set lower bit
for(int x=0; x<128; x++) {
LcdData(0);
} // for x
} // for page
} // ClearScreen
void Hello()
{
const uint8_t BitMap[] =
{
0x7f,0x08,0x08,0x08,0x7f,0x00, // H
0x38,0x54,0x54,0x54,0x18,0x00, // e
0x00,0x41,0x7f,0x40,0x00,0x00, // l
0x00,0x41,0x7f,0x40,0x00,0x00, // l
0x38,0x44,0x44,0x44,0x38,0x00, // o
0x00,0x58,0x38,0x00,0x00,0x00, // ,
0x00,0x00,0x00,0x00,0x00,0x00, // ' '
0x3f,0x40,0x38,0x40,0x3f,0x00, // W
0x38,0x44,0x44,0x44,0x38,0x00, // o
0x7c,0x08,0x04,0x04,0x08,0x00, // r
0x00,0x41,0x7f,0x40,0x00,0x00, // l
0x38,0x44,0x44,0x48,0x7f,0x00, // d
0x00,0x00,0x5f,0x00,0x00,0x00 // !
};
LcdCommand(0xb0); // Page address set
LcdCommand(0x10); // Column address set upper bit
LcdCommand(0x00); // Column address set lower bit
for(int i=0; i<sizeof(BitMap); i++) {
LcdData(BitMap[i]);
} // for i
} // Hello
void setup() {
pinMode(DI_PIN,OUTPUT);
pinMode(CS_PIN,OUTPUT);
digitalWrite(CS_PIN,HIGH);
SPI.begin();
SPI.setClockDivider(SPI_CLOCK_DIV32);
SPI.setDataMode(SPI_MODE3);
SPI.setBitOrder(MSBFIRST);
InitializeLcd();
ClearScreen();
Hello();
}
void loop() {
}
setup関数では、RS信号および/CS信号に使うピンの初期化とSPIの初期化を行った後、InitializeLcd関数を呼び出でAQM1248Aの初期化を行い、ClearScreen関数で画面の消去を行ない、さらにHello関数を呼び出して"Hello, World!"の表示を行っています。
Hello関数では、BitMapというuint8_t型(符号なし8ビット整数)の配列定数に、先ほど作成したビットマップデータを保存しています。
Hello関数は、Page address setコマンド(表9参照)、Column address set upper bitコマンド(表10参照)、Column address set lower bitコマンド(表11参照)を順に発行して、画面の左上のビットマップを転送できる状態にしておいてから、BitMap配列定数の内容を順にLcdData関数で送信しています。
このスケッチを実行したときの画面写真29に示します。
スケッチ内にビットマップを埋め込む場合、リスト9に示す様に、16進数がずらずらと並ぶ、暗号の様な読みにくいスケッチになってしまいます。
リスト9では何の文字のビットマップであるかをコメントで示し、何とかスケッチの可読性(読みやすさの事。英語でreadabilityという)を確保していましたが、文字列ではなく、絵のビットマップの場合は、コメントではビットマップの内容を説明しにくい事もあります。
このコラムでは、マクロ定義をうまく使う事で、リストを見ればどんなビットマップが埋め込まれているかが一目で分かるようにする方法を紹介します。この方法は、MGLCDライブラリの中のフォント定義にも使われています。
まず、リスト10に示すs、M、_の3種類のマクロを定義します。マクロに使う文字はなんでもいいのですが、Mはできるだけ目立つ文字に、_はできるだけ目立たない文字にしてあるところがポイントです。
#define s ((((((((((((((((0
#define M <<1)+1)
#define _ <<1))
そして、図52の様な01111111Bのパターンだと、0x7fと書く代わりに、次の様に記述します。
s _ M M M M M M M
この記述は、マクロを展開すると次の様に解釈されます。
((((((((((((((((0<<1))<<1)+1)<<1)+1)<<1)+1)<<1)+1)<<1)+1)<<1)+1)<<1)+1)
一見複雑な式ですが、落ち着いて式を見ると、左シフトと足し算を組み合わせて、ビットマップの2進数を評価する式になっている事が分ります。計算結果は、もちろん16進表記で7FHになります。
このマクロを使ってリスト9のHello関数を書き直すと、リスト13の様になります。このリストを見ると、一目でどんなビットマップが埋め込まれているかが理解できる様になっているのが分かるでしょう。反面、リストがとても長くなるのが欠点です。(コンパイル後はリスト9と同じコードになるので、Arduinoのフラッシュメモリをたくさん使ったり、実行速度が低下する事はありません)
void Hello()
{
#define s ((((((((((((((((0
#define M <<1)+1)
#define _ <<1))
const uint8_t BitMap[] =
{
s _ M M M M M M M ,
s _ _ _ _ M _ _ _ ,
s _ _ _ _ M _ _ _ ,
s _ _ _ _ M _ _ _ ,
s _ M M M M M M M ,
s _ _ _ _ _ _ _ _ ,
s _ _ M M M _ _ _ ,
s _ M _ M _ M _ _ ,
s _ M _ M _ M _ _ ,
s _ M _ M _ M _ _ ,
s _ _ _ M M _ _ _ ,
s _ _ _ _ _ _ _ _ ,
s _ _ _ _ _ _ _ _ ,
s _ M _ _ _ _ _ M ,
s _ M M M M M M M ,
s _ M _ _ _ _ _ _ ,
s _ _ _ _ _ _ _ _ ,
s _ _ _ _ _ _ _ _ ,
s _ _ _ _ _ _ _ _ ,
s _ M _ _ _ _ _ M ,
s _ M M M M M M M ,
s _ M _ _ _ _ _ _ ,
s _ _ _ _ _ _ _ _ ,
s _ _ _ _ _ _ _ _ ,
s _ _ M M M _ _ _ ,
s _ M _ _ _ M _ _ ,
s _ M _ _ _ M _ _ ,
s _ M _ _ _ M _ _ ,
s _ _ M M M _ _ _ ,
s _ _ _ _ _ _ _ _ ,
s _ _ _ _ _ _ _ _ ,
s _ M _ M M _ _ _ ,
s _ _ M M M _ _ _ ,
s _ _ _ _ _ _ _ _ ,
s _ _ _ _ _ _ _ _ ,
s _ _ _ _ _ _ _ _ ,
s _ _ _ _ _ _ _ _ ,
s _ _ _ _ _ _ _ _ ,
s _ _ _ _ _ _ _ _ ,
s _ _ _ _ _ _ _ _ ,
s _ _ _ _ _ _ _ _ ,
s _ _ _ _ _ _ _ _ ,
s _ _ M M M M M M ,
s _ M _ _ _ _ _ _ ,
s _ _ M M M _ _ _ ,
s _ M _ _ _ _ _ _ ,
s _ _ M M M M M M ,
s _ _ _ _ _ _ _ _ ,
s _ _ M M M _ _ _ ,
s _ M _ _ _ M _ _ ,
s _ M _ _ _ M _ _ ,
s _ M _ _ _ M _ _ ,
s _ _ M M M _ _ _ ,
s _ _ _ _ _ _ _ _ ,
s _ M M M M M _ _ ,
s _ _ _ _ M _ _ _ ,
s _ _ _ _ _ M _ _ ,
s _ _ _ _ _ M _ _ ,
s _ _ _ _ M _ _ _ ,
s _ _ _ _ _ _ _ _ ,
s _ _ _ _ _ _ _ _ ,
s _ M _ _ _ _ _ M ,
s _ M M M M M M M ,
s _ M _ _ _ _ _ _ ,
s _ _ _ _ _ _ _ _ ,
s _ _ _ _ _ _ _ _ ,
s _ _ M M M _ _ _ ,
s _ M _ _ _ M _ _ ,
s _ M _ _ _ M _ _ ,
s _ M _ _ M _ _ _ ,
s _ M M M M M M M ,
s _ _ _ _ _ _ _ _ ,
s _ _ _ _ _ _ _ _ ,
s _ _ _ _ _ _ _ _ ,
s _ M _ M M M M M ,
s _ _ _ _ _ _ _ _ ,
s _ _ _ _ _ _ _ _ ,
s _ _ _ _ _ _ _ _
};
#undef s
#undef M
#undef _
LcdCommand(0xb0); // Page address set
LcdCommand(0x10); // Column address set upper bit
LcdCommand(0x00); // Column address set lower bit
for(int i=0; i<sizeof(BitMap); i++) {
LcdData(BitMap[i]);
} // for i
} // Hello
図51のワークシートを作成する方法の説明では、ビットマップから16進数列への変換は、人が行っていました。この変換をExcelに任せる事が出来ます。
まず、下のリンクから、bmp2hex.xlsxというExcel 2013用のワークシートをダウンロードしてください。
ダウンロードしたbmp2hex.xlsxを開くと、図54の様な画面になります。
最初はD0~D7(1行目~8行目)には何も入力されていませんが、試しにE4のセルに1を入力してみます。(図55参照)
そうすると、1を入力したE4のセルが黒に塗りつぶされ、下のE9のセルには、その列のビットマップを16進数に変換した08が表示されます。
16進数に変換したいビットマップを、黒にしたいセルに1を入力する事により入力していくと、リアルタイムでビットマップが16進数に変換されていきます。もし間違ったセルに1を入力した場合は、その1を削除すると白に戻ります。
"Hello, world!"の文字列のビットマップを入力し終わると、図56の様になります。
次のページでは、指定した座標に点を描画する関数を作成します。
商品名 | 122X32モノクログラフィックLCDシールド | |
税抜き小売価格 | 3333円 | |
販売店 | スイッチサイエンス | |
サポートページ | 122X32モノクログラフィックLCDシールドサポートページ |
商品名 | GLCD学習シールドキット | |
税抜き小売価格 | 1410円 | |
販売店 | スイッチサイエンス | |
サポートページ | GLCD学習シールドキットサポートページ |