電子工作日和

初めての電子工作と雑記書いていこうかと思います。

第4回 タクトスイッチを使ったArduinoでのLED制御


さて、Arduinoの使い方は前回の記事でおおかた理解できたと思いますので、今回はLEDとタクトスイッチを使った一つのシステムを作りながら電子工作におけるArduinoやタクトスイッチの性質上の問題をみながら解消していきたいと思います。

 

前回はスイッチを押したときだけ光り、指を離すと消えました。
しかし、もし暗いところで作業をするとき、これでは常にボタンを押し続けなければなりません。

 

そこで今回はボタンを押した、という行動をArduinoに記憶させることでボタンから指を離した後も光り続けるプログラムを作っていきます。

 

1.スイッチを押した後も光り続けるLED


今回はスケッチをいじるだけなので、Arduinoの配線自体は前回と同じです。

書き忘れていましたがLEDはアノード側を13ピン、カソード側をGNDに接続しています。

f:id:ru-san3:20180424150023p:plain

 

ただし、スケッチは前回とは違ってきます。今回はArduino"ボタンを押した状態"を記憶させるために、stateという変数を使っています。

ちなみに変数のvalとかstate、LED、BUTTONはただこっちが勝手につけた名前なので、stateをjoutaiとかにしても別に問題はありません。

 

f:id:ru-san3:20180502115138p:plain

前回は詳しい説明をが画像内に書いていましたが、見にくいので以下に順番に書いていきます。上の画像で理解できた方は飛ばしてもらって構いません。

const int LED = 13;
13ピンにLEDを設定constはconstant(定数)の略でこのスケッチ上で変わることはない。
intは整数型。今はまだ"おまじない(?)"と思えばいい。

const int BUTTON = 7;
7ピンにBUTTONを設定。

int val = 0;
valという変数を設定。

int state = 0;
stateという変数を設定。

void setup(){}
{}内のブロックをsetup()という名前に設定。setup()は電源を入れた時に一度だけ実行される初期設定みたいなもの。

pinMode(LED, OUTPUT);
pinMode()はピンの状態を入力か出力か設定する。
なのでここでは、LEDというピン(つまり13ピン)をOUTPUT(出力)に設定。

pinMoede(BUTTON, INPUT);
同じように、BUTTONというピン(つまり7ピン)をINPUT(入力)に設定。

void loop(){}
{}内のブロックをloop()という名前に設定。loop()はその名のとおり{}内をずっと繰り返す。

val = digitalRead(BUTTON);
digitalRead()は()内のピンのデジタル値(0か1)を読み取り、LOWかHIGHで返す。
ここではBUTTONという入力ピンのデジタル値を読み取り、その結果をLOWかHIGHで返す。
それを変数valに格納。

if(val == HIGH){}
ifは言葉通り「もし」の意味で、
()内の条件が満たされていれば、直後の{}内のブロックが実行される。
満たされていなければ、{}内は実行されず、そのまま次のコードに移る。
ここでは変数valがdigitalRead(BUTTON)で読み取った結果のHIGHであれば、直後の{}が実行される。

state = 1 - state;
直前のif文の()が満たされていなければ、何も実行されず、このコードを無視して
次の行のコードに移る。
二つの値を比較するときは=ではなく、==を用いる。

ここでは、valの値がHIGHのとき、state = 1 - state;を実行する。
プログラミングの世界では左辺が新しく定義した変数で、右辺がそれを決める式になる。
つまり、
state = 1 - state;は初期値が0なので、0 = 1 - 0 というわけの分からない式ではなく、
state = 1 - 0 すなわちstate = 1 ということになる。

if(state == 1){}else{}
if(){}else{}が一つのかたまり。ifの直後の()の条件が満たされると、その直後の{}内が実行され、そうでなければ、elseの直後の{}内を実行する。

ここでは、一つ前のif文のstate = 1 - state;の左辺が1であるならば、直後の{}が実行され、そうでなければ、else{}の{}内が実行される。

digitalWrite(LED, HIGH);
digitalWrite()はピンにHIGHかLOWを出力する。
ここでは、state == 1が満たされれば、LEDがHIGHを出力する。(LEDが光る。)

digitalWrite(LED, LOW);
ここでは、state == 1が満たされなければ、LEDがLOWを出力する。(LEDが消える。)

 

 

流れはこんな感じです。ただし、もう試してみた方は気づくでしょうが、正しくは動作してないと思います。指を離したら光も消えてしまうことがあります。

 

これにはちゃんと原因があります。Arduinoは小さく性能も普通のパソコンよりは劣りますが、それでも1秒間に数百万のコードを実行できるそうです。となると、ボタンを押している間に何回もstateの値が1と0を繰り返しているから正しく動作できないのです。

 

