Raspberry Pi Picoを手に入れたので、気になっていたPIOを使用し、VGA出力を試した時のメモです。
Raspberry Pi Picoをいじっていると、サイズの大きなデータの転送を行っていると動作が不安定になることがあるため、やはりシリアルフラッシュメモリ上にあるプログラムが遅いのか?と言うことで、まずフラッシュメモリ速度も調査しました。
Raspberry Pi Picoのオーバークロックに対応するため、画面モードとAPIの仕様を変更。
スプライトの最大数と1行あたりのスプライト数をコンフィグで設定できるように変更。
今回の変更により、タイル面を2枚使用した多重スクロールや、スプライトとテキストの同時表示、スプライト数の増加が可能。
PIO処理やデータフォーマットなどの説明を追加。
8bitビットマップ面の解像度に、256x240を追加。
ストレッチありによる320x240へ引き延ばしと、ストレッチなし時に水平表示位置を最大64dotへ移動を追加。
テキスト面に、シャドーフラグを追加。
シャドーフラグを使用することで、透過色、または透過色以外の領域の輝度を50%下げる(黒画との半透明50%と同じ)を追加。
4bitビットマップ面を追加。
・4bitビットマップ面追加に合わせて、スプライトやテキストなどの4bitデータの並びを4bitビットマップ面と同じリトルエンディアンに変更。
タイルフォーマットが抜けていたのを追加。
VGA端子の仕様が良く分かりませんが、VSyncが検出用にプルダウンされているようで、一部モニターでVSyncが少し不安定になります。
そのため、良いかわかりませんがピン設定の Drive Strength を上げて対応しました(仕様ではVSyncは5V?)。
また、使用するコンパイラ(arm-none-eabi-gcc)のバージョンが上がるとビットを絡めたif分などで効率の悪いコードを吐き出します(40%程度速度低下)。
もしかしたらコンパイルオプションで指定できるのかもしれませんが、現状回避方法が分かりません。
今までLinux Mint 19.02のaptで取得したコンパイラ(6.3.1)で開発を行っていましたが、このバージョンは最適化に2つの不具合に遭遇(逆アセンブルすると生成されたコードが呼ばれない箇所が発生するなど新しいバージョンでは正常に動作)。
そのため、バージョンを上げた(10.3.1)所、描画負荷が高くなると画面が崩れるが問題が発生。
Raspberry Piのコンパイラ(7.3.1)でビルドして確認した所、こちらでも画像崩れが発生するが、速度は20%程度低下と新しいバージョンの方が遅い。
処理負荷をオシロで波形確認すると、特定の描画処理が極端に遅くなることを確認。
問題の発生するコードを逆アセンブルすると、以下のようにコードが出力されました。
■C言語 uint32_t value ; value = (pattern >> 27) & 0x1e ; if( value ) { p_buff[0] = *(uint16_t *)&p_palette[value] ; } ※1 value = (pattern >> 23) & 0x1e ; if( value ) { p_buff[1] = *(uint16_t *)&p_palette[value] ; } ※2 □正常(6.3.1) □問題発生(7.3.1 / 10.3.1) ※1 20000578: 001e movs r6, r3 2000059e: 0edd lsrs r5, r3, #27 20000586: 0ed7 lsrs r7, r2, #27 20000588: 4451 add r1, sl 200005a2: 400d ands r5, r1 2000058a: 403e ands r6, r7 2000058c: 423b tst r3, r7 200005a4: d001 beq.n 200005aa 2000058e: d001 beq.n 20000594200005a6: 5b55 ldrh r5, [r2, r5] 20000590: 5b8e ldrh r6, [r1, r6] 200005a8: 8005 strh r5, [r0, #0] 20000592: 8006 strh r6, [r0, #0] ※2 20000594: 001e movs r6, r3 200005aa: 0ddd lsrs r5, r3, #23 20000596: 0dd7 lsrs r7, r2, #23 200005ac: 400d ands r5, r1 20000598: 403e ands r6, r7 2000059a: 423b tst r3, r7 200005ae: d001 beq.n 200005b4 2000059c: d001 beq.n 200005a2 200005b0: 5b55 ldrh r5, [r2, r5] 2000059e: 5b8e ldrh r6, [r1, r6] 200005b2: 8045 strh r5, [r0, #2] 200005a0: 8046 strh r6, [r0, #2]
なぜか and 後に比較を行っていたり、レジスタを逆にしたりと、シフトが絡むと怪しい。
コンパイラのバージョンが上がるほど処理速度が低下するので、Cortex-m0は無視されているのか?
最悪アセンブラで作成すれば良いが汎用性がなくなるので、Cypressのように片方のコアだけでもCortex-m3/m4になってくれないかな。
Thumb-2が使用できればビット関連に強く、IT命令などが使用できるため高速化できるのに。
PSOCなどのマイコンがシリアルフラッシュメモリ上にあるプログラムを、通常のROMに見せかけて実行する仕組みを持っており、どの程度の速度で動作するか気になっていた時に、この仕組みを使用しているRaspberry Pi Picoの動作が不安定になる問題が発生したため、簡単な速度計測をしてみました。
Raspberry Pi Picoのプログラムを配置する2MBのフラッシュメモリはW25Q16で、QSPI接続ではありませんが以前使用したことがあるため、W25Q16の読み込みは、「インストラクション+アドレス24bit(+ダミー)+データ」と1バイト読むだけでも結構なクロック数が必要となるので凄く遅いかなと思いましたが、
実機でテストプログラムを動作させてみたら結構な速度で動作していました。
どうもXIPという仕組みに16kBのキャッシュを持っているようで、小さいプログラムを動作させている時はこのキャッシュ上で動作しているため、フラッシュメモリへのアクセスが発生していないようです。
そのため、大きいサイズのデータへアクセスすると速度に影響が出ると思い、以下テストを行い、オシロで時間を確認しました。
テスト内容 | 転送方法 | 速度 |
---|---|---|
RAM 0クリア(320x240バイト) | DMA(8bit) | 0.616ms |
DMA(32bit) | 0.154ms | |
memset | 0.308ms | |
stm命令を並べて64バイト単位 | 0.220ms | |
RAM → RAM コピー(320x240バイト) | DMA(8bit) | 0.920ms |
DMA(32bit) | 0.154ms | |
memcpy | 0.500ms | |
ldm/stm命令を並べて64バイト単位 | 0.412ms | |
ROM → RAM コピー(320x240バイト) | DMA(8bit) | 9.400ms |
DMA(32bit) | 8.300ms | |
memcpy | 8.300ms | |
ldm/stm命令を並べて64バイト単位 | 8.300ms |
やはり、ROM → RAM時に、キャッシュサイズを超えた時に劇的に遅くなります。
また、コピーを行う時のプログラムもキャッシュから外れてしまい、速度低下が発生しているようです。
XIPの設定を確認してみると、キャッシュを有効・無効にするアドレスなどの設定がありそうです。
サイズの大きなデータコピーによる速度低下の回避は無理ですが、サイズの大きなデータの配置先をキャッシュ無効、
プログラムの配置アドレスのみキャッシュ有効とすれば、それなりに効率良く動作できそうです。
が、cmakeの仕組みなどが良く分かっていないため、リンカスクリプトなどの定義ができないためテストできていません。
VGAの出力に関する処理タイミングが不安定になると正常に表示できなくなるため、今回はVGA出力に関する関数はGCCの属性指定(__attribute__で.dataセクションへ配置)を使用し、
プログラムコードをRAMへ配置+画像データもRAMへ配置することで、動作が不安定になることを回避しています。
PIOを1つと3ステートマシン、DMAを2ch(ピクセルデータ転送と背景色塗りつぶし)使用してD-SUB15ピンによるVGA(640x480)出力を試しました。
VGAを選択したのは、PIOのクロックが125MHzのため、PIOの出力を5分周すれば25MHzとなり、VGAのピクセルクロック25.175 MHzとほぼ同じになる+現在のモニターでも表示できる解像度のため。
VGAタイミング | 回路図 |
最大色数を16bit(回路図は12bitで、R=4bit,G=4bit,B=4bitの4096色)とします。
ただ、VGAで表示しようとするとVRAMサイズが約600KB(640x480x16bit)必要となりRAMが全然足りず。
仕方ないので表示領域のピクセルクロックを倍、縦は2度同じデータを表示することで、320x240x16としました。
それでもVRAMサイズが約150kB必要なため、ダブルバッファ分のRAMを確保できず。
せっかくCPUがマルチコアなので、1つコアを描画専用に使用することで、以下の画面モードをサポートすることにしました。
表示面 | 表示内容 | 機能 |
---|---|---|
背景 | 単色 | ・16bit中1色(今回は4096色中1色) |
背面 | 4bitビットマップ |
・16色パレット(透過色+15色パレット) ・解像度320x240 ・ダブルバッファ可能 |
8bitビットマップ |
・256色パレット(透過色+255色パレット) ・解像度320x240 / 256x240(ストレッチのあり・なし選択可) ・ダブルバッファ可能。 | |
16bit ビットマップ |
・4096色(ピクセルデータの最上位ビットが立っている時は背景透過) ・解像度320x240 ・RAM不足のためダブルバッファ不可(解像度を落とせば可能)。 | |
タイル(BG) |
・仮想画面512x512 ・サイズ8x8 ・16色パレット(透過色+15色)x16 ・水平・垂直反転 ・水平・垂直スクロール | |
前面 | タイル(BG) | 機能は背面のタイルと同じ |
その他 | スプライト |
・仮想画面512x512 ・サイズ16x16 ・16色パレット(透過色+15色)x16 ・水平・垂直反転 ・水平・垂直オフセット ・表示優先度(非表示・背面より奥・前面より奥・手前) ・定義数(コンフィグにより設定) ‐オーバークロックなし時:64個程度 ‐オーバークロックあり時:100個程度 ・1行あたり表示数(コンフィグにより設定) ‐オーバークロックなし時:16個程度 ‐オーバークロックあり時:24個程度 |
テキスト |
・サイズ8x8 ・16色パレットx16 ・透過色有無 ・垂直スクロール ・シャドー(透過色、または透過色以外の色の下にある画像の輝度を50%) |
背景、背面、前面、スプライト、テキストを組み合わせて表示する。
ただし、組み合わせと表示する画像により処理負荷(透過しないドットが多いほど処理負荷は高い)が高くなり、正常に表示できなくなるので注意すること。
※オーバー+クロックなし時は、背景+背面+スプライト(1行あたり16個・定義数64個)、または背景+背面+テキストで安定動作。
オーバークロックあり時は、背景+背面+前面+スプライト(1行あたり24個・定義数100個)、または背景+背面+前面+テキストで安定動作
表示優先順位 (スプライト(prio2)は、前面タイル使用時のみ) |
ラインバッファにより描画を行うため、4/8/16bitのビットマップ面以外はRAM使用量を減らすことが可能。
また、HSync中の操作により、ラスタースクロール、スプライト数の増加、画面モードの切り替え、パレットの切り替えなどが可能。
PIOは非常に高速(ベースクロックで動作)で、1つの命令で複数の処理を同時に行うことができる非常に面白い機能ですが、大きな値をPIO内で生成することができません(たぶん)。
大きな値を扱うには、マイコンから値をpio_sm_put_blocking関数などでpushしたものを、PIOからpullでレジスタへ受け取るといった方法で行うと思います。
が、この方法だとただでさえPIOで使用できるの命令数は32個と非常に少ない(PIO毎にステートマシンが4つありますが、それぞれ値を代入しようとすると4命令消費し28命令しか使用できなくなる)が、
この値を代入ためだけに少ない命令数を消費するのはもったいない+マイコン側から値を渡すのも面倒。
PIOにはマイコン側から1命令実行するための機能(pio_sm_exec_wait_blocking関数など)が存在するため、今回はこの機能を初期化時に使用し、タイミングなどの値をスクラッチレジスターYへ値を代入しています(vga/vga.cのinitPioSm関数の最後)。
VGAの出力には1つのPIO、3つのステートマシン(水平クロックジェネレータ、垂直クロックジェネレータ、有効ピクセル転送)、32個の命令を使用します。
水平クロックジェネレータ処理は、水平同期信号を生成します。
スクラッチレジスターYは、vga/vga.cのm_pioSmInfo[0]から、(((48+640+16)*VGA_CFG_DOTCLK-1-1)<<16) | (96*VGA_CFG_DOTCLK-3-1)を代入。
下位16ビットに同期のドットクロック数、上位16ビットにバックポーチ・有効ピクセル・フロントポーチのドットクロック数を設定している。
水平クロックジェネレータタイミング |
.program vga_horz .side_set 1 opt .wrap_target ; ---------------------------------------------------------------- ; 同期期間(①) ; ---------------------------------------------------------------- mov osr, y side 0 ← HSyncピンを0(③) irq clear 4 ← 割り込み4待ちしている垂直クロックジェネレータを動作 ; 同期 out x, 16 vga_horz_sync: jmp x--, vga_horz_sync ; ---------------------------------------------------------------- ; バックポーチ・有効ピクセル・フロントポーチ期間(②) ; ---------------------------------------------------------------- ; バックポーチ・有効ピクセル・フロントポーチ out x, 16 side 1 ← HSyncピンを1(④) vga_horz_back_pixel_front: jmp x--, vga_horz_back_pixel_front .wrap
垂直クロックジェネレータ処理は、垂直同期信号の生成と、有効ピクセル転送で転送する画像を生成するタイミングをマイコンへ割り込みで通知します。
スクラッチレジスターYは、vga/vga.cのm_pioSmInfo[1]から、((480-1)<<16) | (33-2-1)を代入。
下位16ビットにバックポーチ行数-2、上位16ビットに有効ピクセル行数を設定している
(バックポーチを-2しているのは、有効ピクセルに表示する画像を2行前から生成するタイミング調整を行うため)。
上記以外のタイミングPIO内で調整。
垂直は行単位で処理を行う必要があるため、水平クロックジェネレータの割り込み待ちでタイミングを合わせている。
垂直クロックジェネレータタイミング |
.program vga_vert .side_set 1 opt .wrap_target mov osr, y side 1 ; ---------------------------------------------------------------- ; フロントポーチ期間(①) ; ---------------------------------------------------------------- ; フロントポーチ set x, (10-1) vga_vert_front: irq wait 4 jmp x--, vga_vert_front ; ---------------------------------------------------------------- ; 同期期間(②) ; ---------------------------------------------------------------- ; 同期 irq wait 4 side 0 ← VSyncピンを0(⑤) irq wait 4 ; ---------------------------------------------------------------- ; バックポーチ-2行期間(③) ; ---------------------------------------------------------------- ; バックポーチ - 2 out x, 16 side 1 ← VSyncピンを1(⑥) vga_vert_back: irq wait 4 jmp x--, vga_vert_back ; ---------------------------------------------------------------- ; ピクセル転送開始割り込み(⑦)+バックポ―チ2行期間 ; ---------------------------------------------------------------- ; ピクセル転送開始割り込み+バックポーチ irq 0 rel ← マイコンに対し割り込み発生(⑦) irq wait 4 irq wait 4 ; ---------------------------------------------------------------- ; 有効ピクセル期間(④) ; ---------------------------------------------------------------- ; 有効ピクセル out x, 16 vga_vert_pixel: irq wait 4 irq clear 5 ← 割り込み5待ちしている有効ピクセル転送を動作 jmp x--, vga_vert_pixel .wrap
有効ピクセル転送処理は、マイコンからDMAで受けた画像データを、有効ピクセルのタイミングに合わせD-Sub15ピンのRGBへ出力します。
スクラッチレジスターYは、vga/vga.cのm_pioSmInfo[2]から、((320/2-1)<<16) | ((96+48)*VGA_CFG_DOTCLK-4-1)を代入。
下位16ビットに水平同期信号の同期+バックポ―チドットクロック数(水平方向調整)、上位16ビットに表示幅ドットクロック数/2を設定している
(バックポーチを-2しているのは、有効ピクセルに表示する画像をマイコン側で生成するためのタイミングを、割り込みで通知するため)。
上記以外のタイミングPIO内で調整。
また、転送行数はマイコン側のDMAで管理する(PIOは行数を意識しない)。
垂直クロックジェネレータタイミング |
.program vga_pixel .wrap_target mov pins, null mov osr, y irq wait 5 ; ---------------------------------------------------------------- ; 水平同期信号の同期+バックポ―チ(①) ; ---------------------------------------------------------------- ; 同期・バックポーチ out x, 16 vga_pixel_sync_back: jmp x--, vga_pixel_sync_back ; ---------------------------------------------------------------- ; 有効ピクセル転送(②) ; ---------------------------------------------------------------- ; 有効ピクセル出力 out x, 16 vga_pixel_out: pull noblock out pins, 16 [dotclk+dotclk-1] out pins, 16 [dotclk+dotclk-3] jmp x--, vga_pixel_out .wrap
色データは16bitで、以下フォーマットとする。
※色フォーマットは、config.hのVGA_CFG_PIN_PIXELとVGA_CFG_PIN_PIXELCOUNTと、抵抗値で変更可能。
15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
- | B | G | R |
名称 | 説明 |
---|---|
- | 未使用(自由に使用可)。 |
B | 青。 |
G | 緑。 |
R | 赤。 |
4bitビットマップ、タイル、スプライト、テキストは16色パレットを参照する。
16色パレットは、16個の色データを1つのパレットとして定義し、最大16個のパレットを持つことができる。
各パレットの0番目を透過色として扱うため、1つのパレットに付き最大15色が使用可能。
ただし、テキスト面のみ、透過色+15色、または透過なしの16色を選択可能。
8bit ビットマップ面は256色パレットを参照する。
256色パレットは、256個の色データを1つのパレットとして持つ。
ただし、0番目を透過色として扱うため、最大255色が使用可能。
幅8×高さ8×4bitドットデータを1つのパターンデータとして定義する。
4bitドットデータは、16色パレット内の色インデックス番号を指定する。
31 | 30 | 29 | 28 | 27 | 26 | 25 | 24 | 23 | 22 | 21 | 20 | 19 | 18 | 17 | 16 | 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
ドット7 | ドット6 | ドット5 | ドット4 | ドット3 | ドット2 | ドット1 | ドット0 |
連続した4つの8x8パターンデータを、スプライト用16x16パターンデータとして使用する。
16x16パターンデータとして使用する8x8パターンの定義順は以下。
0 | 2 |
1 | 3 |
16色パレットと4ビットの画像データ(16色パレット番号)によるビットマップ画像を表示します。
解像度は320x240で、y方向の円筒スクロールをサポートします。
7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|
16色パレット番号 (奇数ドット) |
16色パレット番号 (偶数ドット) |
uint8_t bitmap[VGA_BMP_HEIGHT][VGA_BMPH_WIDTH] ;
※VRAMアドレスは、4バイトにアラインメントする必要あり
VRAMアドレスと表示位置関係 |
256色パレットと8ビットの画像データ(256色パレット番号)によるビットマップ画像を表示します。
解像度は320x240、または256x240でy方向の円筒スクロールをサポートします。
256x240時のみ、320x240へのストレッチ、または水平表示位置の移動をサポートします。
7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|
256色パレット番号 |
uint8_t bmp4[VGA_BMP_HEIGHT][VGA_BMPH_WIDTH] ;
※VRAMアドレスは、4バイトにアラインメントする必要あり
VRAMアドレスと表示位置関係(1バイトアクセスの場合) |
uint8_t bmp8[VGA_BMP_HEIGHT][VGA_BMPL_WIDTH] ;
※VRAMアドレスは、4バイトにアラインメントする必要あり
VRAMアドレスと表示位置関係 |
16ビットの画像データによるビットマップ画像を表示します。
解像度は320x240で、y方向の円筒スクロールをサポートします。
15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
T | - | B | G | R |
名称 | 説明 |
---|---|
T | 透過フラグ(0:不透明, 1:透過)。 |
- | 未使用。 |
B | 青。 |
G | 緑。 |
R | 赤。 |
uint16_t bmp16[VGA_BMP_HEIGHT][VGA_BMPH_WIDTH] ;
※VRAMアドレスは、4バイトにアラインメントする必要あり
VRAMアドレスと表示位置関係 |
8x8のパターンデータの番号を並べた画面で、仮想画面512x512とx・y方向の球面スクロールをサポートします。
各タイルは、16個のパレットの中から1つ、最大1024個定義可能なパターンデータから1つ選択と、上下反転表示をサポートします。
15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
C | H | V | P |
名称 | 説明 |
---|---|
C | カラーパレット番号(0-15) |
H | 水平反転(0:反転なし,1:反転あり) |
V | 垂直反転(0:反転なし,1:反転あり) |
G | パターン番号(0-1023) |
表示範囲と座標空間 (スクロールオフセットX=96,Y=32(左)、X=300,Y=352(右)) |
uint16_t tile[VGA_TILE_HEIGHT_COUNT][VGA_TILE_WIDTH_COUNT] ;
※VRAMアドレスは、4バイトにアラインメントする必要あり
VRAMアドレスと表示位置関係 |
8x8のパターンデータの番号を並べた画面で、y方向の円筒スクロールをサポートします。
タイルと違い、仮想画面とx方向のスクロールはサポートしていません。
各テキストは、16個のパレットの中から1つ、最大1024個定義可能なパターンデータから1つ選択と、背景色透過、シャドーをサポートします。
15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
C | S | T | P |
名称 | 説明 | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
C | 16色パレット番号(0-15) | ||||||||||||
S |
シャドーフラグ(0:無効, 1:有効) T=0時:透過色部の輝度を50% T=1時:透過色部以外の輝度を50%
安全に使用できるのは1行あたり10個程度(背面を非表示、またはオーバークロック時は1行をシャドーにすることが可能)。 |
||||||||||||
T | 不透過フラグ(0:16色パレットの0番を透過する, 1:16色パレットの0番を透過しない) | ||||||||||||
P | パターン番号(0-1023) |
uint16_t txt[VGA_TXT_HEIGHT_COUNT][VGA_TXT_WIDTH_COUNT] ;
※VRAMアドレスは、4バイトにアラインメントする必要あり
VRAMアドレスと表示位置関係 |
指定の座表へ、16x16のパターンデータを表示します。
仮想画面は512x512で、x・y方向へ循環します。
最大64個定義可能で、1行辺り16個表示可能です(HSync中に切り替えることで、64個以上の表示が可能)。
各スプライトは、16個のパレットの中から1つ、最大1024個定義可能なパターンデータから1つ選択と、上下反転表示をサポートします。
表示範囲と座標空間 |
スプライトには優先順位があり、低い数値ほど上に表示されます。
VRAMアドレスと表示優先順位 |
バイトオフセット | 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | x | - | XP |
バイトオフセット | 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
2 | y | - | YP |
バイトオフセット | 31 | 30 | 29 | 28 | 27 | 26 | 25 | 24 | 23 | 22 | 21 | 20 | 19 | 18 | 17 | 16 | 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
4 | O | - | C | H | V | P |
名称 | 説明 |
---|---|
x | x座標オフセット(0-511) このビットが立っている時はvga_setSpriteOffset関数で設定したx座標を加算する。 |
y | x座標オフセット(0-511) このビットが立っている時はvga_setSpriteOffset関数で設定したy座標を加算する。 |
- | 未使用 |
XP | 表示x座標(0-511) |
YP | 表示y座標(0-511) |
O | 表示優先順位(0:非表示, 1:背面の奥、2:前面の奥(前面未使用時無視)、3:前面の手前) |
C | カラーパレット番号(0-15) |
H | 水平反転(0:反転なし,1:反転あり) |
C | 垂直反転(0:反転なし,1:反転あり) |
P | パターン番号(0-1023) |
※未使用領域が多いため、フォーマットを変更するかも
VGASPR spr[VGA_SPR_MAX] ;
以下の画像を生成+コード上に用意。
16bitビットマップ面画像 | |
スプライト用画像 | |
テキスト用画像 |
static void isr_vsync(uint gpio, uint32_t events) { m_pos++ ; vga_setFgSprite(&m_vram.fg.spr[0], &m_vram.pattern[0][0], &m_vram.palette[0][0]) ; } : static void hsync( uint32_t line ) { if( line >= 200 ) { if( line == 200 ) { vga_setFgText(&m_vram.fg.txt[0][0], &m_vram.pattern[0][0], &m_vram.palette[0][0]) ; vga_setTextOffset(0, 200) ; } } else { vga_setSpriteOffset((int32_t )-m_sinData[(m_pos+line)&255]*96/256, line-(line%48)) ; } } : int main( void ) { setup_default_uart() ; memset(&m_vram, 0, sizeof( m_vram )) ; memcpy(&m_vram.palette[0][0], &m_palette[0], sizeof( m_palette )) ; memcpy(&m_vram.pattern[256][0], &m_pattern[0], sizeof( m_pattern )) ; setBitmap16() ; createFontPattern((uint8_t *)&m_vram.pattern[0][0], &m_fontData[0], sizeof( m_fontData ), 15, 0) ; setSpriteData() ; setTextData() ; vga_init() ; vga_setBgBitmap16( (const uint32_t *)&m_vram.bg.bmp16[0][0] ) ; vga_setSprite(&m_vram.fg.spr[0], &m_vram.pattern[0][0], &m_vram.palette[0][0]) ; vga_setHSyncProc( hsync ) ; vga_start() ; // VSync 割り込み gpio_set_irq_enabled_with_callback(VGA_CFG_PIN_VSYNC, GPIO_IRQ_EDGE_FALL, true, isr_vsync) ; while( 1 ) { ; } return 0 ; }
ソースコードなどはダウンロードページにあります。
今回VGA表示を試してみて、現状でも未使用ピンも結構あり、SPI、I2C、UART、PWM、DMAなども大量に空いており、また、本体の値段も安く、消費電力も小さいため、アイデア次第で色々なことに使えそう。
ただ、Cortex-M0+と言うのが少し厳しい。
あと、スプライトの回転拡大縮小、線、ポリゴンや、スプライトなどは、8bitビットマップ面をダブルバッファで使用することで現状でも簡単に実現可能。