セラロックのみでの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の周波数の発振子を捜さないといけない。まぁ、たぶん普通にあるんだろうけど。