51单片机精确定时

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

精确定时与计数
一、定时器相关寄存器
TCON的高4位
TF1 TR1 TF0 TR0
TF1(TCON.7):定时器1的溢出中断标志位
TR1(TCON.6):定时器1的运行控制位
TF0(TCON.5):定时器0的溢出中断标志位
TR0(TCON.4):定时器0的运行控制位
TMOD
GATE1 C/T1 M1 M0 GATE0 C/T0 M1 M0
定时器1 | 定时器0
TH0、TL0、TH1、TL1
中断允许控制寄存器IE中的三位。

ET0(IE.1)、ET1(IE.4)、EA(IE.7)
定时器的结构(以T0为例),把定时器分为六个部分来研究:
脉冲源控制端计数器中断请求位中断允许控制中断服务程序
晶振T0 TH0、TL0 TF0 ET0 EA void Tm0() interrup1 using 1
脉冲源:用作定时器时,取晶振作为脉冲源。

每12个振荡周期(即一个机器周期)计数器(即TH0、TL0)加一。

用作计数器时T0脚出现下降沿(管脚从1到0)跳变时,计数器加一。

定时器和计数器的区别就是脉冲源不同,除此之外其他的工作过程完全相同。

配置TMOD的C/T0可以选择脉冲源。

置0是定时器,置1是计数器。

控制端:相当于一个开关,开关打开时,脉冲源的信号才能传到计数器(TH0,TL0)中,计数器会不断增一。

关闭这个开关,脉冲源的信号不能使计数器(TH0,TL0)增一。

控制端的开启和关闭状态由TR0、GATE0和INT0脚电平决定。

控制端的开启条件是TR0&(~GATE0 | INT0)如下图。

一般情况下令TR0=1,GATE0=0开启控制端。

TR0=0关闭控制端。

当需要INT0引脚控制计数器时,令TR0=1,GATE0=1,这样INT0脚为高电平时计数,低电平时停止计数,这样可以很方便的测量脉冲宽度。

计数器,中断请求位:这里说的计数器是指TH0、TL0这两个寄存器。

每收到一个脉冲源输出的脉冲,这个计数器就会增一。

计数器计满溢出时,会置位TF0,产生中断请求。

注意,这里只是产生中断请求,是否能够进入中断程序,还要由中断允许位决定。

直接对TF0置位,也可以产生中断请求。

计数器TH0、TL0一共有四种计数方式:
方式0(M1=0 M0=0)13位计数器。

它由TH0的8位和TL0的低5位构成。

TL0大于0x1F时就向TH0进位。

TH0计满溢出就向TF0置位请求中断。

方式1(M1=0 M0=1)16位计数器。

与方式1差不多。

由TH0的8位和TL0的8位构成。

TH0计满溢出就向TF0置位请求中断
方式2(M1=1 M0=0)8位定时器。

TL0计满溢出时,置位TF0请求中断,并且将TH0中的数值重新装入TL0中。

方式3(M1=1 M0=1)这个方式只有定时器0有,把定时器0当成两个8位定时器来用。

定时器1没有方式3,如果设成方式3就相当于停掉了定时器1。

中断允许控制:上一步产生中断请求(TF0被置1),并不代表会响应中断。

还要看中断允许控制位,这是一个开关,只有开关在开启状态,中断才会响应。

每个中断源都有自己的分开关,比如T0的中断允许位是ET0,T1的中断允许位是ET1.还有一个总开总EA,它关闭时所有的中断都被禁止。

必须是分开关和总开关都打开时,才能进入中断服务程序。

#include <reg51.h> //11.0592M
void timer0() interrupt 1 using 1//5ms中断一次定时器中断处理函数
{
TH0=0xEE; //重置定时初始值。

其他程序
}
void main (void)
{
TMOD|=0x01; //选择定时器0,工作模式1,16位定时器
TH0=0xEE; //置定时初始值
TL0=0x00;
ET0=1; //开启定时器0中断允许,允许定时器0中断。

EA=1; //开启全局中断允许。

允许所有中断
TR0=1; //开启控制端
while(1){。

主程序}
}
其他中断源的向量表:
中断源 C语言
中断序号例子
外部中断0(INT0) 0 void _INT0() interrupt 0 using 1
定时器T0中断 1 void _T0() interrupt 1 using 1
外部中断1(INT1) 2 void _INT1() interrupt 2 using 1
定时器T1中断 3 void _T1() interrupt 3 using 1
串行口中断 4 void _UART() interrupt 4 using 1
定时器T2中断 5 void _T2() interrupt 5 using 1
定时器例程之一:精确定时1秒钟。

晶振频率11.0592M。

用定时器0的工作方式1实验。

因为工作方式1,最大的计数是65536(2^16)个机器周期。

晶振是11.0592M时,最长溢出时间是71111.1111111111us=71ms,远远不够1秒,计算出TH0=0xEF,TL0=0x00。

溢出时间是5毫秒,计200次中断,就是1秒。

