グラフィックLCD画面のスクロールライブラリ公開
2013年は年明け早々から、久しぶりのソフト開発に熱中していた。やっとのことでAitendoのモノクログラフィックLCDにキャラクターを出してスクロールするライブラリが出来たので公開することにする。その前に、ちょっと電子工作以外の話題をご紹介。
久しぶりの家族旅行を楽しむ(1/7/2013)
お正月明けに何年ぶりかで家族揃って泊りがけの旅行に出かけた。行き先は鳥取県の皆生(かいけ)温泉というところで、お目当ては温泉と松葉ガニをたらふく食べることである。
昔なら東京から山陰と言うと汽車を乗り継いで1日がかりの旅だったが、今は、飛行機でひとっ飛びである。昨年結婚した娘も夫婦で参加し賑やかな家族旅行になった。まだひとり、娘が残っているので、お礼参りと縁結びを願って出雲大社に初詣する。
宿の蟹料理は、評判どおり何から何まで蟹づくしで、暫く蟹を見たくなくなるほど堪能した。2日目の夕食は蟹を断ったほどである。物価は明らかに東京より3割は安い。松江城の近くで昼食にとった出雲そばの鴨せいろも¥900でたっぷり鴨がのっていた。
松江は落ち着いた地方都市で、松江城のお堀端にある小泉八雲の旧邸や近くの武家屋敷は俗な観光地化がされていなく(人力車などいない)風情のある佇まいが残され、とても気に入った。温泉を少し離れたところにしてしまったので今度はもう少し近いところに宿をとり、もういちど訪ねてみたいところだ。
AVRでフォントファイルをオブジェクトファイルとして組み込む(1/3/2013)
それはともかく、電子工作である。AitendoのモノクログラフィックLCD(JCG12864)をSTM8Sで動かしてやろうと言うプロジェクトは、とりあえずAVRで始めている。そら。さんのソースを利用させてもらって、画面に文字が出てくるところまでは確認した。
前回の記事で、このプロジェクトのロードマップを提示したが、当面の目的であるスクロールを開発する前に、やっておきたいことがある。現在のフォントは8X16なので、128X64の現在の画面ではスクロールさせるには大きすぎて具合が悪い。もう少し小さいフォントにしたい。そうでないとスクロールのありがたみが薄れる。
というので、ChaNさんの「FONTXの使い方」や「32ビットのお誘い」のページを参考に、小さいフォントを取り込んでみることにした。STM8Sではオブジェクトファイルを埋め込むことはできないが、EEPROMで入れる予定で、フォントファイルそのものも勉強しておきたい。
ウェブであちこち適当なフォントを探す。すると、Shuji009さんのブログで洒落たフォントをTFTに沢山表示されており、ソースコードも公開されているのを見つけた。ありがたく頂く。おお、結構沢山のフォントが収容されている。
フォントデータの実装は、ChaNさんのオブジェクトファイルに埋め込む方式が一番スマートだ。詳しい解説も載っている。shuji009さんのフォントライブラリーから、6X12のフォントを選んで、objcopyの方法でFONTXファイルのオブジェクトを作ることとした。
AVRではobjcopyではなくavr-objcopyを使う(1/5/2013)
ところが、WINAVRには、objcopyがないのである。調べてみると、avr-objcopyというのがあり、これで代用できそうだ。とりあえずWinXPのDOS画面で試してみる。長いパラメーターを打ち込んで実行。ふむ、指示通りオブジェクトファイルが出来たようだ。AVRStudioに持ち込んで、makeしてみる。
だめだ。「出力ファイルのフォーマットが違う」というエラーではねられる。ChaNさんのコマンド例のelf32-littleではないようだ。なになに、avr-objcopyのヘルプメッセージに、サポートする出力フォーマットの種類が出ている。10個近くあるうち、AVRの名が付いた、elf32-avrというのがくさい。
ビンゴ!であった。コンパイルとmakeが通った。よーし、出来たぞ。あれ、SRAMが5KBもある。なにー、フォントデータがフラッシュではなくSRAMに入ってしまったようだ。うーむ、ChaNさんのコマンド例はどうもARM用のようで、AVR用ではないようだ。
ふつうの組み込みマイコンはフラッシュに較べるとSRAMははるかに少ない。SRAMにこういうデータを入れることはまず有り得ない。何かパラメーターがあってそれが抜けているだけなのだろうが、それが何なのかわからない。objcopyの機能は盛りだくさんでそう簡単には見つからない。
また、あれこれマニュアルや、ウェブサイトで情報を漁る。そのうち、ChaNさんのページの別の手法のアセンブラーステートメントのコメントに、
.section ".rodata" // ROM(定数領域)に配置(AVRなら.progmem)
と書いてあるのを見つけた(下線は筆者)。これだ! objcopyのパラメーターのひとつに、
.data=.rodata
というのがある。ここを.progmemに直せば良いのではないか。試してみる。やった、やった。これも大当たりであった。フォントファイルは、SRAMでなくフラッシュに埋め込まれた。いやあ、山勘が2つも当たってすこぶる機嫌が良い。
これから同じようなことをしようとする人の参考のために、AVRでobjcopyを使うときの正しいコマンド例を以下にお示しすることにする。ここでは、m6x12.fnt というファイルを、m6x12.oというオブジェクトにして、フラッシュ領域に入れる。
avr-objcopy -I binary -O elf32-avr --rename-section .data=.progmem,alloc,
load,readonly,data,contents m6x12.fnt m6x12.o
下線が、ChaNさんの例と異なる部分である。入力ファイル名はカレントディレクトリに置かないとフルパスが外部参照名になるので注意されたい。色々考えるより、このコマンド例で動くようにファイルをカレントディレクトリに持ってくるのが簡単だ。
GLCDとフォントのスキャン方向が違う(1/6/2013)
ソースコードを調整し、Makefileの定義を直して、新しい6X12フォントが出るようにプログラムを修正する。この手間は、フォントデータをオブジェクトファイルにするよりずっと簡単に終わった。早速、わくわくしながらプログラムを動かす。ふーむ、出てきたフォントは、2つに分割され、位置が90°ずれている。
そうか、そら。さんが言っていたフォントのXY軸を変換したと言うのはこのことだな。6X12のフォントだから、8ドットのフォントグリフ(ビットマップ列のこと)が2つに分かれて横になってしまっている。そら。さんは速度を理由に、フォントグリフそのものを変換されたようだが、こちらは余りフォントデータはいじりたくない。
フォントグリフのXY変換は、以前LEDマトリックスの電光掲示板でやったことがあるので作業にそれほどの不安はない。しかし、沢山のフォントを試してみたい時に、いちいち変換していくのは大変である。
それに、STM8Sはオブジェクトファイルを使わず、EEPROMにフォントデータを入れようと考えている。そうすると容量が大きくなるので、日本語フォントまで出せるかもしれない。なおさらフォントファイルはそのままにしておきたい。
しかし、なぜ、そら。さんがフォントの方向をY軸に変換したのか、その理由はプログラムを調べるにつれて明らかになってきた。このGLCDのハードの構造からプログラム上での変換処理が恐ろしく面倒になるのである。
このGLCDはドットの表示が縦(Y軸)に8ドットづつで1バイトのシリアルデータが入力単位である。GRAMバッファーは縦8ドット単位に128バイト(正確には129バイトで4ドット使わない)で1ページを構成し、これが8ページあることで全体のドットエリアをカバーする。
一方、FONTXデータは、横方向(X軸)に8ドット単位のフォントグリフを構成し、端数を切り上げて次の段のフォントとなる。この縦横変換は一筋縄では行かない。フォントの大きさが8ドットの倍数だったり、描きだすところが8の倍数単位なら、それほどでもないが、端数から描きだし、フォント幅が8の倍数以外の端数になると、とてつもなく難しくなる。
GLCDのページアドレスに苦戦(1/10/2013)
GLCDのページアドレスという構造そのもの自体が、簡単にプログラムが出来る代物ではない。端数からドットが始まる時は、1バイトデータの中のビット単位のシフト演算を繰り返して、隣接するバッファーにデータを埋め込む必要がある。
どういうことかと言うと、横線のように横にドットが並ぶ時は、1バイトの同一ビット位置にデータをセットし、これを、VRAMアドレス単位に繰り返して始めて直線が引ける。縦線は8の倍数なら、VRAMアドレスを横軸幅単位に飛び飛びにとって描きこめば描けるが、端数から始めたい時は、始点と終点でのビット演算が不可欠になる。
横スキャンのフォントデータを、縦に8ドットづつスキャンしてレクタングル(四方形)に埋め込むソースコードを書き始めた。久しぶりの本格的なプログラミングである。擬似コーディングというより、一種のパズルに近い。何枚もメモ用紙にGLCDのページとフォントのビット列の図を書いては、ロジックを組み上げていく。
参考にしているオリジナルのソースコードが難解である。このGLCDのVRAMバッファーに縦横ドット単位のレクタングル(四辺形)を埋め込む関数は、LcdWriteImage()という関数なのだが、これがさっぱり何をやっているのかわからない。
コメントをこちらでいくつも追加して何とか理解しようとするが、わかったのは、レクタングルのデータのスキャン方向が縦であるということくらいである。縦のデータの配分は、シフト演算で埋め込んでいるようだが、VRAM側のシフトと、レクタングル側のビット単位のシフトが錯綜して、すっきり頭の中に入ってこない。
こういう構造の中の縦横変換である。8X8などの変換ならまだしも、今度のように6X12という端数の出るフォントの変換はとてつもなく難しい。しかも、ビット順がリトルエンディアンだとすると、2バイトにわたるデータでは見た目が逆になるので大変だ。
何とかソースをでっちあげてテストするが、さっぱり文字の形にならない。軽い気持ちでフォントスキャンの方向変換を始めたことを後悔する。そら。さんがやったようにフォントデータの方を変換して先に進みたい誘惑にかられる。
しかし、負けず嫌いの性分だから簡単に引き下がるわけには行かない。UARTに途中経過の数値を吐き出させてはロジックを確認して先に進める。しかし、いつまでたっても画面はゴミのような斑点が出るだけで、一向に文字らしい図形はあらわれない。
やっと6X12フォントの表示に成功。速度はこれくらいなら問題ないか(1/13/2013)
結局、何とか文字らしいものが出るまでに1週間もかかってしまった(旅行の3日が入っているが)。自分としては珍しく、プログラムの構造を途中で換えた。3段ループを2段ループに変えて、やっと目鼻がついてゴミが文字らしくなってきた。
反省点はやっぱり擬似コーディングというか、抽象レベルでの念入りな検討が不足していたことにつきる。早くコーディングに入りすぎた何よりの証拠は、プログラムの構造を途中で変えたことである。人に偉そうに言う割には、つい目の前のソースコードに気を取られてプログラムを書いてしまう。
まだ時々ゴミが出るが、やっとのことで文字が正しく表示された。嬉しくて記念撮影する。横スキャンのフォントデータをビット単位に読み込んで縦方向に展開して表示しているので速度が心配されたが、全く気にならない。
それでも、これまでの縦にバイト単位に展開するのに較べれば、単純に考えても処理時間は8倍になっているはずである。どれくらい速度が違うのか調べたいおきたいところである。
ということでロジアナを引っ張り出して測定してみた。オリジナルの16X8フォントを画面一杯に出すプログラムで全体の表示時間を調べたあと、新しく開発した12X6のプログラムの時間を測る。
おやあ、最初が10.05msで、XY変換した方が9.75msだ。えー、おかしい。あ、そうか、フォントのドット数が違う。128ドットと、72ドットだ。表示するドットが2倍近く多い。しかし条件を揃えて測定するのは、プログラムを書き直す必要があって面倒だ。
そこで、ロジアナのチャートを見ながら、SPIの実測速度から転送時間を引いて(SPI速度は4Mhzあるが前後の準備で1Mhz程度)、XY変換にかかるロジックの時間だけを推定してみた。理論上は、同じ方向にバイト単位のフォントグリフを流し込む最初の方式にくらべ、ドット単位にデータを移していくので8倍は多くかかる勘定である。
計算によると、6X12ドットの1文字あたりの処理時間は、87μsで、16X8ドットは44.4μsであった。意外に大きな差ではない。8MhzのCPUクロックなので、40μsの時間差は300ステップ位である。
まあ、この程度なら、わざわざフォントを変換して入れ直さなくても大丈夫なのではないか。速度(クロック8Mhz)を上げればもう少し早くなる。見た目も殆ど変わりがない。
スクロールの速度自体も6X12フォントだと、64X128画面で100文字ちょっと。全面書き換え(スクロール時)で16ms程度で納まる。これなら見た目もスムーズなスクロールになるはずだ(テレビの速度がインターレースで20ms)。
スクロールは思ったより簡単に出来た(1/15/2013)
まだ、文字の下にゴミが時々出たりして完全ではないが、FONTX形式のフォントを直接出力できるようになったので、次のステップのスクロール機能の開発に進んだ。今の画面(128X64)に6X12のフォントで文字を出してスクロールさせるのは、精々が21字、5行というわずかなスペースだが、ライブラリの形で開発しておけば、今後の汎用的な使い方につながる。
スクロールの一番大変なところは、画面が一杯になったときに、画面全体を1行分上に描き直す処理をつくることである。データシートによれば、このGLCDのコントローラー(ST7565)には内蔵のVRAMを自由にアクセスする機能があるようだが(データシート26Pにdisplay start line address set commandというコマンドの紹介がある)、実装例がないので、そう簡単には手が出せない。
となると、アプリケーション側にあるVRAMを全部描き直してもういちど出力させなければならない。文字の位置を決める画面管理はあとにして、まず、この画面を描き直す関数LcdScroll()を新たに開発し始めた。
例のLcdWriteImage()の解析が相当進んで、ページアドレスのデータ処理に慣れてきたこともある。要するに、ひとつの8ドット(1バイト)のデータをシフトさせてY軸方向のバッファーに移動させて行くテクニックを連続して行うことである。
思ったより早くコードが出来た。ステップ数もわずかだ。早速main.cにコマンドを新設してテストしてみた。おおお、一発で動いた。いやあ、嬉しい。1ドットづつのスクロールも綺麗に動く(配布するソースには入っていません。テストのため暫定的に作ったコマンドです)。
図形も入れてスクロールテストプログラムの完成(1/18/2013)
勢いに乗って、直接キャッシュバッファーに書き込む一文字関数の開発に着手する。というのは、文字の出力がどうも不安定で、文字の下の部分(恐らく縦12ドットの下、残り4ドット)にゴミがでるバグがつきとめられないからである。
現行のやり方は、フォントをレクタングルに描いたあと、更にそのレクタングルを例のLcdWriteImage()を通してキャッシュバッファーに書き込むという2重の手間をかけている。どの部分が悪さをしているのか、どうしてもつきとめることが出来ない。
こういうときは、現行をあっさり諦めて、新規に作り直すほうが生産性が高い。一文字関数(LcdChr_Ank())に直接LCDバッファーに書き込むコードを追加する。経験値が上がってきているのだろう。こいつもそれほど時間もかからず完成した。
勇躍、テストに入る。良いぞ。ちゃんと文字が出る。速度も心なしか速いようだ(まさか)。スクロールする。うわっ、画面が目茶目茶になった。何だ、何だ。
これは、誰かがプログラムの定数部を壊しているからに違いない。慎重にprintfを入れて調査を始める。よーし、フォントアドレスの値が、スクロールをした直後、0になっている。スクロールを上にしたあと残ったエリアを0で埋めているところが臭い。
やっぱり、ここが犯人だった。1ページ余分に0にしておりVRAMバッファーを越えている。バッファーの直後にあったと見られる外部変数が壊されている。この不具合を直したら、今まで不調だった色々な処理や画面のゴミが一掃された。快調にスクロールが動く。フォントアドレスの内部変数を新設したことで派手に事故が起こり、思わぬトラブル解決に役立った。
余裕が出来たので、そら。さんが#if 0で止めていた、図形の関数を生かして遊んでみる。点と直線だが問題なく動いた。ちょっと面白いのでGLCDの初期画面(記事の最初)に応用する。ソースコード公開に向けて準備を始める。
回路図も作る。単にGLCDにSPIをつないだだけである。図の中のプルダウン抵抗はなくても動くと思うが、現行はついているので念のためつけておいた。
ソースコードは、そら。さんのプログラムを相当程度使わせてもらっている。公開に当たっては本人の事前の承認を得ておかねばならない。ライセンスはこれまでと同様、GPLではなく、もっと制限の緩い、ChaNさんのライセンス条件と同じ、商用、非商用を含めて、使用、複製、改変、配布一切自由、ただし著作権は放棄しない、全くの無保証という条件である。
間もなく、そら。さんから許諾を頂いた。スクロールライブラリのデモプログラムなので、メインのところはかなりやっつけである。簡単に説明しておこう。まず立ち上がると初期画面があらわれ、同時につないであるPCのコンソール(UARTがないとまだ動かない。38.4kbps 8ドットパリティなし)のキーで、いくつかのコマンドが動く。
ST7565.cとST7565.hのライブラリは汎用性があるように作ったので、FONTXの大抵のフォントファイルで動くだろう。 ST7565.hのヘッダーファイルでフォントを指定する。フォントバッファーの値も修正する必要があるかもしれない。Makefileのインクルードするフォントのオブジェクトファイルの修正も忘れないように。
以下に例によってAVRStudioのフォルダーをzipで固めたファイルを置きます。xitoa..Sや、xitoa.hはデバッグ用に入れたxprintfのためで特に必要ではありません。
| 固定リンク
| コメント (6)
| トラックバック (0)
最近のコメント