Arduinoでtimerを使った割込み処理の方法

 ArduinoでTimerを使った割込み処理の方法をメモ書きしておきます。

スポンサーリンク
スポンサーリンク

概要

 割込みにもいろいろ種類(方法)がありますが、今回はTimerを使った時間割込みを行ってみます。例えば、「1秒ごと」に「LEDを点滅」といったような処理です。

 ある時間間隔(タイミング)で何かしらの処理を行います。普通にメインループの中で時間操作すれば処理できることもあるのですが、どうしても無駄時間発生するなど、いろいろ弊害がでることもあります。

 割込み処理では、処理発生のタイミングをTimerまかせにできるため、その間はほかの処理ができたり、かなり正確なタイミングを計ることができたり、と利点があります。

Timerを使用した割込み

 Timerを使用した割込み処理をおおざっぱに言うと、Timer(Arduino)が自動でステップカウントします。決められた数値までカウントされたら、関数が呼び出され処理します。と同時にカウントをリセットしてまた自動でカウント・・・。といった具合です。メインループで処理がされていても割り込んで関数が呼び出されます。処理する内容はその関数に記述するといった感じです。 

 レジスタ操作で割込み設定をするのですが今回の例のように単純な時間割込みだけならそれほど手順は多くないです。

  1. 割込みモードの選択
  2. ステップカウント速度の設定
  3. カウント値の設定
  4. 割込み時の処理内容を記述

 と基本はこの設定するだけです。以降順を追って説明していきます。

Timer0、Timer1、Timer2

 ArduinoUNO(ATmega328/328P)にはTimerが3つ準備されてます。3つのTimerは以下のような構成です。

bit数対応PWMピン使用されてる関数など
Timer085、6delay()、millis() など
Timer1169、10
Timer283、11tone()

 このような感じでArduinoでは時間に関する関数やPWMピンにTimerが既に使用されています。ですのでレジスタ触るときは既存の関数(特に時間関係の関数に)には何かしら影響がでるかも、と注意したほうがいいでしょう。

 bit数はカウンタのMax値です。8bitでは0~255、16bitでは0~65535までカウントできます。

 で今回は試しにTimer2を使用する前提でレジスタ(割込み方法)の使い方を見てみます。

スポンサーリンク

Timer割込みの設定方法(レジスタ操作)

 先ほど説明したようにTimerを使用した時間割込みの手順はそれほど多くないです。順を追ってみていきます。

①モードの選択

 まずはモードの選択です。Timer2の割込みモードに関しては「TCCRA2」「TCCRB2」の二つのレジスタでモード選択とカウント速度を設定することができます。

▼TCCRA2レジスタ▼

bit76543210
レジスタ名COM2A1COM2A0COM2B1COM2B0WGM21WGM20

▼TCCRB2レジスタ▼

bit76543210
レジスタ名FOC2AFOC2BWGM22CS22CS21CS20

となってます。モード設定は「WGM20~WGM22」bitで設定できます。

▼Timer2のモード一覧▼

ModeWGM22WGM21WGM20動作種別
0000標準モード
1001PWM動作
2010CTCモード
3011PWM動作
4100(予約)
5101PWM動作
6110(予約)
7111PWM動作

 単純な一定時間間隔の割込み処理であればmode2のCTCモードに設定します。ですので「TCCRA2」レジスタの「WGM21」bitのみを「1」にします。

 言語風に記述すると

 TCCRA2 |= b00000010;

もしくは

 TCCRA2 |= (1<<WMG21);

 などと記述すれば 「TCCRA2」レジスタ の「WMG21」ビットを「1」にできます。後者の表現が分かり易いためなのか?よく見かけますね。

 CTCモードはカウンタの上限値(カウント数値)が直接指定できるため、時間割込みするときにはこのモードが便利です。なのでこれに設定しておけば問題ないかと・・。他のモードについてはまた機会があれば説明したいと思います。

