amobbs.com 阿莫电子技术论坛

标题: STC 单片机使用定时器 定时1S 求助! [打印本页]

作者: pdenjoh    时间: 2012-4-17 16:25
标题: STC 单片机使用定时器 定时1S 求助!
    请问各位朋友,怎么我用STC单片机定时器 来定时 1S 怎么误差这么大, 10 多分钟下来相差了 10 多秒。
我使用的晶振是 18.432 M 的,请问各位如何才能更精确的定时到 1S。

下面是我的代码
定时器初始化部分还是使用官方的软件计算的。
void Timer0Init(void)                //10毫秒@18.432MHz
{
        AUXR &= 0x7F;                //定时器时钟12T模式
        TMOD &= 0xF0;                //设置定时器模式
        TMOD |= 0x01;                //设置定时器模式
        TL0 = 0x00;                //设置定时初值
        TH0 = 0xC4;                //设置定时初值
        TF0 = 0;                //清除TF0标志
        TR0 = 1;                //定时器0开始计时
}


中断部分

void Timer0_ISR(void) interrupt 1
{
        static timer_ms = 0;

        TL0 = 0x00;                //设置定时初值
        TH0 = 0xC4;                //设置定时初值

        timer_ms ++;
        if (timer_ms >= 100)
        {
                timer_ms = 0;
                sys_timer.timer.S ++;                                                                        /*-- 秒--*/
               
                if (sys_timer.timer.S > 59)
                {
                        sys_timer.timer.S = 0;
                        sys_timer.timer.M ++;                                                                /*-- 分--*/
                       
                        if (sys_timer.timer.M > 59)
                        {
                                sys_timer.timer.M = 0;
                                sys_timer.timer.H ++;                                                        /*-- 时--*/

                                if (sys_timer.timer.H > 23)
                                {
                                        sys_timer.timer.H = 0;
                                }
                        }
                }
        }
}


哪位朋友能帮忙看一下。谢谢!
作者: ourdev850725    时间: 2012-4-17 16:37
你要精确到什么程度呢?要求不是太高可以慢慢调程序校准,如果要求高只能用时钟芯片了
作者: rclong    时间: 2012-4-17 16:45
这样长时间产生的累积误差无法修正
应该采用RTC
作者: usk5yenj4id04dm    时间: 2012-4-17 16:46
  1.         TL0 = 0x00;                //设置定时初值
  2.         TH0 = 0xC4;                //设置定时初值

复制代码
在ISR里面这样处理是有误差的. 因为这时候TL0/TH0已经不是0

作者: pdenjoh    时间: 2012-4-17 16:47
本帖最后由 pdenjoh 于 2012-4-17 16:49 编辑
ourdev850725 发表于 2012-4-17 16:37
你要精确到什么程度呢?要求不是太高可以慢慢调程序校准,如果要求高只能用时钟芯片了 ...


不知道24小时的误差控制在1分钟内能否实现,对于这个精度应该不算太高吧?关于时钟芯片,应为某些原因暂时不考虑使用了。

手误,打错字了,修改了一下。
作者: zhenglu891028    时间: 2012-4-17 16:48
直接采用时钟芯片
作者: pdenjoh    时间: 2012-4-17 16:54
usk5yenj4id04dm 发表于 2012-4-17 16:46
在ISR里面这样处理是有误差的. 因为这时候TL0/TH0已经不是0

我使用的是 STC12C5A60S2 如果使用自动重装,好像只有8位的。
不知道是否会加大误差?
作者: Lavind    时间: 2012-4-17 16:56
用C语言校准很蛋疼的。。。我觉得需要根据反汇编出来的代码调整(这个我只是猜测,没有试过)
涉及到精确的计时,我都是用汇编。。。

作者: ourdev850725    时间: 2012-4-17 17:28
pdenjoh 发表于 2012-4-17 16:47
不知道24小时的误差控制在1分钟内能否实现,对于这个精度应该不算太高吧?关于时钟芯片,应为某些原因暂 ...

如果要求这么精确,最好还是放弃吧,晶体受温度变化频率会有一点的偏差,时间长了累计的误差也很大的,就算误差可以做到24小时内不超过1分钟,可是你怎么去测试这个时间?太浪费费时间了,要不停的修改代码再测试时间误差. 如果用32.768K的晶体应该就可以达到要求,但是运行速度就太慢了,看你实际应用能否满足要求了
作者: zwx.glacier    时间: 2012-4-17 20:48
12M晶振好一些的
作者: millwood0    时间: 2012-4-17 21:22
这样长时间产生的累积误差无法修正


