Project 2.2 デバウンス/MINI table lamp

前回はLEDをスイッチで制御しました。

あわせて読みたい
Project 2.1 スイッチ/Button & LED 前回はブレッドボードの使い方の練習でした。プログラムはProject0.1で使用したのものを利用しました。 https://kaneshige.org/?p=167 https://kaneshige.org/?p=145 さ...

今回はスイッチの動作を変えます。スイッチを一度押すとLEDが点灯して、もう一度押すと消灯するように動作を変えてみましょう。

目次

プッシュボタンのデバウンス

プッシュボタンが押された瞬間、すぐに初期状態から別の状態へ切り替わるわけではありません。 繊細な機械的振動により、人間には検出できないほど高速ではありますが、別の状態に完全に到達するまで短い間、連続的に小刻みな揺れ(バウンド)が起こります。プッシュボタンが離されたときにも、同様の現象が起こります。 この望ましくない現象は「バウンス」と呼ばれています。

以下の図を見てみましょう。

上の緑のグラフが我々の思い描くスイッチの動作ですが、実際には下のように瞬間的にオンオフを繰り返してしまいます。

そのため、プッシュボタンの状態をそのままプログラムで読み取ると、1回の押下サイクルの中で、実際には複数回のオン・オフが検出されてしまいます。この小刻みな揺れ(バウンド)が、高速で動作するマイコン(マイクロコントローラー)を混乱させ、誤動作を引き起こす原因となります。 したがって、このバウンドの影響を取り除く工夫が必要になります。

その解決策は、ボタンの状態を複数回判断することです。 ボタンの状態がある一定時間安定している場合にのみ、初めてボタンが実際にオン状態になった (押された)と判断します。

このプロジェクトでは、前のセクションで使用したものと同じ部品と回路が必要です。

ソースコードは以下のとおりです。

/**********************************************************************
  Filename    : TableLamp
  Description : Control led by button.
  Auther      : www.freenove.com
  Modification: 2022/10/19
**********************************************************************/
#define PIN_LED    2
#define PIN_BUTTON 13
// the setup function runs once when you press reset or power the board
void setup() {
  // initialize digital pin PIN_LED as an output.
  pinMode(PIN_LED, OUTPUT);
  pinMode(PIN_BUTTON, INPUT);
}

// the loop function runs over and over again forever
void loop() {
  if (digitalRead(PIN_BUTTON) == LOW) {
    delay(20);
    if (digitalRead(PIN_BUTTON) == LOW) {
      reverseGPIO(PIN_LED);
    }
    while (digitalRead(PIN_BUTTON) == LOW);
    delay(20);
    while (digitalRead(PIN_BUTTON) == LOW);
  }
}

void reverseGPIO(int pin) {
  digitalWrite(pin, !digitalRead(pin));
}

コード解説

肝になるのは18~26行目ですね。

/**********************************************************************
  Filename    : TableLamp
  Description : Control led by button.
  Auther      : www.freenove.com
  Modification: 2022/10/19
**********************************************************************/
#define PIN_LED    2
#define PIN_BUTTON 13
// the setup function runs once when you press reset or power the board
void setup() {
  // initialize digital pin PIN_LED as an output.
  pinMode(PIN_LED, OUTPUT);
  pinMode(PIN_BUTTON, INPUT);
}

// the loop function runs over and over again forever
void loop() {
  if (digitalRead(PIN_BUTTON) == LOW) {
    delay(20);
    if (digitalRead(PIN_BUTTON) == LOW) {
      reverseGPIO(PIN_LED);
    }
    while (digitalRead(PIN_BUTTON) == LOW);
    delay(20);
    while (digitalRead(PIN_BUTTON) == LOW);
  }
}

void reverseGPIO(int pin) {
  digitalWrite(pin, !digitalRead(pin));
}

プッシュボタンの状態を判定する際、「押し下げられた」と検出した場合、バウンスの影響を排除するために一定時間待ってから再度検出します。押し下げが確実であると確認できたら、LEDをオン/オフ反転させます。その後、ボタンが離されるのを待ち、離された後も一定時間待ってからバウンスの影響を取り除きます。

この『一定時間待つ』という処理のことを『デバウンス時間/遅延』と呼びます。通常は数ミリ秒から数十ミリ秒程度です。

復習になりますが、ボタンを押しているときはGPIO13は電気的に0Vとなるので、LOWの状態になります。

18行目でLOWを検知したら
19行目で20ミリ秒待機して、
20行目で再度LOWを検知したら
21行目でGPIOを反転させる

という動作です。

23行目は、LEDを切り替えた後、コードはwhileループに入ります。このループは、ボタンの状態を継続的に確認します。ボタンが押されている(ロー状態)限り、ループは続行されます。これは、ボタンが完全に離されるまでLEDが切り替えられたままになることを保証します。

ボタンが離されると(ハイ状態)、ループは終了します。しかし、さらに24行目でdelay(20)が追加されます。この最後の遅延は、次のボタン押下サイクルに進む前に、リリース関連のバウンスも排除するための予防策です。

25行目で再度ボタンが離れるまでループを続行し、完全に離れたら次のサイクルに進みます。

21行目のreverseGPIOについて見てみましょう。reverseGPIOは29~31行目で定義されています。

/**********************************************************************
  Filename    : TableLamp
  Description : Control led by button.
  Auther      : www.freenove.com
  Modification: 2022/10/19
**********************************************************************/
#define PIN_LED    2
#define PIN_BUTTON 13
// the setup function runs once when you press reset or power the board
void setup() {
  // initialize digital pin PIN_LED as an output.
  pinMode(PIN_LED, OUTPUT);
  pinMode(PIN_BUTTON, INPUT);
}

// the loop function runs over and over again forever
void loop() {
  if (digitalRead(PIN_BUTTON) == LOW) {
    delay(20);
    if (digitalRead(PIN_BUTTON) == LOW) {
      reverseGPIO(PIN_LED);
    }
    while (digitalRead(PIN_BUTTON) == LOW);
    delay(20);
    while (digitalRead(PIN_BUTTON) == LOW);
  }
}

void reverseGPIO(int pin) {
  digitalWrite(pin, !digitalRead(pin));
}

ピンの状態を読み取って、逆の状態を出力しています。プログラムでは『!』が反転(0なら1、1なら0)を意味します。

次回はLEDをフォンフォンさせます。

あわせて読みたい
Project 3.1 配列と繰り返し/Flowing Light 前回までで単独のLEDを制御することを学びました。 https://kaneshige.org/?p=203 今回は複数のLEDを制御する方法を学びます。 このプロジェクトでは、複数のLEDを使用...
よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

金重総合研究所の主席研究員。
子供の頃から研究者を目指し、ライフワークとして日々様々な研究をしています。
経営・マネジメント・金融・DXあたりが本職です。
私を採用したい人、私と一緒に働きたい人、一緒に知識を肥やしていきたい人はぜひお声がけ下さい。

コメント

コメントする

目次