标题: 用单片机制作最高精度时间系统,可移植到任何时钟程序中 [打印本页]

作者: npn    时间: 2016-9-27 18:36
标题: 用单片机制作最高精度时间系统,可移植到任何时钟程序中
用单片机制作最高精度时间系统,可移植到任何时钟程序中
可移植到任何数字时钟程序中,根据单片机型号以及晶振频率修改TH0和TL0的值。
智能日期时间累加,平闰年判断,当月天数判断功能,年计数值0~9999。
使用1秒的延迟函数,再加上时间判断计数函数的时间,误差非常大。
采用5毫秒定时器中断计数,中断触发后重置计数值并且5毫秒计数加1。
处理5毫秒计数以及时间判断计数函数,定时器仍在计数,准确触发高精度的5毫秒中断。
5毫秒计数加到200时,5毫秒计数清零并且触发时间计数函数,包含所有判断日期时间的指令在5毫
秒内完成,如5毫秒内无法完成请改成更长毫秒时间中断1次,避免出现中断重入等问题。
若发现bug或其他建议意见请及时在楼下回复。
下面是源码:


  1. #include "reg51.h"
  2. #include "intrins.h"
  3. sfr AUXR   = 0x8E;
  4. sbit led=P3^2; //秒闪烁指示灯
  5. #define ui unsigned int
  6. #define uc unsigned char
  7. ui y; //定义变量年
  8. uc m,d,h,i,s,w; //定义变量月、日、时、分、秒、周
  9. uc ms5; //5毫秒定时器中断计数
  10. void InitTimer0(void){ //初始化5毫秒定时器,根据晶振频率修改TH0和TL0的值。
  11.     TMOD = 0x01;
  12.     TH0 = 0x28;
  13.     TL0 = 0x00;
  14.     EA = 1;
  15.     ET0 = 1;
  16.     TR0 = 1;
  17. }

  18. bit if_leap_year(ui y){ //平闰年检测,闰年返回1,平年返回0
  19.   //闰年是4的倍数且不是100的倍数或者是400的倍数,否则是平年。
  20. if((y%4 == 0 && y%100 != 0) || y%400 == 0){
  21. return 1; //返回1表示闰年
  22. } else { //否则就是平年
  23. return 0; //返回0表示平年
  24. }
  25. }
  26. uc get_mon_day_nbr(ui y,uc m){ //通过年月获取该月有多少天
  27. if(m == 1 || m == 3 || m == 5 || m == 7 || m == 8 || m == 10 || m == 12) { //大
  28. 月则31天
  29.   return 31;
  30. }
  31. if(m == 4 || m == 6 || m == 9 || m == 11){ //小月则30天
  32.   return 30;
  33. }
  34.   if(m == 2){ //2月
  35.    if(if_leap_year(y) == 1){ //闰年29天
  36.    return 29;
  37.    } else { //平年28天
  38.    return 28;
  39.    }
  40.   }
  41. return 0;
  42. }

  43. bit dt_is_ok(ui y,uc m,uc d,uc w,uc h,uc i,uc s){ //检测设置的日期时间格式是否有效 1有
  44. 效0无效
  45. if(w > 7 || w < 1) return 0; //星期大于7或小于1 返回0
  46. if(y > 9999 || m > 12 || m < 1 || d < 1) return 0; //年大于999 或 月大于12 或
  47. 月小于1 或日小于1 返回0
  48. if(d > get_mon_day_nbr(y,m)) return 0; //通过年月获取当月天数 大于该值 返回0
  49. if(h > 23 || i > 59 || s > 59) return 0; //时大于23 或 分大于59 或秒大于59 返回
  50. 0
  51. return 1; //检测有效 返回1
  52. }
  53. bit set_time(ui yy,uc mm,uc dd,uc ww,uc hh,uc ii,uc ss){ //设置日期时间
  54.   if(dt_is_ok(yy,mm,dd,ww,hh,ii,ss) == 1){  //检查日期时间格式有效
  55.   y=yy;m=mm;d=dd;w=ww; //设置年月日周
  56.   h=hh;i=ii;s=ss; //设置时分秒
  57.   return 1;  //设置成功 返回1
  58.   } else { //格式无效 返回0
  59.   return 0;
  60. }
  61. }

  62. void add_time(){ //时间步进1秒
  63. s++; //秒加1
  64. if(s>=60){ //秒加到60
  65. s=0; //秒清零
  66. i++; //分加1
  67.   if(i>=60){ //分加到60
  68.   i=0; //分清零
  69.   h++; //时加1
  70.    if(h>=24){ //时加到24
  71.    h=0; //时清零
  72.    d++; //日加1
  73.    w++; //星期加1
  74.    if(w > 7){ //星期超过7
  75.    w=1; //星期等于1
  76.    }
  77.     if(d > get_mon_day_nbr(y,m)){  //日超过当前年月的天数
  78.     d=1; //日清1
  79.     m++; //月加1
  80.      if(m > 12){ //月加到12以上
  81.      m=1; //月清1
  82.      y++; //年加1
  83.       if(y >= 10000){ //年加到10000
  84.       y=0; //年清零
  85.       }
  86.      }
  87.     }
  88.    }
  89.   }
  90. }
  91. }
  92. void main(){ //入口函数,上电复位后在此开始执行指令。
  93. P3=0xFF; //设置P3口全部为高电平
  94. set_time(2000,1,1,6,0,0,0); //设置日期时间
  95.     AUXR |= 0x80;  //关闭定时器0的12分频
  96.     InitTimer0(); //初始化5毫秒定时器
  97. //死循环内可添加其他程序,只有中断触发后退出,中断返回后继续在原地运行程序。
  98.   while(1);

  99.   }
  100. void Timer0Interrupt(void) interrupt 1{ //5毫秒定时器中断
  101. //重置TH0与TL0的值,根据晶振频率修改TH0和TL0的值。
  102.     TH0 = 0x28;
  103.     TL0 = 0x00;
  104.    ms5++; //5毫秒中断计数加1
  105.     if(ms5%100 == 0)led=~led;
  106.      //5毫秒中断计数加到200 200*5=1000毫秒 1秒触发1次
  107.     if(ms5 >= 200){
  108.     ms5=0; //5毫秒计数清零
  109.     add_time(); //时间步进1秒
  110.     }
  111.    

  112. }
