打印
[51单片机]

单片机定时器误差问题,求高手指教!

[复制链接]
4355|28
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
本帖最后由 kangdawa 于 2018-3-19 00:50 编辑

本人新手,尝试用51单片机编一个时间显示,郭天祥的板子,开始用定时器工作方式1编写,因为程序执行误差,不太准确。然后改为定时器工作方式2,本以为很正确,可是跑了大概十小时出头后出现3s误差(和电脑比较)。困惑?是晶振不稳定么?



#include<reg52.h>
#define uchar unsigned char
#define uint unsigned int
sbit dula=P2^6;
sbit wela=P2^7;
uint n;
long t;
uchar H,M,S,Hh,Hl,Mh,Ml,Sh,Sl,temp,num;
uchar code table[]={0x3f,0x06,0x5b,0x4f,
                                        0x66,0x6d,0x7d,0x07,
                                        0x7f,0x6f,0x77,0x7c,
                                        0x39,0x5e,0x79,0x71};
uchar code weixuan[]={0xfe,0xfd,0xfb,0xf7,0xef,0xdf};
void delayms(uint);
void delayms(uint xms)
{        
        int i,j;
        for(i=xms;i>0;i--)
                for(j=110;j>0;j--);
}
void time0() interrupt 1
{
        n++;
        if(n==3600)
        {
                n=0;
                t++;
                P1=~P1;
        }
}
void display()
{        
        S=t%60;
        Sl=S%10;
        Sh=S/10;
        M=(t/60)%60;
        Ml=M%10;
        Mh=M/10;
        H=t/3600;
        Hl=H%10;
        Hh=H/10;
        wela=1;
        P0=weixuan[0];
        wela=0;
        P0=0xff;
        dula=1;
        P0=table[Hh];
        dula=0;
        P0=0xff;
        delayms(1);
        wela=1;
        P0=weixuan[1];
        wela=0;
        P0=0xff;
        dula=1;
        P0=table[Hl];
        dula=0;
        P0=0xff;
        delayms(1);
        wela=1;
        P0=weixuan[2];
        wela=0;
        P0=0xff;
        dula=1;
        P0=table[Mh];
        dula=0;
        P0=0xff;
        delayms(1);
        wela=1;
        P0=weixuan[3];
        wela=0;
        P0=0xff;
        dula=1;
        P0=table[Ml];
        dula=0;
        P0=0xff;
        delayms(1);
        wela=1;
        P0=weixuan[4];
        wela=0;
        P0=0xff;
        dula=1;
        P0=table[Sh];
        dula=0;
        P0=0xff;
        delayms(1);
        wela=1;
        P0=weixuan[5];
        wela=0;
        P0=0xff;
        dula=1;
        P0=table[Sl];
        dula=0;
        P0=0xff;
        delayms(1);
}
void main()
{
        t=50580+120;
        TMOD=0x02;
        TH0=0;
        TL0=0;
        EA=1;
        ET0=1;
        TR0=1;
        P1=0xaa;
        while(1)
        {
        display();
        if(t==86400)
        {
                t=0;
        };
        };        
}

QQ图片20180319004914.png (373.01 KB )

QQ图片20180319004914.png
评论
xch 2018-3-20 17:18 回复TA
先用频率计测量你的基准没问题再说。你可以在定时中断之中设置P口翻转,这种方法测量频率不影响振荡槽路 

相关帖子

沙发
kangdawa|  楼主 | 2018-3-19 00:40 | 只看该作者
截图

使用特权

评论回复
板凳
ayb_ice| | 2018-3-19 08:35 | 只看该作者
定时器周期是多少,自由运行吗

使用特权

评论回复
地板
kangdawa|  楼主 | 2018-3-19 09:46 | 只看该作者
ayb_ice 发表于 2018-3-19 08:35
定时器周期是多少,自由运行吗

八位计时,自动装初值,晶振频率11.0592MHZ。

使用特权

评论回复
5
ayb_ice| | 2018-3-19 09:55 | 只看该作者
kangdawa 发表于 2018-3-19 09:46
八位计时,自动装初值,晶振频率11.0592MHZ。

重装载值为0表示计时周期256,这样理论上就有误差
12/11.0592*256=277.77。。。。微秒
这样肯定不行,还不如用12M晶振,计时250us

使用特权

