打印

请教:用单片机做的电子钟,11.0592晶振,约三分钟就慢1秒!

[复制链接]
楼主: 89cpu
手机看帖
扫描二维码
随时随地手机跟帖
21
ayb_ice| | 2007-12-5 09:42 | 只看该作者 回帖奖励 |倒序浏览

晶振频率不是问题,关键是PPM

使用特权

评论回复
22
89cpu|  楼主 | 2007-12-5 10:27 | 只看该作者

ayb_ice,什么是PPM啊?

PPM?
程序......?

使用特权

评论回复
23
ayb_ice| | 2007-12-5 10:52 | 只看该作者

百万分之一

使用特权

评论回复
24
winloop| | 2007-12-5 15:52 | 只看该作者

用一个时钟芯片不就行了么

使用特权

评论回复
25
新好男孩| | 2007-12-6 08:33 | 只看该作者

11.0592MHz晶振一样

是不是用这个11.0592MHz晶振的原因呢? 

12/11.0592=1.085us.
最终导致了误差的产生和累积.

程序里用的是模式1. (是LCD显示的,网上下载的程序)


我以前做过一个(是LED显示的,网上下载的程序),用的是12MHz的晶振,就很准的啊!!! 

不对,用11.0592M的晶体时钟一样很准,条件是必须是5的倍数,看:
// 单片机执行一条指令所需要的时钟周期数 (6或12)
// 在此使用的是AT89C52芯片,每个机器周期=12*时钟周期
#define OSC_PER_INST (12)


// 晶体为11.0592M或22.1184M
//#define OSC_FREQ (11059200UL)
#define OSC_FREQ (22118400UL)

// 创建可移植的硬件定时溢出
// 此处定时溢出(TIMEROUT必须等于5或是5的倍数)
#define    TIMEROUT (5)    // 单位是毫秒(最小基数是5)    
#define PRELOAD_mS (65536 - (TIMEROUT * OSC_FREQ) / (OSC_PER_INST * 1000))
// 当晶体是11.0592M或是其倍数是,定时器低位自动装载
#define PRELOAD_mS_H (PRELOAD_mS /256)

注意: 要用定时器低位自动装载或T2的高低位自动装载.好像论坛上有一个贴就是讨论时钟误差的问题的,上面的代码我就是受到它的启发定成的.关于定时器误的问题.hotpower 和 所长都有说过,LZ存细找找看.

例:
/*--------------------------------------------------------------------------------*-

    MAIN.H (V1.1)

    项目头文件 (Project Header)

-*---------------------------------------------------------------------------------*/

/*
* Copyright (c) 2006,wsl于深圳xxxx硬件工程部
* All rights reserved

* 文件名称: main.h
* 文件标识: 项目头文件(Project Header) 
* 摘     要: 包含控制器类型,振荡器的频率,执行一条指令
*             所需要的振荡器的周期数,公共的数据类型,
*             及定义中断向量值.

* 当前版本: 1.1             * 取代版本:
* 作     者:              * 原作者  : Michael J.Pont
* 完成日期: 2006-09-14         * 完成日期:
**********************************************************************************/

#ifndef    _MAIM_H        //防止main.h被重复引用
#define _MAIN_H

/**********************************************************************************/

#include<intrins.h>
#include <string.h>


#ifndef NULL
#define NULL ((void *) 0L)        /*Stdlib.h and String.h already include NULL */
#endif
//---------------------------------------------------------------------------------
// 每个项目都要修改这一段
//---------------------------------------------------------------------------------
// 控制器类型
//#include "REG52.H"
#include "STC12C5410AD.H"
/**********************************************************************************/
// 单片机执行一条指令所需要的时钟周期数 (6或12)
// 在此使用的是AT89C52芯片,每个机器周期=12*时钟周期
#define OSC_PER_INST (12)


// 晶体为11.0592M或22.1184M
//#define OSC_FREQ (11059200UL)
#define OSC_FREQ (22118400UL)

// 创建可移植的硬件定时溢出
// 此处定时溢出(TIMEROUT必须等于5或是5的倍数)
#define    TIMEROUT (5)    // 单位是毫秒(最小基数是5)    
#define PRELOAD_mS (65536 - (TIMEROUT * OSC_FREQ) / (OSC_PER_INST * 1000))
// 当晶体是11.0592M或是其倍数是,定时器低位自动装载
#define PRELOAD_mS_H (PRELOAD_mS /256)


