トップ 新規 編集 差分 一覧 ソース 検索 ヘルプ PDF RSS ログイン

Getting Started Notes - Timer2

最終更新時間:2007年08月26日 13時12分53秒

タイマ2の使い方

 概要

タイマ2は0から$FFまでカウントできる8ビットのタイマです。タイマモードでは駆動に内蔵クロック信号を使います。タイマはポーリングモードでも割り込みモードでも使うことができます。

用語説明

MAX値
TCNT0/TCNT1/TCNT2が表現できる最大の値。8bitタイマで$FF,16bitタイマで$FFFF。
TOP値
実際の動作でTCNT0/TCNT1/TCNT2がとりうる最大の値。PWMモードや比較一致モードではMAX値より小さく制限されることがある
BOTTOM値
TCNT0/TCNT1/TCNT2がとりうる最小の値。通常は0だが、オーバーフロー割り込みなどで意図的に他の値を入れることで変更することができる。

 タイマ2のレジスタ

レジスタ名 名称
TCCR2 タイマ2コントロールレジスタ
TCNT2 タイマ・カウンタ2レジスタ
OCR2 タイマ・カウンタ2出力比較一致レジスタ
ASSR タイマ・カウンタ2非同期ステータスレジスタ

割り込み関連
レジスタ名 名称
TIFR タイマー割り込み要求フラグレジスタ
TIMSK タイマー割り込みマスクレジスタ

TCCR2のビットとその機能(AT90Sシリーズ)
bit 名称 機能
  7 予約 予約
  6 PWM2 位相基準PWM動作許可
5〜4 COM21:COM20 比較一致出力設定。下記参照。
  3 CTC2 一致クリア許可
2〜0 CS22:CS21:CS20 タイマ2のプリスケール値

TCCR2のビットとその機能(megaシリーズ)

bit 名称 機能
  7 FOC2 比較一致が起こったような動作を行わせる(*1)
  6 WGM20 WGM-bit0(*2)
5〜4 COM21:COM20 比較一致出力設定。下記参照。
  3 WGM21 WGM-bit1(*2)
2〜0 CS22:CS21:CS20 タイマ2のプリスケール値

  • *1 OC2ピンの変更のみで、割り込みは発生しない
  • *2 WGM21,WGM20の2ビットで、タイマの動作モードを決定します。詳細は「各種モード設定:WGM21-0」の項参照。

ASSRのビットとその機能

bit 名称 機能
7〜4 予約
  3 FOC2 比較一致が起こったような動作を行わせる(*1)
  2 TCN2UB TCNT2-Update Busy.
  1 OCR2UB OCR2 -Update Busy.
  0 TCR2UB TCCR2-Update Busy.

  • TCNT2が第二クロックで動かされるときは、TCNT2,OCR2,TCCR2それぞれがバッファ動作を行うため、これらの値の変更をしたい場合は確実にバッファに入っていて入力待ちである値がないことを確実にしなければなりません。そのため各々に対しTCN2UB/OCR2UB/TCR2UBというビットが用意されています。これらのビットが1の時は対応する各レジスタに書き込みしてはいけません。

TIMSKのビットとその機能(Timer2関連のみ)
bit 名称 機能
 7 OCIE2 OC2割り込み許可
 6 TOIE2 Timer2 オーバーフロー割り込み許可

TIFRのビットとその機能(Timer2関連のみ)

bit 名称 機能
 7 OCF2 Timer2比較一致割り込み要求フラグ
 6 TOV2 Timer2オーバーフロー割り込み要求フラグ

 各種モード設定

megaのモード設定:WGM21-0

2bitのWGMで動作を設定します。
WGM21:WGM20 動作 TOP OCR2更新 TOV2
00 標準動作 0xFF 即時 MAX
01 位相基準PWM 0xFF TOP BOTTOM
10 CTC動作 OCR2 即時 MAX
11 高速PWM $FF TOP MAX

AT90Sシリーズの各種モード設定:CTC2,PWM2

AT90SシリーズのAVRでは、同じ2bitですが名称が異なるビットにて以下のように設定します。高速PWMモードがありません。