これを解消するためには、ボタンを押した瞬間だけstateの値を変更させ、それ押している間はstateを変更させなければいいのです。そのためにはvalという一つの変数だけでloopさせるのではなく、もう一つ別の変数を設定する必要があります。片方の変数に古い状態を保存して、もう片方の変数に新しい状態を保存し、その二つの状態が違うときだけstateを変えればいいのです。

 

今はまだわからなくても問題ありません。私もこの時点では意味が分かりませんでした(笑)。
下のほうに全体の流れを書いているのでそこで納得してもらえれば構いません。

 

 

では、変数old_valを追加して改善してみます。

f:id:ru-san3:20180502121945p:plain

 

追加された部分を説明していきます。上の画像で理解できた方は飛ばしてもらって構いません。

int old_val = 0;
valと同様、変数old_valを設定。ここにはvalが新しい状態を記憶したとき、今までvalが記憶していた一つ古い状態を記憶させます。

 

f:id:ru-san3:20180514165037p:plain
&&は日本語で「且つ」の意味。すなわち(val == HIGH)と(old_val == LOW)の両方が満たされているときのみ直後の{}内のブロックが実行される。

old_val = val;
先ほどのif文でvalとold_valの比較をし終えたので、valの値は古いvalとしてold_valに保存します。

 

全体の流れを確認します。


最初にボタンを押す。
①ボタンを押すとval = digitalRead(BUTTON)で入力を読み取り、HIGHで返す。
②一つ目のif文でvalとold_valの値を比較します。old_valは初期値が0なのでLOW。よってval == old_valの条件が満たされるので、state = 1 - 0 =1となる。
③②で二つの変数の比較が終わったので、valのHIGHをold_valに格納する。
④二つ目のif文でstateは1なので、LEDピンをHIGHにする。

 

ボタンを押している途中。
⑤①と同じようにval = digitalRead(BUTTON)で読み取り、HIGHで返す。
⑥一つ目のif文でvalとold_valの値を比較します。今度はvalもold_valもHIGH。
よってstate =1 - stateは実行されません。
⑦⑥で二つの変数の比較が終わったので、valのHIGHをold_valに格納する。
⑧二つ目のif文でstateは1のままなので、LEDピンをHIGHにする。つまりボタンを押している間は変化しない(押した瞬間さへ考えればいい)。

 

ボタンから指を離す。
⑨①と同じようにval = digitalRead(BUTTON)で読み取り、今度はLOWで返す。
⑩一つ目のif文でvalとold_valの値を比較します。valはLOW、old_valはHIGH。よってstate =1 - stateは実行されません。
⑪⑩で二つの変数の比較が終わったので、valのLOWをold_valに格納する。
⑫二つ目のif文でstateは1のままなので、LEDピンをHIGHにする。
つまり、ボタンから指を離した後も光り続ける。

 

再びボタンを押す。
⑬val = digitalRead(BUTTON)で読み取り、HIGHで返す。
⑭一つ目のif文でvalとold_valの値を比較します。valはHIGH、old_valはLOW。よってval == old_valの条件が満たされるので、state =1 - state = 1 - 1 =0となる。
⑮⑭で二つの変数の比較が終わったので、valのHIGHをold_valに格納する。
⑯二つ目のif文でstateは0なので、LEDピンをLOWにする。
これでLEDは消える。押している途中、離すときも同様に消えている。

 

一連の流れは大体つかめたと思います。ただボタンを押すだけでとても複雑な処理を行っているのが目に見えますね。

しかしこれでも、正常に動いていないと思います。原因はアナログ的なスイッチを扱う際に処理すべき点が残っているからです。
タクトスイッチのような押すと中の金属が触れて通電するようなものは、必ずしも触れた瞬間に接着し続けるのではなく、押した衝撃でわずかながら振動しているからです。
これをバウンシングと言います。(チャタリングではありません)

 

これを解消するにはdelay()という関数をもちいます。これは()内で指定した時間(単位はミリ秒)だけプログラムの実行を止める働きがあります。第3回のLEDを点滅させるために使われた関数です。

ではdelay()をどこに入れるべきか。タクトスイッチの振動の影響を最初に受けてしまうコードのところに入れるべきです。
すると下の画像のようになります。

f:id:ru-san3:20180502122508p:plain

時間は10~50ミリ秒で十分なようです。一つ目のif文の{}内にdelay()を入れることで、valの値に変化があるときだけdelay()が働くようになります。

これでうまく動作するようになったと思います。
動作がまだ不安定な場合はdelay()の数字を少し大きくしてみるといいかもしれません。

 

Arduinoをはじめよう 第3版 (Make:PROJECTS)

Arduinoをはじめよう 第3版 (Make:PROJECTS)

 
Arduinoをはじめようキット

Arduinoをはじめようキット