/***************************************************************************************/
//---------------------------------------------------------
// 以下部分不需要修改
//---------------------------------------------------------
// 杂项宏定义
#ifndef TRUE
#define FALSE 0
#define TRUE (!FALSE)
#endif


#define INT_EA    EA
#define INT_ET2    ET2
#define INT_ES    ES
#define INT_ET1    ET1
#define INT_EX1    EX1
#define INT_ET0    ET0
#define INT_EX0    EX0

#define INTERRUPT_ENABLE()        INT_EA = TRUE        /*开总中断标志位*/
#define INTERRUPT_DISABLE()        INT_EA = FALSE        /*禁止所有中断*/
#define INTERRUPT_GET()            INT_EA                /*读中断*/
#define INTERRUPT_SET(bit)        INT_EA = (bit)        /* 设置中断,即EA = 0或1 */

#define SETBIT(A,B)        (A |= 1 << (B))                  /*A=Register, B=Bitnumber (7..0)*/
#define RESETBIT(A,B)     (A &= ~(1 << (B)))          /*A=Register, B=Bitnumber (7..0)*/
#define GETBIT(A,B)           ((A >> B) & 0x01)





#endif

/*---------------------------------------------------------*-
  --------------------NED OF FILE--------------------------
-*---------------------------------------------------------*/

/*--------------------------------------------------------------------*-

    SysTick.C (V1.00)

    这里是系统的时标.
    
     
-*---------------------------------------------------------------------*/

/*
* Copyright (c) 2007
* All rights reserved

* 文件名称: SysTick.C  
* 文件标识: 
* 摘     要: 系统的时标(20mS)

* 当前版本: 1.0         * 取代版本:
* 作     者:          * 原作者  : 
* 完成日期: 2007-07-31     * 完成日期:
************************************************************************/

#include "LCD_Dis.h"
#include "SysTick.h"


// 串行口发送
unsigned char Txd_Buffer[MAX_TRANSMIT_LENGTH];
unsigned char data Txd_Counter;                        // 串行口要发送的字节数
bit Sendit;                                            // 串口发送状态. 0 发送中, 1 发送完毕

unsigned char G_speek = 0;    
unsigned char dot = 8;                    

// 私有函数
static void serial_transmit(unsigned char *pBuffer,unsigned char number,unsigned char MaxLen);




/*---------------------------------------------------------*-
* 函数名称: T0_TICK_INIT()
* 入     口: xx
* 出     口: xx
* 函数功能: T0初始化

* 说     明: 为了时标的准确性,系统采用T0模式1,16位加载模式.
*             将T0的低位计数器设计为自动加载.
*              
* 当前版本: 1.0         * 取代版本:
* 作     者:          * 原作者  : 
* 完成日期: 2007-07-31     * 完成日期:
-*---------------------------------------------------------*/
void T0_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             * 完成日期:
-*---------------------------------------------------------*/
void SystemTick(void) interrupt 1 using 1
{
    TH0 = PRELOAD_mS_H;

}
上面就是系统时标完整的代码.看看对89cpu是否有用.

使用特权

评论回复
26
89cpu|  楼主 | 2007-12-6 11:31 | 只看该作者

谢谢! C51还没学呢.

我现在在调那初始的定时赋值TL0. 用模式1.
调了很多次,不过还是有1秒的误差.还没彻底调好.

使用特权

评论回复
27
myic200610| | 2007-12-6 23:27 | 只看该作者

write back:

呵呵!精益求精!

使用特权

评论回复
28
wolf128| | 2007-12-7 09:54 | 只看该作者

我觉得不应该是晶体的问题

着重看看你填写的定时器时间常数,需要结合你使用的晶体,简单计算一下.如果是第一次编写时钟程序的话,1小时误差1-2秒,属于正常,可以根据的具体误差进行微调,当然,是在中断程序里面

使用特权

评论回复
29
陈双君| | 2007-12-7 10:15 | 只看该作者

单片机差不多就只能到这种精确度了.

单片机差不多就只能到这种精确度了.大家再讨论一下还有什么好的办法没有.