评论回复
评论
datouyuan 2018-3-19 11:41 回复TA
不对,11.0592比12更适合计时。 能保证定时器低8为0. 
6
kangdawa|  楼主 | 2018-3-19 10:32 | 只看该作者
ayb_ice 发表于 2018-3-19 09:55
重装载值为0表示计时周期256,这样理论上就有误差
12/11.0592*256=277.77。。。。微秒
这样肯定不行,还 ...

你的这种计算方法我理解了,没问题!我又检查了我的计算方法,也感觉没问题啊,奇了怪晶振频率11.0592MHZ,意味着晶振11.0592*10^6次为一秒,即计时器总计时次数11.0592*10^6/12=921600次为一秒,即计时器总溢出921600/256=3600次为一秒。所以我就设n=3600,按照现在的现象,你的算法更站得住脚,但是我这么算误差产生在哪里呢?

QQ图片20180319102851.png (12.31 KB )

QQ图片20180319102851.png

使用特权

评论回复
7
gx_huang| | 2018-3-19 10:44 | 只看该作者
程序没有多少问题,检查一下晶体的频率是否准确。

使用特权

评论回复
8
ayb_ice| | 2018-3-19 10:48 | 只看该作者
kangdawa 发表于 2018-3-19 10:32
你的这种计算方法我理解了,没问题!我又检查了我的计算方法,也感觉没问题啊,奇了怪晶振频 ...

确实,你的也是对的,可能是晶振本身的误差吧

使用特权

评论回复
9
dirtwillfly| | 2018-3-19 11:29 | 只看该作者
你的晶振是什么精度的?
如果是100PPM级别的,误差就是那么大

使用特权

评论回复
10
datouyuan| | 2018-3-19 11:55 | 只看该作者
本帖最后由 datouyuan 于 2018-3-19 14:19 编辑

怀疑楼主的运算量在277.77uS内完成有些勉强,才造成误差的。要保证每次定时器中断产生的运算才下一次中断来之前处理完。
楼主应该设n=180,定时器用16位计时,自动重装。

另外读取全局变量t应该如下操作。
EA=0;
i=t;//i为自动变量
EA=1;

这是使用51需要注意的,用其它mcu不需要这样。

使用特权

评论回复
11
datouyuan| | 2018-3-19 12:00 | 只看该作者
我用过50ppm级别的,一周才差个10秒。
100ppm应该不会差那么多。

使用特权

评论回复
12
ayb_ice| | 2018-3-19 13:02 | 只看该作者
3/(15*3600)*1000000=55.6ppm
正常的

使用特权

评论回复
13
datouyuan| | 2018-3-19 14:34 | 只看该作者
ayb_ice 发表于 2018-3-19 13:02
3/(15*3600)*1000000=55.6ppm
正常的

对,看来主要是晶振的问题。

使用特权

评论回复
14
m564522634| | 2018-3-19 15:33 | 只看该作者
方案都不对,晶振自己本身都是有误差的,在加上定时中断后代码在中断中有执行时间上的浪费 你做计时时间一长误差的积累就会放大了,不是程序本身的问题,是你做时钟不应该用这种方案了。 建议你上专门的计时芯片做时间处理。 做一个玩具玩的话这种就可以了,给你们老师演示你们老师也不会关心10小时后的问题了。

使用特权

评论回复
15
叫啥就开始9:| | 2018-3-19 17:21 | 只看该作者
无源晶振就这个水平

使用特权

评论回复
16
ningling_21| | 2018-3-19 19:21 | 只看该作者
换有源钟振试试

使用特权

评论回复
17
coody| | 2018-3-19 19:31 | 只看该作者
排除计算误差,则晶振误差为3/(10*3600)=83ppm,50~100ppm的无源晶振差不多是这个水平。

使用特权

评论回复
18
oufuqiang| | 2018-3-19 20:37 | 只看该作者
电脑+50ppm,单片机-50ppm,100ppm就出来了。

使用特权

评论回复
19
Lbsonggz| | 2018-3-19 22:05 | 只看该作者
使用整数频率的高精度有源晶振。这个频率是用于串口通信精确产生9600,4800等波特率的,其实波特率有5~10%的误差都问题不大

使用特权

评论回复
20
linqing171| | 2018-3-19 23:05 | 只看该作者
if(t==86400)
         {
                 t=0;
         };
这句最后放中断 t++的后面。if {}后不需要分好。   防止将来任务多了,两个地方给t赋值容易出问题。


代码review了一遍,感觉像是晶振的问题。
用同一个有源晶振,给标准时钟和MCU同时供时钟,看看一天能差多少。

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

3

主题

6

帖子

0

粉丝