AVRマイコンと7セグメント表示器でデジタル時計

AVRマイコンでデジタル時計を作ってみました。使ってる技術要素としては

  • 7セグLED表示出力
  • タイマ割り込み
  • ボタン入力

くらいで、入門で初めて作るものとしては最適かと。

開発環境

開発環境はこんな感じです。

マイコン ATMEGA168-20PU(リンク先はATMEGA168P-20PUですが)
ライタ AVRISP mk?
評価ボード ATMEGAマイコンボード・キット(液晶付)

ライタのAVRISPがISPで出力するので、それをマイコンへつなぐために、
今回は転がってたマイコンボードキットを使いました。


ソフトウェアの環境は以下のようになってます。

コンパイラなどのセット WINAVR-20100110
統合開発環境 AVRStudio 4(Version 4.18)

WINAVRは http://winavr.sourceforge.net/ からダウンロードしてきたものを、
AVRStudioはAVRISPに付属のCDのを使いました。
(これらは、特にどうでもいいですが。)

材料

材料はこんなところです。

AVRマイコン ATMEGA168-20PU 1個
7セグメント表示器 OSL40562-IRA 1個
抵抗 10Ω 4個
タクトスイッチ 青色と赤色 それぞれ2個ずつ
電池 CR2032 1個
ボタン電池基板取付用ホルダー CR2032用(小型タイプ) 1個
ブレッドボード EIC-108J 1枚
ジャンパワイヤ - 適量

7セグメント表示器は、今回はアノードコモンのものを使いました。

回路図


回路図の作成には、水魚堂ホームページで公開されているBSch3Vを使いました。
BSch3Vのプロジェクトファイルとかはこちらです。


なお、7セグメントLEDの各セグメントへ流れる際にそのセグメントのLEDをいくつ光らせるかで、
マイコンの出力ポートからの電流が2つや6つに分散します。
ですが、今回はトランジスタを用いた電流の増幅をしていないため、

  • 1を表示するときは比較的明るく(セグメントで光らせるLEDが2つ)
  • 8を表示するときは比較的暗い(セグメントで光らせるLEDが7つ)

なことになっています。

ソースコード

/***********************************
デジタル時計 Ver. 1.1 (2012/03/03 09:03)

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

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

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

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

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

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

	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] = {
	0xff - 0xfc,
	0xff - 0x60,
	0xff - 0xda,
	0xff - 0xf2,
	0xff - 0x66,
	0xff - 0xb6,
	0xff - 0xbe,
	0xff - 0xe0,
	0xff - 0xfe,
	0xff - 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] &= 0xfe;
	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 = 0xff;
	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));
	}
}

7セグメントLEDがアノードコモンタイプなので、to_num_pattern配列では
0xffから目的のパターンの差分をとっています。

完成写真

こんな感じで出来上がりました。

左から2つ目のセグメントのDPを時表示と分表示の区切りに使っています。
そして、このDPで1秒おきに明滅を繰り返すことで秒を表現しています。
(あと、「時計らしさ」も表現しています。)


また、回路図作成の際に問題点としていた
各セグメントで光らせるLEDの数に応じた明るさの変化については
実際に見てみるとそれほど気になるものではありませんでした。


また、時刻のずれについては、
電池の出力電圧が2.9Vほどへ落ちた時点でも
2時間ほど使用し続けた際に1分のずれも起こりませんでした。
(まだまだ、実際に使ってみないと分からないですが。)

今後の方針

あとは、ブレッドボード上に実装したものを、
ユニバーサル基盤上に実装します。

参考文献

AVRマイコン活用ブック―わかるマイコン電子工作

AVRマイコン活用ブック―わかるマイコン電子工作

追記

2012年3月31日
BSch3V用ファイルを追加しました。