bingo!

这样长时间产生的累积误差无法修正


yes, it can be corrected, and quitely easily so.

如果使用自动重装


no need for auto-reloading.

google "black roman zero error"

作者: zhuxm    时间: 2012-4-17 21:32
中断中设定定时初值时,可以先关定时器,设定后再开定时器,设定值在计算值基础上加12(根据精度调节),另外如果其他方面能接受,10ms中断改成40ms中断,定时装载的误差可以更小。另外普通晶振稳定度是100ppm,极限误差2个多小时1秒。
作者: pdenjoh    时间: 2012-4-18 08:32
本帖最后由 pdenjoh 于 2012-4-18 09:22 编辑
zhuxm 发表于 2012-4-17 21:32
中断中设定定时初值时,可以先关定时器,设定后再开定时器,设定值在计算值基础上加12(根据精度调节),另 ...


刚刚试了一下 40 ms 中断,误差稍有减小,
但是如果把单片机丢到温度变化大的设备上去,恐怕精度会随着温度像 股市一样啊。

如果不是老板抠门,早就直接用时钟芯片消灭这个问题了。
作者: cc224    时间: 2012-4-18 10:07
晕死,用时钟芯片精度并不见得会高,你现在用的也是晶振,时钟芯片上用的也是晶振
精度高低完全取决于所用晶振的精度
如果你用的晶振精度高于时钟芯片上的晶振,你完全可以获得更好的计时精度
软件上完全可以做到没有误差,也没有什么累积误差
在中断中重设定时器初值的方法不是很好,需要修正,还要防止更高级中断打断,用c语言写程序时不方便修正等等
你完全可以用16位方式,不需要重新设置初值
这样一来定时器中断频率是18432000/12/256/256=6000/256
也就是说6000次循环时间是256秒
你在你的中断函数中设置一个静态变量,每次加上256,发现大于6000就减去6000,同时秒数加1
这样的程序不管是c还是汇编都很容易实现,也没有累积误差
也不需要特殊调整
作者: BXAK    时间: 2012-4-18 11:08

单片机定时器中断时间误差的分析及补偿

来源:微计算机信息


1 前言

单片机内部一般有若干个定时器。如8051单片机内部有定时器0和定时器1。在定时器计数溢出时,便向CPU发出中断请求。当CPU正在执行某指令或某中断服务程序时,它响应定时器溢出中断往往延迟一段时间。这种延时虽对单片机低频控制系统影响甚微,但对单片机高频控制系统的实时控制精度却有较大的影响,有时还可能造成控制事故。为扩大单片机的应用范围,本文介绍它的定时器溢出中断与CPU响应中断的时间误差、补偿误差的方法和实例。

2  误差原因、大小及特点

产生单片机定时器溢出中断与CPU响应中断的时间误差有两个原因。一是定时器溢出中断信号时,CPU正在执行某指令;二是定时器溢出中断信号时,CPU正在执行某中断服务程序。

2.1. CPU正在执行某指令时的误差及大小

由于CPU正在执行某指令,因此它不能及时响应定时器的溢出中断。当CPU执行此指令后再响应中断所延迟的最长时间为该指令的指令周期,即误差的最大值为执行该指令所需的时间。由于各指令都有对应的指令周期,因此这种误差将因CPU正在执行指令的不同而不同。如定时器溢出中断时,CPU正在执行指令MOV  A, Rn,其最大误差为1个机器周期。而执行指令MOV  Rn,  direct时,其最大误差为2个机器周期。当CPU正在执行乘法或除法指令时,最大时间误差可达4个机器周期。在8051单片机指令系统中,多数指令的指令周期为1~2个机器周期,因此最大时间误差一般为1~2个机器周期。若振荡器振荡频率为fosc,CPU正在执行指令的机器周期数为Ci,则最大时间误差为Δtmax1=12/fosc×Ci(us)。例如fosc=12MHZ,CPU正在执行乘法指令(Ci=4),此时的最大时间误差为:
Δtmax1=12/fosc×Ci=12/(12×106)×4=4×10-6(s)=4(μs)

2.2 CPU正在执行某中断服务的程序时的误差及大小

