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のログから時間を抜き出して設定する部分です。
でも、その前にボタンのチャタリング対応が先かも知れません。
こちらこそ、今後もよろしくお願いします!