②ステップカウント速度の設定

 分周比の設定です。Timerでカウントしていくのですが、そのカウント速度(1カウントする時間間隔)の設定です。

 分周無しの場合、Arduinoのクロック周波数は16MHzのため、1カウントは1/16MHz=0.0625μsとなります。

 例えば分周比「8」とした場合は1カウントが1/(16MHz/8)=0.5μsとなります。

 分周比は「TCCR2B」レジスタの「CS20~CS22」ビットの組合わせで設定します。

 ▼分周比の組合わせ▼

CS22CS21CS22分周比
000停止(カウンタ動作停止)
001分周なし
0108分周
01132分周
10064分周
101128分周
110256分周
1111024分周

 例えば「分周8」に設定する場合はCS21ビットのみ「1」にします。

 言語風に記述すると

TCCR2B |=b00000010;

 もしくは

TCCR2B |= (1 << CS21);

 などとと記述します。

③カウンタ値の設定

 最後にいくつまでカウントしたら割込み処理を発生させるかを設定します。例えば「100」カウントで割込み発生させるならば「OCR2A」レジスタに直接数字を入れるだけです。

 OCR2A = 100;

 これだけで完了です。これで100カウント毎に割込み関数が呼び出されます。Timer2は8bitなので0~255までの数字しか指定できないので注意です・・。

 ▼で最後に割込みを許可します。▼

 TIMSK2 |= (1 << OCIE2A); 

 ここはこうするもんだと覚えてます・・・。

 ▼設定をまとめて記述すると▼

  TCCR2A  = 0;           
  TCCR2B  = 0;          
  TCCR2A |= (1 << WGM21);   //CTC mode
  TCCR2B |= (1 << CS21);    //分周8
  OCR2A   = 100;            //100カウントごと
  TIMSK2 |= (1 << OCIE2A);

 となります。これでTimer割込みの設定は完了です。最初の2行は念のためレジスタをリセットしてます。基本この内容は1度実行するだけなので、setup()内に記述すればO.K。上記の設定例であれば100カウント(50μs)毎に関数が呼び出されます。

 Timerを使用した時間割込みの設定はこれで完了です。説明の多さの割には記述は少ないです・・。で最後に割込み時の処理を記述します。

スポンサーリンク

割込み処理内容

 OCR2Aの数字とタイマーカウンタが一致して割込みが発生した場合、

ISR (TIMER2_COMPA_vect) {
    //処理内容//
}

 という関数が呼び出されます。。こういう関数が準備されているものと覚えましょう。

 ▼例えば▼

ISR(TIMER1_COMPA_vect) {
  digitalWrite(13, !digitalRead(13));
}

 こうすれば50μs毎に、UNOであればオンボードのLEDが点滅します。早すぎて点灯してるようにしか見えませんが・・・・・。

 ピンポイントにしぼった内容ですが、一定時間間隔で割込みを行うだけでよければこれだけで0.kです。Timer0やTimer2を使用する場合も基本的なことは同じです。ほかより詳しことはデータシートを参照して下さい。

 Arduinoは結構前から触っていたのですが、なぜか今まで割込み処理をする機会がありませんでした。今回割込みを処理を行う必要があっていろいろ調べたのですが、Arduino(IDE)特有の便利関数(といっていいのか・・、)がTimer割込みに関してはほとんど準備されていない??調べてもあまり情報が出てこなかったです。

 ライブラリもいくつかありましたが(自分には)ちょっと微妙な感じでだったので今回は直接レジスタ操作て割込み処理を行ってみたということです。

サンプルソース

 おまけです。以下はTimer1を使用して0.5秒置きにUnoのオンボードLED(13pin)を点滅するスケッチです。

 ▼サンプルソース▼

void setup() {
  pinMode(13, OUTPUT);

  TCCR1A  = 0;
  TCCR1B  = 0;
  TCCR1B |= (1 << WGM12) | (1 << CS12);  //CTCmode //prescaler to 256
  OCR1A   = 312500;
  TIMSK1 |= (1 << OCIE1A);
}

ISR (TIMER1_COMPA_vect) {
  digitalWrite(13, !digitalRead(13));
}

void loop() {
}

コメント