CTC2:PWM2 動作 TOP OCR2更新 TOV2
00 標準動作 0xFF 即時 MAX
01 位相基準PWM 0xFF TOP BOTTOM
10 CTC動作 OCR2 即時 MAX
11 位相基準PWM 0xFF TOP BOTTOM

標準動作

このモードでは、TCNT2レジスタ値はクロックサイクル毎に1ずつ増加します。この信号はシステムクロックx回に1度生成されます。xは 1, 8, 32, 64, 128, 256, 1024 から選べます。例えば、1024を指定すればタイマ値はシステムクロック1024回ごとに1つ増やされます。

この設定(プリスケーリング)はレジスタTCCR0の下位3bitに以下の値を書き込むことで実行できます。
値と機能の対応がTimer0/Timer1と異なります。ご注意ください。

TCCR2 タイマクロック
  0 STOP
  1 CK/1
  2 CK/8
  3 CK/32
  4 CK/64
  5 CK/128
  6 CK/256
  7 CK/1024

ckはシステムクロックです。Timer0/1と異なり、外部信号をカウントするカウンタモード設定はありません。第二クリスタル発振子の信号を数える「非同期カウント動作」はありますが、これはここではなくASSRレジスタで設定します。非同期カウンタモードの項を参照ください。

TCCR2 = 7;  /* 1024分周する例 */

 ポーリングで使用

/*
このプログラムはタイマ2をポーリングで使用します。
タイマがオーバーフローしたとき、TIFRのオーバーフロービット(TOV2)がセットされ、
これを検知して変数 led を1つ増加し、PORTBに書き込んでLED点灯で表示します。
Leitner Harald 07/00 , modified by anonymous @ 2ch.net
*/
#include <avr/io.h>
#define TCNT2_BOTTOM (0x10)

uint8_t led;
uint8_t state;

int main( void )
{
    DDRB  = 0xFF;         /* PORTB全ピンを出力に */
    TCNT2 = TCNT2_BOTTOM; /* TCNT2初期化 */
    TCCR2 = 7;            /* プリスケーラはck/1024*/
    led = 0;
    for (;;)
    {
        until_bit_is_set(TIFR,TOV2);  /* TIFRのbit-TOV2がセットされるまで待ち */
        TCNT2 = TCNT2_BOTTOM;         /* TCNT2のスタート値 */
        PORTB = ~led;
        led++;
        TIFR = _BV(TOV2); /* ポーリングで使用する場合はフラグを手動でクリア
        せねばなりません。0ではなく1を書き込むことでクリアされることに注意 */
    }
}

TCCR2に7(CK/1024設定)がセットされ、タイマレジスタに初期値0x00をセットしています。TCCR0やTCCR1とは値が異なることに注意してください。システムクロック1024周期毎にTCNT2が1つ増やされます。for(;;){......} は無限ループで繰り返し動作をするためのものです。until_bit_is_set(TIFR,TOV2);は、TIFRレジスタのbit6(bit-TOV2)がセットされるまで繰り返しチェックし、セットされたら次に進むマクロです。このビットはTCNT2値が$FFからオーバーフローして$00になる瞬間にセットされるビットです。

オーバーフローが起こったら、プログラムは次の行の命令を実行し、変数 led をPORTBに出力し、その後1だけ増やされます。ここでは、さらにTCNT2に値TCNT2_BOTTOMを入れ、タイマのスタート値をセットしています。スタート値が0でも良いならこれは削除してください。

ポーリングモードでは、セットされたTOV2ビットをクリアするのを忘れないでください。このビットをクリアするには、0でなく1を書き込みます。データシートで確認してください。

 割り込みで使用

ポーリングよりも、割り込みでの使用の方がよく用いられます。この場合はTOV2ビットをずっと監視する必要はありません。オーバーフローが起こったとき、コントローラは今まで実行していたプログラムから適切な割り込みベクタアドレスに飛びます。通常そこにはジャンプ命令が書かれており、そこから割り込みルーチンへ飛びます。割り込みルーチンが終わると、元のプログラムの、割り込み発生時実行していた場所からプログラム実行を再開します。

/*
 タイマ/カウンタ0割り込みのテストプログラム
 タイマが割り込みルーチンを呼ぶたびに変数 ledがPORTBに出力され、+1されます。
 Leitner Harald 07/00 , arranged by anonymous @ 2cn.net
*/
#include <avr/io.h>
#include <avr/interrupt.h>

