グラフィック・ディスプレイ表示の高速化
Arduinoでグラフィック・ディスプレイを使用するで紹介したGLCD(SG12864A)の表示高速化を行いました。
高速化の手法
前回記載した手法を採用ししました。内容を再掲します。
現状描画時の書き込みは、VRAMを更新すると同時にチップへの書き込みを行っています。チップへはランダムアクセスとなるため、データーを書き込む毎にコントロールレジスタにアドレスを設定しており、その点がオーバーヘッド要因と思われます。そのため、描画の際は一旦VRAMを更新した後、一定周期毎に1ライン分のVRAMデーターをブロック転送します。ブロック転送の際は、GLCD側でアドレスを自動インクリメントしてくれるためアドレス設定のオーバーヘッドがなくなる分高速化が期待できます。
一方で、前回の処理は変更が発生した箇所のみGLCDにデーターを送っているのに対して、今回のブロック転送方式では画面全体を更新するためデーター転送量が増加し、こちらが別のオーバーヘッド要因になってしまう可能性があります。
結果は最後に示しますが、微妙なところでした。
コードの概要
WriteData()関数で、vlanに画面データーを保存すると同時にデバイスへの書き込み(青字の部分)を行っていましたが、デバイスへの書き込みを削除。
void ks0108::WriteData(uint8_t data) {
uint8_t displayData, yOffset;
GotoXY(this->Coord.x, this->Coord.y);
yOffset = this->Coord.y%8;
if(yOffset != 0) {
// first page
displayData = this->ReadData();
displayData |= data << yOffset;
if(this->Inverted)
displayData = ~displayData;
//lcdDataOut( displayData); // write data
//this->Enable(); // enable
// -- Addition for Frame Buffer --
vram[Coord.page][Coord.x] = displayData; // Write back vram
// second page
this->GotoXY(this->Coord.x, this->Coord.y+8);
displayData = this->ReadData();
displayData |= data >> (8-yOffset);
if(this->Inverted)
displayData = ~displayData;
//lcdDataOut(displayData); // write data
//this->Enable(); //enable
// -- Addition for Frame Buffer --
vram[Coord.page][Coord.x] = displayData;
//this->GotoXY(this->Coord.x+1, this->Coord.y-8);
Coord.x++;
Coord.y -= 8;
}
else {
// just this code gets executed if the write is on a single page
if(this->Inverted)
data = ~data;
//EN_DELAY();
//lcdDataOut(data); // write data
//this->Enable(); // enable
// -- Addition for Frame Buffer --
vram[Coord.page][Coord.x] = data;
this->Coord.x++;
}
}
Update()関数を追加して、この関数の中で1ページ分(Y座標8ライン分に相当)のデーターをブロック転送します。スケッチの中で、GLCD.Update()を周期的に呼び出すことで画面を更新していきます。7 Seg LEDのダイナミック点灯と同じ原理です。
void ks0108::Update()
{
uint8_t chip, colum, x, cmd;
uint8_t displayData;
for (chip = 0; chip < DISPLAY_WIDTH/CHIP_WIDTH ; chip++)
{
cmd = LCD_SET_PAGE | page;
this->WriteCommand(cmd, chip);
cmd = LCD_SET_ADD | 0;
this->WriteCommand(cmd, chip);
fastWriteHigh(D_I); // D/I = 1 (Data)
for (colum = 0; colum < CHIP_WIDTH; colum++)
{
if (chip == 0)
x = colum;
else
x = colum + CHIP_WIDTH;
displayData = vram[page][x];
lcdDataOut(displayData);
this->Enable();
}
}
page++;
if (page >= DISPLAY_PAGE)
page = 0;
}
茶色の箇所でデバイスにデーターを書き込みます、Enable()を呼び出して、Enable信号をHigh→Lowに変化させる立ち下がりタイミングでアドレスの自動インクリメントが行われると思われます(データーシートには詳細な情報なし)。
デバイスへの書き込みを行った後にデバイスのビジー状態を確認するためのWaitReady()という関数がありますが、この関数を使ってタイミングを取るとうまく表示ができませんでした。WaitReady()関数はコントロールレジスタの読み出し(リード)を行いますが、データーのライト後にリードサイクルが入るとインクリメントがうまくいかないようです。(ひょっとして、リードサイクルでもインクリメントが発生して余計にアドレスを進めているのか?)。
そのため、ビジー状態は確認せずに連続して書き込みを行いますが、Enable()の中で使用しているdelay値(EN_DELAY_VALUE)を12まで大きくしないと書き込み速度にデバイス(コントローラー)が追随できませんでした。
Updateの呼び出し
MsTimer2ライブラリを使って、タイマー割り込みを発生させUpdate()関数を呼び出しています。MsTimer2ライブラリに割り込みハンドラーを登録するする必要があるのですが、ks0108ライブラリーの初期化コードの中でUpdate()関数を登録しようとしてもエラーが発生してうまく行きませんでした。
仕方がないので、メインのスケッチの中で、以下のコードを書いてUpdate()を割り込みハンドラーとして登録しています。
#include <ks0108.h>
#include <MsTimer2.h>
void setup(){
GLCD.Init(NON_INVERTED);
MsTimer2::set(1000/REFRESH/8, GlcdUpdate); //割り込みハンドラーを登録
MsTimer2::start(); //タイマー割り込みを開始
}
void GlcdUpdate()
{
GLCD.Update();
}
効果の程は
例によって、FPSを計るデモスケッチを使って画面の書き換え速度を計測します。更新速度はUpdate()を呼び出すタイマー割り込み周期に依存します。
先ずは、通常のLCDディスプレーと同レベルの60Hzを設定します。一回のUpdate()呼び出しで1ページ分を更新しており、画面全体を更新するためには8回の呼び出しが必要です。そのため、1秒間に60回画面全体を更新するための割り込み周期(ms)は;
1000(ms) / 60 (回) / 8 = 2ms
となります。
リフレッシュ60HzでのFPS値は、なんと13 FPSで、前回のVRAM版の性能を下回りました・・・ やはり画面全体の転送を1sに60回も行うとこちらの処理オーバーヘッドの方が大きくなります。
リフレッシュレートを、24Hz (割り込み周期5ms)にすると、20FPS!となりました。これなら(自己)満足の数字です。前回の結果とあわせて測定値の一覧を示します。
| 項目 | Original | VRAM版 | ブロック転送 |
|---|---|---|---|
| 最小DELAY値 | 4 | 1 | 12 |
| FPS | 8 | 14 | 20 |
動作中の写真を以下に示します。
ブレッドボード左側のチップは漢字フォント格納用のEEPROMです。漢字表示は現在トライ中ですので、動くようになったら公開します。
最後に今回使用したks0108ライブラリー(改)のコード一式を以下のリンクにアップしておきます。
「ks0108_wb.zip」をダウンロード
注)このソースは、オリジナルのks0108ライブラリを高速化のためにArduino Mega専用に改造しております。そのため、Mega以外のボードでは動作いたしません。
« ArduinoライブラリのMCUタイプ依存性 | トップページ | Arduinoで漢字表示 »
「Arduino」カテゴリの記事
- GLCDシールドの制作(2009.07.21)
- Arduino Ethernet Shieldのパワーオンリセット(2010.06.13)
- Arduino RSSリーダー(2009.09.06)
- LiquidCrystal(LCD)ライブラリの初期化コード(2009.05.25)
- ArduinoでNTPを使用する(2009.06.07)



コメント