タイマーを使う
今まで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 作成