PicoのVGAボードで静止画を表示させてみた

今回も引き続きPicoのVGA出力の話題です。今回はフラッシュメモリ上の画像データを表示する方法を見ていきます。

scanvideoライブラリではピクセル列を表示するデータ構造(COMPOSABLE_RAW_RUN)がありますので、静止画を表示すること自体は難しくありません。

320×240ピクセル、1ピクセル16ビットの画像データが配列imageに入っているとします。

const int image_width  = 320;
const int image_height = 240;

static uint16_t image[] = 
{(略)

この画像データの1ライン分を画面に表示するためのscanvideoライブラリへのコマンドは以下のようになります。

これを実装したコードはこちらにあります。これで、QVGAサイズの画像をモニターに表示することができます。

それでは、より大きいVGAサイズの画像を表示させるにはどうしたらいいのでしょうか。配列imageをRAM上に置いておくと、画像のサイズはRAM容量(264KB)の制限を受けます。QVGAサイズの画像で150キロバイトが必要であり、VGAサイズの画像はRAMには入りきりません。

配列を定数にし、フラッシュメモリの中に格納すれば、より大きな画像データが使えます。Picoのフラッシュメモリの容量は2MBですから、1ピクセル2バイトとして1000×1000ピクセル程度の画像を格納しておくことができます。

そこで先ほどのコードを以下のように修正してみます。 これで配列imageはフラッシュメモリの中に格納されます。

const uint16_t image[] = 
{

しかしこれで実際に動作させてみると、画像が表示されません。調べてみると、スキャンラインバッファのデータを準備するのが間に合っていないようです。横方向のピクセル数を減らしていくと、ある閾値を切ったあたりで表示されるようになってきます。おおむね横210ピクセル程度を描画するのが限界で、それ以上描画すると時間切れになってしまいます。

以下の画像は、実際に時間切れになっている時の画面表示での様子です。このように、時間切れになると画面の左半分が青右半分が黒になってしまいます。

しかし、pico-playgroundに入っているデモのひとつ「flash_stream」では、 VGA サイズの画像を安定して表示できています。調べてみると、このデモではフラッシュメモリからスキャンラインバッファへのデータ転送を CPUを介さずにDMAで直接行なっていました。クロックも200MHzに上げています。なおフラッシュからRAMへのDMA転送については、こちらにもサンプルがあります。

このコードを拝借して、 配列imageからスキャンラインバッファへのデータ転送をDMAで行うと、画像サイズが VGAでも安定して表示することができました。

また、「flash_stream」では、 このDMA転送を行う関数を、フラッシュメモリではなくRAM上に置くようにしています。命令コードを読み出すためにフラッシュメモリへアクセスすることを避けるためと思われます。ただ実際にはキャッシュが効いているためか、これをやらなくてもあまり大きな違いは出ないようです。

なお、「flash_stream」では画像データをuf2ファイルを介さずに直接フラッシュメモリへ書き込んでいますが、これは描画スピードには影響は無く、uf2ファイルのサイズを小さくするために行っているようです。

そのほか、このようにフラッシュメモリから高速にデータを読み出すことが必要な場合、scanvideoの処理とUSBプロトコルスタックを同じCPUコアで動作させるのは避けた方が良いようです。標準入出力を USB 経由にしてみたところ、 画像出力が全く安定しませんでした。

今回作ったコードはRAM版(画像はQVGA)・フラッシュメモリ版(画像はVGA)ともに、以下に置いてあります。

pico_test_projects/vga-test3 at main · boochow/pico_test_projects
Some projects to test Raspberry Pi Pico unique functionalities, such as interpolators or scanvideo library. - boochow/pi...

コメント