image image


概要

Raspberry Pi Picoを手に入れたので、気になっていたPIOを使用し、VGA出力を試した時のメモです。

Raspberry Pi Picoをいじっていると、サイズの大きなデータの転送を行っていると動作が不安定になることがあるため、やはりシリアルフラッシュメモリ上にあるプログラムが遅いのか?と言うことで、まずフラッシュメモリ速度も調査しました。


2021/02/05

Raspberry Pi Picoのオーバークロックに対応するため、画面モードとAPIの仕様を変更。
スプライトの最大数と1行あたりのスプライト数をコンフィグで設定できるように変更。

今回の変更により、タイル面を2枚使用した多重スクロールや、スプライトとテキストの同時表示、スプライト数の増加が可能。


2021/01/26

PIO処理やデータフォーマットなどの説明を追加。


2021/10/25

8bitビットマップ面の解像度に、256x240を追加。
ストレッチありによる320x240へ引き延ばしと、ストレッチなし時に水平表示位置を最大64dotへ移動を追加。
テキスト面に、シャドーフラグを追加。
シャドーフラグを使用することで、透過色、または透過色以外の領域の輝度を50%下げる(黒画との半透明50%と同じ)を追加。


2021/10/12

4bitビットマップ面を追加。
・4bitビットマップ面追加に合わせて、スプライトやテキストなどの4bitデータの並びを4bitビットマップ面と同じリトルエンディアンに変更。
タイルフォーマットが抜けていたのを追加。


2021/09/18

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 20000594 
200005a6:   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(プログラマブルI/O)によるVGA出力

PIOを1つと3ステートマシン、DMAを2ch(ピクセルデータ転送と背景色塗りつぶし)使用してD-SUB15ピンによるVGA(640x480)出力を試しました。
VGAを選択したのは、PIOのクロックが125MHzのため、PIOの出力を5分周すれば25MHzとなり、VGAのピクセルクロック25.175 MHzとほぼ同じになる+現在のモニターでも表示できる解像度のため。


image image
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個)、または背景+背面+前面+テキストで安定動作


image
表示優先順位
(スプライト(prio2)は、前面タイル使用時のみ)

ラインバッファにより描画を行うため、4/8/16bitのビットマップ面以外はRAM使用量を減らすことが可能。
また、HSync中の操作により、ラスタースクロール、スプライト数の増加、画面モードの切り替え、パレットの切り替えなどが可能。



PIO処理について

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個の命令を使用します。


水平クロックジェネレータ処理(vga/vga.pio:15)

水平クロックジェネレータ処理は、水平同期信号を生成します。

スクラッチレジスターYは、vga/vga.cのm_pioSmInfo[0]から、(((48+640+16)*VGA_CFG_DOTCLK-1-1)<<16) | (96*VGA_CFG_DOTCLK-3-1)を代入。
下位16ビットに同期のドットクロック数、上位16ビットにバックポーチ・有効ピクセル・フロントポーチのドットクロック数を設定している。

image
水平クロックジェネレータタイミング
  .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

垂直クロックジェネレータ処理(vga/vga.pio:36)

垂直クロックジェネレータ処理は、垂直同期信号の生成と、有効ピクセル転送で転送する画像を生成するタイミングをマイコンへ割り込みで通知します。

スクラッチレジスターYは、vga/vga.cのm_pioSmInfo[1]から、((480-1)<<16) | (33-2-1)を代入。
下位16ビットにバックポーチ行数-2、上位16ビットに有効ピクセル行数を設定している
(バックポーチを-2しているのは、有効ピクセルに表示する画像を2行前から生成するタイミング調整を行うため)。
上記以外のタイミングPIO内で調整。

垂直は行単位で処理を行う必要があるため、水平クロックジェネレータの割り込み待ちでタイミングを合わせている。

image
垂直クロックジェネレータタイミング
  .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

有効ピクセル転送処理(vga/vga.pio:74)

有効ピクセル転送処理は、マイコンから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は行数を意識しない)。

image
垂直クロックジェネレータタイミング
  .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と、抵抗値で変更可能。


データフォーマット

1514131211109876543210
- B G R
名称説明
- 未使用(自由に使用可)。
B 青。
G 緑。
R 赤。

16色パレット

4bitビットマップ、タイル、スプライト、テキストは16色パレットを参照する。
16色パレットは、16個の色データを1つのパレットとして定義し、最大16個のパレットを持つことができる。
各パレットの0番目を透過色として扱うため、1つのパレットに付き最大15色が使用可能。
ただし、テキスト面のみ、透過色+15色、または透過なしの16色を選択可能。


256色パレット

8bit ビットマップ面は256色パレットを参照する。
256色パレットは、256個の色データを1つのパレットとして持つ。
ただし、0番目を透過色として扱うため、最大255色が使用可能。



タイル・スプライト・テキストで使用するパターンデータについて

幅8×高さ8×4bitドットデータを1つのパターンデータとして定義する。
4bitドットデータは、16色パレット内の色インデックス番号を指定する。


データフォーマット

31302928272625242322212019181716 1514131211109876543210
ドット7 ドット6 ドット5 ドット4 ドット3 ドット2 ドット1 ドット0

スプライト用16x16パターンデータ

連続した4つの8x8パターンデータを、スプライト用16x16パターンデータとして使用する。

16x16パターンデータとして使用する8x8パターンの定義順は以下。

02
13



4bitビットマップについて

16色パレットと4ビットの画像データ(16色パレット番号)によるビットマップ画像を表示します。
解像度は320x240で、y方向の円筒スクロールをサポートします。


データフォーマット

76543210
16色パレット番号
(奇数ドット)
16色パレット番号
(偶数ドット)

VRAM定義例

uint8_t		bitmap[VGA_BMP_HEIGHT][VGA_BMPH_WIDTH] ;

※VRAMアドレスは、4バイトにアラインメントする必要あり

image
VRAMアドレスと表示位置関係


8bitビットマップについて

256色パレットと8ビットの画像データ(256色パレット番号)によるビットマップ画像を表示します。
解像度は320x240、または256x240でy方向の円筒スクロールをサポートします。
256x240時のみ、320x240へのストレッチ、または水平表示位置の移動をサポートします。


データフォーマット

76543210
256色パレット番号

VRAM定義例(320x240の場合)

uint8_t		bmp4[VGA_BMP_HEIGHT][VGA_BMPH_WIDTH] ;

※VRAMアドレスは、4バイトにアラインメントする必要あり

image
VRAMアドレスと表示位置関係(1バイトアクセスの場合)

VRAM定義例(256x240の場合)

uint8_t		bmp8[VGA_BMP_HEIGHT][VGA_BMPL_WIDTH] ;

※VRAMアドレスは、4バイトにアラインメントする必要あり

image
VRAMアドレスと表示位置関係

16bitビットマップについて

16ビットの画像データによるビットマップ画像を表示します。
解像度は320x240で、y方向の円筒スクロールをサポートします。


データフォーマット

1514131211109876543210
T - B G R
名称説明
T 透過フラグ(0:不透明, 1:透過)。
- 未使用。
B 青。
G 緑。
R 赤。

VRAM定義例

uint16_t	bmp16[VGA_BMP_HEIGHT][VGA_BMPH_WIDTH] ;

※VRAMアドレスは、4バイトにアラインメントする必要あり

image
VRAMアドレスと表示位置関係


タイルについて

8x8のパターンデータの番号を並べた画面で、仮想画面512x512とx・y方向の球面スクロールをサポートします。
各タイルは、16個のパレットの中から1つ、最大1024個定義可能なパターンデータから1つ選択と、上下反転表示をサポートします。


データフォーマット

