Raspberry Pi Picoでプログラミング ⑯ gpio スイッチを押すとLEDが点灯する STEP2
前回、スイッチが押されると、LEDを点灯させるというプログラムを作りました。while()文でループを作り、定期的に、if()文で、スイッチの状態を見に行きます。これをポーリングといいます。
このプログラムは、入力がスイッチで出力がLEDというシンプルな構成でした。しかし、現実は、スイッチは複数あるし、LEDも複数あって、というプログラムになることが多いです。そういうときには、ポーリングでは間に合わなくなって、スイッチの状態を見逃すことがあります。
割り込みを使います。
●スイッチを押すとLEDが点灯する STEP2
割り込みでは、
- 条件を設定し、イネーブルにする
- 条件に合ったときに実行する割り込み処理関数
という二つがペアになります。
gpioの中から割り込み関係のファンクションを抜き出しました。
物理的なピンの状態=イベントによって割り込みがかかります。イベントは、0;Low level、1;High level、2;Edge low、3;Edge highと書かれていますが、プログラムでは、Low levelは0x1u、High levelは0x2u、Edge lowは0x4u、Edge highは0x8uを設定します。
ファンクション | 要約 |
---|---|
void gpio_set_irq_enabled (uint gpio, uint32_t events, bool enabled) |
指定されたGPIOの割り込みを有効または無効にする。 イベント(0;Low level、1;High level、2;Edge low、3;Edge high) |
void gpio_set_irq_enabled_with_callback (uint gpio, uint32_t events, bool enabled, gpio_irq_callback_t callback) |
指定されたGPIOの割り込みを有効にする。 callback;GPIOirqを呼び出すためのコールバック・ユーザ関数 |
void gpio_set_dormant_irq_enabled (uint gpio, uint32_t events, bool enabled) | 指定されたGPIOの休止状態のウェイクアップ割り込みを有効にする |
void gpio_acknowledge_irq (uint gpio, uint32_t events) | GPIO割り込みを確認。gpio番号、イベント(0;Low level、1;High level、2;Edge low、3;Edge high)をクリア |
●blink.cを修正
gpio_set_irq_enabled_with_callback(SW_PIN, 0x4u, false, callback_ledflash);
で、割り込みの設定を行います。スイッチが押され、High->Lowになったとき、callback_ledflash関数を呼びます。falseなので、設定だけです。trueだと、この関数の設定直後に割り込みが有効になります。
gpio_set_irq_enabled(SW_PIN, 0x4u, true);
割り込みを有効にします。
while(1){}がメイン・プログラムです。スイッチを押さない限り、この文が実行され続けます。sleep_ms()が実行中でも割り込みはかかります。
割り込み処理関数callback_ledflash()では、入った最初に割り込みをディセーブルし、関数を出る直前にイネーブルにします。これは、割り込み処理中に割り込みが入らないようにするための処置です。実際に、割り込み禁止にしないと、スイッチのチャタリングによって割り込みが複数回かかることが確認できます。
処理内容によっては、割り込みを禁止しないこともあります。それは、ほかにも割り込みがあって、割り込み処理関数がある場合です。そちらのほうが緊急な処理をするなら、このLEDを点灯する関数では、割り込みを禁止しないほうがよい場合があります。
LEDを点灯する時間を sleep_ms()で作ると、この割り込み処理ルーチンが停止してしまいました。そのため、無駄時間を作るために、+文字を128個印字するというルーチンを追加しました。もっと良い方法があると思います。
#include "pico/stdlib.h"
#include <stdio.h>
#define LED_PIN 25
#define SW_PIN 9
void callback_ledflash() {
gpio_set_irq_enabled(SW_PIN, 0x4u, false);
// printf("\nHello,int1\n");
gpio_put(LED_PIN, 0);
gpio_put(LED_PIN, 1);
for (int i=0; i<128; ++i){printf("+");}
printf("\n");
// printf("\nHello,int2\n");
gpio_put(LED_PIN, 0);
gpio_set_irq_enabled(SW_PIN, 0x4u, true);
}
int main() {
stdio_init_all();
printf("\nHello,IRQ\n");
gpio_init(SW_PIN);
gpio_set_dir(SW_PIN, GPIO_IN);
gpio_pull_up(SW_PIN);
gpio_init(LED_PIN);
gpio_set_dir(LED_PIN, GPIO_OUT);
// printf("\nHello,1\n");
gpio_set_irq_enabled_with_callback(SW_PIN, 0x4u, false, callback_ledflash);
// gpio_set_irq_enabled_with_callback(SW_PIN, 0x4u, true, callback_ledflash);
// printf("\nHello,2\n");
gpio_set_irq_enabled(SW_PIN, 0x4u, true);
// printf("\nHello,3\n");
while(1){
printf(".");
sleep_ms(2000);
}
return 0;
}
●マクロを使う
IRQのenableとdisableが長くてわかりにくいので、マクロにしてすっきり記述ができるようにしました。
#include "pico/stdlib.h"
#include <stdio.h>
#define LED_PIN 25
#define SW_PIN 9
#define IRQ_enable gpio_set_irq_enabled(SW_PIN, 0x4u, true);
#define IRQ_disable gpio_set_irq_enabled(SW_PIN, 0x4u, false);
void callback_ledflash() {
IRQ_disable
gpio_put(LED_PIN, 0);
gpio_put(LED_PIN, 1);
for (int i=0; i<128; ++i){printf("+");}
printf("\n");
gpio_put(LED_PIN, 0);
IRQ_enable
}
int main() {
stdio_init_all();
printf("\nHello,IRQ\n");
gpio_init(SW_PIN);
gpio_set_dir(SW_PIN, GPIO_IN);
gpio_pull_up(SW_PIN);
gpio_init(LED_PIN);
gpio_set_dir(LED_PIN, GPIO_OUT);
gpio_set_irq_enabled_with_callback(SW_PIN, 0x4u, false, callback_ledflash);
IRQ_enable
while(1){
printf(".");
sleep_ms(2000);
}
return 0;
}