找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 7490|回复: 20
打印 上一主题 下一主题
收起左侧

关于用51单片机做时钟显示准确度的问题

  [复制链接]
跳转到指定楼层
楼主
ID:301968 发表于 2018-7-28 11:35 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
一直看到好多人说,用单片机做时间显示不准确.
其实我认为用单片机做时间显示,可以达到非常 精确的地步,比如三天误差小于1S.不知大家认为这种误差是大还是小.
当然要用外部晶体喽.
用定时器1MS中断,计算1000MS,累进一秒.
以互联网北京时间实时时间比对.
先测试24小时误差,一般初次会误差较大,几秒到十秒都有可能.误差大了,就先修改定时值.
误差小了,我们就用另外一种方法来消除误差.
比如,10小时,误差慢1秒钟,36000秒,慢一秒,就是36000MS慢1MS
定时器,1000MS,计时36次时,将MS计时数强行加1,依此原理.
如3天慢一秒,259200秒,慢1秒.
在定时器中断函数中
计算259次累进一秒时,MS强行加1
利用这种方式,可以做出非常精确的时间显示功能
谢谢大家,有好的方法,请分享给大家,互相学习!!!



评分

参与人数 1黑币 +80 收起 理由
admin + 80 共享资料的黑币奖励!

查看全部评分

分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享淘帖 顶 踩
回复

使用道具 举报

沙发
ID:357194 发表于 2018-7-28 19:56 | 只看该作者
谢谢分享
回复

使用道具 举报

板凳
ID:380012 发表于 2018-7-28 21:24 | 只看该作者
晶振会产生温漂,软件上的初尝只能解决特定环境下的时钟精度,不能解决所有的环境。
回复

使用道具 举报

地板
ID:111634 发表于 2018-7-29 16:44 | 只看该作者
单片机用定时器产生秒基准做的时钟不精确,必须用外部时钟芯片。
回复

使用道具 举报

5#
ID:619208 发表于 2019-10-4 13:52 | 只看该作者
我为啥一分钟差了十多秒#include<reg51.h>
#include "DS1302.h"
#define uchar unsigned char
#define uint unsigned int
sbit LED1 = P0^0;
sbit LED2 = P0^1;
sbit LED3 = P0^2;
uint count=0;
uint counter=0;
uint b=0;
uchar i=0;
uchar code table[10]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};         //共阴极段码。
/*void Time1_Init()
{
        TMOD = 0x60;
        TL1 = 0xFE;
        TH1 = 0xFE;
        ET1 = 1;
        EA=1;
        TR1=1;
}  */

void Time1_Int() interrupt 3  //计数器的中断。
{
          counter++;          //记录脉冲数。
}
void Time0_Init()           //定时计数器的初始化
{
        TMOD = 0x61;   //T1计数器工作方式2(自动重装初值),T0计时器工作方式1。
        TH0 = 0xD8;           //T0初值为55536,计时10ms。
        TL0 = 0xF0;
        TL1 = 0xFF;           //T1初值为FF,记录脉搏脉冲,每次都中断。
        TH1 = 0xFF;
        ET0 = 1;           //开启T0中断使能;
        ET1 = 1;           //开启T1中断使能;
        EA = 1;                   //总中断是能开关开启;
        TR0 = 1;           //T0开始计时;
        TR1=1;                   //T1可以开始计数;
}
void Time0_Int() interrupt 1          //定时器的中断。
{
        TH0=0xD8;                //重装初值。
        TL0=0xF0;
        count++;                //记录10ms个数;
}
void DelayMS(uchar x)                //普通延时;
{
        while(x--)
        {
                uint i;
                for(i=0;i<10;i++)
                {
                        ;
                }
        }
}
void Int0_Int() interrupt 0                 //外部中断0中断程序:初始化定时计数器0、1;
{               
        b++;                                                 //b自加
        if(b==1)                                         //如果b等于1,说明按钮只按了一次,开始测试。
                 Time0_Init();                         //初始化T0,T1,开始计时计数。
        if(b>1)                                                 //如果b>1,说明一次测试已完成第二次按下按钮,重新测试。
        {
                counter=0;                                 //清零示数。
                i=0;
                Time0_Init();                         //初始化T0,T1,开始计时计数。
        }
}
void main()                         //主程序
{         
        //uchar i=0;
        //Time0_Init();                  //初始化定时计数器;
        //Time1_Init();
        EA = 1;                                  //中断总使能开关开
        EX0 = 1;                          //外部中断0是能开关开
        while(1)                          //循环;
        {
                if(count==100)          //如果10ms个数为100个,即经过1s时,i自加一,count清零;
                {
                        i++;
                        count=0;
                }
        /*        P2=table[i % 10];
                P1=0x02;*/
                        P2=table[i % 10];        //P2输出显示i秒的个位数;
                        P1=table[counter % 10];
                        LED1=0;                                //P0^0点位低电平;
                        DelayMS(1);                        //延时一会;
                        LED1=1;                                //P0^0电位高电平
                        P2=table[(i / 10)%10];                                //输出显示i秒的十位;
                        P1=table[(counter / 10)%10];
                        LED2=0;                                //P0^1电位低电平;
                        DelayMS(1);                        //延时一会;
                        LED2=1;
                        P1=table[(counter/100)%10];
                        LED3=0;
                        DelayMS(1);
                        LED3 = 1;
                                                 //P0^1电位高电平;
                if(i==60)                                //当i达到60秒时,停止中断使能,计数和计时停止;
                {
                        ET0=0;
                        ET1=0;                               
                }
               
        }

}
回复