uint8_t led;
 ISR(TIMER2_OVF_vect)
{
    PORTB = ~led; /* write value of led on PORTB */
    led++;
}
int main( void )
{
    DDRB =0xFF; /* use all pins on PORTB for output */
    TIMSK = _BV(TOIE2);   /* タイマカウンタ2オーバーフロー割り込みを有効に*/
    TCNT2 = TCNT2_BOTTOM; /* TCNT2初期化 */
    TCCR2 = 0x07;         /* プリスケーラはCK/1024設定 */
    led = 0;
    sei(); /* 割り込み許可 */
    for (;;){}
}

割り込みルーチンはキーワード SIGNALによって導入されます。オーバーフローが発生するとすぐにこの割り込みルーチンが実行されます。割り込みを有効にするには、メインプログラムで割り込みを有効とするビットをセットし、さらにsei();を実行しなければなりません。ここではTIMSKのbit-TOIE2をセットし、sei();でSREG(ステータスレジスタ)のi-bit(全般割り込み有効)をセットすることになります。

割り込み周期の調整

オーバーフロー割り込みが掛かった時点でTCNT2の値は0ですが、オーバーフロー割り込みの中でTCNT2に値を入れると、割り込み周期が調整できます。たとえば以下のように割り込み内でTCNT2に0x10を入れると、割り込み周期はおよそ0x10/0x100=1/16だけ短くなります。プリスケーラ値が小さい場合は、割り込みが掛かってからTCNT2に値を代入するまでの時間が無視できないので計算通りに逝かないことがあることにご注意ください。

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

uint8_t led;
#define TCNT2_BOTTOM (0x10)

ISR(TIMER2_OVF_vect)
{
    TCNT2 = TCNT2_BOTTOM;
    PORTB = ~led;
    led++;
}
int main( void )
{
    DDRB =0xFF; /* use all pins on PORTB for output */
    TIMSK = _BV(TOIE2);   /* タイマカウンタ2オーバーフロー割り込みを有効に*/
    TCNT2 = TCNT2_BOTTOM; /* TCNT2初期化 */
    TCCR2 = 0x07;         /* プリスケーラはCK/1024設定 */
    led = 0;
    sei(); /* 割り込み許可 */
    for (;;){}
}

 非同期カウント動作

カウンタモードもタイマ0/1とは多少異なります。タイマ2では、以下のようになります。

  • カウントするのは外部信号ではなく、TOSC1/TOSC2につながれた第二発振子の信号です。タイマ・カウンタ2のカウンタモードではTOSC1の信号を内部クロックとは非同期にカウントしますが、ここに外部からの信号を入力することは推奨されていません。
  • カウンタ設定はプリスケーラ設定部ではなく、タイマ・カウンタ2-非同期ステータスレジスタ ASSRのbit-AS2(bit3)である。これは同時にピンTOSC1/TOSC2を第二発振子駆動に切り替える機能を持ちます。ただし、Timer2による割り込みを使う場合は、AS2の切り替えにはいささか面倒な手順が要ります。
TIMSK &= ~((1<<OCIE2)|(1<<TOIE2)); 
   // タイマ2比較一致割り込み、オーバーフロー割り込みを禁止
ASSR = (1<<AS2); // 第2発振子の信号を非同期カウントさせる
OCR2=xx; //必要ならOCR2,TCNT2,TCCR2の値設定を行う
while (ASSR&7);  // ASSRのTCN2UB, OCR2UB, TCR2UBがクリアされるのを待つ
TIMSK |= ((1<<OCIE2)|(1<<TOIE2)); //必要な割り込みを許可する
  • 上記以外にも非同期カウント故の制限がいろいろあるようです。全部書くとデータシートのコピーになりそうなので、データシートを読んで下さい〜
  • 注意:AT90S8535にはバグがあって、32768Hz水晶発振子をTOSC1/TOSC2につないだ場合、4.0V以上の電源電圧で駆動することができません。これは、低電圧・低クロック用のAT90LS8535でないとこの機能は使えないことを意味します。(AT90S8535の駆動電圧は4.0V〜6.0V)

 比較一致動作

