前回はLEDをスイッチで制御しました。
今回はスイッチの動作を変えます。スイッチを一度押すと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をフォンフォンさせます。
コメント