タイマーを使う
今までforループを使った,wait()関数を使っていましたが, これはあまり正確ではありません. 単位がどれくらいなのか,経験的にしか分かりませんし, コンパイルする環境によって,待ち時間がかわったりします.
今回使うMEGA88には3つの独立したタイマーがあり,一定時間ごとに決められた動作をすることが出来ます. タイマー0~2がありますが,0と1は,それぞれモータとサーボに使っているので, 残っているのはタイマー2です.
出来ること
- プログラムの動作とは無関係に一定の速度でカウント
- カウンタが溢れて0になるときに割り込み(オーバーフロー割り込み)
- 指定した値になったときに割り込み(比較一致割り込み)
- 指定した値になるとカウンタリセット(比較一致クリア)
これらを組み合わせると,一定のタイミングでプログラムを動作させる ことができます.
とりあえず,詳しい使い方は抜きにして例だけ書きます.
オーバーフロー割り込み
タイマーを使って決められた動作をするために,割り込みという機能を使います. 割り込みを使うと,イベントが起きた瞬間にその動作をさせて,終わったら もとの処理に戻るということが簡単にできます.
割り込みを使うには,「avr/interrupt.h」をインクルードする必要があります. (古いバージョンのavr-gccでは,avr/signal.hも必用かもしれません) 先頭に以下の行を加えてください.
#include <avr/interrupt.h>
レジスタの設定
タイマーを使うには,いくつかのレジスタを設定する必要があります. さらに,割り込みをつかうので,sei()で割り込みを有効にしてください.
// タイマー2割り込み設定
TCCR2A = 0;
TCCR2B = 0x03; // 32分周 (1KHz)
TCNT2 = 0; // カウンタ初期化
TIMSK2 = 1<<TOIE2; //オーバーフロー割り込み許可
sei();//割り込み有効
今回のAVRは8000KHzで動いているので,32分周すると250KHzになります. さらに,8ビットのカウンタなので,256回に一度オーバーフローします. オーバーフローで割り込みが起きるようにしているので, つまり,256/250 = 1.024msごとに,割り込みが起きます.
(比較一致の機能を使えば,正確に1msごとに割り込みを掛けることもできますが, 各自やってください)
次に,割り込み時に呼ばれる関数を書きます. 今回は,変数をインクリメント(++)しているだけです. 割り込みが起きるたびに,wait_timeの値が増加していきます.
// Timer-2 Overflow (これは古い記述です,現在はISRマクロを使います)
volatile uint16_t wait_time=0;
SIGNAL(SIG_OVERFLOW2) {
wait_time ++;
}
wait()関数
void wait(uint16_t w){
wait_time = 0;
while (wait_time<w);
}
wait_timeを0に設定して,wait_timeがw以上になるのを待つだけです. wait_timeは,タイマー割り込みで勝手に増加していきます.
プログラム例
一秒ごとにLEDを順番にONにしていって,またOFFします.
timer_test.c
////////////////////////////////////////////////////////////////////////////
// タイマー割り込み サンプル
//
#include <avr/io.h>
#include <avr/interrupt.h>
volatile uint16_t wait_time=0;
// Timer-2 Overflow
SIGNAL(SIG_OVERFLOW2) {
wait_time ++;
}
void wait(uint16_t w){
wait_time = 0;
while (wait_time<w);
}
int main()
{
int i;
// ポート初期化
DDRB = 0xFF; // モータ
DDRC = 0x00; // ボタン,センサー
DDRD = 0xFF; // モータ,LED
PORTC |= 3; // プルアップON
// タイマー2割り込み設定
TCCR2A = 0;
TCCR2B = 0x03; // 32分周 (1KHz)
TCNT2 = 0; // カウンタ初期化
TIMSK2 = 1<<TOIE2; //オーバーフロー割り込み許可
sei();//割り込み有効
//LED 点灯確認
for(i = 0; i <= 3; i++){
PORTD |= 1<<i;
wait(1000);
}
for(i = 0; i <= 3; i++){
PORTD &= ~(1<<i);
wait(1000);
}
for(;;);
}
解説
特に無し.
void wait2(uint16_t w){
while (wait_time<w);
wait_time = 0;
}
というような関数を作ると,間に色々な処理を入れても,前回からの待ち時間を一定にできるので, ループの周期を一定に保つときは便利かもしれません.
この文書の履歴
- 2006-09-01 作成