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