タイマ2には比較一致出力機能が1組あります。これはOCR2レジスタとタイマ値TCNT2を比較し、比較一致時にTCNT2値をクリアしたり、ピン(OC2、PORTD.7)に信号を出力したりすることができます。

各種機能はタイマ2コントロールレジスタTCCR2で設定できます。タイマ0/1と異なり、TCCR2はプリスケール値設定以外の機能も持っている点に注意してください。

COM21:COM20によるモード選択(比較一致動作)
Com21 Com20 比較一致時ピンOC2の動作
 0  0  無効
 0  1  トグル(0なら1へ、1なら0へ)
 1  0  Lowにクリア
 1  1  Highにセット

 比較一致割り込み

比較一致時割り込みを行いたければ、TIMSKレジスタのビットOCIE2をセットしてください。比較一致時SIGNAL(SIG_OUTPUT_COMPARE2)で指定したルーチンが割り込みで呼ばれます。

/*
タイマ2の比較一致モードを利用して、PORTBに接続されたLEDを点滅させます。
Leitner Harald 07/00 , modified by anonymous @ 2ch.net
*/
#include <avr/io.h>
#include <avr/interrupt.h>

uint8_t delay;

ISR(TIMER2_COMP_vect) 
{
    PORTB = 0x00; /* turn on leds on PORTB */
}
ISR(TIMER2_OVF_vect)
{
    PORTB = 0xFF; /* turn off leds on PORTB */
    TCNT2 = delay;
}
int main( void )
{
    DDRB = 0xFF; /* define PORTB as output (leds) */
    delay = 0x10;
    TIMSK = _BV(TOIE2)|_BV(OCIE2); 
        /* オーバーフロー割り込み有効、比較一致割り込み有効 */
    TCNT2 = delay;    /* タイマ初期値(BOTTOM値) */
    OCR2  = 0x80;     /* 比較一致レジスタ設定 */
    TCCR2 = 0x07;     /* プリスケール選択 ck/1024 */
sei();
for (;;){}
}

タイマは値$10からスタートし、TCNT2==OCR2となったときにLEDが点灯されます。TCNT2がオーバーフローしたとき、オーバーフロー割り込みでLEDを消灯します。

このプログラムでは、タイマ2のコンペアモードの使用例を示します。タイマ2はmain()内で初期化され、TCNT2には初期値 delay (=$10)が与えられ、比較一致レジスタOCR2には$80が与えられています。

タイマ2は$10よりカウントアップし、$80に達し、OCR2と一致して比較一致割り込みが起こり割り込みルーチンが呼ばれます。ここでLEDが点灯されます。その後タイマ2はさらにカウントアップを続け、オーバーフロー($FF→$00)し、今度はオーバーフロー割り込みが起こります。割り込みルーチンではLEDの消灯と、次の周期のためにTCNT2の初期値(delay)設定が行われます。これにより、LEDは($80-delay)時間だけ消灯し、($80)時間だけ点灯する動作を繰り返します。

ここでは説明のためにLED点滅動作を比較一致割り込みで行いましたが、LED点滅動作だけなら後に述べるPWMモードの方が簡単です。

CTC(比較一致クリア)動作モード

比較一致時(TCNT2==OCR2)にタイマ2の値をクリアしたければ、TCCR2のビットCTCをセットしてください。これにより、タイマ2は0からOCR2値までカウントアップ後再び0からカウントアップする動作を繰り返します。

設定は、以下のように行います。

//mega シリーズ:TCCR2レジスタの ビットWGM21=1,WGM20=0
TCCR2 = (1<<WGM21)|5; //プリスケーラ設定値=5の場合
//AT90Sシリーズ:TCCR2レジスタの ビットCTC2=1,PWM2=0
TCCR2 = (1<<CTC2)|5; //プリスケーラ設定値=5の場合

割り込み周期の調整

これにより、OCR2の値を設定するだけで任意の周期の割り込みを発生させることができます。たとえば先のオーバーフロー割り込みのプログラムで、周期を0xF0カウントにしたい場合は以下のように簡単に実現できます。

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