复制代码



作者: lzxwsk    时间: 2016-9-29 06:06
很好...............51黑有你更精彩
作者: snln    时间: 2016-10-9 06:16
多长时间误差1秒??
作者: middfat    时间: 2016-10-9 16:06
是只用单片机内部震荡器实现吗?
作者: HWL0541    时间: 2016-10-28 08:13
看看高到什么程度
作者: dzljp    时间: 2016-10-28 08:21
这个要学习学习,谢谢楼主!
作者: tangquan    时间: 2016-10-28 09:08
谁不是这么做的??这个的主要误差来源晶振的精度,不过如果定时器没有定时正确误差也会很大
作者: westlife96    时间: 2016-10-29 23:15
我觉得目前高精度还是需要温补晶振。如果是我的话,我会选择1s的中断
作者: 小白吕    时间: 2016-10-30 20:37
51hei有你更精彩
作者: 黃昭旨    时间: 2016-11-3 15:41
參考看看,希望能得到收穫
作者: 84533243    时间: 2016-11-3 18:29
很好,,,,
作者: 张伟灵    时间: 2016-11-3 21:23
真心谢谢楼主,谢谢
作者: xuguoying_cz    时间: 2016-11-3 23:19
如果是我的话,我会选择1s的中断
作者: npn    时间: 2016-11-4 08:25
xuguoying_cz 发表于 2016-11-3 23:19
如果是我的话,我会选择1s的中断

你觉得TH0和TL0计数器计满需要1s时间?经过计算晶振频率要用32.768khz的,配置CLK_DIV寄存器为0x07即对系统时钟128进行分频,所以需要4.194304Mhz的晶振,分频后串口通讯的波特率最高1024bps,最后中断里处理时间计数指令平均需要几十毫秒。


作者: babyqq2x    时间: 2016-11-5 19:36
看看吧,反正也不懂
作者: xgm2t    时间: 2016-11-6 18:21
年误差多少?

作者: fire_205    时间: 2016-11-6 18:44
这个是51单片机的程序吗?还是兼容stm系列的
作者: davg1    时间: 2016-11-6 21:05
会有温漂吧。
作者: npn    时间: 2016-11-6 21:53
fire_205 发表于 2016-11-6 18:44
这个是51单片机的程序吗?还是兼容stm系列的

只针对51指令集系列而设计的,其他指令集的可能需要修改一些头文件以及io口变量常量的定义声明方法以及配置的定时器的寄存器和中断触发方法就行,其他的日期时间设置走时判断语句大部分都不需要修改的。
作者: npn    时间: 2016-11-6 22:01
davg1 发表于 2016-11-6 21:05
会有温漂吧。