使用特权

评论回复
30
xwj| | 2007-12-7 10:39 | 只看该作者

单片机时钟校准的3种方案

一、硬件方式:
    晶振的匹配电容用可变电容,然后用频率检测频率即可

二、软硬件方式:
    CPU你做个频率程序,检测标准频率源的频率,然后记下来用于重载

三、纯软件方式:
    先调好当前时间,按校准键先记下当前时间;等一段时间后必然会产生误差,这时按二次调整键重新输入时间,程序自动计算中间的偏差和实际走过的时间,就知道校准的比率了,把它存下来用于实际校准


自己根据仪器情况和是否有非易失存储器来决定吧
个人制作的话我比较偏向于方案三.

呵呵^_^

使用特权

评论回复
31
89cpu|  楼主 | 2007-12-7 11:30 | 只看该作者

现在情况是这样的:

修改那个定时的TL0,慢时就加大它,快了就减小它.
有效果! 我用手机校时的,我的手机特准时!!

但是, 现在,改到#39H,感觉差不多了: 
若再加1,就会变快; 若再减1,就会变慢; 

可没多久的时间后, 还是有些误差!  

是不是需要微调一下晶体边上的那两个瓷片电容了呢?
那两个瓷片电容,加大会变快还是变慢呢? 

谢谢!

使用特权

评论回复
32
将军令| | 2007-12-7 11:38 | 只看该作者

强烈建议使用12M或者4M的晶体

或者2的N次方倍率的,比如4.096Mhz

使用特权

评论回复
33
xwj| | 2007-12-7 11:49 | 只看该作者

将军令,51的话用11.0592MHz比用12MHz好的多

11025900=12×256×16×225,可以保证1秒重载时低12位不用动,也可以保证1/16秒重载时低8位不需动,这样就是一个新手也不会产生重载时的软件误差;

而对于12M,每个指令周期1uS,这个对于人来说时方便,但对于机器来说却是很不方便的数字。

跟重要的是11.0592MHz可以很简单的获得高的标准波特率,还可以把T2 省出来


最后:
其实,只要设计者稍微花点心思,不管用多少的晶振,都可以做到软件无累计误差的,此时的“误差”应该叫错误才对。

使用特权

评论回复
34
gyt| | 2007-12-7 12:13 | 只看该作者

LS有道理

使用特权

评论回复
35
sharks| | 2007-12-7 12:15 | 只看该作者

51的中断机制有问题

   任何CPU都必须在当前一条指令执行完之后才能响应中断,51的指令可是1/2/3/4周期五花八门。没准进中断要延迟多少个周期!

  所以,主程序就一句话“pcon|=1”休眠,从休眠进中断的延迟是确定的。
  当然,T2自装载模式也可以解决问题

使用特权

评论回复
36
xwj| | 2007-12-7 12:37 | 只看该作者

是的,51的不等长周期对于精确中断来说确实是恶劣透顶

但是对于时钟来说什么时候计数是没关系的,只要把当前值加进去就能消除掉累计误差的,选择合适的晶振频率更是可以彻底避免重载误差。

因此没必要非要主程序休眠,而且毕竟很多时候系统总得多多少少做些别的事,不可能只作时钟
--要是只作时钟,就算是傻子也能做到没有累计误差得:-)


当然,对于52,则使用T2的自动重载模式才是最方便的。

使用特权

评论回复
37
lelee007| | 2007-12-7 12:40 | 只看该作者

先用示波器掐

掐准了在让他跑一个星期
不过就做个电子钟用51也太浪费了吧
4位的单片机用32.768K振荡就可以保证很稳很准

使用特权

评论回复
38
tgxzer| | 2007-12-7 12:47 | 只看该作者

参考

硬件方面,竞争应选用钟表晶振32.768k,精度高、温漂小(可参考晶振的器件手册)。软件对定时器设置初值设置应准确,设置可采用网上一些初值设置工具(懒人51等)。

使用特权

评论回复
39
qq41026267| | 2007-12-7 14:01 | 只看该作者

你选的晶振错了

要用32768的

使用特权

评论回复
40
xiaowei999| | 2007-12-7 16:08 | 只看该作者

顶一个!搬个凳子好好学习

使用特权

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

本版积分规则