unsigned char ms_5=0;
void timer0() interrupt 1 using 1//5ms中断一次定时器中断处理函数
{
TH0=0xEE; //重置定时初始值
if (++ms_5>=200)
{ ms_5=0; //程序每1秒钟进入这里一次。

}
}
主程序如下:
void main (void)
{
TMOD|=0x01; //选择定时器0,工作模式1,16位定时器
TH0=0xEE; //置定时初始值
TL0=0x00;
ET0=1; //开启定时器0中断允许,允许定时器0中断。

EA=1; //开启全局中断允许。

允许所有中断
TR0=1; //开启控制端
到这里我们把定时器0做成了一秒钟的程序完成了。

有误差,但可以控制到极其微小的程度。

下面我们发析一下误差的产生,以及控制方法。

晶振的误差:我们的晶振一般误差都是20PPM的,百万分之二十。

想提高精度,只能选择误差更小的晶振,但它毕竟不是为精确定时设计的,很难达到时钟芯片晶振的精度。

单片机中断系统的误差:定时器产生中断请求以后,并不一定能马上响应这个中断,单片机要把当前的指令执行完。

51的指令是1到4个周期,如果赶上两周期指令,就会延误一个指令周期,最慢的情况会延误3个周期响应中断。

这点误差倒是没什么关系。

但是如果单片机正处理其他的中断(同级或更高级)。

要等其执行完其他中断,再执行一条主程序指令,才会响应定时器0中断。

因为程序千差万别,所以其他中断占用的时间,就没准儿了。

更要命的是,这类影响是随机的,你根本无法纠正。

看起来好像没有办法了,但是你深入研究定时器的工作原理以后,你会发现这个问题还是有可能解决的。

请仔细看一下,我上面的中断程序,“TH0=0xEE;” 你是否注意到,我没有给TL0重新赋值。

这可不是疏忽忘了。

我们知道定时器只要开着,TH0和TL0就会不断的增一,增到FF FF,再增一就溢出,这时TF0被硬件置1(也就是中断请求)。

我们要注意的就是不管定时器中断是否被响应,TH0和TL0仍然会不断增一,FF FF增一00 00 再增
一 00 01 再增一 00 02 。

这就是我为什么要选择5毫秒作为定时长度的原因。

因为TH0=EE TL0=00。

最主要的就是TL0=00。

定时器在溢出产生中断以后,不论响应还是不响应,TL0并不停止计数。

虽然中断响应有可能被延迟,但是延迟的时间仍然被计算。

延迟的时间在下一次中断时会“补上”。

这就是只对TH0重赋值的原因。

从理论上说,真正是一个微秒都不差。

还有一点需要注意。

其他的中断占用的时间太长,TL0增数超过256(2^8),定时器中断响应时TH0已经大于0了,直接写TH0=0xEE;就有误差了.可以改成 TH0=TH0+0xEE;但这样也会有一点点问题,我们不在这里详细讨论。

最好还是控制其他的中断占用时间不要超过240个机器周期。

每秒钟最后一次入中断的误差:原因和上面说的相同,误差在下一秒也会“补上”。

定时器例程之二:模拟时钟
void init_timer0(void)
{
//以下为初始化定时器
TMOD|=0x01; //选择定时器0,工作模式1:16位定时器
TH0=0xEE; //置定时初始值
TL0=0x00;
//初始化完毕。

ET0=1; //开定时器0中断,允许定时器0中断。

EA =1; //开全局中断。

允许所有中断
TR0=1; //开始计数
}
unsigned char time_allow; //整点报时标志
unsigned char time_num; //报时的次数,
unsigned char fmq_times; //整点报时蜂鸣器声音维持时间计数unsigned char set_kk_times;
unsigned char ms5_times; //5ms中断计次
unsigned char hour,min,sec; //定义时,分,秒。

void timer0() interrupt 1 using 1 //5ms中断一次定时器中断处理函数
{
//重新置位计数初始值在工作方式1下,需要重新置位定时初始值,程序才会再一次进入中断,工作方式0,3也是如此,只有工作方式2不需要重新置位初始值。

TH0=0xEE; //置定时初始值
if(++ms5_times>=200) //5ms中断一次,计数200次达到1s
{
ms5_times=0;
dc1=0; //处理小数点点亮
sec++; //时钟秒+1
if(sec>=60) //秒计数达到60
{
sec=0;
min++; //分钟+1
if(min>=60) //分钟计数达到60
{
min=0;
hour++; //小时+1
if(hour>23) hour=0; //24小时制,计数达到24,清零 }
}
}
if(0==sec)
{if(0==min) //如果时间达到整点。

允许报时功能
{if(hour>12) time_num=hour-12; //如果时间超过12点,报时声音次数减12 else if(0==hour) time_num=12; //如果时间为零点。

报时声音为12次
else time_num=hour; //报时次数为时间值
time_allow=1; //报时允许标志置位 }
if(30==min) //如果时间达到半点,允许报时功能
{time_allow=1; //报时标志置位
time_num=1; //报时次数 1次 }}
if(1==time_allow) //如果报时允许
{if(fmq_times++>200)
{fmq_times=0;
spk=1; //蜂鸣器停止发声
time_num--; //报时次数减 1 }
if(100==fmq_times) spk=0; //蜂鸣器发声
if(0==time_num) time_allow=0;//报时结束清零报时标志位}
if(ms5_times==80) dc1=1; //处理小数点熄灭
if(set_kk_times++>200) set_kk_times=0;
disp_LED(display); //刷新数码管}。

相关文档
最新文档