AVR CPUのAT90USB1287で動作する1msのタイマを作成しました。16ビットタイマでは最大1024分周と考えると8MHzでは、65535 * 1024 / 8,000,000 = 8.38818(秒)しか計測できません。このため、オーバフローの割り込みを使用して、オーバフローの割り込みが発生すると、8.38818(秒)経過したとして、このオーバフロー割り込みの発生回数を記録しておきます。経過時間が必要なときは、記録された発生回数と現状の16ビットタイマー値を基に計算します。
1msタイマー関数の作成
AT90USB1287のタイマ1(TCCR1B/TCNT1)を使用します。Atmel Studio 6.2でコンパイルできることを確認しました。
StartTimerルーチン
タイマーを起動するときに1回だけ呼び出されます。
//CPUのクロック周波数を1000で割ったもの #define F_CPU_DIV1000 8000 volatile int32_t current_msec; void StartTimer() { current_msec = 0; cli(); //タイマ/カウンタ1制御レジスタAの設定 TCCR1A = 0x00; //タイマ用ピンとして使わない //タイマ/カウンタ1制御レジスタBの設定 TCCR1B = (1<<CS12)|(1<<CS10);//1024分周でタイマON //タイマ/カウンタ1割り込みマスク レジスタ設定 TIMSK1 = (1<<TOIE1);//タイマ1オーバーフロー割り込み許可 sei(); }
GetTimeルーチン
現状の経過時間を知りたいときに呼び出します。戻り値に経過時間が設定されます。タイマー値を、オーバフロー発生回数から時間を計算した「current_msec」と現状のタイマレジスタ値「TCNT1」から計算します。
int32_t GetTime() { uint8_t sreg; sreg = SREG; cli(); //現在のタイマ値を取得 uint16_t t = TCNT1; //SREGを戻す。これによって割り込み禁止状態が戻る。(SREGのIビットを戻すから) SREG = sreg; int32_t tw = (int32_t)t; return current_msec + (tw * 1024) / F_CPU_DIV1000; }
ResetTimeルーチン
タイマーをリセットします。経過時間が「0」になります。
void ResetTime() { uint8_t sreg; sreg = SREG; cli(); //タイマを0にする TCNT1 = 0; SREG = sreg; //経過時間も0にする current_msec = 0; }
EndTimerルーチン
タイマーを削除します。
void EndTimer() { TCCR1B = 0;//タイマoff }
ISR(TIMER1_OVF_vect)ルーチン
タイマ1のオーバフロー割り込み処理関数です。オーバフロー割り込みが発生するごとに経過した時間を計算して、「current_msec」に加算していきます。
ISR(TIMER1_OVF_vect) //タイマオーバフロー割り込み { current_msec += (65536L * 1024L)/ F_CPU_DIV1000; }
TimerTestルーチン
タイマー関数を試験するための関数です。10秒間、タイマーを動作させます。1秒ごとに経過した時間をメッセージに編集してを仮想シリアルポートに出力します。
「USBStream」や「CDC_Device_USBTask(CDC_Interface)」は、実装された仮想シリアルポートに対して出力するための変数あるいは関数です。詳細については、「AT90USBKEY2による仮想シリアルポート 」に示します
void TimerTest() { char str[100]; int32_t time_elapsed; int32_t prevtime = 0; //タイマ起動時に一回だけ実行 StartTimer(); fputs( "Timer 1sec\n", USBStream); CDC_Device_USBTask(CDC_Interface); prevtime = 0; while (1) { //時刻を知りたいとき time_elapsed = GetTime(); if(((time_elapsed%1000 ) == 0) && (time_elapsed != prevtime)) { prevtime = time_elapsed; sprintf(str,"%d\n",(int)time_elapsed); fputs( str, USBStream); } if(time_elapsed>10000) { break; } CDC_Device_USBTask(CDC_Interface); } //時間をリセットしたいとき ResetTime(); time_elapsed = GetTime(); sprintf(str,"%d\n",(int)time_elapsed); fputs( str, USBStream); CDC_Device_USBTask(CDC_Interface); //タイマーが不要になったとき EndTimer(); }
1msタイマー関数の動作確認
ATMEL コンパイルした1msタイマー関数と開発した仮想シリアルポートを、AVR評価ボード「AT90USBKEY2」に書き込み、1msタイマー関数を書き込んだAT90USBKEY2とパソコンと接続します。パソコン上でTeratermを実行し、AT90USBKEY2の仮想シリアルポートからのメッセージをタイムスタンプと共にログして、このタイムスタンプが1秒ごとにTeratermで受け取られていることを確認します。
Teratermのログを次に示します。AT90USBKEY2から1秒ごとにメッセージ送信されていることが確認できます。
[Wed Dec 23 07:43:14.843 2015] Timer 1sec [Wed Dec 23 07:43:14.846 2015] 0 [Wed Dec 23 07:43:15.843 2015] 1000 [Wed Dec 23 07:43:16.843 2015] 2000 [Wed Dec 23 07:43:17.843 2015] 3000 [Wed Dec 23 07:43:18.843 2015] 4000 [Wed Dec 23 07:43:19.843 2015] 5000 [Wed Dec 23 07:43:20.843 2015] 6000 [Wed Dec 23 07:43:21.843 2015] 7000 [Wed Dec 23 07:43:22.843 2015] 8000 [Wed Dec 23 07:43:23.844 2015] 9000 [Wed Dec 23 07:43:24.843 2015] 10000