定时器溢出中断信号时,若CPU正在执行同级或高优先级中断服务程序,则它仍需继续执行这些程序,不能及时响应定时器的溢出中断请求,其延迟时间由中断转移指令周期T1、中断服务程序执行时间T2、中断返回指令的指令周期T3及中断返回原断点后执行下一条指令周期T4(如乘法指令)组成。中断转移指令和中断返回指令的指令周期都分别为2个机器周期。中断服务程序的执行时间为该程序所含指令的指令周期的总和。因此,最大时间误差Δtmax2为:
Δtmax2=(T1+T2+T3+T4)12/fosc=(2+T2+2+4)12/ fosc=12(T2+8)/ fosc

若设fosc=12MHZ,则最大时间误差为:
Δtmax2=12(T2+8)/ fosc =12(T2+8)/12×106=(T2+8)×10-6(s)=T2+8(μs)。

由于上式中T2一般大于8,因此,这种时间误差一般取决于正在执行的中断服务程序。当CPU正在执行中断返回指令RETI、或正在读写IE或IP指令时,这种误差在5个机器周期内。

2.3 误差非固定性特点

定时器溢出中断与CPU响应中断的时间误差具有非固定性特点。即这种误差因CPU正在执行指令的不同而有相当大的差异。如CPU正在执行某中断服务程序,这种误差将远远大于执行一条指令时的误差。后者误差可能是前者误差的几倍、几十倍、甚至更大。如同样只执行一条指令,这种误差也有较大的差别。如执行乘法指令MUL AB 比执行MOV  A, Rn指令的时间误差增加了3个机器周期。这种误差的非固定不仅给误差分析带来不便,同时也给误差补偿带来困难。

3 误差补偿方法

由于定时器产生溢出中断与CPU响应中断请求的时间误差具有非固定性,因此,这种误差很难用常规方法补偿。为此,本文介绍一种新方法。现介绍该方法的基本思路、定时器新初值及应用情况。

3.1 基本思路

为使定时器溢出中断与CPU响应中断实现同步,该方法针对中断响应与中断请求的时间误差,对定时器原有的计数初值进行修改,以延长定时器计数时间,从而补偿误差。在该方法中,当定时器溢出中断得到响应后,即停止定时器的计数,并读出计数值。该计数值是定时器溢出后,重新从OOH开始每个机器周期继续加1所计的值。然后,将这个值与定时器的停止计数时间求和。若在定时器原计数初值中减去这个和形成新计数初值,则定时器能在新计数初值下使溢出中断与CPU响应中断实现同步,从而达到误差的补偿要求。

3.2 定时器新计数初值

若定时器为计数方式,操作方式为1,则计数器初值X0=216-t0×fosc/12。式中fosc为振荡器的振荡频率。t0为需要定时的时间,也为中断的间隔时间。X0为定时器原计数初值。在对定时器溢出中断与CPU响应中断时间误差进行补偿时,定时器的新计数初值X1为:
X1=216-t3× fosc/12
t3=t0+t1+t2

式中t0为中断间隔时间。t1为定时器停止计数时间,该时间为定时器停止计数到重新启动计数之间所有程序指令周期数的总和。t2为定时器溢出中断后,重新从OOH开始直至计数器停止时计的值。在误差补偿中,若将定时器计数初值X1取代X0,则可使定时器下次的溢出中断与CPU响应中断实现同步。

3.3 实例

要求补偿定时器每1ms产生一次溢出中断时的中断响应延迟的误差。若振荡器振荡频率fosc=12MHZ,定时器工作在计数方式,工作模式为1,则补偿中断响应时间误差时的定时器新初值X1为:
     X1=216-t3× fosc/12=216-(t0+ t1)- t2=216-(1000+ 13)- t2

误差补偿程序为:
……
0    CLR   EA                     ;关CPU中断
1    CLR   TRi                     ;停止定时器计数
2    MOV   R0,  #OOH             ;R0清零
3    MOV   R0,  #LOW(216)       ;定时器最大计数值的低8位送R0
4    MOV   A,   R0
5    SUBB   A,   #LOW(1000+13)   ;216的低8位减去( t0+ t1)的低8位送累加器A
6    SUBB   A,   TLi               ;216的低8位减去( t0+ t1+ t2)的低8位送TLi
7    MOV  TLi,   A                 
8    MOV  R0,   #OOH              ;R0清零
9    MOV  R0,   #HIGH(216) ;216 的高8位送R0
10   MOV  A,    R0                       
11   SUBB  A,    #HIGH(1000+13)  ;216的高8位减去( t0+ t1)的高8位送A
12   SUBB  A,    THi      ;216的高8位减去( t0+ t1 +t2)的高8位送A
13   MOV  THi,   A
14 SETB  TRi   ;重新启动定时器
      ……

