これまで delay() でタイミングを調整しながらGPIOを制御してLEDを点滅させていました。今回はタイマー割り込みという機能を使って点滅させます。
サンプル(1):複数のLEDを同時に点灯する
Lチカの演習を元に、5×5の左上のLEDと、その右隣のLEDを2個同時に光らせてみてください。下のプログラムはそれぞれのLEDに対応するGPIOを単純に設定しているだけです。動作に変化が無いので loop() の中は何もありません。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
void setup() { //左上のLED pinMode(26, OUTPUT); //ROW1 pinMode(3, OUTPUT); //COL1 //2番目のLED pinMode(27, OUTPUT); //ROW2 pinMode(23, OUTPUT); //COL4 //左上のLEDオン digitalWrite(26, HIGH); digitalWrite(3, LOW); //2番目のLEDオン digitalWrite(27, HIGH); digitalWrite(23, LOW); } void loop() { } |
結果は意図しないLEDまで計4個のLEDが光ってしまったはずです。
課題1
2個のLEDを光らせようとしたのに、4個光ってしまった理由を説明してください。
サンプル(2):2個のLEDを交互に点滅する
下のプログラムを micro:bit に書き込んで何が起きるか見てみましょう。
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 42 43 |
#include "nrf.h" //LED状態を制御するための変数 int status = 0; extern "C" void TIMER2_IRQHandler(void) { NRF_TIMER2->EVENTS_COMPARE[0] = 0; // 割り込みイベントクリア if(status==0){ //左上のLEDオン digitalWrite(26, HIGH); digitalWrite(3, LOW); }else{ //左上のLEDオフ digitalWrite(26, LOW); digitalWrite(3, HIGH); } status =! status; } void setup() { //左上のLEDを初期化 pinMode(26, OUTPUT); //ROW1 pinMode(3, OUTPUT); //COL1 //タイマ設定 NRF_TIMER2->TASKS_STOP = 1; // タイマストップ NRF_TIMER2->TASKS_CLEAR = 1; // カウンタクリア NRF_TIMER2->MODE = TIMER_MODE_MODE_Timer; // モード設定:タイマモード NRF_TIMER2->PRESCALER = 8; // プリスケーラ設定:256分周(62.5kHz) NRF_TIMER2->BITMODE = TIMER_BITMODE_BITMODE_16Bit; // カウンタ長設定:16ビット長指定 NRF_TIMER2->CC[0] = 62500/2; // コンパレータ0の設定:0.5秒周期 NRF_TIMER2->INTENSET = // 割り込み設定:コンパレータ0と比較 (TIMER_INTENSET_COMPARE0_Enabled << TIMER_INTENSET_COMPARE0_Pos); NRF_TIMER2->SHORTS = // ショートカット設定:クリアタスク指定 (TIMER_SHORTS_COMPARE0_CLEAR_Enabled << TIMER_SHORTS_COMPARE0_CLEAR_Pos); // タイマ割り込み設定 NVIC_SetPriority(TIMER2_IRQn, 3); // 割り込み優先度設定 NVIC_ClearPendingIRQ(TIMER2_IRQn); // 保留割り込みクリア NVIC_EnableIRQ(TIMER2_IRQn); // 割り込み許可 NRF_TIMER2->TASKS_START = 1; // タイマスタート } void loop(){ } |
左上のLEDが0.5秒毎にオン・オフしたと思います。このプログラムで何が起きたのか説明します。
1行目の #include “nrf.h” は micro:bit に搭載されている ARM Cortex-M0 nRF51822 の機能を利用するためのライブラリをインクルードしています。setup() の中の「//タイマ設定」のコメント以降に10行ほどにわたってタイマー割り込みという機能についての設定情報が書いてあります。その内容は nRF51822 のスペックシート を照合すればわかるのですが、詳細に過ぎるのでここでは省略します。この設定によって0.5秒毎にタイマー割り込みが発生します。0.5秒という時間を設定しているのはこの部分です。
1 |
NRF_TIMER2->CC[0] = 62500/2; // コンパレータ0の設定:0.5秒周期 |
以上の設定によって TIMER2_IRQHandler() という関数が0.5秒の割り込みの都度コールされます。このような、割り込み処理でコールされる関数は、割り込みハンドラーと呼ばれることが多いです。この関数の中で左上のLEDを オン→オフ→オン… と切り替えています。オン/オフの区別は status という変数を使って制御しています。
メインループの loop() には何も書いてないことに注目してください。割り込み機能を使うと、メインの処理とは独立して別の動作させることができます。
課題2
サンプル(2)を元に、5×5の左上のLEDと、その右隣のLEDを交互に点滅させるプログラムを作成してください。
サンプル(3):2個のLEDを交互に “高速に” 点滅する
課題(2)のネタバレになりますが、サンプル(2)のプログラムの前半を次のように書き換えれば左上のLEDと、その右隣のLEDを交互に点滅します。
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 |
#include "nrf.h" //LED状態を制御するための変数 int status = 0; extern "C" void TIMER2_IRQHandler(void) { NRF_TIMER2->EVENTS_COMPARE[0] = 0; // 割り込みイベントクリア if(status==0){ //左上のLEDオン digitalWrite(26, HIGH); digitalWrite(3, LOW); //2番目のLEDオフ digitalWrite(27, LOW); digitalWrite(23, HIGH); }else{ //左上のLEDオフ digitalWrite(26, LOW); digitalWrite(3, HIGH); //2番目のLEDオン digitalWrite(27, HIGH); digitalWrite(23, LOW); } status =! status; } void setup() { //左上のLEDを初期化 pinMode(26, OUTPUT); //ROW1 pinMode(3, OUTPUT); //COL1 //2番目のLED pinMode(27, OUTPUT); //ROW2 pinMode(23, OUTPUT); //COL4 (以下、同じ) |
課題3
サンプル(2)で、割り込みが発生する0.5秒の時間を設定しているプログラム部分を説明しました。そこを次のように変更して、どのような動作になるか観察し、なぜそのなるのか説明してください。
1 |
NRF_TIMER2->CC[0] = 62500/100; |
課題4
タイマー割り込みを使って、任意のLEDを同時にオン/オフする機能を作ってください。オン/オフさせる場所の指定は LED[5][5] という配列を使うものとします。
ヒント:時間を3分割してROW1/ROW2/ROW3に接続されたLEDだけをCOL1〜COL9で制御すれば実現できます。
課題5
課題4のプログラムについて解説してください。
- 変数 LED[5][5] の役割
- 変数 row の役割
- 変数 ROWLIST[3][9][2] の役割
コメント