タイマーを使う

今までforループを使った,wait()関数を使っていましたが,これはあまり正確ではありません.単位がどれくらいなのか,経験的にしか分かりませんし,コンパイルする環境によって,待ち時間がかわったりします.

今回使うMEGA88には3つの独立したタイマーがあり,一定時間ごとに決められた動作をすることが出来ます.タイマー0~2がありますが,0と1は,それぞれモータとサーボに使っているので,残っているのはタイマー2です.

出来ること

これらを組み合わせると,一定のタイミングでプログラムを動作させることができます.

とりあえず,詳しい使い方は抜きにして例だけ書きます.

オーバーフロー割り込み

タイマーを使って決められた動作をするために,割り込みという機能を使います.割り込みを使うと,イベントが起きた瞬間にその動作をさせて,終わったらもとの処理に戻るということが簡単にできます.

割り込みを使うには,「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;
}

というような関数を作ると,間に色々な処理を入れても,前回からの待ち時間を一定にできるので,ループの周期を一定に保つときは便利かもしれません.

この文書の履歴

Copyright © 瓶詰堂 all rights reserved.