フェーズ1

E1-08 ボタン入力を安定させる(デバウンス):1回押したのに複数回反応するのを減らす

シリーズ:実験シリーズ(フェーズ1)
対応ロードマップ:フェーズ1 / E1-08
今回の役割:E1-07で見えたチャタリングに対して、簡単なソフト対策を入れ、1回押したのに何回も反応する問題を減らす

1. 目的

今回は、ボタン入力を安定して扱うための最初の対策 を行います。
E1-07では、ボタンを1回押したつもりでも、

  • PRESSED
  • RELEASED
  • PRESSED
  • RELEASED

のように、短時間に何回も変化したような反応が出ることがありました。

これは、ボタンの接点が押した直後・離した直後に細かく揺れる チャタリング が原因です。
今回は、変化を見つけたら少し待って、まだ同じ状態のままなら確定する という方法で、誤反応を減らします。

2. 前提・環境

  • 評価ボード:RTK7EKA8M2S00001BE
  • 開発環境:e² studio + FSP
  • 前提:
    • E0-01〜E0-06 が完了している
    • E1-03 の外付けLED配線ができている
    • E1-05 のボタン入力(ポーリング)が動作している
    • E1-06 のプルアップ/プルダウンを体験している
    • E1-07 のチャタリングを観察している
  • 使用部品
    • LED × 1
    • 抵抗:1kΩ × 1
    • タクトスイッチ × 1
    • ブレッドボード
    • ジャンパ線
  • 使用するGPIO
    • 外付けLED用のGPIO:P409(J25-9)
    • ボタン入力用のGPIO:P704(J25-10)
    • GND:J25-11

3. 今回の変更点

今回は、配線とFSP設定はE1-07と同じ です。
変更するのは、コードのボタン判定部分 です。

3-1. 今回追加する考え方

前回までは、読んだ入力をそのまま使っていました。
今回は、次のようにします。

  1. まずボタン入力を読む
  2. 前回確定した状態と違っていたら、すぐには確定しない
  3. 少し待つ(今回は20ms待つことにします)
  4. もう一度読んで、まだ同じなら本当に変化したとみなす

これが、今回の デバウンス です。

4. 回路・配線

今回は、E1-07と同じ配線 で行います。

  • LED側
    LED用GPIO → 1kΩ抵抗 → LED → GND
  • ボタン側
    ボタン入力用GPIO → タクトスイッチ → GND

今回も、内部プルアップを使う前提 で進めます。
そのため、押していないときは High、押したときは Low を読む構成です。

4-1. 配線の考え方

  • LEDは、ボタンの確定した状態を表示するために使います
  • ボタンは、押した瞬間の揺れをそのまま使わず、少し待ってから確定します
  • 評価ボードの GND と ブレッドボード側のGND は必ずつなぎます

4-2. 配線図・配線写真

E1 05 配線全体
外付けLED配線 ブレッドボード E1 05

4-3. 注意点

  • 今回は配線ミスよりも、コードの判定の仕方がポイントです
  • ボタンを押した瞬間の値を、そのまま「確定」としないことが大事です
  • 電源を切ってから配線を確認します

5. 接続手順

5-1. 先に電源を切る

最初に評価ボードの電源を切ります。
通電したまま配線を触ると、ショートや誤配線の原因になります。

5-2. E1-07の配線をそのまま確認する

まず、前回の配線がそのまま正しいか確認します。

  • LEDの向きが合っているか
  • 抵抗が直列に入っているか
  • ボタンがGPIOとGNDにつながっているか
  • GNDが共通になっているか

5-3. ボタンのつながり方を確認する

  • 押していないとき:入力は内部プルアップで High
  • 押したとき:GNDにつながって Low

この前提でコードを書きます。

5-4. 配線を確認する

次の2本ができているか確認します。

  • LED用GPIO → 1kΩ抵抗 → LED → GND
  • ボタン入力用GPIO → タクトスイッチ → GND

6. FSP設定

今回は、FSP設定もE1-07と同じ です。

6-1. FSP ConfiguratorのPinsを確認する

  1. FSP Configurator(configuration.xml)→ Pins を開きます
  2. LED用GPIO:P409 を次のように設定します
    • Mode:Output mode (Initial Low)
  3. ボタン入力用GPIO:P704 を次のように設定します
    • Mode:Input mode
    • Pull-up/down:Input pull-up
  4. 設定を変更した場合は「Generate Project Content」を実行します

7. コード

今回は、入力に変化が見えたら20ms待って、もう一度同じ状態なら確定する という最小構成にします。
タイマはまだ使わず、まずは考え方をつかむことを優先します。

7-1. 先に考え方

やることは、次の4つです。

  1. ボタン入力を読む
  2. 今の入力が、前回確定した状態と違うか確認する
  3. 違っていたら少し待つ
  4. もう一度読んで同じなら、その状態を新しい確定値にする

今回は、押したら Low になる前提 で判定します。

7-2. 最小コード

#include "hal_data.h"

