温度計にモード切り替え(最高温度、最低温度)を付けたかったので、タクトスイッチの使い方を調べることにした。
まずは、マイコンを使わずにタクトスイッチを使って、LEDを点灯・消灯させる。
かなり苦戦したのだけど、いろいろな人に教えてもらって何とか完成させることができた。
タクトスイッチの構造
タクトスイッチは押すとカチッと手応えのある押しボタンだ。押している間だけ電流が流れるらしい。
足は4本付いていて、押すことで2本の足が接続され電流が流れるという、単純な仕組みのようだ。
下記のページを参考にさせてもらった。
arduino使い方:スイッチの入/切でLEDを点灯
マイコンを使ってタクトスイッチを操作
続いて、マイコンを使ってLEDを点灯させてみることにした。
PD3にLEDを接続、PD4にタクトスイッチのアノードを接続した。
PD4は入力になるため、DDRDレジスタでビット4を0にし、PD3は出力で使うため、ビット3を1にする。
また、内部プルアップを有効にするために、PORTDレジスタのビット3を1にする必要がある。
これは、スイッチがどこにも接続されていない場合、電圧が不安定になり0なのか1なのか分からなくなってしまうためだ。
それを防ぐためにスイッチとの間に抵抗をはさむ必要があり、これをプルアップ抵抗という。
AVRの場合、プルアップ抵抗を内蔵しているので、上記のようにPORTDレジスタのビットを立てると、内部プルアップ抵抗が有効になる。
あとは、bit_is_clear関数でPD4が0になっているかチェックし、LEDの点灯させるだけだ。
bit_is_clear関数は、第一引数にチェックするレジスタ(PORTBやPORTD)、第二引数にチェックするビット番号を指定する。
0になっているとtrueが返ってくる。同様にbit_is_set関数は、1になっているとtrueが返ってくるようだ。
この関数を使えば、簡潔に記述することができる。
この回路はそのものズバリのものが、下記ページに書いてあったので参考にさせてもらった。
LED点灯 ( IO入出力 )
top
長押しを判定する
スイッチのオン・オフの他に、長押しを実装してみることにした。
モード切り替えはスイッチの短押しを使って、最高温度や最低温度は長押しでリセット出来るようにするためだ。
長押しを判定するためにタイマ割り込みを使うことにした。しかし、とてもむずかしく長い道のりとなった…。
短押しと、長押しを判断するのがこんなにむずかしいとは思わなかった。
普段何気なく使うことが多いのだけど、単純な仕組みでも一から作るとなるとむずかしいもんだ。
回路図は下記のようになった。
タイマ割り込みを使う
タイマ割り込みについては説明しているサイトがたくさんあるので割愛する。
下記のサイトが詳しく説明されていて参考になった。
http://d.hatena.ne.jp/hijouguchi/20100620/1276997802
簡単に説明すると、カウンタ用レジスタ(TCNT0)があって、一定周期毎にこのレジスタの値がカウントアップされていく。
レジスタがオーバーフローしたり、設定した値に到達すると割り込みハンドラが呼び出されるというものだ。
大事なのは、クロック数と分周比から何秒ごとに割り込みハンドラが呼び出されるのかを考えることだと思う。
今回は、ATTINY2313を8MHzで動かしていて、分周比を1024に設定した。(他にも256や8など設定できる)
分周比は、「TCCR0B」レジスタで設定する。分周比1024なので、0b00000101となった。
8000000(8MHz) / 1024(分周比) = 7812.5Hz
となるので、1秒間に7812回動作するとうことだ。
1(秒) / 7812(Hz) = 約0.000128秒 = 0.128ms(ミリ秒)
0.128ms毎にカウントアップされていくことになる。
今回はカウンタ用レジスタがオーバーフローしたときに割り込みハンドラを呼び出すようにした。
カウンタ用レジスタは8ビットなので、256になるとオーバーフローすることになる。
0.128(ms) * 256 = 32.768ms
約32ms毎に割り込みハンドラが呼び出されるようだ。
タイマ割り込みの実行
タイマ割り込みを使うためには、「interrupt.h」をインクルードする。
TCCR0Bレジスタ、TIMSKレジスタを設定した後、sei関数を呼び出すことで実行される。
sei関数は、全割り込みを有効にさせる関数だ。逆にcei関数は、全割り込みを無効にさせる関数になる。
割り込みハンドラは、ISRマクロを使って設定する。
最初ISRは関数だと思ったのだけど、よくよく見てみるとマクロになっていた。
第一引数に使うハンドラ名を入れる。
今回は、TIMER0のオーバーフローということで以下のようになった。
ISR(TIMER0_OVF_vect) { }
長押しをどのように判断するか
短押しと長押しを区別させるのに苦労した。
32ms毎にスイッチが押されているか判断し、3秒以上押されてい場合に、長押しと判断させることにした。
3秒未満でスイッチを離した場合は、短押しとなるようにした。
押した場合に判定させてしまうと、短押しと長押しのイベントが同時に起きてしまい、動作がおかしくなってしまう。
なので、長押しと区別させるために、短押しの場合、リリース時(スイッチを離した時)に判定させることで上手くいった。
また、長押しされている時間を調べるために変数にハンドラ呼び出された回数を保存することにしたのだけど、ずっと長押ししているとカウンタ変数がオーバーフローしてしまい誤動作するということがあった。
カウンタ変数を大きいものして一時しのぎすることも出来るのだけど、今回は「長押し判定時間の3秒を越えた場合、カウントアップさせない」ことで対応した。
今回のコードもGitHubにアップした。
pontago/avr-TactSwitchTest · GitHub
参考サイト
[AVRexample] Timer0 オーバーフロー | 花夢電科雑多猫
無機物の週末 超☆ゆとり的電子工作 その2!
http://d.hatena.ne.jp/hijouguchi/20100620/1276997802
http://www.hokutodenshi.co.jp/PUPPYSupportPage/ensyu/timer/timer2.html
AVR timer(1) | stastaka's Blog
タイマ/カウンタ1を使う
[example] 外部割り込み – 花夢電科雑多猫 マニュアル&メモ
つくろぐ 技術系