uint8_t led;
ISR(TIMER2_OVF_vect)
{
    PORTB = ~led;
    led++;
}
int main( void )
{
    DDRB =0xFF; /* use all pins on PORTB for output */
    TIMSK = _BV(TOIE2);    /* タイマカウンタ2オーバーフロー割り込みを有効に*/
    TCNT2 = 0;             /* TCNT2初期化 */
    OCR2  = 0xF0;          //0〜F0までカウントしたらゼロリセットとする
    TCCR2 = (1<<WGM21)|7;  /* CTC動作、プリスケーラはCK/1024設定 */
    led = 0;
    sei(); /* 割り込み許可 */
    for (;;){}
}

位相基準PWMモード

PWMモードが選択されると、タイマ2は8bit free-running PWMとして動作します。タイマ2はup/downカウンタとして動作し、$00から$FFまでカウントアップの後$00までカウントダウンの動作を繰り返します。設定は、以下のように行います。

//mega シリーズ:TCCR2レジスタの ビットWGM21=0,WGM20=1
TCCR2 = (1<<WGM20)|5; //プリスケーラ設定値=5の場合
//AT90Sシリーズ:TCCR2レジスタの ビットCTC2=1,PWM2=0
TCCR2 = (1<<PWM2)|5; //プリスケーラ設定値=5の場合

カウンタ値(TCNT2)が比較一致レジスタ(OCR2)に一致した時の比較一致出力ピンOC2(PORTD.7)の動作をTCNT2のビットCOM21:COM20で選択できます。COM21:COM20によるモード選択(PWMモード時):比較一致時ピンOC2の動作
COM21 COM20 カウントアップ時 カウントダウン時 セット条件 DUTY比(*2)
 0  0  無効  無効  無効  無効
 0  1  無効  無効  無効  無効
 1  0  クリア セット TCNT2 < OCR2 OCR2/256
 1  1  セット クリア TCNT2 > OCR2 1-(OCR2/256)
*1:COM21=TCCR2のbit5、COM20=TCCR2のbit4
*2:TCNT2が0から開始される場合の値

/*
タイマ・カウンタ2のPWMモードテストプログラム
free-running PWMで動作し、$00から$FFまでカウントアップの後
$00までカウントダウンの動作を繰り返します。
比較一致時(TCNT2==OCR2)、カウントアップ時はOC2ピンはセットされ、
カウントダウン時はクリアされます。
Leitner Harald 07/00 , modified by anonymous @ 2ch.net
*/
#include <avr/io.h>
int main( void )
{
    DDRD  = 0xFF; /* use all pins on PORTD for output */
    TCNT2 = 0x00; /* start value of T/C2 */
    OCR2  = 0x19; /* OCR2の値 */
    // 位相基準PWM設定、プリスケールck/1024
    //TCCR2 = _BV(PWM2)|_BV(COM21)|_BV(COM20)|7; //AT90Sの場合
    TCCR2 = _BV(WGM20)|_BV(COM21)|_BV(COM20)|7; //megaシリーズの場合
    for (;;);
}

プリスケールは ck/1024 、PWM設定はCOM21=1,COM20=1でカウントアップ時セット、カウントダウン時クリア動作になります。OC2ピンの出力のデューティ比は1-(0x19/0x100)=およそ0.9になります。

高速PWMモード(megaシリーズのみ)

こちらのモードの場合は、TCNT2は標準動作と同様0から255までカウントアップを繰り返す動作になります。位相基準PWMに比べて、途中でOCR2の値を変更すると波形の位相がずれるという欠点がありますが、同じプレスケーラ値でより高速なPWM周波数を得ることができる利点も持ちます。このモードはAT90SシリーズのAVRにはありません。

//mega シリーズ:TCCR2レジスタの ビットWGM21=1,WGM20=1
TCCR2 = _BV(WGM21)|_BV(WGM20)|5; //プリスケーラ設定値=5の場合
#include <avr/io.h>
int main( void )
{
    DDRD  = 0xFF; /* use all pins on PORTD for output */
    TCNT2 = 0x00; /* start value of T/C2 */
    OCR2  = 0x19; /* OCR2の値 */
    // 位相基準PWM設定、プリスケールck/1024
    TCCR2 = _BV(WGM21)|_BV(WGM20)|_BV(COM21)|_BV(COM20)|7; //megaシリーズのみ
    for (;;);
}