7セグ時計をATMEGA168Pに対応

前回まででは、ATMEGA168を使用していました。
ですが、今は「ATMEGA168P」のほうが主流な様で、秋月電子でも千石通商でもATMEGA168ではなくATMEGA168Pを取り扱っているようです。


予備として買ってあるのもATMEGA168Pなので、これに対応させておきたいです。

ソースコード

そんなわけで、以下のようにプログラムを修正してみました。

/***********************************
デジタル時計 V3.0 (2012/03/18 11:55)

ATMEGA168P対応
割り込み周波数を1にして、
タイマ割り込み自体が1秒ごとに発生するように設定してみた。
ちょっとみたところうまくいっているようではあるが、
未だ厳密に調べてはいない。

参考:
AVR タイマー実験
http://www9.plala.or.jp/fsson/NewHP_elc/AVR/Avr_Tmr0_OVF.html

ユアネーム・7セグ・12セグフォント大全集
http://www.yourname.jp/soft/digitalfonts-20090306.shtml

WinAVR付属のドキュメント
C:/WinAVR-20100110/doc/avr-libc/avr-libc-user-manual/group__avr__interrupts.html
************************************/

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

#define BTN_TH 20
#define F_CPU 1000000	/* CPUクロック周波数[Hz] */
#define PITCH 1	/* 割り込み周波数[Hz] */
#define PRESC 64	/* プリスケーラ値 (分周比) */
#define INTRCOUNT (0x10000 - ((F_CPU / PRESC) / PITCH))

unsigned int led_seg[4] = {0x00, 0x00, 0x00, 0x00};
unsigned char hour = 0, min = 0, sec = 0, cnt = 0, on_dp = 1;

ISR (TIMER1_OVF_vect)
{
	TCNT1 = INTRCOUNT;	/* タイマ1の初期値設定 */

	on_dp = !on_dp;
	sec++;
	if (sec > 59) {
		sec = 0;
		min++;
		if (min > 59) {
			min = 0;
			hour++;
			if (hour > 23)
				hour = 0;
		}
	}

/*	cnt++;
	if (cnt > 99) {
		cnt = 0;
		on_dp = !on_dp;
		sec++;
		if (sec > 59) {
			sec = 0;
			min++;
			if (min > 59) {
				min = 0;
				hour++;
				if (hour > 23)
					hour = 0;
			}
		}
	}*/
}

void timer1_init(void)
{
	TCCR1A = 0;	/* TCCR1A タイマモード */
	TCCR1B = 3;	/* プリスケーラ (64分周) */

	TIFR1 |= (1 << TOV1);	/* オーバーフローフラグをクリア */
	TIMSK1 |= (1 << TOIE1);	/* オーバーフロー割り込み許可 */
}

const unsigned int to_num_pattern[10] = {
	0xfc,
	0x60,
	0xda,
	0xf2,
	0x66,
	0xb6,
	0xbe,
	0xe0,
	0xfe,
	0xf6};

void scan(void)
{
	static unsigned char line = 0;

	PORTD = led_seg[line];

	DDRB = 1 << line;
	line = (line + 1) % 4;
}

void display(unsigned long num)
{
	led_seg[3] = to_num_pattern[(num / 1000) % 10];
	led_seg[2] = to_num_pattern[(num / 100) % 10];
	if (on_dp)
		led_seg[2] |= 0x01;
	led_seg[1] = to_num_pattern[(num / 10) % 10];
	led_seg[0] = to_num_pattern[num % 10];
}

int main(){
	unsigned char btn_cnt[4] = {0};

	/* ポートのデータ方向(入力/出力)設定 */

	DDRB = 0x00;
	DDRC = 0x00;
	DDRD = 0xff;

	/* ポートの初期値設定 */

	PORTB = 0x00;
	PORTC = 0xff;
	PORTD = 0x00;



	timer1_init();

	sec = 0;
	min = 0;
	hour = 0;
	cnt = 0;
	TCNT1 = INTRCOUNT;	/* タイマ1の初期値設定 */
	sei();



	while (1) {
		scan();

		/* M-- についてのチェック */
		if ((PINC & 0x01) == 0) {
			if (btn_cnt[0] > BTN_TH) {
				btn_cnt[0] = 0;
				cnt = 0;
				sec = 0;
				min--;
				if (min == 255)
					min = 59;
			} else {
				btn_cnt[0]++;
			}
		} else {
			btn_cnt[0] = 0;
		}

		/* M++ についてのチェック */
		if ((PINC & 0x02) == 0) {
			if (btn_cnt[1] > BTN_TH) {
				btn_cnt[1] = 0;
				cnt = 0;
				sec = 0;
				min++;
				if (min > 59)
					min = 0;
			} else {
				btn_cnt[1]++;
			}
		} else {
			btn_cnt[1] = 0;
		}

		/* H-- についてのチェック */
		if ((PINC & 0x04) == 0) {
			if (btn_cnt[2] > BTN_TH) {
				btn_cnt[2] = 0;
				cnt = 0;
				sec = 0;
				hour--;
				if (hour == 255)
					hour = 23;
			} else {
				btn_cnt[2]++;
			}
		} else {
			btn_cnt[2] = 0;
		}

		/* H++ についてのチェック */
		if ((PINC & 0x08) == 0) {
			if (btn_cnt[3] > BTN_TH) {
				btn_cnt[3] = 0;
				cnt = 0;
				sec = 0;
				hour++;
				if (hour > 23)
					hour = 0;
			} else {
				btn_cnt[3]++;
			}
		} else {
			btn_cnt[3] = 0;
		}

		display((unsigned long)(hour * 100 + min));
	}
}

主な変更点は、タイマのオーバーフロー割り込みの箇所です。


33行目あたりが、

ISR (SIG_OVERFLOW1)

から、下のように変更しています。

ISR (TIMER1_OVF_vect)

これは、ATMEGA168Pでの書き方で、「SIG_OVERFLOW1」のままだと警告が出ます。


また、オーバーフロー割り込み自体を1秒ごとに発生させて、割り込み周期は1に設定しています(24〜28行目のdefine)。
もともと割り込み周期を100にしていたのは、ATMEGA168のタイマには8ビットのタイマがあるためでした。
今回は16ビットのタイマ(タイマ1)を使用しているので、タイマ/カウンタレジスタ(TCNT1)に1秒分の値を設定できるため、
このように変更しています。
(実は、以前からタイマ1を使用しているため、割り込み周期100の設定はあまり意味はありませんでした。)

所感

ATMEGA168を使用した時ほどの動作確認はしていないのですが、
少し時間の進みが早いような気がします。


といっても、2・3時間動かした限りでは、1分もずれたりはしていませんでした。