タイマー割り込みプログラム | arduinoロボットプログラミング

2023年11月30日

Arduinoでタイマー割り込みのプログラムを作る場合に、MSTimerやFlexTimerなどのライブラリーを使うと簡単ですが、実はそれらを使わずCPUを直接コントロールする下記のような方法もあります。

Arduinoは、電子プロトタイピングプラットフォームとして広く使用されており、様々なプロジェクトに適しています。Arduinoを使ったプログラミングでは、センサー、アクチュエータ、ディスプレイなどのデバイスを制御することが一般的です。しかし、特定のタイミングで何らかのアクションを実行する必要がある場合、タイマー割り込みが非常に有用です。この記事では、Arduinoのタイマー割り込みを使用したプログラムの基本を初心者向けに解説します。

タイマー割り込みとは何か?

タイマー割り込みは、特定の時間間隔または条件が満たされたときに、Arduinoマイクロコントローラ内で事前に設定されたコードブロックを実行する方法です。これは、正確なタイミングが必要なアプリケーションに非常に便利です。例えば、LEDを一定の間隔で点滅させたり、センサーデータを定期的に取得したりする際に使用できます。

Arduinoとは何か?

Arduinoは、オープンソースの電子プロトタイピングプラットフォームで、プログラム可能なマイクロコントローラを搭載したボードと、それをプログラムするための開発環境から成り立っています。Arduinoボードは、センサー、モーター、LED、ディスプレイなどのデバイスを制御するのに最適で、プログラムを書いてボードにアップロードすることで、様々な電子プロジェクトを実現できます。

タイマー割り込みを使用する理由

なぜタイマー割り込みを使用するのでしょうか?例を挙げてみましょう。
LED が 1 秒ごとに点滅する単純なプロジェクトを作成したいと想像してください。 これを行う 1 つの方法は、プログラムを 1000 ミリ秒 (1 秒) 一時停止する Arduino の Delay(1000) 関数を使用することです。 ただし、この遅延の間、Arduino は他のことを行うことができません。 その瞬間は基本的に「止まっている」ことになります。

この方法は、非常に単純なアプリケーションには適していますが、複雑なプロジェクトでは問題が発生する可能性があります。たとえば、同時にセンサーデータを収集したり、通信を行ったり、他のタスクを実行したりする必要がある場合、delay()関数を使用すると、Arduinoが他のタスクを実行できなくなります。

ここでタイマー割り込みが役立ちます。タイマー割り込みを使用すると、Arduinoはタイマーが設定した時間間隔または条件を満たすたびに、特定のコードブロックを実行します。つまり、LEDを点滅させるタイミングを正確に制御できます。同時に他のタスクも実行できるため、マルチタスクプログラミングに適しています。

タイマー割り込みの基本プログラム

では、具体的なプログラム例を見てみましょう。
以下の例では、クムクム入門モデル(R3J)を使用し、LEDを3色delayを使って100ミリ秒ごとに点滅させながら、確実に1秒ごとにBEEP音を鳴らすプログラムを作成します。

#include <avr/io.h>
#include <avr/interrupt.h>

void setup() {
    noInterrupts(); // 割り込みを一時的に無効化
    TCCR1A = 0; // Timer1の設定初期化
    TCCR1B = 0; // Timer1の設定初期化
    TCNT1 = 0; // カウンタ初期化
    OCR1A = 15624; // 1秒間のカウント数(16MHzクロックを使って設定、1Hz)
    TCCR1B |= (1 << WGM12); // CTCモードを設定
    TCCR1B |= (1 << CS12) | (1 << CS10); // プリスケーラを256に設定
    TIMSK1 |= (1 << OCIE1A); // Timer1比較一致A割り込みを許可
    interrupts(); // 割り込みを有効化
}
void loop() {
    digitalWrite(A0,HIGH);
    delay(100);
    digitalWrite(A0,LOW);
    delay(100);
    digitalWrite(A1,HIGH);
    delay(100);
    digitalWrite(A1,LOW);
    delay(100);
    digitalWrite(A2,HIGH);
    delay(100);
    digitalWrite(A2,LOW);
    delay(100);

}
//  Timer1比較一致A割り込みハンドラ
ISR(TIMER1_COMPA_vect){
    tone(12,440,10);
}
 

プログラムの冒頭で、#includeディレクティブを使用して、必要なライブラリとヘッダーファイルをインクルードしています。
<avr/io.h>と<avr/interrupt.h>は、タイマー割り込みを使用するために必要なライブラリです。
<avr/io.h>は、マイクロコントローラのレジスタやピンの設定を制御するのに役立ちます。
<avr/interrupt.h>は、割り込み関連の設定や割り込みハンドラを扱うのに必要です。

  1. setup()関数は、Arduinoプログラムの初期化部分です。通常はLEDポートをpinMode();で、出力設定ますが、(クムクム入門モデル(R3J))の場合はA0,1,2に接続をしているため特にここでの設定は不要です。
  2. 次に、Timer1の設定を行います。Timer1はArduinoボード上の一つのタイマーです。タイマーは特定の時間間隔で割り込みを発生させるために使用されます。以下がその設定の詳細です。noInterrupts();で割り込みを一時的に無効化します。これは、タイマーの設定中に割り込みが発生しないようにするためです。TCCR1ATCCR1Bは、Timer1の設定レジスタです。これらを0で初期化して、設定をクリアします。TCNT1 = 0;で、Timer1のカウンタを初期化します。OCR1A = 15624;では、1秒間のカウント数を設定しています。これはArduino (クムクム入門モデル(R3J))のクロック周波数(16MHz)に合わせて設定されており、1Hzのタイマー割り込みを生成します。TCCR1B |= (1 << WGM12);で、Timer1をCTC(Clear Timer on Compare Match)モードに設定します。このモードでは、カウンタがOCR1Aの値に達すると比較一致割り込みが発生します。TCCR1B |= (1 << CS12) | (1 << CS10);では、プリスケーラを256に設定しています。これにより、クロック周波数を16MHzから62500Hzに減少させ、1秒ごとにタイマー割り込みが発生します。最後にTIMSK1 |= (1 << OCIE1A);で、Timer1比較一致A割り込みを許可します。
  3. interrupts();で割り込みを有効にします。これにより、Timer1が正常に動作し、割り込みハンドラが実行されます。
  4. loop()関数はArduinoプログラムのメインループですが、この例では空です。なぜなら、タイマー割り込みがタイミングを制御するため、特別なループ内のコードは必要ありません。
  5. 最後に、ISR(TIMER1_COMPA_vect)でTimer1比較一致A割り込みハンドラを定義しています。このハンドラは、Timer1がOCR1Aの値に達したときに呼び出されます。ハンドラ内では、LEDの状態を反転させることで、LEDを1秒ごとに点滅させています。

このプログラムのキモは、Timer1を設定し、割り込みハンドラを使用して定期的なタイマー割り込みを処理することです。
これにより、Arduino(クムクム入門モデル(R3J))は、loop内にあるLED点滅に使われた delayに関係なく、正確なタイミングでtone関数をコールしBEEP音を鳴らすことがができます。

タイマー割り込みを使用することで、正確なタイミングで何らかのアクションを実行でき、同時に他のタスクも実行できるため、Arduinoをより多くのアプリケーションに適用できるようになります。初めてタイマー割り込みを使用する場合でも、この例を参考にして、自分自身のプロジェクトに応用してみてください。