|
如何使自制的单片机电子钟走时精准 作者
舒新生
相信有很多爱好单片机的朋友都用单片机制作过电子钟,这的确是一个很好的锻炼课题。可是当在你享受成功的快乐或是在朋友面前炫耀的时候,你会突然间发现你当初对着电视校准的电子钟的时间竟然变快或是变慢了。于是你就尝试用各种方法来调整它的走时精度,但是最终的效果还是不尽人意,只好每过一段时间手动调整一次了。渐渐的你有点烦了,不再去管它或是直接弃之不用。 我和大家一样对此深有体会,于是我开始查找翻阅资料,试图找出一个解决的好方法。终于有一天…… 废话太多——stop 原因分析: 1.
单片机电子钟的计时脉冲基准是由外部晶振的频率经过12分频后提供,采用内部的定时/计数器来实现计时功能。所以,外接晶振频率精确度直接影响电子钟计时的准确性。 2.
单片机电子钟利用内部定时/计数器溢出产生中断(12M晶振一般为50ms)再乘以相应的倍率来实现秒、分、时的转换。大家都知道从定时/计数器产生中断请求到响应中断需要3-8个机器周期(如不明白请参考其它资料),定时中断子程序中的数据入栈和重装定时/计数器的初值还需要占用数个机器周期,还有从中断入口转到中断子程序也要占用一定的机器周期。
例如: ORG 00H LJMP START ORG 0BH
LJMP
TIMER
;2个机器周期 ORG 30H START: MOV 30H, #0 MOV 31H, #0 MOV 32H, #0 MOV 33H, #0 MOV 20H, #10 MOV 21H, #2 MOV SP, #40H MOV IP, #00H MOV IE, #82H ;开EA﹑ET0 MOV TMOD, #01H ;定时器模式1 MOV TH0, #03CH ;50MS初裝值 MOV TL0, #0B0H SETB TR0 ;启动TR0 LOOP: …… TIMER: ;定時器中断子程序
PUSH
ACC
;2个机器周期
PUSH
PSW
;2个机器周期 MOV TL0, #0B0H+6+3 MOV TH0, #03CH …… RETI END 从上面的例子大家可以看出从中断入口到定时/计数器初值的低8位装入需要占用2+2+2=6个机器周期。所以我们在编程时一般会把这8个机器周期加入定时/计数器的初值。但是从定时/计数器溢出中断请求到执行中断需要几个机器周期(3-8个机器周期)我们很难确定其准确值,因此导致了电子钟计时不准。 解决方法: 1.
采用高精度晶振方案 2.
动态同步修正方案
TIMER:
PUSH ACC
PUSH PSW
MOV A,
#0B0H
ADD A, TL0
;初值和TL0中的数相加即为同步修正值
MOV TL0,
A
;修正值送定时/计数器低8位
MOV TH0,
#03CH
……
RETI 采用了此种方法后相信你的电子钟的精度已经大大提高了。别走开,后面内容更精彩。 3.
自动调整方案
TIMER:
;定時中斷程序
PUSH ACC
;數據保護
PUSH PSW
…… T_3:
INC A_1
MOV A,
A_1
CJNE A,
#50, RETI_1
;到50小时了吗?
INC S_1
;到50小时秒加1
MOV A_1,
#OOH RETI_1:
POP PSW
POP ACC
RETI 使用此方法调整较费时间,但是效果非常好,经实验一次调整可以将月误差控制在1秒左右,如按此方法再次测出误差1秒所需的天数并进行二次调整,其精度会更高。 电子钟源程序:(修改后)修改前的源程序可以到论坛<单片机技术交流>里看我发的<简单的电子钟源程序>一文
S_1 EQU
30H
;秒寄存器
M_1 EQU
31H
;分寄存器
H_1 EQU
32H
;時寄存器
A_1 EQU
33H
;自动调整寄存器
ORG 00H
LJMP START
ORG 03H
RETI
ORG 0BH
;定時中斷入口
LJMP TIMER
ORG 13H
RETI
ORG 1BH
RETI
ORG 30H START:
MOV S_1,
#0
;秒、分、時寄存器清0
MOV M_1,
#0
MOV H_1,
#0
MOV A_1,
#0
MOV 20H,
#10
;0.5秒鐘中斷次數,0.5s=500ms=50msx10
MOV 21H,
#2
;2個0.5秒即為1秒
MOV SP,
#40H
;堆棧指針設置
MOV IE,
#82H
;開定時器0中斷及總中斷
MOV TMOD,
#01H
;定時器0模式1
MOV TH0,
#03CH
;50ms初值
MOV TL0,
#0B0H
SETB TR0
;啟動定時器0 LOOP:
ACALL DISP
;調用顯示
JNB P3.4,
MT
;查詢分調整鍵
JNB P3.5,
HT
;查詢時調整鍵
AJMP LOOP MT:
;分調整
ACALL DISP
JNB P3.4,
MT
;鍵消抖
INC M_1
;分加1
MOV A,
M_1
CJNE A,
#60, LOOP
;沒到60分返回,到60分清0
MOV M_1,
#0
AJMP LOOP HT:
;時調整
ACALL DISP
JNB P3.5,
HT
INC H_1
MOV A,
H_1
CJNE A,
#24, LOOP
MOV H_1,
#0
AJMP LOOP DISP:
;顯示子程序
MOV DPTR,
#NUMTAB
;表地址送數據指針
MOV A,
M_1
;分送A
MOV B,
#10
DIV AB
;十進制調整
ADD A,
R0
;查表偏移量調整
MOVC A,
@A+DPTR
;查表
MOV P1,
A
;分十位送p1口顯示
CLR P3.2
;開分十位顯示
ACALL D1MS
;延時1ms
SETB P3.2
;關顯示
MOV A,
B
;分個位p1口顯示
ADD A,
R0
MOVC A,
@A+DPTR
MOV P1,
A
CLR
P3.3
ACALL D1MS
SETB P3.3
MOV A,
H_1
;時送A
MOV B,
#10
DIV AB
ADD A,
R0
MOVC A,
@A+DPTR
MOV P1,
A
CLR P3.0
;顯示時十位
ACALL D1MS
SETB P3.0
MOV A,
B
ADD A,
R0
MOVC A,
@A+DPTR
MOV P1,
A
CLR P3.1
;顯示時個位
ACALL D1MS
SETB P3.1
RET
;返回 TIMER:
;定時中斷程序
PUSH ACC
;數據保護
PUSH PSW
MOV A,
#0B0H
;同步修正
ADD A,
TL0
MOV TL0,
A
;重置50ms定時值
MOV TH0,
#03CH
DJNZ 20H,
RETI_1
;到0.5秒了嗎?
MOV 20H,
#10
CPL 25H.0
;取反秒點閃爍標志位
JNB 25H.0,
T_1
;標志位為0轉T_1
MOV R0,
#0
;查表偏移量寄存器置0(不顯示秒點)
AJMP T_2 T_1:
MOV R0,
#10
;查表偏移量寄存器置10(顯示秒點,秒點每秒閃爍1次) T_2:
DJNZ 21H,
RETI_1
;到1秒了嗎?
MOV 21H,
#2
INC S_1
;秒加1
MOV A,
S_1
CJNE A,
#60, RETI_1
;到60秒了嗎?
MOV S_1,
#0
;到60秒清0
INC M_1
;分加1
MOV A,
M_1
CJNE A,
#60, RETI_1
;到60分了嗎?
MOV M_1,
#0
INC H_1
;時加1
MOV A,
H_1
CJNE A,
#24, T_3
;到24小時了嗎?
MOV H_1,
#00H T_3:
;自动调整
INC A_1
MOV A,
A_1
CJNE A,
#50, RETI_1
INC S_1
MOV A_1,
#OOH RETI_1:
POP PSW
POP ACC
RETI D1MS:
;1毫秒延時
MOV R7,
#2 D_1:
MOV R6,
#250
DJNZ R6,
$
DJNZ R7,
D_1
RET NUMTAB:
DB 10H,0D3H,48H,41H,83H,21H,20H,53H,00H,01H;不顯示秒點
DB 14H,0D7H,4CH,45H,87H,25H,24H,57H,04H,05H;顯示秒點
END 由于本人接触单片机的时间不长,文章中难免有错误之处,还请大家多指教。如有其他问题可以在论坛上讨论。本文可以自由传播,但请注明作者及出处。
2007-5-31 |