32.768kHzの水晶発振子+タイマー1へ

セラロックのみでのPIC時計は50時間経過後に4分30秒ほど遅れていた。
さすがに2日間で5分近く遅れるのはいかがなものかと、32.768kHzの水晶発振子をつけてタイマー1でチェックすることにした。

その前に時分表示のみだったのを7セグLEDを追加して時分秒表示にした。
また、タイマー1に外部クロックを入れるために、PORTCを空けてPORTBで桁選択するように全体の配線をしなおした。
タイマー0用のコードはHI-TECH Cのタイマーのサンプルコードを流用してきてあまり理解していなかったので、タイマー1に切り替えるのに苦労した。

水晶発振子は33pFのコンデンサー2つと組み合わせて接続したけど、実際にタイマー1に割り込みが入るまではちゃんと動いているのか不安だった。
あと、T1SYNCは1が同期だと思っていたけど、1をセットすると微妙にだけど明らかに速くなる。フラグの意味を調べなおしたら0が同期だったので、0にセットしなおしたらずれは感じなくなった。

#include <pic.h>

__CONFIG(XT & WDTDIS & PWRTEN & BOREN & LVPDIS & UNPROTECT);

#define USE_TIMER_1

// 現在の時間
#define CURRENT_HOUR	4
#define CURRENT_MIN		17

#ifdef USE_TIMER_1
// タイマー1の設定
#endif // USE_TIMER_1

#ifdef USE_TIMER_0
// タイマーの設定
#define	PERIOD	1000000		// period in uS - one second here
#define	XTAL	4000000		// crystal frequency - 4MHz

#define IPERIOD	(4 * 1000000 / XTAL)	// Period of instruction clock in uSeconds

#define	SCALE	256		// Timer 0 prescaler
#define T0_TICKS 256	// Number of counts for interrupt

#define TICK_PERIOD (SCALE * IPERIOD)	// Period (uSec) of one increment of timer 0

#define	RELOADS	((PERIOD/T0_TICKS)/TICK_PERIOD)

near unsigned char reload = 0;
#endif // USE_TIMER_0

// GLOBALS
unsigned char dp;
unsigned char sec = 0;
unsigned char min = 0;
unsigned char hour = 0;
unsigned char selector = 0;

#ifdef USE_TIMER_1
void interrupt timer1itr(void)
{
	TMR1L = 0;
	TMR1H = 0x80;
	TMR1IF = 0;
	sec++;
	if (sec > 59) {
		sec = 0;
		min++;
		if (min > 59) {
			min = 0;
			hour++;
			if (hour > 23) {
				hour = 0;
			}
		}
	}
}
#endif // USE_TIMER_1

#ifdef USE_TIMER_0
void interrupt timer0_proc(void)
{
	if(reload == 0){
		// effect a change on PORTB whenever our desired period is reached.
		// Note this timing will contain a margin of error.
		if (sec & 1) {
			reload = RELOADS;
		} else if (sec & 2) {
			reload = RELOADS;
		} else {
			reload = RELOADS + 1;
		}
		sec++;
		if (sec > 59) {
			sec = 0;
			min++;
			if (min > 59) {
				min = 0;
				hour++;
				if (hour > 23) {
					hour = 0;
				}
			}
		}
	}
	reload--;
	T0IF = 0;
}
#endif // USE_TIMER_0

void delay(unsigned char t)
{
	for (unsigned char a = 0; a < t; a++) {
		for (unsigned char b = 0; b < 255; b++) {
		}
	}
}

void selectDigit(unsigned char sel)
{
	switch (sel) {
	case 0:
		RB0 = 0;
		RB1 = 1;
		RB2 = 1;
		RB3 = 1;
		RB4 = 1;
		RB5 = 1;
		break;
	case 1:
		RB0 = 1;
		RB1 = 0;
		RB2 = 1;
		RB3 = 1;
		RB4 = 1;
		RB5 = 1;
		break;
	case 2:
		RB0 = 1;
		RB1 = 1;
		RB2 = 0;
		RB3 = 1;
		RB4 = 1;
		RB5 = 1;
		break;
	case 3:
		RB0 = 1;
		RB1 = 1;
		RB2 = 1;
		RB3 = 0;
		RB4 = 1;
		RB5 = 1;
		break;
	case 4:
		RB0 = 1;
		RB1 = 1;
		RB2 = 1;
		RB3 = 1;
		RB4 = 0;
		RB5 = 1;
		break;
	case 5:
		RB0 = 1;
		RB1 = 1;
		RB2 = 1;
		RB3 = 1;
		RB4 = 1;
		RB5 = 0;
		break;
	default:
		RB0 = 1;
		RB1 = 1;
		RB2 = 1;
		RB3 = 1;
		RB4 = 1;
		RB5 = 1;
		return;
	}
}

