前回に引き続き、Raspberry Pi PicoのVGA demo baseボードを使っていきたいと思います。今回はVGAボードの3つのタクトスイッチの入力を見ていきます。
こちらの記事で書いたように、VGAボードのタクトスイッチはGPIOをRGB信号の最下位ビットと共有しています。スイッチは入力、RGB信号は出力ですが、垂直ブランキング期間中は出力は必要ないので、この期間だけGPIOを入力モードに切り替え、スイッチの状態を検出します、
この処理を行っているサンプルコードとして、pico-playground/apps/popcornがありました。 今回はこのコードを見ていきます。
まず、垂直ブランキング期間の検出は割り込み(IRQ)を使います。IRQはVSYNC信号に割り当てられているGPIOの立ち上がりと立ち下がりでかけます。
void vga_board_init_buttons() {
gpio_set_irq_enabled(VSYNC_PIN, GPIO_IRQ_EDGE_FALL | GPIO_IRQ_EDGE_RISE, true);
irq_set_exclusive_handler(IO_IRQ_BANK0, vga_board_button_irq_handler);
irq_set_enabled(IO_IRQ_BANK0, true);
}
1行目のgpio_set_irq_enabled()で立ち上がり・立ち下がりでIRQをかけるように指定しています。2行目でIRQハンドラを登録し、3行目でIRQを有効にしています。なおIO bank0は、ユーザ利用GPIO(いわゆるGPIO)のことを指します。(詳細はRP2040データシートの2.3.2を参照してください。)
割り込みがかかると、IRQハンドラが呼び出されます。IRQハンドラは以下のようになっています。
// Registered as GPIO interrupt on both edges of vsync. On vsync assertion,
// set pins to input. On deassertion, sample and set back to output.
void vga_board_button_irq_handler() {
int vsync_current_level = gpio_get(VSYNC_PIN);
gpio_acknowledge_irq(VSYNC_PIN, vsync_current_level ? GPIO_IRQ_EDGE_RISE : GPIO_IRQ_EDGE_FALL);
// Note v_sync_polarity == 1 means active-low because anything else would be confusing
if (vsync_current_level != scanvideo_get_mode().default_timing->v_sync_polarity) {
for (int i = 0; i < count_of(button_pins); ++i) {
gpio_pull_down(button_pins[i]);
gpio_set_oeover(button_pins[i], GPIO_OVERRIDE_LOW);
}
} else {
uint32_t state = 0;
for (int i = 0; i < count_of(button_pins); ++i) {
state |= gpio_get(button_pins[i]) << i;
gpio_set_oeover(button_pins[i], GPIO_OVERRIDE_NORMAL);
}
button_state = state;
}
}
gpio_acknowledge_irq()でIRQを受け付けたことを通知します(たぶんIRQフラグをクリアしているのでしょう)。立ち上がりだったか立ち下がりだったかは、現在のVSYNCピンがHなのかLなのかで判定できます。
次のif~elseは、今が垂直ブランチングの終了なのか開始なのかを判定します。終了の場合(if節)はGPIOを出力モードに切り替えて、出力レベルをLowにします。開始の場合(else節)は、GPIOを入力モードに切り替えてタクトスイッチの状態を取得します。
この条件判定の中身が若干分かりにくいかもしれませんが、現在のVSYNCピンが例えばLだったとして、それは垂直ブランキングの開始なのか終了なのかは、VGAの解像度によって異なるのです(参考リンク)。scanvideo_get_mode().default_timing->v_sync_polarityは、現在の解像度におけるVSYNCの極性を表しています。これが現在のVSYNCピンの状態と一致するなら、すなわち今は垂直ブランキングが始まったところだということになります。
ちなみに標準のVGA解像度(640×480)では、VSYNC・HSYNCともに負極性(ブランティング期間中はLow)です。
ともかく、垂直ブランキングが始まったタイミングで、タクトスイッチの状態はグローバル変数button_stateに格納されます。
このコードをお借りして、簡単なサンプルプログラムを作ってみました。
3つのタクトスイッチを押すと、押されたスイッチに応じて画面上に赤・緑・青のバーを描画するものです。
コードは以下にアップロードしてあります。
コメント