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分もずれたりはしていませんでした。