KUMIKOMI NO SEKAI ~ #2 Pressing Button to Light UP LED
こんにちは。前回はLEDが1秒周期で点滅するプログラムを作りましたが、今回はボタンを押したらLEDが点灯し、ボタンを離したらLEDが消灯するプログラムを作ります。ボタンからの入力を元にLEDへの出力を変化させるプログラムになります。
関連モジュール
今回関連するモジュール(ハードウェア)はLEDとボタンになります。以下の概略図の濃い青色のモジュールが対象となります。
プログラムの設計
ソースコードを作成する前にプログラムの動きを考えることを、プログラム設計と言います。今回のプログラムの動きをフローチャートで表現してみます。先ず、ボタンの状態を取得します。その状態がHighであれば、LEDを点灯し、LowであればLEDを消灯します。そして、この動きが無限にループします。たったこれだけです。簡単ですね。
ソースコード作成
例によってArduino IDEを起動し、メニューバーのファイルから新規ファイルを作成し、名前を付けて保存からLightUpLedの名前で一旦保存します。LightUpLed.inoができたら、 以下のコードをコピペしましょう。自分でコードを作っても良いです。
#define BUTTON_PIN 6
#define LED_PIN 4
int buttonState = 0;
void setup() {
pinMode(LED_PIN, OUTPUT); // LEDピンをOUT方向に設定
pinMode(BUTTON_PIN, INPUT); // ボタンピンをIN方向に設定
}
void loop() {
buttonState = digitalRead(BUTTON_PIN); // ボタンのピンの状態を取得する
if (buttonState == HIGH) {
digitalWrite(LED_PIN, HIGH); // LEDを点灯する
} else {
digitalWrite(LED_PIN, LOW); // LEDを消灯する
}
}
ボタンの状態を取得する
フローチャートでボタンの状態を取得とサラッと書いていますが、実際にコードにする場合にどうすれば良いのかと言うと、Arduinoにはこれまた便利な関数が用意されていますね。digitalReadと言う関数を使います。この関数に状態を取得したいピンの番号を引数で渡すと、その状態を戻り値として返してくれます。1がHighで0がLowです。High/Lowの状態をbuttonState変数で受けています。
ボタンの状態を確認する
これはフローチャートで言うところのひし形の箇所に該当します。ここに関してはもう説明は要りませんよね。if文を使って処理を分岐させます。HighならばLEDを点灯、LowならばLEDを消灯です。digitallWrite関数については、前回も登場しているので省略します。
動作確認
ソースコードができたら、Arduino IDEの✔マークをクリックしてコンパイルし、➡でボードにプログラムを書き込みましょう。書き込みに成功したら、ボタンを押してみましょう。ボタンを押している間はLEDが点灯し、ボタンを離すとLEDが消灯しました。
消灯状態
点灯状態
トグル動作にしてみよう
上で作ったプログラムでは、ボタンを押しっぱなしにしないとLEDが点灯しません。なので次はボタンをトグルスイッチ化してみましょう。ボタンを一回押すとLEDが点灯し、もう一度ボタンを押すとLEDが消灯する動きです。いわゆる良くある照明のスイッチと同じ動きにするということです。実はこれ結構難しいですよ。私は一年坊主の時、これが出来なくて同期で一人だけ夜中まで居残りさせられていました。辛かったなあ。
1プッシュを分解して考える
ボタン入力の処理を考える際に、1プッシュを分解して考える必要があります。「1回押された」ということをどう定義するかです。今回は一旦ボタンが押されてから離された時を「1回押された」ことにします。図で表すと以下のようになります。
チャタリングチェック(chattering check)
ボタン入力の処理で必ず必要になるのがチャタリングチェックです。チャタリングチェックとは、ボタン入力のHigh/Lowのバタつきを除去するために必要な処理です。人がボタンを押したり、離したりする時、押し始めや離し始めは指先の震え等で、入力ピンに入ってくる信号が下図のようにバタつきます(高速でHigh/Lowが切り替わる)。このバタつきのことをチャタリングと言います。chatteringは英語でぺちゃくちゃ喋ると言う意味で、信号がバタつくことを表しているのでしょうね。プログラムを実行しているCPUはとんでもない速度で動いているので、Loop処理で入力ピンの状態を取得すると、青い矢印の箇所でボタンの状態を全部取得してしまいます。そのため、チャタリングチェックをしなければ、誤ってもの凄いボタン連打をされているように判断してしまうのです。
トグルボタン処理
チャタリングチェックを含めた、トグルボタン処理は以下のようにしました。
① LowからHighに変化したら、50ミリ秒待ちます。
② 50ミリ秒経過したら、まだHigh状態が継続しているかを確認します。
Highであれば、よし!押されたと判断します。
③ HighからLowに変化したら、50ミリ秒待ちます。
④ 50ミリ秒経過したら、まだLow状態が継続しているのかを確認します。
Lowであれば、この時点で1プッシュが確定です。
トグル動作ソースコード作成
以下ソースコードです。最初に作った「押したらLEDが点き、離したら消える」のと比べるとかなり複雑になります。High/Lowのチェック状態を管理する必要がありますからね。以下1)~5)の状態をプログラムで管理しています。
1)Low ⇒ Highに変化するのを待っている状態
2)Highが50ミリ秒以上継続するのを確認している状態
3)High ⇒ Lowに変化するのを待っている状態
4)Lowが50ミリ秒以上継続するのを確認している状態
5)1プッシュが確定した状態
5)まで行った時に、LEDの現在の状態を一旦読み出し、点灯していたら消灯、消灯していたら点灯させています。そして、再び1)の状態から再スタートさせています。
#define BUTTON_PIN 6 // 6番ピンはボタンと繋がっている
#define LED_PIN 4 // 4番ピンはLEDと繋がっている
#define WAIT_HIGH 0 // High待ち
#define CHECK_HIGH 1 // Highチャタリングチェック中
#define WAIT_LOW 2 // Low待ち
#define CHECK_LOW 3 // Lowチャタリングチェック中
#define CONFIRM 4 // 1プッシュ確定
int buttonState = 0; // ボタンの状態
int buttonCheck = 0; // チャタリングチェック状態
void setup() {
pinMode(LED_PIN, OUTPUT); // LEDピンをOUT方向に設定
pinMode(BUTTON_PIN, INPUT); // ボタンピンをIN方向に設定
}
void loop() {
buttonState = digitalRead(BUTTON_PIN); // ボタンのピンの状態を取得する
if (buttonCheck == WAIT_HIGH) { // High待ち
if (buttonState == HIGH) { // Low ⇒ Highに変化した!
buttonCheck = CHECK_HIGH; // Highのチャタリングチェック中へ移行
delay(50); // 50ms待ち
}
}
else if (buttonCheck == CHECK_HIGH) { // Highチャタリングチェック中
if (buttonState == HIGH) { // まだHighが継続中(押下は確定)
buttonCheck = WAIT_LOW; // High ⇒ Lowに変化待ち中へ移行
}
}
else if (buttonCheck == WAIT_LOW) { // Low待ち
if (buttonState == LOW) { // High ⇒ Lowに変化した!
buttonCheck = CHECK_LOW; // Lowのチャタリングチェック中へ移行
delay(50); // 50ms待ち
}
}
else if (buttonCheck == CHECK_LOW) { // Lowチャタリングチェック中
if (buttonState == LOW) { // まだLowが継続中
buttonCheck = CONFIRM; // 1プッシュ確定
}
}
// 1プッシュが確定したので、LEDの点灯状態を変更する
if (buttonCheck == CONFIRM) {
int led = digitalRead(LED_PIN);
if (led == HIGH) {
digitalWrite(LED_PIN, LOW);
} else {
digitalWrite(LED_PIN, HIGH);
}
buttonCheck = WAIT_HIGH; // Low ⇒ Highへの変化待ち中へ移行
}
}
まとめ
今回はボタンとLEDを使ったプログラムを動かしてみましたが、如何だったでしょうか。ボタン操作一つでも、結構考えないといけないことがあるのが分かったと思います。チャタリングチェックの話は、まさに組込みっぽい内容でした。入力ピンからの信号を適切に処理しないと、ボタンを1回しか押していないのに、LEDがチカチカしたり、点けたつもりが消えてしまったりしちゃいます。試しにdelay(50);の一行を消して動かしてみて下さい。変な動きになりますよ。次回はアナログ信号を使う予定です。それでは、今回はこの辺で。お疲れ様でした。