1514131211109876543210
C H V P
名称説明
Cカラーパレット番号(0-15)
H水平反転(0:反転なし,1:反転あり)
V垂直反転(0:反転なし,1:反転あり)
Gパターン番号(0-1023)

image image
表示範囲と座標空間
(スクロールオフセットX=96,Y=32(左)、X=300,Y=352(右))

VRAM定義例

uint16_t	tile[VGA_TILE_HEIGHT_COUNT][VGA_TILE_WIDTH_COUNT] ;

※VRAMアドレスは、4バイトにアラインメントする必要あり

image
VRAMアドレスと表示位置関係


テキストについて

8x8のパターンデータの番号を並べた画面で、y方向の円筒スクロールをサポートします。
タイルと違い、仮想画面とx方向のスクロールはサポートしていません。
各テキストは、16個のパレットの中から1つ、最大1024個定義可能なパターンデータから1つ選択と、背景色透過、シャドーをサポートします。


データフォーマット

1514131211109876543210
C S T P
名称説明
C 16色パレット番号(0-15)
S シャドーフラグ(0:無効, 1:有効)
 T=0時:透過色部の輝度を50%
 T=1時:透過色部以外の輝度を50%
透過なし・シャドー無効
(T=1, S=0)
透過あり・シャドー無効
(T=0, S=0)
透過なし・シャドー有効
(T=1, S=1)
透過あり・シャドー有効
(T=0, S=1)
image
image
image
image
パターンデータを
そのまま表示
16色パレット0番を透過 16色パレット0番の
背景の輝度を50%
16色パレット0番を透過し
その他背景の輝度を50%
※シャドーフラグを有効にすると処理負荷が高くなるため、背面描画に時間が掛かると正常に表示できなくなる可能性あり。
 安全に使用できるのは1行あたり10個程度(背面を非表示、またはオーバークロック時は1行をシャドーにすることが可能)。
T 不透過フラグ(0:16色パレットの0番を透過する, 1:16色パレットの0番を透過しない)
P パターン番号(0-1023)

VRAM定義例

uint16_t	txt[VGA_TXT_HEIGHT_COUNT][VGA_TXT_WIDTH_COUNT] ;

※VRAMアドレスは、4バイトにアラインメントする必要あり

image
VRAMアドレスと表示位置関係


スプライトについて

指定の座表へ、16x16のパターンデータを表示します。
仮想画面は512x512で、x・y方向へ循環します。
最大64個定義可能で、1行辺り16個表示可能です(HSync中に切り替えることで、64個以上の表示が可能)。
各スプライトは、16個のパレットの中から1つ、最大1024個定義可能なパターンデータから1つ選択と、上下反転表示をサポートします。


image
表示範囲と座標空間

スプライトには優先順位があり、低い数値ほど上に表示されます。


image
VRAMアドレスと表示優先順位

データフォーマット

バイトオフセット1514131211109876543210
0 x - XP
バイトオフセット1514131211109876543210
2 y - YP
バイトオフセット313029282726252423222120191817161514131211109876543210
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)

※未使用領域が多いため、フォーマットを変更するかも


VRAM定義例

VGASPR		spr[VGA_SPR_MAX] ;


使用例

以下の画像を生成+コード上に用意。

16bitビットマップ面画像image
スプライト用画像 image
テキスト用画像 image

スプライトを、パレット違いのスプライトを横に並べ、赤はy座標オフセットあり、青紫はx+y座標オフセットありで定義。

image


オフセットありで定義しているのは、HSync割り込み中に、vga_setSpriteOffsetを操作することで、簡単にスプライトを複数表示するため。

image


テキスト面は、以下のように並べる(赤は透過ありで定義)。

image


これらを、HSync中に操作することで、ラスタースクロールや、スプライト数を増やし、表示するすことが可能。

image


ソースコード

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ビットマップ面をダブルバッファで使用することで現状でも簡単に実現可能。