void outputDigit(unsigned char val)
{
	switch (val) {
	case 0:
		PORTD = 0b00111111;
		break;
	case 1:
		PORTD = 0b00000110;
		break;
	case 2:
		PORTD = 0b01011011;
		break;
	case 3:
		PORTD = 0b01001111;
		break;
	case 4:
		PORTD = 0b01100110;
		break;
	case 5:
		PORTD = 0b01101101;
		break;
	case 6:
		PORTD = 0b01111101;
		break;
	case 7:
		PORTD = 0b00000111;
		break;
	case 8:
		PORTD = 0b01111111;
		break;
	case 9:
		PORTD = 0b01101111;
		break;
	default:
		PORTD = 0;
		break;
	}
}

void outputDP(unsigned char dp)
{
	if (dp) {
		RD7 = 1;
	} else {
		RD7 = 0;
	}
}

int main()
{
	// 時間設定
	hour = CURRENT_HOUR;
	min = CURRENT_MIN;
	// ポートの設定
	TRISB = 0xC0;	// 0, 1, 2, 3, 4, 5 出力
	TRISD = 0x00;	// 全部出力
	// ポートの初期化
	RB4 = 1;
	RB5 = 0;
	RB0 = 1;
	RB1 = 1;
	RB2 = 1;
	RB3 = 1;
#ifdef USE_TIMER_1
	// タイマー1の設定
	TMR1CS = 1;	// 0=内部クロック, 1=外部クロック
	T1CKPS1 = 0;
	T1CKPS0 = 0;
	T1OSCEN = 1;
	T1SYNC = 0;     // 0=同期, 1=非同期
	TMR1L = 0;
	TMR1H = 0x80;
	TMR1IF = 0;
	TMR1IE = 1;
	TMR1ON = 1;
	PEIE = 1;
	GIE = 1;
#endif // USE_TIMER_1
#ifdef USE_TIMER_0
	// タイマー0の設定
	OPTION = 0b0111;	// prescale by 256
	T0CS = 0;			// select internal clock
	T0IE = 1;			// enable timer interrupt
	GIE = 1;			// enable global interrupts
#endif // USE_TIMER_0
	while (1) {
		dp = sec % 2;

		selectDigit(99);
		outputDigit(10);
		outputDP(0);
		selectDigit(selector++);
		outputDigit(sec % 10);
		outputDP(dp);
		delay(1);

		selectDigit(99);
		outputDigit(10);
		outputDP(0);
		selectDigit(selector++);
		outputDigit(sec / 10);
		outputDP(0);
		delay(1);

		selectDigit(99);
		outputDigit(10);
		outputDP(0);
		selectDigit(selector++);
		outputDigit(min % 10);
		outputDP(0);
		delay(1);

		selectDigit(99);
		outputDigit(10);
		outputDP(0);
		selectDigit(selector++);
		outputDigit(min / 10);
		outputDP(0);
		delay(1);

		selectDigit(99);
		outputDigit(10);
		outputDP(0);
		selectDigit(selector++);
		outputDigit(hour % 10);
		outputDP(0);
		delay(1);

		selectDigit(99);
		outputDigit(10);
		outputDP(0);
		selectDigit(selector++);
		outputDigit(hour / 10);
		outputDP(0);
		delay(1);

		selector = 0;
	}
	return 0;
}

しかし、32.768kHzなんてよくまぁコンピューターで処理するのに都合のいい水晶があるんだなぁと最初は思ったけど、これで1/100秒とかを測ろうとしたら不都合だと思った。
そういう場合は2^n×10^mの周波数の発振子を捜さないといけない。まぁ、たぶん普通にあるんだろうけど。

カテゴリー: PIC, 作業, 部品 タグ: , , , , パーマリンク

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

CAPTCHA


このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください