购物车0种商品
IC邮购网-IC电子元件采购商城
单片机做的电子钟比正常慢的原因讨论
(2011/10/12 9:14:00)
有什么好法子较准它呢? 需要加大晶振旁边的那两个瓷片电容?还是减小呢? 程序上可有好的调校办法呢? 谢谢!

不过,这样能改到比如说一个月误差只一秒或更少吗? 程序上除了定时,有一些语句也占用了时间的啊.

那么单片机的水平是不是就可以说有了一个较大的提高了呢? 或者说也不过是刚刚起步而已. 我现在多是在网上下载程序来看,做试验板试的.


一般都是在定时中断里给个变量累加, 就把定时器初始值变小(中断周期变大),变量累加值改小,即可!
我用8032做的电子钟走了一个星期了,还是和电视上显示的时间一致!没加修正程序.倒是我家电子表差了好几秒.
老x太出名了
如果是11.0592晶振的问题,那么必须修正!!! 相关链接:http://www.ednchina.com/blog/hotpower/13661/message.aspx

那么大的误差肯定是程序问题,改电容是改不过来的
其实在查询或中断中只读定时器误差就完全取决你所用时钟的误差!用普通4M精度也不错.
12/11.0592=1.085us. 最终导致了误差的产生和累积. 程序里用的是模式1.(是LCD显示的,网上下载的程序) 我以前做过一个(是LED显示的,网上下载的程序),用的是12MHz的晶振,就很准的啊!!!
相关链接:http://shop35088126.taobao.com
我也是一个新手,我对单片机的研究也有一段时间了,可是也很难做到误差很小啊。

PPM? 程序......?


是不是用这个11.0592MHz晶振的原因呢? 12/11.0592=1.085us. 最终导致了误差的产生和累积. 程序里用的是模式1.(是LCD显示的,网上下载的程序) 我以前做过一个(是LED显示的,网上下载的程序),用的是12MHz的晶振,就很准的啊!!! 不对,用11.0592M的晶体时钟一样很准,条件是必须是5的倍数,看: //单片机执行一条指令所需要的时钟周期数(6或12) //在此使用的是AT89C52芯片,每个机器周期=12*时钟周期 #defineOSC_PER_INST(12) //晶体为11.0592M或22.1184M //#defineOSC_FREQ(11059200UL) #defineOSC_FREQ(22118400UL) //创建可移植的硬件定时溢出 //此处定时溢出(TIMEROUT必须等于5或是5的倍数) #defineTIMEROUT(5)//单位是毫秒(最小基数是5) #definePRELOAD_mS(65536-(TIMEROUT*OSC_FREQ)/(OSC_PER_INST*1000)) //当晶体是11.0592M或是其倍数是,定时器低位自动装载 #definePRELOAD_mS_H(PRELOAD_mS/256) 注意:要用定时器低位自动装载或T2的高低位自动装载.好像论坛上有一个贴就是讨论时钟误差的问题的,上面的代码我就是受到它的启发定成的.关于定时器误的问题.hotpower和所长都有说过,LZ存细找找看. 例: /*--------------------------------------------------------------------------------*- MAIN.H(V1.1) 项目头文件(ProjectHeader) -*---------------------------------------------------------------------------------*/ /* *Copyright(c)2006,wsl于深圳xxxx硬件工程部 *Allrightsreserved * *文件名称:main.h *文件标识:项目头文件(ProjectHeader) *摘要:包含控制器类型,振荡器的频率,执行一条指令 *所需要的振荡器的周期数,公共的数据类型, *及定义中断向量值. * *当前版本:1.1*取代版本: *作者:*原作者:MichaelJ.Pont *完成日期:2006-09-14*完成日期: **********************************************************************************/ #ifndef_MAIM_H//防止main.h被重复引用 #define_MAIN_H /**********************************************************************************/ #include #include #ifndefNULL #defineNULL((void*)0L)/*Stdlib.handString.halreadyincludeNULL*/ #endif //--------------------------------------------------------------------------------- //每个项目都要修改这一段 //--------------------------------------------------------------------------------- //控制器类型 //#include"REG52.H" #include"STC12C5410AD.H" /**********************************************************************************/ //单片机执行一条指令所需要的时钟周期数(6或12) //在此使用的是AT89C52芯片,每个机器周期=12*时钟周期 #defineOSC_PER_INST(12) //晶体为11.0592M或22.1184M //#defineOSC_FREQ(11059200UL) #defineOSC_FREQ(22118400UL) //创建可移植的硬件定时溢出 //此处定时溢出(TIMEROUT必须等于5或是5的倍数) #defineTIMEROUT(5)//单位是毫秒(最小基数是5) #definePRELOAD_mS(65536-(TIMEROUT*OSC_FREQ)/(OSC_PER_INST*1000)) //当晶体是11.0592M或是其倍数是,定时器低位自动装载 #definePRELOAD_mS_H(PRELOAD_mS/256) /***************************************************************************************/ //--------------------------------------------------------- //以下部分不需要修改 //--------------------------------------------------------- //杂项宏定义 #ifndefTRUE #defineFALSE0 #defineTRUE(!FALSE) #endif #defineINT_EAEA #defineINT_ET2ET2 #defineINT_ESES #defineINT_ET1ET1 #defineINT_EX1EX1 #defineINT_ET0ET0 #defineINT_EX0EX0 #defineINTERRUPT_ENABLE()INT_EA=TRUE/*开总中断标志位*/ #defineINTERRUPT_DISABLE()INT_EA=FALSE/*禁止所有中断*/ #defineINTERRUPT_GET()INT_EA/*读中断*/ #defineINTERRUPT_SET(bit)INT_EA=(bit)/*设置中断,即EA=0或1*/ #defineSETBIT(A,B)(A|=1<<(B))/*A=Register,B=Bitnumber(7..0)*/ #defineRESETBIT(A,B)(A&=~(1<<(B)))/*A=Register,B=Bitnumber(7..0)*/ #defineGETBIT(A,B)((A>>B)&0x01) #endif /*---------------------------------------------------------*- --------------------NEDOFFILE-------------------------- -*---------------------------------------------------------*/ /*--------------------------------------------------------------------*- SysTick.C(V1.00) 这里是系统的时标. -*---------------------------------------------------------------------*/ /* *Copyright(c)2007 *Allrightsreserved * *文件名称:SysTick.C *文件标识: *摘要:系统的时标(20mS) * *当前版本:1.0*取代版本: *作者:*原作者: *完成日期:2007-07-31*完成日期: ************************************************************************/ #include"LCD_Dis.h" #include"SysTick.h" //串行口发送 unsignedcharTxd_Buffer[MAX_TRANSMIT_LENGTH]; unsignedchardataTxd_Counter;//串行口要发送的字节数 bitSendit;//串口发送状态.0发送中,1发送完毕 unsignedcharG_speek=0; unsignedchardot=8; //私有函数 staticvoidserial_transmit(unsignedchar*pBuffer,unsignedcharnumber,unsignedcharMaxLen); /*---------------------------------------------------------*- *函数名称:T0_TICK_INIT() *入口:xx *出口:xx *函数功能:T0初始化 * *说明:为了时标的准确性,系统采用T0模式1,16位加载模式. *将T0的低位计数器设计为自动加载. * *当前版本:1.0*取代版本: *作者:*原作者: *完成日期:2007-07-31*完成日期: -*---------------------------------------------------------*/ voidT0_TICK_INIT(void) { TMOD&=0xf0; TMOD|=0x01; TL0=0; TH0=PRELOAD_mS_H; ET0=1; TR0=1; } /*---------------------------------------------------------*- *函数名称:SystemTick() *函数功能:系统时标.任何任务执行时间都不能大于系统时标. *说明:T0的低位计数器为自动加载.(系统时标为5mS) *当前版本:1.0*取代版本: *作者:*原作者: *完成日期:2007-07-31*完成日期: -*---------------------------------------------------------*/ voidSystemTick(void)interrupt1using1 { TH0=PRELOAD_mS_H; } 上面就是系统时标完整的代码.看看对89cpu是否有用.
我现在在调那初始的定时赋值TL0.用模式1. 调了很多次,不过还是有1秒的误差.还没彻底调好.
呵呵!精益求精!
着重看看你填写的定时器时间常数,需要结合你使用的晶体,简单计算一下.如果是第一次编写时钟程序的话,1小时误差1-2秒,属于正常,可以根据的具体误差进行微调,当然,是在中断程序里面
单片机差不多就只能到这种精确度了.大家再讨论一下还有什么好的办法没有.
一、硬件方式: 晶振的匹配电容用可变电容,然后用频率检测频率即可 二、软硬件方式: CPU你做个频率程序,检测标准频率源的频率,然后记下来用于重载 三、纯软件方式: 先调好当前时间,按校准键先记下当前时间;等一段时间后必然会产生误差,这时按二次调整键重新输入时间,程序自动计算中间的偏差和实际走过的时间,就知道校准的比率了,把它存下来用于实际校准 自己根据仪器情况和是否有非易失存储器来决定吧 个人制作的话我比较偏向于方案三. 呵呵^_^
修改那个定时的TL0,慢时就加大它,快了就减小它. 有效果!我用手机校时的,我的手机特准时!! 但是,现在,改到#39H,感觉差不多了: 若再加1,就会变快;若再减1,就会变慢; 可没多久的时间后,还是有些误差! 是不是需要微调一下晶体边上的那两个瓷片电容了呢? 那两个瓷片电容,加大会变快还是变慢呢? 谢谢!
或者2的N次方倍率的,比如4.096Mhz
11025900=12×256×16×225,可以保证1秒重载时低12位不用动,也可以保证1/16秒重载时低8位不需动,这样就是一个新手也不会产生重载时的软件误差; 而对于12M,每个指令周期1uS,这个对于人来说时方便,但对于机器来说却是很不方便的数字。 跟重要的是11.0592MHz可以很简单的获得高的标准波特率,还可以把T2省出来 最后: 其实,只要设计者稍微花点心思,不管用多少的晶振,都可以做到软件无累计误差的,此时的“误差”应该叫错误才对。

任何CPU都必须在当前一条指令执行完之后才能响应中断,51的指令可是1/2/3/4周期五花八门。没准进中断要延迟多少个周期! 所以,主程序就一句话“pcon|=1”休眠,从休眠进中断的延迟是确定的。 当然,T2自装载模式也可以解决问题
但是对于时钟来说什么时候计数是没关系的,只要把当前值加进去就能消除掉累计误差的,选择合适的晶振频率更是可以彻底避免重载误差。 因此没必要非要主程序休眠,而且毕竟很多时候系统总得多多少少做些别的事,不可能只作时钟 --要是只作时钟,就算是傻子也能做到没有累计误差得:-) 当然,对于52,则使用T2的自动重载模式才是最方便的。
掐准了在让他跑一个星期 不过就做个电子钟用51也太浪费了吧 4位的单片机用32.768K振荡就可以保证很稳很准
硬件方面,竞争应选用钟表晶振32.768k,精度高、温漂小(可参考晶振的器件手册)。软件对定时器设置初值设置应准确,设置可采用网上一些初值设置工具(懒人51等)。
要用32768的

可以自己做的玩玩,但这种东西只能在实验室里与要求不高的场合使用.当然你不考虑成本采用带温补的晶振是有可能的.现在有些供电公司装的分时电能表,各厂家都是用硬件时钟(R8025)做的,但不做温度补偿也做不到0.5s/d的
贵吗?好像没见过.

换带副振的芯片,用32.768的晶振,可以做到几乎没误差~
最开始不太懂修正,做出来的时钟误差应该应该说很离谱。 至于现在做时钟一般是用32768的晶振吧,但是大家有没有去仔细的注意那个频率呢?是否用示波器测过其晶振波形呢? 前段时间做了个时钟类的小产品,能过不同的电容,调晶振的频率都精确到32.7680KHZ,所做的实验表明,用22pf电容最合适(25的也可以)。而我在现在做的一个ARM项目中,时钟要求不严格,用的30pf电容,我随便一测,频率为 32.75x(不太记得是不是这么多,也可能是32.765x),可见其精度相差甚远。对于一般的要求也不用太在意,但如果是真正的时钟,还是得多花些时间多测一下。不然做出的产品绝对不合格。
用单片机编译器的跑表啊, 什么11.0592.什么12M都行.怎么加语句都行. 你跑表以后可以看到一个中断差多少,再该中断的定时.(11.0592改不到很准,建议用12M的,但是调节后怎么不会3小时差一秒.)应该会一个月差几秒了. 还有就是晶振的质量要好,最好选那种长一点的.那种会稳定些.
"但是,现在,改到#39H,感觉差不多了: 若再加1,就会变快;若再减1,就会变慢;" 这个很难解决. 精度要那么高,还是用"4位的单片机用32.768K振荡就可以保证很稳很准"---37楼. 手表的晶振就是32.768Khz的.
下面内容是在保证晶体准确度的情况下而进行的探讨: 首先:11059200=144*64*100*12,其中12为机器周期和指令周期的关系。100是为了保持准确度取的10ms为单位,1秒的次数,当然,也可以取100ms为单位,那么该次数就为10.前面那个144是8位自动装载的值,这个值尽量大,这样可以少一些中断次数,有些可以使用16位的自动装载,那么问题就非常简单了。64这个数字就不用太解释了。 假设T0自动装载的值为144,装载后每次进入一个中断,中断的任务是CNT_T0加一 那么 if(++Cnt_t0==64) { Cnt_10ms++;//表示10ms计数加一 Cnt_t0=0; } //64次自动装载后,总的时间为10ms。这个只要保证T0的优先级,保证T0自动装载的时候,一定会发生中断就可以。至于是否马上或等待几个周期后进入,这个是没有关系的。 主程序中,就使用CNT_10ms就可以了。我不管在什么时候使用CNT_10ms,本程序不会产生累计误差的。实际我们平时要求没有太高,显示问题可以早几个ms,晚几个ms.但是视觉上是没有多大的变化的。同时又能够保证定时的准确度。 个人不太同意使用软件补偿的方法来做。毕竟补偿的方法是不可靠的。 这个方法我本人是经过测试过的。每天系统的误差在3秒以内。这个3秒还是晶体的温度变化引起的。 希望对你有点儿用处。

144周期就中断一次,中断太过于频繁了 再就是,“软件补偿的方法”和避免低位重载的方法完全可以做到无累计误差的,根本就不存在什么“不可靠”,做的不对那也是你的问题。 至于时钟校准的3种方案,看我30楼的贴吧 估计没人仔细看过...
11059200=144*64*100*12=256*36*100*12 这样做行不?
11059200=2^14*3^3*5^2 51或别的单片机都可以很容易的获得标准波特率和能被256整除的重载值。 比如10mS、5mS、2.5mS、10/3mS的整数倍的定时时间,而这些定时值低位都不需要重载!
可以减少定时中断程序里的命令行,转移到主程序中。
用什么频率的晶振不是问题。。。


现在已经很明确了,可以完全消除软件引入的误差,下一步该讨论晶体的精度了. 消费电子中做电子钟的晶体大部分都用32.768KHz,4.19304MHz,它们的精度通常是多少PPM的? 我们51所用的12MHz,11.0592MHz通常又是多少PPM的?
都是新手,就几个建议供参考: 1.首先程序要对 2.频率最好能整除 3.晶振精度要高,我想搂主用的晶体精度不会超过20ppm。你自己可以计算一下一年能有几分钟的误差。
已彻底解决
我搞过,和计算机的时间还看不出积累误差. 通过T2自动重装定时,
原则上,用MCU的定时器实现软件RTC,最好选择有自动重加载功能定时器的MCU
用模拟IIC的,可以学者玩玩
可以想办法换成12Mhz的晶振,也可以设计程序来改善哈。
通过其它计算修正时间,而不能修改TH/TL的值
我认为是程序问题。程序中的一些语句要占用一些时间,在设置定时中断初值时要考虑这个问题。 把晶阵换成12MHz或6MHz,这样便于计算机器周期。

我也是用11.0592的晶振,请问中断服务程序应该不会占时间吧。 在重新装载常数时把定时器关了,装完再开管用吗?
12M的不可以吗
下面链接如果搞明白了,就会知道为什么手表晶振是32.768KHz 51用11.0592MHz的人很多,几乎占80%(俺保守的估计) 明白了俺在解答~~~ 一直看这个贴子在增高,真搞不明白楼主的程序是怎么编写的~~~ 相关链接:http://blog.ednchina.com/hotpower/13661/message.aspx
11.0592晶振是没有错的,理论上不会有偏差的,但是器件与理论模型是有差距的,所以最好用时钟测试仪测试偏差,然后修正两个起振电容,可以达到目的(但依然有偏差,不过很小)。

我的问题: "晶振旁边的那两个瓷片电容增大或者是减小对时钟快慢有何影响呢?" 我没试过.理论上该是怎么讲呢? 若把那两个瓷片电容增大些,时钟会变快还是会变慢呢? hotpower,您好!让您见笑了! 那个程序是从网上下载的一个时钟程序. 我焊完板弄好单片机插上,时钟不准,是慢了; 后来我就调那个TLO了,是有效果啊! 把TLO调到某一个数时,好像是#3AH,就差不多了,但还是不准。 这时再加大TLO,如改成#3BH,就会又快1秒。 这时再减小TLO,如改成#39H,就会又慢1秒。 感谢大家的解答。


首先是晶振的正确选择. 在51中,由于一般为12分频,当然现在还有6分频和1分频(不分频)的. 为什么在严格定时里,如1秒,...1分种...1小时等不能用12的倍数呢???? 实际我在。。。 先到这里~~~俺倒塌STM32首次成功,赶写作文...
我经常用2051做一些简单的设备,程序框架都是固定的。 用T0做个1/N秒的时标,大部分的任务的时序都根据1/N秒的整数倍来运行,也附加个时间显示功能。12M、11.0592M、3.6864、6M的这几种晶体我在完全相同的程序上(用什么晶体就只改了一下FOSC定义)用过,几天内都几乎没有误差。 我是这样做的: #defineFOSC11.0592 #defineMACHINE_NUM12 #defineTICKS_PER_SEC50 #defineT0_RELOAD(uint16)(-FOSC*1000000/TICKS_PER_SEC/MACHINE_NUM) 在T0中断服务程序返回前做如下处理: TR0=0; abc=(uint16)(T0_RELOAD+15)+(TL0|(TH0<<8)); TL0=(uint8)abc; TH0=(uint8)(abc>>8); TR0=1; 上面“T0_RELOAD+15”中的15是上述代码(用Keil编译)运行的时间,假如用汇编的话效率更高,比15要小。 xwj说得很对,11.0592M是个很好用的频率,它可以做一秒的分频数很灵活(20、30、40、50、60、80、90等都可以在程序上不会有丝毫误差),而12M就不能做30、60之类的分频。
虽然晶振和电容会对误差有影响,那也应该是1天才会快慢几秒钟。 你这个不仅要考虑定时器的初始化,还要考虑指令的时钟周期。

如果你是用定时器做的话 那么应该是初值设置的问题应该用11.0592mhz来准确计算
老帖子被翻出来了 又学到了很多
软件累积误差很容易就可以做到 0.01ppm 以下,而且还可以以 0.01ppm 为单位微调计时快慢(这个功能 用于校准硬件误差)。 说软件做不到的人,是他还没找到方法。 有人说硬件误差都有 10ppm 了,软件累积误差没必要做到 0.01ppm,确实是这样,但有些人连 100ppm 的软件微调都做不到,这就显现出这个方法的价值了。
直接用1302时钟芯片啊 那家伙的误差可小多了啊
1:不是所有的晶体都适合做时钟的,32.768K是最好的 2:晶体一般采用三点式电容振荡电路,就是单片机的那种,这个时候晶体等价为电感 3:32.768K附近的晶体,等效的电感的Q值最大,对外界的抗干扰能力最强 4:大家可以这么理解,晶体频率从0到无穷大,肯定有一个性能最好的Q值晶体频率,而这个频率就在32K附近,其他都是比这个频点差的。这是简单的数学思维 5:在11.0592MHz,就算它的精度达到10ppm,但抗干扰稳定性,还是非常差的,Q值很低,这没有任何意义的,频率再往上,Q值更低,振荡都更加难,不是说频率可以无限制的增长的。 以上都在不考虑软件问题的情况下提的,若你的软件就有问题,没什么可谈的。 创易电子 www.designeasy.com.cn
误差是可以减小的
理解透彻就该用32768的晶体了。
我做过,一天就差1秒左右。3分钟1秒这个程序肯定有问题
18楼的回答是一种非常大的可能,再有可能就是定时器没有用自动重载功能,程序没有计算进入中断的时间。 用11.0592的晶振分频需要是27的整数倍 做个广告,去看俺写的《删繁就简》吧,里面有答案
既然知道误差率,在程序里修正不就行了嘛,这个有什么好说的
这样的问题一般是软件引起的 最好嵌入汇编。
用定时器定时方式二,9216次是10ms,就可以精确s了,TL0,TH0均赋初值为00H,这样256*36=9216 精确10ms 再100次就精确1s
这坟挖了又挖
这坟挖的好,我来捡几个古董。
看你什么单片机51的话还是12M吧 1US

浏览:(2381)| 评论( 0 )
博文评论

  • 昵 称:
  • 内 容:10~250个字符
  • 验证码: 验证码看不清楚?请点击刷新验证码
  •                      
  • 博文分类

    热点博文

    最新博文

    最新评论

    IC电子元件查询
    IC邮购网电子元件品质保障