ライン

PICマイコン+外付けSRAMによるカラービデオ出力実験

ライン

2013年5月30日

 これまでPICマイコンでカラービデオ出力の実験と応用を行ってきましたが、グラフィック版では解像度が160×100ドット、8色同時出力という信じられないほどの低解像度しか実現できませんでした。これは一般的なPICの内蔵SRAMがせいぜい8KBや16KBと少ないためで、やや高価なPIC32MXシリーズでも128KBが現在のところ最大のようです。もう少し解像度を上げて、色数も増やしたいと考え、外付けSRAMを探してみると、CY7C1041DV33というサイプレスの4Mビット品(256Kワード品)がありました。価格は手ごろで、アクセス速度も10nsと結構使えそうです。これをビデオメモリとして使い、240×224ドット、15ビット色のビデオ出力の実験をしてみました。

実験結果画像
実験結果写真。ビデオキャプチャーボードの性能が低くて少し色が乱れています。

ハードウェア設計

 ビデオ出力に関してはこれまで同様に、3.579545MHz(以下3.58MHzと記載)の水晶をPICの外付けクロック源とし、汎用I/Oポートに接続された5ビットのDAコンバーターでコンポジット信号を生成します。そして、外付けSRAMとしてCY7C1041DV33をつなげるのですが、このメモリをフル活用するには、アドレスバスが18本、データバスが16本、書き込み制御に最低1本の信号線が必要となります。データバスは8ビットずつのアクセスもできますが、速度の面から不利なため、16ビットで使うことにします。ここまでで40本のI/Oポートが必要です。探してみると、最初にカラービデオ出力の実験に使った64ピンTQFPのdsPIC33FJ256GP506が使えそうです。検討した結果、以下のような回路となりました。

回路図
回路図は非常にシンプルなのですが...

 データバスは唯一16ビットが全て使えるPORT Bを割り当てます。アドレスバスは下位アドレスがなるべく連続でとれるように、PORT Dの12ビットと、ばらばらになりますがPORT Gを使うことにしました。映像用のDA出力にはPORT Fの下位5ビットが使えます。
 それにしても、どうしてこんなに汎用I/Oポートが歯抜けで搭載されているんでしょう。たとえばPORT Gは0〜3、6〜9、12〜15があって、4、5、10、11はないので使いにくくて困ります。
 今回、DAコンバーターの抵抗値の見直しを行いました。これまで、5ビットDAといいながら、実際には0〜24くらいまでの出力しか使っていませんでした。広大なビデオメモリで理論上の色数も増えるので、実際に表現できる色解像度を高くするため、0〜31までフルに使うようにしました。まだ最適値ではないようですが、手持ちの抵抗の制限もあって、回路図に記載のようにしました。

実験写真
バスの配線をピンソケットとジャンパー線でするとぐちゃぐちゃです。下にあるのがdsPIC、右側がSRAMです。

クロック設計

 コンポジット信号の詳細については、これまでに実験したこちらをご参照ください。
 これまでの経験から、PICでカラーコンポジット信号出力して実用に耐え得る解像度は、横が240ドット程度であることがわかっています。縦は水平ライン数の関係から220ドット程度まで出力が可能です。横240ドットの出力は、以前PIC32シリーズで作成したカラーテキスト出力の時と同じで、カラーサブキャリアの4分の3周期を1ドットとすることで実現できます。図のようにカラーサブキャリアの90度ごとに信号を出力すれば、だいたい目的の色が出力されます。
 PIC32では3.58MHzの外部クロックを内部で16倍して、約57MHz(=57MIPS)で動作させました。今回のdsPICでも同じことができないかと、内部32倍の114.5MHz(1命令2クロックなので57MIPS)で動かしてみましたが、さすがにまるで動きませんでした。私の手元のチップでは、最大で内部30倍の約107MHzが限界でした。カラーサブキャリアの90度ごとに信号出力するためには、命令クロックが3.58MHzの4の倍数でないといけないので、内部で24倍して1周期12命令、1ドット9命令で作成することにしました。これなら、以前の最初のカラーグラフィック版と同じクロックなので、約43MIPSとややオーバークロックとはいえ、安定動作の実績があります。
 ところで、どうしようもないことなのですが、この方式では1ドットの縦横比が1:1にはなりません。今回の場合、1.3倍程度横に長い長方形となります。

波形

映像信号タイミング

 43MIPSで動作させる場合の水平1ラインのタイミングは下図のようになります。これまでは、同期信号の立ち下がりのみタイマー割り込みを使い、あとはタイマーに同期させた出力コンペア割り込みを複数使って、タイミングを取っていました。しかし、今回のPICでは、出力コンペアを使うためには、なんとPORT Dを犠牲にしなければなりません。28ピンの少ピンPICなら周辺機能はピン割り当てを無効にできるのですが、64ピンPICだと強制的にピンが割り当てられていて、しかも汎用ポートと共通ピンとなっているため、融通がききません。仕方がないので、全てタイマー割り込みで代用することにしました。幸い、タイマーは9本もあります。内4本を使いました。使い方は、タイマー周期を全て同じにし、タイマー開始位置をうまく調整して、一斉にタイマースタートすれば、希望のタイミングでそれぞれの割り込みを発生させることができます。