使用道具 举报

6#
ID:67199 发表于 2019-10-4 19:46 | 只看该作者
imanoob 发表于 2019-10-4 13:52
我为啥一分钟差了十多秒#include
#include "DS1302.h"
#define uchar unsigned char

先调整重装初值  TH0=0xD8;   TL0=0xF0;   再根据实测误差,加上修正值。  
        
回复

使用道具 举报

7#
ID:491577 发表于 2019-10-5 23:41 | 只看该作者
时钟精准度只是与计时用的晶振的精度有关,与时钟芯片无关,同样一个晶振,不管用什么时钟芯片或者用单片机都是一样的精度,高精度钟芯片都是自带晶振的而且带有温度补偿,所以精度高,如果单片机使用高精度晶振并且检测晶振的温度做温度补偿,一样可以做到很高的精度。楼主还缺一个温度检测,要标定晶振在不同的温度下的偏差,在程序中做补偿还可以提高精度。没有温度补偿,夏天和冬天误差不一样的。
回复

使用道具 举报

8#
ID:612180 发表于 2019-10-7 14:39 | 只看该作者
其实51单片机的内部电路会对时钟电路有一定的影响,而晶振的频率也会有影响,所以要做时间显示的话可以用STM32单片机,更精确一点。
回复

使用道具 举报

9#
ID:158375 发表于 2019-10-7 15:08 | 只看该作者
楼主想的有点简单喽,实际过程要复杂多多.
回复

使用道具 举报

10#
ID:620317 发表于 2019-10-8 10:20 | 只看该作者
如果想要更准确的时间,建议用时钟芯片。比如microchip的MCP7940,Resolution 1 ppm.
回复

使用道具 举报

11#
ID:301968 发表于 2021-6-12 13:52 | 只看该作者
我用了一个几毛钱的片子做了一个闹钟,几个月都没调时间,误差也没过1分钟。
回复

使用道具 举报

12#
ID:47286 发表于 2021-6-12 14:30 | 只看该作者
时钟这东西 说白了就是对精度要求有多高 如果1年允许误差1个月 什么方法都没问题 要求1年差1s还得以内 我觉得 你的方法没戏

以下是我对时间模块的理解而已 今天放假 多写了点 有喜欢的就看看 不喜欢掠过即可

我认为时钟准确不准确主要是时钟源的问题 和用什么做关系不是特别大 综合精度 消耗精力 成本三者评比

GPS最好 精度高 消耗精力小 成本高

GPS时钟源精度和世界钟精度一样 但GPS经过的节点少 卫星的时钟完全不是普通设备可以比的 那东西要给军用提供授时 GPS接收模块可以提供1ms级别的通讯授时 高手可以根据收到的时间 每次提前若干ms开始处理 这样若干年下来也不会有误差 除非断电了 而且电波传输速度接近光速 节点就是卫星到模块 中间经过的节点少 物理延迟很小

网络授时 精度较高 消耗精力小 成本高

网络授时是从世界钟或者叫RTC服务器发出的数据包获得时间 理论上也很准 问题是 你并不是用一根网线直接连到RTC服务器上 以太网的结构全球有无数个节点 一个时间数据别说全球 仅国内就要经过若干个网关 而TCP/IP协议的效率是很低的 一个数据包有7层 包括各种数据 而且以太网是分时复用传输数据 并不是连贯的 每一个网关 直到本地接收后 都要重新打包 解包 还原数据 所以延迟比GPS要大 GPS需要专用模块 以太网获取时间也需要 成本上并没多大区别

