[讨论] 关于51定时器 工作模式2 的一些看法

辛昕   2010-6-2 11:52 楼主

一点看法,如果说的不对,请大家见谅,指正!!

 

51定时器 的 模式2 有个特点就是 不用重装初值,我的单片机课本上这么写着:

“每次计满溢出,寄存器(这里指的是TL0 和 TH0,当然还有 定时器1 的初值。)全部清零,下一次定时计数还要重新装入初值,这样不仅在编程时麻烦,且影响定时时间精度。”

                     ——这本书,是我们老师自己编写的课本,所以就不写出书目信息了。

 

而我们老师似乎还真是这么做的,在他编写的 实验手册 里,定时器的实验,他还是这么做的。

1秒钟的定时,他把初值设为 100个机器周期,然后软件计数1万次。

 

做实验的时候我没留意,这两天在编一个类似的程序时,我学习了这个思路,但我却发现,这个思路,在大多数情况下,恰好没有 他所说 的这两个优点:编程简单,定时精度高。

 

我们就以这个1秒钟的定时来分析一下吧:

 

首先,因为要软件计数10000次,所以这个计数初值需要两个8位单元存储,就是高低位。(这是麻烦的根源。)

而在中断中,我们需要不断地减1,但是因为,这是高低两位,涉及到低位为0要借高位,所以,不仅麻烦,同时增加了中断程序的执行周期。

我们的实验是这么干的:

      下面的HIGH是计数初值的高8位;   

       LOW是计数初值的低8位,这里我没有实际试一下在编程中用这两个字,因为它们好像是关键字。

       这里只是为了明确意义。  

TIMER EQU  10000

HIGH   EQU   30H

LOW    EQU   31H          

-----------------------------           

             MOV   A,  LOW;  (2)

            JNZ   GOON;            (2)

            DEC  HIGH;              (2)

GOON:

            DEC  LOW;            (2)

            MOV  A, LOW;          (2)

            ORL   A, HIGH;         (2)

            JNZ    EXIT;              (2)

            MOV  HIGH,#HIGH(TIMER);    (3)

            MOV  LOW,#LOW(TIMER);     (3)

   EXIT:RETI;                                        (1)

 

 

  每句代码后括号里的数字,是以MCS-51标准查到的 相应代码的指令周期。

  也就是说,在中断程序中,要完成计数这一部分的功能,它就需要攻击16个指令周期(最后一次,加重装计数初值,是22个)。

  

   我们知道,定时过程是这样的:定时器完成0到255个机器周期的定时时间,然后进入中断,完成一次计数,然后进入下一个计数循环,也就是说,在这个过程中,我们往往忽略了一个重要的问题——中断程序的执行周期也在每一次计数的执行时间里,也就是说,我们每次定时器定时100个机器周期,但执行起来,实际是100+。

   而且,对于 定时模式2 ,因为最大定时只有255个,也就是说,上述这个计数的方法,我们耗掉了22个,这里的定时误差就已经达到了10%。

  另外,进了中断,我们可能还要完成别的功能,这就意味着误差会更大。

   最核心的问题仍然是 最大定时个数太少,只有255个,只要有20个执行周期就可以达到10%左右,可是,20个执行周期意味着什么呢?

  意味着10来条指令,这是很有限的。

 

  前面说过,分成高低两位的计数初值是麻烦的根源,前面说的是它占用了不少的实现计数功能的执行周期以外,它还很麻烦:

   因为最大定时初值只有255个,一般我们取250个吧,(4×250=1000)

   所以,对于一般的定时要求,比如1秒钟,我们的计数初值就要4000,如果时间再长点,可能要用到三个8位,那事情会更麻烦,而且误差也会更大。

   当然,今天我想起了一个以前看到的别的方法,比如说,定时5秒,做一个1秒钟的定时子程序(就是上面的那个程序作为子程序)。

   然后用DJNZ 循环计数,这样的话,虽然可能还是用3个8位,但是误差还是跟用两位计数初值一样。但是,到底是麻烦。

 

   而用定时模式1,我们也来算一算,我们会发现,这个方法简单,而且定时精度其实要比模式2更好:

    这时,中断程序中的计数很简单:

 

这里TIMER是定时初值,一般我取50毫秒,5万

----------------------------

    DEC   R0;                                            (2)

    MOV   A,R0;                                         (1)

    JNZ   EXIT;                                          (2)

 //MORE CODE FOR OTHER FUNCTION;

    MOV  TL0,#LOW(TIMER);                  (3)

    MOV  TH0,#HIGH(TIMER);                 (3)

EXIT:RETI;                                              (1)

 

 这里,总共只有6个机器周期,(在满一秒后,是12个机器周期)。

就算是12个周期,重要的事实是,每次的定时长度是50000个周期,相比而言,只有万分之6的误差。

 