水平1ラインのタイミング

SRAMへのアクセス方法

 今回の実験の主役であるSRAMへのアクセスは、書き込み、読み出しともいくつかの方法があるようですが、最もシンプルなものにしました。SRAMのCE、OE端子は常時接地しているので、WE端子だけで読み出しモードか書き込みモードかの変更ができます。普段はWEをHにして、読み出しモードにしています。この場合、PICから出力されたA0〜A17のアドレスに書かれている16ビットデータが、I/O 0〜15に出力されます。アドレスを変更すると、10ns後にデータが出てくるので、それを読み出すだけです。今回のシステムクロック数(85.9MHz)では、1命令クロック約23nsなので、ウェイトなしでもいいような気がするのですが、実験で確認したところ、2命令のウェイトが必要でした。PICの信号の立ち上がり時間を考慮しても、そんなにはいらない気がするのですが。
 書き込みを行うときは、書き込みたいアドレスとデータをPICから出力しておき、WEにH→L→Hのパルスを入れます。Lの期間は1命令のウェイトが必要でした。
 実際のプログラムは右記のようになります。インラインアセンブラーで記述しました。割り込み禁止は絶対に必要です。また、なるべくムダな時間を減らしたいので、ウェイトにはNOPの代わりに、必要な命令を実行したりしています。
 なお、映像出力時の画像データ読み出しは連続して高速に読み出す必要があるため、この読み出し関数は使用していません。

SRAMアクセスプログラム

ビデオメモリ形式とメモリマップ

 CY7C1041DV33はアドレスバスが18本、データバスが16本あり、0x00000〜0x3FFFFの各アドレスに16ビットのデータを記憶できます(256Kワード)。今回の設計では、映像出力中は、1ドットに対して、9命令中に3回の色信号波形出力が必要となります。残る6命令でビットマップデータを読み出し、波形に変換しアドレス更新をすることはとてもできません。そこで、ビットマップの格納領域には、色の情報ではなく、実際に出力する3回分の波形データそのものを記録することにしました。5ビットDAコンバーターなので、5ビット×3回分=15ビットと16ビット内に収まります。これにより、映像出力中は、1ドット当たり読み出し1回、信号出力3回、5ビットシフト2回、読み出しアドレス更新1回の計7命令となり、9命令内に収めることができそうです。これだけ余裕があれば、ちょっと工夫が必要ですが、ループカウンタとジャンプ命令でループを回すこともできます。
 1ドットが16ビットに収まったので、240×224ドットは、64Kワード内に入ります。メモリに余裕があるので、縦横とも256ドット分の領域を割り当てました。
 ビットマップ領域に波形データを格納することにしましたが、描画するたびに色から波形を計算するのは大変です。そこで、各色ごとの波形データを事前に計算し、メモリに記憶しておくことにします。波形データは各色5ビット×4回分必要です(波形図のS0〜S3)。1ワード(16ビット)には収まらないので、各色2ワードを使用します。これが15ビット色分あるので、やはり64Kワードの領域を必要とします。
 点を描画する関数内では、指定の色に相当する2ワードの波形データをメモリから読み出し、X座標に応じて必要な3つの信号を取り出し、1ワードにしてビットマップの指定領域に書き込む、という処理を行います。
 なお、この方式では座標を指定してその点の色が何色か判別することはできません。また、1ドット単位での横スクロールにも対応できません。
 メモリマップを見るとわかるように、まだ128Kワードもの未使用領域があります。例えばビデオメモリを2枚にして、ダブルバッファとすることも簡単にできますし、大容量の観測データなどを記録しておくことも可能です。

メモリマップ

プログラム

 実験用に作成したプログラムのソースを公開しておきます。起動すると最初にクロック設定とI/Oの初期化を行い、その後ビデオ関係の初期化を行います。ビデオ関係の初期化では、ビットマップ領域のクリアと、信号波形データ領域に15ビット色分全ての波形の計算を行い格納します。そして、タイマー割り込みの設定を行い、コンポジット信号出力を開始します。
 コンポジット信号出力割り込みルーチンは、これまでの実験同様に、ほぼインラインアセンブラーを使って記述しています。今回の工夫としては、割り込みルーチンの最初にTimer値を読み込んで、割り込み発生タイミングのばらつきを補正しています。dsPICではDWORDを扱う命令や、フラッシュ領域からのデータ読み出し(PSV領域へのアクセス)、アイドル状態からの復帰などで、割り込みタイミングがずれる場合があります。それに何より、SRAMアクセス時に割り込み禁止をしており、これらを考慮すると最大10クロック程度遅れる場合があります。

