用T0做精确时钟,为了做到尽量精确,必须减少中断的次数,所以选择使用方式1,它最多可以计数65536次。但是,方式1中断后需要重新给定时器赋初值,这样就要耽误几个机器周期,很难保证时钟的精确。
有两个方法可以解决这个问题。
第一个方法:你可以计算出中断处理时重新给定时器赋初值所用的机器周期数,在你计算出的初值里除去这几个机器周期,作为补偿。这个方法,只是在每次定时器中断后,都可以按时得到执行时很精确,但事实上中断什么时候执行谁都说不准,所以这个方法,只能做到尽量精确。
第二个方法:计算出一个凑巧的初值,使TL0正好等于0x00,这样每次中断溢出后,TL0都从0x00开始计数,即使中断没有得到执行,TL0也会继续计数。利用这一点,在中断处理函数中,只需要对TH0重新赋值,不需要管TL0。以下是示例程序:
//定时器T0时钟参数
unsigned char T0_S = 0; //秒
unsigned char T0_M = 0; //分
unsigned char T0_H = 0; //时
unsigned char T0_Cycle = 0; //循环次数
//本程序所用晶振22.1184MHz,每次定时25ms,循环40次正好1s。
void Timer0_Init(void) //T0初始化函数
{
TMOD = 0x01; //设置T0工作方式1
TH0 = 0x4c; //(65536-46080)/256,设置初值46080,晶振22.1184MHz,
//每个机器周期0.5425微秒,定时25ms
TL0 = 0x00; //(65536-46080)%256 ,TL0恰好=0x00
IE |= 0x82; //开中断
TR0 = 1 ; //T0开始定时
}
//
void Timer0(void) interrupt 1
{
TH0 = 0x4c; //重新给TH0赋值
//TL0 = 0x00; //不对TL0赋值,让其继续计数
TF0 = 0 ; //定时溢出清0
T0_Cycle ++ ;
if(T0_Cycle == 40) //循环40次,每次25ms,定时1s
{
T0_Cycle = 0 ;
T0_S++ ;
if(T0_S == 60)
{
T0_S = 0;
T0_M++;
if(T0_M == 60)
{
T0_M = 0;
T0_H++;
if(T0_H == 24)
{
T0_H = 0;
}
}
}
}
}
以上程序即使T0中断赋初值会耽误几个机器周期,中断没有得到及时执行也没有关系,因为TL0的计数不受影响。但有一种情况必须要注意,虽然这种情况发生的可能性不大。如果T0中断长时间没有得到响应,TL0再一次溢出了,这时这种方法的误差就大了。
经过测试,第二种方法还是比较精确的,时钟跑了一天快了10s,这个误差应该是晶振本身的问题,如果是定时器的问题,应该是慢了,不会快了。
关键字:51单片机 精确时钟
引用地址:
51单片机T0做精确时钟
推荐阅读最新更新时间:2024-03-16 14:55
51单片机实现温度采集与显示(二)
下面简单介绍一下DS18B20: DS18B20 是由 DALLAS 半导体公司推出的一种的“一线总线(单总线) ” 接 口的温度传感器。 与传统的热敏电阻等测温元件相比, 它是一种新型的体积小、 适用电压宽、 与微处理器接口简单的数字化温度传感器。 通过P37模拟单总线通信读取数据和发送命令 上面是DS18B20 的示意图,可以看出他与51单片机相连的只有一个管脚——P37,那么二者是如何通信的呢?——采用软件模拟单总线通信 DS18B20 时序包括如下几种: 初始化时序、 写(0 和 1) 时序、 读(0 和 1) 时序。 DS18B20 发送所有的命令和数据都是字节的低位在前。 初始化时序:简单来说,就是主机发送复位
[单片机]
学51单片机-IIC总线与时序
IIC总线是飞利浦公司上世纪80年代设计出来的一种总线通信方式,主要用来连接整体电路,它可以一个总线结构上连接多个设备。 很多人听到IIC总线、SPI总线、485总线什么的就会晕,其实,数据传输的接线方式,大体上就是两种:一种是并行接口,一种是串行接口。 并行接口是什么?用并行方式来传输数据的接口。假如我想传输几个8位的数据,那好,单片机上用8个IO传送数据,每次就能传送一个。假如想传输几个16位的数据呢?那就要用16个IO!优点是速度快,缺点是占用的IO太多了。 串行接口是什么?是指数据在有限的几个IO上按照顺序,一位一位的进行传输。这类有很多:UART、IIC、SPI、CAN、USB等等
[单片机]
基于51单片机和 ADC0808 ADC0809的自动数字电压表
前言: 之前讲过基于数码管显示的自动数字电压表,接下来讲一下基于LCD1602显示的相关设计,分别利用的是TI公司的ADC0808和ADC0809。 硬件和软件设计 基于51单片机+ADC0808+LCD1602 测试电压范围为2.1~25V(超出这个范围,程序会卡死),精度<0.05 仿真图如下: 部分代码如下: #include AT89X52.H #define LEDDATA P0 #define v20_on {s3=0;s2=0;s1=1;} //宏定义不同量程,不同的开关状态 #define v2_on {s3=0;s2=1;s1=0;} #define v02_on {s3=1;s2=0;s
[单片机]
基于51单片机的步进电机控制以及仿真
给你一段简单的程序代码,控制步进电机的,不过你要好好看看程序的编写,以适应你的硬件(主要是连线),你可以在实现这个的基础上实现你所说的目标,个人觉得你参考这个程序然后自己实现你所说的功能比较好: #include reg52.h unsigned char code F_Rotation ={0x08,0x10,0x20,0x40};//正转表格 unsigned char code B_Rotation ={0x40,0x20,0x10,0x08};//反转表格 void Delay(unsigned int i)//延时 { while(--i); } main() { unsigned char i; wh
[单片机]
基于51单片机的8x8 LED点阵
最近学了LED点阵,并用了三种方式显示字母组,分别为按键控制显示,自动变换显示,和滚动显示 第一种 #include reg51.h #include intrins.h typedef unsigned char u8; typedef unsigned int u16; sbit RCLK=P3^5; sbit SRCLK=P3^6; sbit SER=P3^4; #define GPIP_KEY P1 #define GPIO_DIG P0 u8 keyvalue; u8 code duan ={{0x00,0x42,0x7e,0x42,0x42,0x3c,0x00,0x00},//对字母编码
[单片机]
51单片机冒泡排序汇编语言子程序
;;入口:待排序的N个单字节无符号整数所在的RAM首址存于R3,N存于R4; ;;返回:由小至大排序完毕的N个数据存于原RAM块。 QUE: MOV A, R3 MOV R0, A ;;R0←RAM首址 MOV A, R4 MOV R7, A ;;R7←N CLR PSW.5 ;;交换标志清零,表示未交换数 MOV A, @R0 ;;A←RAM中首个数 LP1: INC R0 ;;RAM指针加1 MOV R2, A ;;送前数于R2暂存 CLR C ;;借位清零
[单片机]
51单片机温度计2.0版ds18b20+0.96寸OLED IIC显示
51单片机温度计LCD1602显示成功,于是又挑战了OLED显示 main.c #include oled.h #include ds18b20_1.h void Timer0Init(void); //void Gao_Wen(void); //void GaoDiInit(void); sbit Deng=P1^0; //LED灯 sbit FengMing=P1^1; //蜂鸣器 sbit GD=P3^0; //两个功能:高限温减小,关闭LED灯 sbit GF=P3^1; //两个功能
[单片机]
51单片机控制的红外遥控LED电子钟设计
自制LED电子钟在很多电子类报刊杂志上都可以见到,但多数在断电后要重新设置时间等参数,给使用带来不便。也有用电池作为备用电源的,但往往体积较大。本文介绍的LED电子钟克服了上述弊端,加上采用家电通用的红外遥控器进行控制,使用方便。该电子钟有一路闹铃输出,可以通过遥控器设置闹铃时间及闹铃允许。 一、工作原理 实时时钟芯片DS1302采用串行数据传输,可为掉电保护电源提供可编程的充电功能,也可以关闭充电功能,芯片采用32768Hz晶振。 AT89C2051作为主控芯片,其功能一是对接收到的红外遥控编码进行判断识别,并进行相应的处理;二是定期读取DS1302中的时间,并把小时和分钟送显示;三是比较设置的闹铃时间与实时时间
[单片机]