若是温漂对晶振频率影响非常大的话,你觉得数显挂钟以及热水器微波炉之类的设备在极端温度下运行,时间系统的误差会有多大?但规定范围内温漂对频率的影响不超过一定值的晶振是合格的。

作者: npn    时间: 2016-11-6 22:12
xgm2t 发表于 2016-11-6 18:21
年误差多少?

对于晶振的轻微温漂以及老化的影响而产生误差,通过gps/gsm/cdma模块更正时间保证零误差,还有长波授时的方法原理如同收音机,其中有bpc/jjy/wwvb/dcf/msf等,不同国家发射的协议以及覆盖范围发射时间都有不同,通常一个发射基站就能覆盖上千公里,根据发射的编码协议解码后获得时间写入内部时间系统走时,通常几小时或几天更正一次时间,而授时信号发射站以及网络授时服务器均采用原子钟计时,数亿年误差1秒,几乎可以忽略不计。

作者: 1123212143255    时间: 2016-11-7 05:48
正想学习做一个,看看必有收获。
作者: yjx189    时间: 2016-11-7 06:28

是只用单片机内部震荡器实现吗
作者: yjj8    时间: 2016-11-7 14:48
这个要看看。。。。。。。。。。
作者: yznj    时间: 2016-11-7 20:59
看看是什么好东东
作者: 15170220697    时间: 2016-11-8 09:56
啦啦啦啦啦啦啦啦啦啦啦
作者: 滴~电子小白    时间: 2016-11-8 11:20
回复,看看学习一下
作者: chaojiceo    时间: 2016-11-8 15:40
多长时间误差1秒??
作者: stz0401    时间: 2016-11-8 19:06
很好,非常感谢
作者: qqqchen321    时间: 2016-11-8 21:52
看一下了,不错的程序
作者: liwenjiefa    时间: 2016-11-10 14:57
看看学学看看
作者: Skipper    时间: 2016-11-10 16:01
感谢楼主分享!!
作者: 南天白云    时间: 2016-11-10 16:06
有这么准吗
作者: sunlei058378    时间: 2016-11-10 19:45
难得时间计算的这么精确
作者: 羊男3    时间: 2016-11-11 14:07
来学习学习····
作者: valiantzhang    时间: 2016-11-12 12:33
学习学习
作者: 黑橙orangerx    时间: 2016-11-13 16:07
感谢分享,先看看
作者: 5083    时间: 2016-11-13 16:55
能不能自动校对时间呢?
作者: npn    时间: 2016-11-13 17:42
5083 发表于 2016-11-13 16:55
能不能自动校对时间呢?

校对时间数据需要模块,获取的时间用set_time();函数即可写入。

作者: 5083    时间: 2016-11-13 19:21
npn 发表于 2016-11-13 17:42
校对时间数据需要模块,获取的时间用set_time();函数即可写入。

什么模块最好?加一个GPS模块用来对时也不错
作者: 维超    时间: 2016-11-13 21:46
学习下楼主的高招
作者: kaka_kaka    时间: 2016-11-13 22:26
厉害,学习学习
作者: 1245812141    时间: 2016-11-14 09:11
感谢分享
作者: mark张    时间: 2016-11-14 10:05
赞一个!!!
作者: djjzf    时间: 2016-11-14 11:49
学习一下, 不知道懂不懂
作者: lantian12310    时间: 2016-11-14 15:39
不错看看怎样
作者: ymsg1983    时间: 2016-11-15 22:20
非常感谢
作者: ymsg1983    时间: 2016-11-15 22:20
太好了,谢谢
作者: a1184569302    时间: 2016-11-22 13:54
么么么么么么
作者: 程世龙    时间: 2016-11-22 22:15
我想用32和ds12b887做一个时钟,借鉴借鉴程序
作者: xsynet    时间: 2016-11-23 08:18
我想看看,学习学习,谢谢!
作者: xsynet    时间: 2016-11-23 08:23
时间这么精确,赞一个!
作者: Demonss    时间: 2016-11-23 12:57
用单片机制作最高精度时间系统,可移植到任何时
作者: cj543464501    时间: 2016-11-23 16:00
感谢楼主分享
作者: 1007327969    时间: 2016-11-23 16:30
谢谢分享
作者: huluguojian    时间: 2016-11-23 23:10
进来学习一下
作者: wuyibin2003    时间: 2016-11-27 18:58
不用晶振吗
作者: 51hdzlt1    时间: 2016-11-30 11:37
看看 感谢楼主分享!!
作者: 1027711848    时间: 2016-12-1 08:03
好的 看看啦 我也不知道
作者: mok3cdma    时间: 2016-12-1 22:00
来学习一下
作者: toman    时间: 2016-12-1 22:00
貌似新的单片机内部都带有时钟的,新手路过勿喷!
作者: ssfc    时间: 2016-12-1 22:37
学习,学习。。
作者: npn    时间: 2016-12-2 06:18
5083 发表于 2016-11-13 19:21
什么模块最好?加一个GPS模块用来对时也不错

