🐱

Raspberry Pi Picoでボタンのデバウンス

2024/10/26に公開

まえがき

マイコンでLチカができると、次にやりたくなるのがボタンを押すとLEDが光るヤツ。そこで問題になるのがボタンのデバウンス(チャタリング除去)。

ハード側で解決する方法とソフト側で解決する方法があるけど、すでにハードは作成済みなので、ソフトでなんとかするしかないです。

Arduinoでは参考文献はWebにあるけど、PicoかつC/C++ SDKだとほぼ皆無でした。

なのでやってみました。

開発環境

C/C++ SDK、PCはThinkpad X260にUbuntu 22.04LTS、Picoは普通のRaspberry Pi Pico。

動作を考える

今回考えた動作は以下の通りです。

  1. ボタンアクションのコールバック関数を登録
  2. 割り込みハンドラが呼ばれたらボタンからの割込みを禁止
  3. ワンショットのタイマーでバウンス時間後に動くハンドラを登録
  4. バウンス時間後にハンドラが呼ばれるのでボタンの状態を確認
    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