🐱
Raspberry Pi Picoでボタンのデバウンス
まえがき
マイコンでLチカができると、次にやりたくなるのがボタンを押すとLEDが光るヤツ。そこで問題になるのがボタンのデバウンス(チャタリング除去)。
ハード側で解決する方法とソフト側で解決する方法があるけど、すでにハードは作成済みなので、ソフトでなんとかするしかないです。
Arduinoでは参考文献はWebにあるけど、PicoかつC/C++ SDKだとほぼ皆無でした。
なのでやってみました。
開発環境
C/C++ SDK、PCはThinkpad X260にUbuntu 22.04LTS、Picoは普通のRaspberry Pi Pico。
動作を考える
今回考えた動作は以下の通りです。
- ボタンアクションのコールバック関数を登録
- 割り込みハンドラが呼ばれたらボタンからの割込みを禁止
- ワンショットのタイマーでバウンス時間後に動くハンドラを登録
- バウンス時間後にハンドラが呼ばれるのでボタンの状態を確認
4.1 押されてたらLED点灯、離されてたらLED消灯
4.2 2.で行ってた割込み禁止を解除
ソースコード
LEDの動作とともにブザーを鳴らすようにしました。
#include <stdio.h>
#include <math.h>
#include "pico/stdlib.h"
#include "hardware/timer.h"
const uint BTN1 = 10; /* 押されてたら0, 離されてたら1 */
const uint LED1 = 20;
const uint BUZZER = 22;
#define BOUNCE_TIME 10 /* バウンス時間は10[ms]より短いと仮定 */
#define PRESSED false
#define LED_OFF 0
#define LED_ON 1
#define BUZZER_OFF 0
#define BUZZER_ON 1
static int64_t button_timer_callback(alarm_id_t id, void *user_data) {
/*
* バウンス時間後にハンドラが呼ばれるのでボタンの状態を確認
* ボタンが押されてたらLEDを点灯、ブザーを鳴らす
* ボタンが離されてたらLEDを消灯、ブザーを止める
*/
if (gpio_get(BTN1) == PRESSED) {
gpio_put(LED1, LED_ON);
gpio_put(BUZZER, BUZZER_ON);
}
else {
gpio_put(LED1, LED_OFF);
gpio_put(BUZZER, BUZZER_OFF);
}
/* 割込み禁止の解除 */
gpio_set_irq_enabled(BTN1, GPIO_IRQ_EDGE_FALL | GPIO_IRQ_EDGE_RISE, true);
return 0;
}
static void push_button_callback(uint gpio, uint32_t events) {
/* ボタンからの割込みを禁止 */
gpio_set_irq_enabled(gpio, GPIO_IRQ_EDGE_FALL | GPIO_IRQ_EDGE_RISE, false);
/* ワンショットのタイマーでバウンス時間後に動くハンドラを登録 */
add_alarm_in_ms(BOUNCE_TIME, button_timer_callback, NULL, false);
}
int main() {
/* ピンの初期設定 */
gpio_init(BTN1);
gpio_set_dir(BTN1, GPIO_IN);
gpio_pull_up(BTN1);
gpio_init(LED1);
gpio_set_dir(LED1, GPIO_OUT);
gpio_init(BUZZER);
gpio_set_dir(BUZZER, GPIO_OUT);
/* ボタンアクションのコールバック関数を登録 */
gpio_set_irq_enabled_with_callback(BTN1, GPIO_IRQ_EDGE_FALL | GPIO_IRQ_EDGE_RISE, true, push_button_callback);
while (true) {
}
}
最後に
バウンスは10[ms]以内で収まるとしてコードを書いています。使用しているボタンによって適正な値にすると良いと思う。
Discussion