void hal_entry(void)
{
    bsp_io_level_t stable_sw_level;
    bsp_io_level_t current_sw_level;

    R_BSP_PinAccessEnable();   // R_BSP_PinWrite / PinRead を使用可能にする

    /* 最初の状態を読んで、確定状態として持っておく */
    stable_sw_level = R_BSP_PinRead(BSP_IO_PORT_07_PIN_04);

    /* 初期状態に応じてLEDを合わせる */
    if (stable_sw_level == BSP_IO_LEVEL_LOW)
    {
        R_BSP_PinWrite(BSP_IO_PORT_04_PIN_09, BSP_IO_LEVEL_HIGH);
    }
    else
    {
        R_BSP_PinWrite(BSP_IO_PORT_04_PIN_09, BSP_IO_LEVEL_LOW);
    }

    while (1)
    {
        /* 現在の入力を読む */
        current_sw_level = R_BSP_PinRead(BSP_IO_PORT_07_PIN_04);

        /* 確定状態と違ったら、すぐには信じず少し待つ */
        if (current_sw_level != stable_sw_level)
        {
            R_BSP_SoftwareDelay(20, BSP_DELAY_UNITS_MILLISECONDS);

            /* もう一度読んで、まだ同じなら本当に変化したとみなす */
            current_sw_level = R_BSP_PinRead(BSP_IO_PORT_07_PIN_04);

            if (current_sw_level != stable_sw_level)
            {
                stable_sw_level = current_sw_level;

                /* 確定した状態でLEDを更新 */
                if (stable_sw_level == BSP_IO_LEVEL_LOW)
                {
                    R_BSP_PinWrite(BSP_IO_PORT_04_PIN_09, BSP_IO_LEVEL_HIGH);
                }
                else
                {
                    R_BSP_PinWrite(BSP_IO_PORT_04_PIN_09, BSP_IO_LEVEL_LOW);
                }
            }
        }
    }
}

7-3. このコードで何が起きるか

E1-05では、読んだ値をそのままLEDに反映していました。
そのため、ボタンを押した直後や離した直後の細かい揺れにも反応してしまうことがありました。

今回は、入力が変わったように見えても、20ms待ってからもう一度確認します。
待ったあとも同じ状態なら、そのとき初めて状態が変わったとみなします。

これにより、押した直後や離した直後の細かい揺れを、そのままLEDの点灯・消灯に反映しにくくなります。
結果として、LEDの点灯と消灯が前回より安定して見えやすくなります。

7-4. 今回の方法の位置づけ

今回の方法は、最も簡単なソフトウェア・デバウンス のひとつです。
まずは、

  • ボタン入力はそのまま使うと不安定なことがある
  • 少し待ってから確定すると、誤反応を減らせる

この2つが分かればOKです。

※実際の製品では、タイマを使った方法や、一定周期でサンプリングする方法を使うこともあります。
ただし今は、フェーズ1で考え方をつかむ ことを優先します。

8. 実行結果

  • ボタンを押している間、外付けLEDが安定して点灯した
  • ボタンを離すと、外付けLEDが安定して消灯した
  • E1-07で見えていたような、押した直後の細かい反応が減った
  • 1回押したときの反応が、前回より落ち着いて見えるようになった

9. ハマりポイントと直し方

9-1. 1回押しただけなのに、LEDが何回も変わったように見える

原因

  • 待ち時間が短すぎる
  • タクトスイッチの個体差で、揺れが長い
  • 配線や接触が不安定

直し方

  • R_BSP_SoftwareDelay(20, BSP_DELAY_UNITS_MILLISECONDS); の 20 を、まずは 30ms にして試す
  • タクトスイッチやジャンパ線の接触を確認する
  • ブレッドボードの挿し方を見直す

9-2. ボタンを押しても反応が遅い

原因

  • デバウンスの待ち時間が長すぎる
  • 押してすぐではなく、少しあとでLEDが変わるように見える

直し方

  • 20ms を 10ms 〜 20ms 程度で試す
  • 今回は「まず安定させる」ことを優先していると理解する

補足

  • 今回はフェーズ1なので、反応速度よりも誤反応を減らすこと を優先しています

9-3. 押したときの動きが逆になる

原因

  • 判定条件が逆になっている
  • 今回は押していないときHigh、押したときLowになる配線なのに、Highで押下判定している

直し方

  • if (stable_sw_level == BSP_IO_LEVEL_LOW) の条件を見直す
  • 今回は 押していないとき High、押したとき Low であることを再確認する

9-4. LEDがまったく変わらない

原因

  • GPIO設定が違う
  • ボタン入力ピンの設定が誤っている
  • 配線が違う列に刺さっている

直し方

  • FSP Configurationで P409 が出力、P704 が入力になっているか確認する
  • P704 が Input pull-up になっているか確認する
  • 配線図どおりか見直す

10. 次回やること

次は、フェーズ2 に進みます。
フェーズ2では、待ちで止まる処理から離れて、時間を見ながら動かす考え方 を学んでいきます。

11. 関連リンク

  • E1-05 ボタン入力でLEDを点ける(ポーリング)(リンク)
  • E1-06 プルアップ/プルダウンを体験(リンク)
  • E1-07 チャタリングを“見える化”(ログ/LED)(リンク)
  • プルアップ/プルダウン入門(リンク)
  • ボタンが不安定なとき(デバウンス入門)(リンク)
  • マイコンとは?RA8M2でLEDを点滅するための超入門(リンク)
  • C言語のはじめ:LED点滅で必要なところだけ(リンク)

-フェーズ1