PIC時計が「どうせ止まっているから・・・」と、この前見直したのを試してみた。
64×25×25×25というのを64×125×125として、プリスケールを64に、タイマー0のカウンターを256-125=131を設定して、タイマー0の関数でさらに125をカウントして、秒をカウントするようにしてテスト。
しかし、どうもあきらかに遅れる(-0.5秒/1分)。
本当はどこが悪いのかチェックしたいけど、精度が出る方法が別にあるので、機能追加の方に力をかけたいので、これで終わりにする。
この時点でのソースコードは以下。
#include <pic.h> __CONFIG(XT & WDTDIS & PWRTEN & BOREN & LVPDIS & UNPROTECT); #define USE_TIMER_0 // 現在の時間 #define CURRENT_HOUR 2 #define CURRENT_MIN 7 #ifdef USE_TIMER_1 // タイマー1の設定 #define SETTING_TIMER1_HI 0x80 #define SETTING_TIMER1_LO 0x02 #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 64 // Timer 0 prescaler #define T0_TICKS 125 // 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 = RELOADS; #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 = SETTING_TIMER1_LO; TMR1H = SETTING_TIMER1_HI; 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 0 if (sec & 1) { reload = RELOADS; } else if (sec & 2) { reload = RELOADS; } else { reload = RELOADS + 1; } #else reload = RELOADS; #endif sec++; if (sec > 59) { sec = 0; min++; if (min > 59) { min = 0; hour++; if (hour > 23) { hour = 0; } } } } TMR0 = 0x100 - T0_TICKS; 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; TMR1L = SETTING_TIMER1_LO; TMR1H = SETTING_TIMER1_HI; TMR1IF = 0; TMR1IE = 1; TMR1ON = 1; PEIE = 1; GIE = 1; #endif // USE_TIMER_1 #ifdef USE_TIMER_0 // タイマー0の設定 OPTION = 0b0101; // prescale by 111=256, 101=64 TMR0 = 0x100 - T0_TICKS; 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; }
ただいまPICで7SEGダイナミック表示時計を作っていまして、誤差に挑戦してます。 x’talは温度保障なしの19.6608MHzを使用して、TMR0フリーラン(256)で
プリスケーラは1/4です。 割り込みでは4800カウントで1秒を作り。
割り込み周期208.333です。 12時間で1秒進む誤差で現在は製作停滞ぎみです。
キャパシタもHigh側に10p、Low側に33pとかにしてますが1秒しか追い込めずに
います。 ちなみにLowに51pに交換すると発振せずです。
x’talはプリスケーラが16進なので、切りのいいHzなら誤差がひどいので
こんな切りの悪いx’talを使っています。 18.432 16.384 16.128 14.7456
12.8 9.8304MHzとかでも良いんですが、入手が困難な物も多いです。
秋月電子の12.8MHzの温度保障のオシレーターでは24時間で狂いなしでしたが、
オシレーターの値段が高いので普通の水晶振動子で挑戦です。
複数個作る予定なのでなるべくコストはダウンしたいのです。
誤差なくしたいですね。
加藤さん
コメントありがとうございます。
PIC時計の誤差はハマりますね。
切りの悪い周波数のものを使うというのは面白いです。
私は今は精度より時間合わせの機能の方が必要になっていて、そちらが中心になっていますが、何かありましたらまたコメントください。
本当に誤差にはハマりますね。
周波数なんですが、少々の誤差はキャパシタで吸収なんてキツいです。
きちんと計算で割り切れる周波数がいいようです。 32.768KHz 6.4MHz 8.192MHz
12.8NHz 16.384MHz 25.6MHz 32.786MHz あたりでは割り切れるので良いようです。
ところで、上記周波数で32.786MHzでPIC16F877Aで動かしたら動くんですヨ、
ひょっとしたら、40MHzでも動いたりして・・・・。
私のPIC時計では16F877Aで、12.8MHzと32.768MHzは誤差が24時間誤差なしは
確認は取れてますが・・・本日で19.6608MHzはあきらめました。
では、お互いがんばりましょう!!
加藤さん
やはりキリのいい周波数の方がいいですか。
PICはかなり許容範囲が広いようですね。
なんか適当な感じでも動くので気楽に手をつけられます。
私の方はようやくGPSとの通信ができるようになったので、これからGPSのログから時間を抜き出して設定する部分です。
でも、その前にボタンのチャタリング対応が先かも知れません。
こちらこそ、今後もよろしくお願いします!