同样的,它对于 中断程序 的执行周期也不像 模式2 那样敏感。前面说过,只有20个机器周期,就可以引起10%左右的误差,而在模式1里,500个执行周期,也才引起1%的定时误差,500个机器周期,至少也是500/3=167条指令。

 

因此我想这么总结一下:

在定时长度较小(至少小于65535个机器周期吧。否则就得高低两位计数初值。)

另外,中断程序还要比较小。这种情况下,使用模式2,会比较方便,引起的定时误差也不大。

 

但是,大家是否觉得这两个条件比较苛刻呢?

其实,仔细想想

模式1也就是要重装初值,也就是两条指令,六个执行周期,而且是50000个周期之后,才多了这6个执行周期,你真的那么介意吗?

 

 

  

 

[ 本帖最后由 辛昕 于 2010-6-2 11:56 编辑 ]
强者为尊,弱者,死无葬身之地

回复评论 (10)

定时中断设定时钟的基数,可产生定时的脉冲,用这个脉冲加减变量可以得到控制需要的定时。我是这样处理的。
一生只做一件事
点赞  2010-6-2 16:08
这是我做的一秒定时,晶振用11.0592MHz。
main:                                                        mov tmod,#22h        ;设置定时器工具模式(定时器0、1均工作于模式2,自动重装方式)
                                mov th1,#00
                                mov tl1,#00
                                                                mov r1,#200
                                                                mov r2,#18

其它程序略

time1_INT:               
                                djnz r1,back_time1_int
                                mov r1,#200
                                djnz r2,back_time1_int
                                mov r2,#18
                                                                                  其它处理程序。
                                         reti
关于“这里,总共只有6个机器周期,(在满一秒后,是12个机器周期)。”这个问题,你是一次有6个误差,多了以后就会产生累计误差。还有如果中断不能及时响应,那么误差就会更多。自动重装的话即使你没有及时响应的话也没关系,它会自动帮你重装。

当然,中断里的程序不要做得太长,否则就会产生“掉包”。

[ 本帖最后由 huchuan987 于 2010-6-8 00:08 编辑 ]
点赞  2010-6-8 00:04

回复 沙发 Lostsun 的帖子

你说的这个,是基本方式。
而我讨论的重点是在 这个基本方式上,对比不同定时长度下,如何选择四种模式中的哪一种,使程序更简便,更精准。
强者为尊,弱者,死无葬身之地
点赞  2010-7-13 12:42
LS这个,,怎么回事?
他做什么了?
强者为尊,弱者,死无葬身之地
点赞  2010-7-21 09:47

回复 5楼 two0426 的帖子

第一次发现这么帅的帖。。。
我的博客
点赞  2010-7-21 09:49
怎么看着...... 本帖最后由 ahshmj 于 2014-6-12 09:29 编辑
点赞  2014-6-12 08:52
心仪同学,你对51单片机工作模式2理解的想法和思路不符合单片机实际工作思路。

首先可以确定的是,TIME工作模式2自动重装初值不会因为你在TIME中断中执行程序时间的长短而影响定时精度,除非你在TIME中断中所执行的指令时间超过了TIME的一次定时周期,那定时就不准了。

我们知道,从定时时间“到”开始,到进入定时中断虽然需要3~8个机器周期,但没关系,只会导致这一次和那一次两次中断时间不准,但是若干次定时器中断周期的平均值还是非常准的。另外,正如你说的,在中断中执行了那么多条指令,也没有关系,因为在执行定时器中断中的指令的时候,定时器仍然在计数,所以这也不影响定时时间。总的定时误差就是这3~8个机器周期,这一次是3到8个,下一次也是3~8个,那么这一次的第8个和下一次的第8个之间的时间差正好就是你设定的计时周期。所以,除了第一次终端有误差3~8,以后大家都3~8反而没有误差了。

用汇编和C编写程序都不会有误差,误差只发生在第一次定时周期内。

这么说吧,如果你的晶振精确度很高,假设一丁点儿误差也没有,那么采用定时器方式2定时工作10年也不会误差1mS的。

定时器方式2唯一不好的就是要频繁的产生中断,这不适合低功耗工作,但是定时精准度却仅仅取决于晶体振荡器的精确度。


可能我没有向你解释的很清楚或者还不能理解,但是你只要相信我说的就可以了。我也知道没有被说服之前很难被承认。那就当这是老人言吧。
相信我。













点赞  2014-6-12 20:35
不过你的这个汇编程序看不懂,为什么有一条
            ORL   A, HIGH;
点赞  2014-6-12 20:56
学习了,还没考虑这么多。只知道用定时器方式2精度高。没注意其他问题
点赞  2014-6-16 10:31
嗯,这就是为毛我现在挺喜欢用库来开发的原因,底层的东西我真的不想自己去作孽了~
点赞  2016-6-15 15:35
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 京公网安备 11010802033920号
    写回复