| 本帖最后由 LBQ691477940 于 2014-7-12 16:10 编辑 
 
    相信有很多爱好单片机的朋友都用单片机制作过电子钟,这的确是一个很好的锻炼课题。可是当在你享受成功的快乐或是在朋友面前炫耀的时候,你会突然间发现你当初对着电视校准的电子钟的时间竟然变快或是变慢了。于是你就尝试用各种方法来调整它的走时精度,但是最终的效果还是不尽人意,只好每过一段时间手动调整一次了。渐渐的你有点烦了,不再去管它或是直接弃之不用。   原因分析: 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.               动态同步修正方案从程序入手,采用动态同步修正方法给定时/计数器赋初值。动态同步修正方法:由于定时/计数器溢出后又会从0开始自动加数,固在给定时/计数器再次赋值前将定时/计数器低位(TL0)中的值和初始值相加后一并送入定时/计数器中,此时定时/计数器中的值即为动态同步修正后的准确值。例如:
                  TIMER:                                                     PUSH     ACC                  PUSH     PSW                  MOV      A,   #0B0H                  ADD       A,   TL0                          ;初值和TL0中的数相加即为同步修正值                  MOV      TL0, A                     ;修正值送定时/计数器低8位                  MOV      TH0,        #03CH                  ……                                    RETI 采用了此种方法后相信你的电子钟的精度已经大大提高了。别走开,后面内容更精彩。 3.               自动调整方案采用了同步修正方案后电子钟的精度虽然提高了很多,但是由于晶振频率的偏差和一些其它未知因素(同一块电路板、同样的程序换了一片单片机后走时误差却不一样,不知是何原因)的影响,时间长了仍然会有积累误差。为此我设计出了此自动调整方案,实际也是一种容错技术。其自动调整原理为:实测出误差1秒所需的时间,然后每隔这样一段时间后就对秒进行加1或减一调整。例如:电子钟每过50小时就慢1秒,其自动调整程序如下:
                  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               ;2个机器周期                           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                              ;数据保护;2个机器周期            PUSH PSW            ;2个机器周期      CLR    C              ;1个机器周期            MOV      A,    #0B7H     ;同步修正本身应0B0但由于中断跳入和堆栈占了几个周期故为0B7            ADD       A,    TL0          ;初值和TL0中的数相加即为同步修正值            MOV      TL0, A                      ;结果赋给TL0            MOV  A,   #03CH      ADDC  A, TH0          ;如果产生进位连进位也加上            MOV      TH0,  A          ;结果赋给TH0 ;C语言为: ; CY = 0; ; TL0 += 0Xb7;        //同步修正本身应0B0但由于中断跳入和堆栈占了几个周期故为0B7 ; TH0 = 0x3C+( unsingend char )CY            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        
 |