|
谈精确定时!--转载国外网站
虽然是英文,但读起来还不是很难,我这板水平都应付了,相信您更没问题! 还是那句话写给菜鸟,大虾别见笑! How can I use my crystal for high precision timebase ? Introduction: O.K. its easy to divide exact 12.000000MHz. But how using other crystals, or correct the deviation ?
The way is always the same: First on 8051 processors, the crystal was divided to give the timer clock. On T0, T1 of 80C51 devices by 12, on the 80C52 for timer T2 by 2, also in the Dallas DS80C320 at T0, T1 by 4. Then you can use the timers to count 8 or 16 bit. The remaining divider stages can be realized in software. On using only 8 of the 128 bytes of the 8051 you can count up to 2 Giga years (the 8051 was dust, before this time was gone) with a 40MHz crystal ! In most cases you get no exact divider values. But this is not important, you must then calculate the error and can correct it, if needed. E.g. if the second differ, nobody can detect it. But if the Error was accumulated over 1 month or year, it can be seen. So correction can be done every second, minute, hour or day. So the error was not accumulated and also not detected.
Lets Practice: Following an example with assembler code:
Random choice of a crystal: 12345701 Hz (use every other crystal for your calculation)
Crystal / 12 = 1028808.417 Using Timer T0: Crystal / 12 / 65536 = 15.69837062 So you need an additional Byte in RAM to divide down to 1 second. If this byte divide by 256 you must not reload it. Then the reload value of the T0: Crystal / 12 / 256 = 4018.782878 rounded to 4019.
The resulting error is: 1 - 4018.782878 / 4019 = 0.000054 = 5 sec / day = 140 sec / month. This is to high for using as RTC. So every second we use a different T0 reload value: Crystal / 12 - 255 * 4019 = 3963.41667 rounded to 3963.
Then the resulting error is: 1 - 1028808.417 / (255 * 4019 + 3963) = 0.0000004 = 1 sec / month = 12 sec / year. This is enough for most RTC applications. But additional correction every minute can reduce it further. For extreme high accuracy also deviation over temperature was important and must be corrected.
Programming example: The C example is easiest to use, since it can make all calculations for you. You must only tell the real crystal frequency, thats all.
t0_int2.a51 t0_int2.c51 Correct deviation: To correct the deviation you need a very high accurate and also very expensive frequency meter to count the real crystal frequency with precision over more than 10 digits. The cheaper way was, let the RTC running over several months. Then the deviation can be measured in seconds to calculate the true frequency. Repeat the previous calculation with this real frequency and program the new values into the micro. E.g. you found an error of +12 seconds during 30 days. Then the true frequency was: 12.345701 Hz * (1 + 12 / (30 * 24 * 60 * 60)) = 12345758Hz. Use this to repeat the above described calculation and further the accuracy was very good.
;************************************************************************/ ;* */ ;* Software reload Example using Timer 0 */ ;* */ ;* Author: Peter Dannegger */ ;* danni@specs.de */ ;* */ ;************************************************************************/
org 0 jmp init org $+5*8+3 ;reserved for interrupt vectors
init: mov tmod, #1 ;set Timer 0 mode 1 mov tl0, #0FFh mov th0, #0FFh ;overflow on next cycle setb tr0 clr f1sec mov Divider_1s, #0 mov ie, #10000010b
main: jbc f1sec main1 ;test and clear jmp main
main1: cpl p1.0 ;change every 1 second jmp main
;Timebase 1 second at 12.345701 MHz: ; 12.3456701 MHz / 12 / (255 * 4019 + 3963) = 1 second ;Deviation: 12 seconds / year
T0_reload equ 4019 T0_sec_reload equ 3963
bseg at 0 F1sec: dbit 1
dseg at 30h Divider_1s: ds 1 cseg
curr_adr equ $
org 000Bh
jmp Timer0_interrupt
org curr_adr
Timer0_interrupt:
push psw djnz divider_1s, ?TI1 ;done 1 time every second:
clr ea ;no additional delay by other interrupts clr tr0 ;no overflow during addition xch a, tl0 add a, #low(8-T0_sec_reload) ;stop for 8 cycle xch a, tl0 xch a, th0 addc a, #high(8-T0_sec_reload) xch a, th0 setb ea ;other interrupts enabled after next instr. setb tr0 pop psw setb F1sec ;say to main: 1 second was finished ;must be cleared my main reti
;done 255 times every second:
?TI1: clr ea ;no additional delay by other interrupts clr tr0 ;no overflow during addition xch a, tl0 add a, #low(8-T0_reload) ;stop for 8 cycle xch a, tl0 xch a, th0 addc a, #high(8-T0_reload) xch a, th0 setb ea ;other interrupts enabled after next instr. setb tr0 pop psw reti
end
/************************************************************************/ /* */ /* High Precise Time Base */ /* */ /* Author: Peter Dannegger */ /* danni@specs.de */ /* */ /************************************************************************/ #pragma cd pl(30000)
#define uchar unsigned char #define uint unsigned int
#i nclude
/************************************************************************/ /* */ /* Insert your crystal frequency and you get exact 1 second */ /* without need understanding why : */ /* */ /**/ #define XTAL 12345701L /**/ /* */ /************************************************************************/
#define T0_RELOAD ((uint) (XTAL / 12.0 / 256.0 + 0.5))
#define T0_RELOAD_1S ((uint) (XTAL / 12.0 - 255.0 * T0_RELOAD + 0.5))
#define T0_STOP_CYCLES 16 // calculated with list file
bit F_1second = 0;
union bw{ int w; struct{ char h; char l; }b; };
void t0_int( void ) interrupt 1 { static uchar prescaler = 0; union bw i; if( --prescaler == 0 ){ EA = 0; TR0 = 0; i.b.l = TL0; i.b.h = TH0; i.w += -T0_RELOAD_1S + T0_STOP_CYCLES; // 1 * every second TL0 = i.b.l; TH0 = i.b.h; EA = 1; TR0 = 1; F_1second = 1; }else{ EA = 0; TR0 = 0; i.b.l = TL0; i.b.h = TH0; i.w += -T0_RELOAD + T0_STOP_CYCLES; // 255 * every second TL0 = i.b.l; TH0 = i.b.h; EA = 1; TR0 = 1; } }
|