在上式和上段程序中,由于fosc=12MHZ,中断间隔时间为1ms,因此t0的机器周期数为1000。由于第1条指令到第14条指令的指令周期的机器周期数之和为13,因此,t1为13个机器周期。CPU虽在执行第一条指令CLR  TRi后停止定时器计数,但在TLi、THi中分别保存了t2的低位数据和高位数据。

4 结束语

由于本文介绍的误差补偿方法能对定时器溢出中断与CPU响应中断的非固定性时间误差进行有效补偿,因此,该方法对于提高高频控制系统实时控制精度和扩大单片机应用范围都有较高的实用价值。


作者: pdenjoh    时间: 2012-4-21 10:45
非常感谢各位的帮助,最后决定先测试着,以后再加时钟芯片、

作者: liweiqiang668    时间: 2012-4-21 20:15
本帖最后由 liweiqiang668 于 2012-4-21 20:19 编辑

如果把时,分计数处理部份放在主程序中会不会好点,因为中断计数达到100次后,要多次查询时、分的变量和更新写入,多少会有影响的。
作者: pdenjoh    时间: 2012-4-23 16:22
liweiqiang668 发表于 2012-4-21 20:15
如果把时,分计数处理部份放在主程序中会不会好点,因为中断计数达到100次后,要多次查询时、分的变量和更 ...

应该会稍微改善一点点,但是现在已经不理会这个问题了,先用着,以后再改用时钟芯片,非常感谢各位的帮助!
作者: fsaok    时间: 2012-4-24 04:59
14L正解,,
作者: amen    时间: 2012-11-21 10:07
先MARK了  慢慢看
作者: linbin250    时间: 2012-11-21 15:12
pdenjoh 发表于 2012-4-17 16:47
不知道24小时的误差控制在1分钟内能否实现,对于这个精度应该不算太高吧?关于时钟芯片,应为某些原因暂 ...

24小时的误差控制在1分钟内 我认为可以实现,我曾经用MSP430计时,12小时慢了15秒。因为我觉得太不准了,再没有做下去。但是控制在1分钟以内,应该可以。
作者: adcr    时间: 2012-11-21 15:22
本帖最后由 adcr 于 2012-11-21 15:35 编辑

不要在中断体内改变定时器的值。
你要1秒时间也好办,做个8位自动装载模式的定时器,算好晶振频率,让它每次进中断正好多少分之一秒……
这种方法理论上是无误差的,实际晶振的水平能做到每天多少秒吧

给你个例程:
/*选择晶振*/
#define XTAL 11059200

void main(void)
{
        TMOD=B00000010; //定时器0开          均为8位模式
        PCON=SMOD_; /* SMOD=1 */
        TH0=0;TR0=1;IE=B10000010;                         //打开定时器0及中断
        while(1);
}
/**************************************************************************************************************/
void time0() interrupt TF0_VECTOR using 1
{
ss++;          //秒记单元3600为一秒
if (ss==3600){ss=0;sec++;if (sec==60){……}}
}
作者: 饭桶    时间: 2016-12-4 08:33
抛开晶体自身频率漂移,定时器自动重装应该是没有误差的,对不对?
作者: ayumi8    时间: 2016-12-4 09:54
直接 某宝 买那种拆机的  DS3231  可以设置发生1HZ   准到没朋友  我买过好像是   1块钱一片
作者: LearningASM    时间: 2016-12-5 09:48
LZ,不是我想说你,自己看图吧

作者: 小李非刀    时间: 2016-12-6 13:52
没有问题,精度决定于你的晶振精度,如果你使用0.1ppm的外部有源晶振,年误差不超过30秒。

方案1:
定时器设置为8位自动重装,12T模式,重装值为0,则定时器中断率为18432000/12/256=6000HZ,则中断里计数6000次就是1秒。

方案2:
定时器设置为16位手动重装,12T模式,重装值为65536-4096=0xf000,即中断中只重装TH0=0xf0,不要改变TL0的值,则定时器中断率为18432000/12/4096=375HZ,则中断里计数375次就是1秒。

作者: hnzlf    时间: 2017-2-8 22:06
正在学习中,标记下
作者: Percychiu    时间: 2017-2-9 00:13
看到有点小激动,几年前的问题现在还有人慷慨地解答!




欢迎光临 amobbs.com 阿莫电子技术论坛 (https://www.amobbs.com/) Powered by Discuz! X3.4