演習2で、digitalRead() という関数でボタンの状態を取得しました。今回は別の方法でボタンを使います。
ハードウエア割り込み
次のサンプルプログラムをコンパイルして動かしてください。Aボタンを押したときのプログラムの挙動をシリアルモニタで観察してください。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
#include <Adafruit_Microbit.h> Adafruit_Microbit_Matrix microbit; int button_A = 0; // ボタンAが押されたことを検知 int count = 0; // グローバル変数(0〜24カウントする) void setup() { Serial.begin(9600); while (!Serial); // ボタンAを初期化 pinMode(PIN_BUTTON_A, INPUT_PULLUP); // ボタンAで割り込み設定 attachInterrupt(PIN_BUTTON_A, irq_by_A_button, FALLING); microbit.begin(); microbit.fillScreen(LED_OFF); } // ボタンAの割り込みハンドラ void irq_by_A_button() { button_A = 1; } void loop(){ int x, y; if (button_A) { button_A = 0; count++; if (25<=count) count=0; Serial.print("count="); Serial.println(count); } y = count/5; // y方向の位置 x = count%5; // x方向の位置 microbit.drawPixel(x, y, LED_ON); delay(100); microbit.drawPixel(x, y, LED_OFF); } |
Aボタンを押すたびにLEDが移動したはずです。なぜこのような動作をするのか説明します。
変数 button_A はボタンAが押された時に “1” となり、イベントが発生したことをメインルーチンの loop() に伝えます。その値は関数 irq_by_A_button() の中でセットされ、loop() の中で必要な処理が終わったらクリアされます。
変数 count はボタンAが押されるたびに1ずつ増え、点灯するLEDの位置を示します。0〜24の間で変化し、24の次は0に戻ります。
loop() の後半では変数 count を元にオンするLEDの位置を決めてオン・オフしています。100ms毎にボタンのイベントを確認して、変化があれば表示に反映します。
演習3ではタイマー割り込みを勉強しましたが、このプログラムではハードウエア割り込みの機能を使っています。setup() の中で呼んでいる attachInterrupt() でハードウエア割り込みを設定しています。
1 |
attachInterrupt(PIN_BUTTON_A, irq_by_A_button, FALLING); |
ボタンAはひとつ前の行で pinMode(PIN_BUTTON_A, INPUT_PULLUP); と宣言されているので、何も操作しなければHIGH状態であり、ボタンを押すとLOWとなり信号が立ち下がります。attachInterrupt() はボタンA “PIN_BUTTON_A” の信号が立ち下がった “FALLING” 時にハードウエア割り込みを発生させます。割り込みが発生すると2番めの引数で指定している割り込み処理関数(割り込みハンドラ)の irq_by_A_button() が実行されます。
もしハードウエア割り込みを使わずにボタンAが押されたことを検知しようとすると頻繁に digitalRead() を呼び出してボタンの状態を監視しなければなりませんが、たとえば表示関数(この例では microbit.drawPixel())が実行中にボタンが押されたらイベントを見逃してしまいます。ハードウエア割り込みを使用すると、必要なイベントを確実に捉えることが可能になります。
なお、割り込みハンドラをコーディングするときの注意事項として、その中には時間がかかる重い処理を書いてはいけません。多重に割り込みが発生して不具合の原因になるからです。今回の例では、関数 irq_by_A_button() は変数 button_A に “1” を代入したら直ちに処理を終えています。
チャタリング
先程のプログラムではボタンAを押した時にチャタリングが発生してLEDが2個分移動してしまうことがあります。micro:bitの個体差によって発生頻度は異なりますが、しつこく試すとそのような現象が見られるはずです。チャタリングの対策は、そもそもチャタリングが起きないような回路設計を行うのが理想(2年の実験授業で勉強したはず)ですが、市販のワンボードマイコンの回路を変更するのは困難です。そのような場合はプログラムで問題の回避を試みます。関数 loop() を次のように変更してみてください。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
void loop(){ int x, y; if (button_A) { button_A = 0; // 3ms待ってボタンAが立ち下がってなければチャタリングと判断 delay(3); if (digitalRead(PIN_BUTTON_A) == HIGH) { Serial.println("CHATTERING!"); return; } count++; if (25<=count) count=0; Serial.print("count="); Serial.println(count); } y = count/5; // y方向の位置 x = count%5; // x方向の位置 microbit.drawPixel(x, y, LED_ON); delay(100); microbit.drawPixel(x, y, LED_OFF); } |
この変更内容は、Aボタンの操作を検出したら3ms後にボタンの状態を digitalRead(PIN_BUTTON_A) で再確認し、信号がHIGHだったら(信号の立ち下がりが完了していなければ)無視するようにしたものです。シリアルモニタに “CHATTERING!” と表示されたらチャタリングがうまく回避されたことを示します。ただし、実際に発生しているチャタリングの波形によって回避方法や最適な時間設定は異なる可能性があります。
演習
最下段のLED5個のうち1個だけをオンして、Aボタンで左に、Bボタンで右に動くプログラムを作ってください。
コメント