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