任何的gps模块都必须有这种功能的,里面还有很多定位参数。

作者: fengxqjack    时间: 2016-12-2 15:30
挺好的,下载了没事可以做做
作者: hongshun325    时间: 2016-12-2 22:10
看看代码效率如何
作者: xht9951    时间: 2016-12-3 09:06
感谢楼主,学习学习,谢谢!~
作者: jcygh    时间: 2016-12-3 10:31
很给力!
赞一个!
作者: jcygh    时间: 2016-12-3 10:33
很给力!
赞一个!
作者: whty2010    时间: 2016-12-3 22:11
感谢楼主分享!!
作者: 冥暝醉了    时间: 2016-12-5 09:34
很好.………
作者: jackhwang    时间: 2016-12-5 11:55
很好奇。
作者: 赶脚好流弊    时间: 2016-12-5 11:57
赞一个,对我们新手很有帮助
作者: 赶脚好流弊    时间: 2016-12-5 11:57
赞一个,对我们新手很有帮助
作者: 赶脚好流弊    时间: 2016-12-5 11:58
赞一个,对我们新手很有帮助
作者: 匆匆岁月    时间: 2016-12-5 13:05
厉害厉害
作者: 九尾    时间: 2016-12-5 16:08
看看吧。。谢谢楼主
作者: wuyutuzhi    时间: 2016-12-5 17:30
bucuobucuo
作者: R0514    时间: 2016-12-5 19:17
谢谢楼主,
作者: 李振魂    时间: 2016-12-5 19:22
ganxie
作者: 猜猜我是谁    时间: 2016-12-5 19:36
真是雪中送炭啊
作者: 15279046525    时间: 2016-12-6 10:38
完美。。。。。。。。。
作者: zrb5688    时间: 2016-12-6 21:48
谢谢楼主分享。
作者: 水水水水水水    时间: 2016-12-6 23:59
得目前高精度还是需要温补晶振。如果是我的话,我会选择1s的中断
作者: a4732710    时间: 2016-12-7 07:18
这个好呀,就想要精确点的
作者: dhwuwe    时间: 2016-12-7 07:47
好好学习学习
作者: VECENT_LIN    时间: 2016-12-7 11:56
谢谢楼主分享!
作者: w854088557    时间: 2016-12-7 17:01
这个要学习一下
作者: Ro-mastar    时间: 2016-12-7 19:25
很好,谢谢
作者: 17190181898    时间: 2016-12-10 16:39
很好
作者: sdbzwhh    时间: 2016-12-10 19:44
专业单片机正要求做这个呢,谢谢了
作者: aa3222505    时间: 2016-12-10 21:12
参考参考
作者: 新手勿喷    时间: 2016-12-12 07:40
我想看看
作者: by64214    时间: 2016-12-12 20:34
谢谢分享   学学看看
作者: 明白    时间: 2016-12-12 21:28
肯定不是高精度,因为没有同步补偿
作者: npn    时间: 2016-12-13 07:26
明白 发表于 2016-12-12 21:28
肯定不是高精度,因为没有同步补偿

这当然是只用1片单片机实现的最高精度,没有任何模块所实现的,精度由晶振决定,时间同步需要扩展gps或网络模块。

作者: 1534545848    时间: 2016-12-13 19:45
感谢楼主分享!
作者: setst    时间: 2016-12-13 21:04
赞一个
作者: 油条大师    时间: 2016-12-14 07:37
O(∩_∩)O谢谢
作者: 张纪元    时间: 2016-12-14 09:02
不错 不错
作者: hhdsdy    时间: 2016-12-14 09:23
下载一个看看,谢谢了!




欢迎光临 (http://www.51hei.com/bbs/) Powered by Discuz! X3.1