ソースプログラムのダウンロード

 私の環境は、MPLAB IDE Ver 8.80です。コンパイルに際して、最適化オプションは特に指定してもしなくても、問題なく動作しています。

実験結果

 240×224ドット、15ビット色がどの程度再現できるのか当初不安でしたが、実際に試してみたところ想像以上によい結果が得られました。特にデジカメで撮影した自然画像はPICで出力しているとは思えないほどです。以下のサンプル画像で縦線にぶれが生じたり、細かくノイズが出ているのは、使用したビデオキャプチャボードの性能のせいです。実際にテレビに出力すると、くっきりした線となっています。
 以下、実験に使用したデモを紹介します。

デモ1

 最初にテストしたものです。解像度と色の再現性を見たかったので、特に面白みはないですが、作ってみました。

<プロジェクトに追加するファイル>
 sramvideodemo1.c
 sramvideo.c
 sramvideo.h

デモ2

 写真データを表示します。本当はSDカードからJPEGファイルを読み込んで表示したかったのですが、マイクロチップ社が提供しているSDカードのライブラリはSPI通信を使っています。SPIに使える端子はもう空いておらず、空き端子を使って自力でSDカードの処理を作るのも大変なので、やめました。簡単に写真データをPICに渡す方法として、ソースファイルにデータを持つことで、PICのプログラム領域に直接書き込むことにしました。
 Windowsのbmp形式で240×224ドット、24ビット色の画像ファイルから、PIC用のアセンブラーソースファイルを出力するWindows用実行ファイルを作成しました(bmp2txt.exe)。使い方は、bmp2txt.exeとbmpファイルを同じフォルダに入れ、コマンドプロンプトでそのフォルダから、

   bmp2txt filename.bmp

とすることで、bmpdata.sというアセンブラーソースを出力します(filename.bmpは任意のファイル名)。このソースには、getbmpdata()というCから呼び出せる関数と画像データが含まれており、引数に何番目のデータかを与えて呼び出すと、16ビットカラーで値を返すようになっています。あくまで実験用なので細かいエラーチェックはしていません。お気を付けください。
 なお、単純に画像ファイルを変換すると1ドットの縦横比の関係で、横方向に間延びした画像がテレビに出力されます。ここに掲載したサンプルは、XGAなどの元々4:3の画像を画像処理ソフトで縦横比を保存せずに240×224ドットに変換してから、bmp2txtにかけています。そうすると、だいたい元通りの縦横比で表示されます。

<プロジェクトに追加するファイル>
 sramvideodemo2.c
 sramvideo.c
 sramvideo.h
 bmpdata1.s または bmpdata2.s
 ※プロジェクトのbuild optionsで、C30のMemory Modelとして、「Large code model」を指定する必要があります。

デモ3 (2013.6.8追加)

 レイトレーシングと呼ばれる手法で3Dグラフィック描画しました。約20年前の大学生の頃に、数値演算プロセッサー(通称コプロ)を載せたPC-9800シリーズ用に作ったプログラムを移植しました。移植と言っても、解像度変更や縦横比の補正などだけで、それ以外の計算部分は何も手を加えることなく動作しました。当時の私のプログラムにはほとんどコメントがないのが難点です。
 main関数の中でstop_composite()という関数をコメントアウトしていますが、これを有効にすると映像信号出力は停止して、計算に専念します。その場合でサンプル画像の描画に3分程度かかります。映像信号出力再開にはstart_composite()関数を使います。出力をしながらだと10分程度かかりますが、途中が見えないと不安なので、最初は出力したままがいいと思います。いずれも最適化オプションを指定しての時間です。
 画面出力を停止して高速動作させるというと、昔の8ビットPCを彷彿させるものがあります。ちなみに、現代のPCで同じものを実行すると一瞬で終わります。
 ※プロジェクトのbuild optionsで、C30のMemory Modelとして、「Large data model」を指定する必要があります。

<プロジェクトに追加するファイル>
 sramvideodemo3.c
 sramvideo.c
 sramvideo.h

サンプル画像1
デモ1-1

サンプル画像2
デモ1-2

サンプル画像3
デモ2-1

サンプル画像4
デモ2-2

サンプル画像5
デモ3

最後に

 PICマイコンに外付けSRAMを接続することで、240×224ドット、15ビット色というまずまず高画質なグラフィックを実現することができました。今のところ静止画の表示しか行っていませんが、ゲームやデジタルサイネージ(電子看板)など、応用範囲はいろいろあると思います。何かよい使い道などありましたら、私の掲示板に投稿いただけると幸いです。ご意見、ご質問などもぜひ書き込んでください。

Copyright (C) KenKen All Rights Reserved.