单片机+芯片 精度一般 消耗精力多 成本较低

这个方法就是片子读片子 精度问题由时钟芯片控制 好芯片 好调试 精度还行 到不了1s/年以下 考虑到要获得较高京精度 需要从软件或者硬件做误差补偿 消耗精力比较多 成本方面 如果用好片子 成本嘛 呵呵 也不比上面两种方式低 用便宜片子嘛 那就多花精力呗

纯单片机 精度不好说 消耗精力大 成本低

这个方案的精度实在不好控制 精度最高的应该算用有源晶振吧 那也得晶振够好 有源晶振又怎么了 一样有优劣区别 无源晶振次之 一样受温度影响 内部RC更是受温度变化影响 这种变化让人头疼的是没准 不一定差多少 通常能用的方法都是观察累积误差然后想办法补偿 但这种补偿说白了都是开环的 一切补偿都基于观察规律 并没有实时反馈的闭环修正 环境变化大也没多大用 成本肯定是最低的 反正只要有振荡源 也不过就是个片子 不需要其它东西

回复

使用道具 举报

13#
ID:883242 发表于 2021-6-12 17:00 | 只看该作者
想准用TCXO好了。OCXO太费电。
回复

使用道具 举报

14#
ID:517951 发表于 2021-6-12 19:29 | 只看该作者
用DS12887和51单片机搭配,精度可以做到一年1分钟以内的误差,51单片机IO8bit4个对应12887的8bit8080接口还有富裕。你们有兴趣可以是试试。
回复

使用道具 举报

15#
ID:301968 发表于 2021-6-17 16:41 | 只看该作者
用DS12887和单片机搭配,可能用的最多,精度这个问题嘛,看场合喽。十天半个月差个几S一般问题不大呵。生活中使用嘛,一般差个一两分钟我们都不会介意。
回复

使用道具 举报

16#
ID:401564 发表于 2021-6-18 13:30 | 只看该作者
用单片机本身的晶振作为时钟,那肯定是不怎么的
容易产生误差,而且是差异化的,你所写的程序只能在这个晶振上有用,换一个晶振就不一定行了
对于单片机晶振的要求太高了,掉电还不能保存
时钟芯片估计是每个学单片机的人都会接触到的,但时钟芯片也有不同各类的,像DS1302这种外置晶振的,只适合批量生产,毕竟它才几毛钱一片,但调试设备却是几千块的.......
DS12887或者DS3231内置晶振是最好的选择
当然,编程能力高一点的,WIFI授时是最有性价比也是一劳永逸的
我做过一个大钟放老家,有一次打雷就复位了,结果老人在家不会调,竟然就那样放了一整年
回复

使用道具 举报

17#
ID:97350 发表于 2021-6-18 14:28 | 只看该作者
反正我觉得DS1302是最差劲的,毕竟价格低廉。
回复

使用道具 举报

18#
ID:272119 发表于 2021-6-18 14:48 | 只看该作者
最近在做定时上传数据到上位机,每秒一次,用的是STC15W内置IRC还没有微调传输几分钟就多发一次数据. 下载烧录就知道用IRC设置时就会有偏差.因为芯片设置22.1184Mhz都无法准确总会有个千分之几的误差.
回复

使用道具 举报

19#
ID:344848 发表于 2024-4-26 09:52 | 只看该作者
单片机时钟显示的准确度,建议采用时钟芯片。
回复

使用道具 举报

20#
ID:619259 发表于 2024-4-26 11:25 | 只看该作者
用单片机定时器做时钟,两点建议:
1.定时器使用方式2(8位自动重装);
2.初始值取100微秒,计数10000次为1秒,若时间有误差,可微调这个计数值。
回复

使用道具 举报

21#
ID:883242 发表于 2024-4-26 19:51 | 只看该作者
Y_G_G 发表于 2021-6-18 13:30
用单片机本身的晶振作为时钟,那肯定是不怎么的
容易产生误差,而且是差异化的,你所写的程序只能在这个晶振 ...

说反了,单片机接的AT-cut晶体稳定性远远优于RTC用的fork crystal,只是耗电量大,不适合掉电运行。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表