定时器
1、定时器如何定时
通过晶振(晶体振荡器)发出脉冲,记录分频后的脉冲的个数来进行定时。51单片机的定时器时钟源分频系数为6T和12T。在下载器的STC-ISP中选择。默认为12T(12分频)。
![](https://i-blog.csdnimg.cn/direct/c6982813a50e4d01b2e5233288e0ddbd.png)
![](https://i-blog.csdnimg.cn/direct/6ae7094e9ff44ffc9acf44b813258905.png)
如图:51单片机的晶振为12MHz
,经过12T后变为1MHz
,则时钟周期为1S/(1M) = 1(us)
,即定时器的计时器每隔1(us)
进行+1。当定时器的计数器使用16位时,最大计数为65536。所以定时器最长定时时间为65536 * 1(us) = 65536(us) = 65.536ms
2、寄存器简介
综上:若12T下需要定时1ms
(即定时器启动,1ms后定时器溢出标志位置位),那么定时器T0的计数器的初值应该设置为多少喃?
1ms = 1000us,即65536 - 1000 = 64536。定时计数器T0的初值应该设置为64536,即定时计时器从64536开始计数。
TH0 = 64536 >> 8;
TL0 = 64536 & 0x00FF
定时器溢出后,计数器从0开始向65535计数。
3、定时器定时
实验①
实验:使用T0,定时器定时10ms,轮询式让LED每隔1s闪烁
①Time.c文件的代码如下:
c
#include "Time.h"
/**
* 定时器T0的初始化
*/
void Time0_Init(void)
{
/* 设置T0的计数器为16,且时钟来源为晶振 TMOD = xxxx 0001 */
TMOD &= 0xF0; //将低4位置0
TMOD |= 0x01; //最低位置1
/* 设置定时计数器的初始值,定时10ms */
TH0 = ((65536 - 10000) >> 8);
TL0 = (65536 - 10000) & 0x00FF;
/* 启动T0 */
TF0 = 0; //清除标志位
TR0 = 1; //启动定时器0
}
②Time.h文件的代码如下:
c
#ifndef __Time_H
#define __Time_H
#include <reg51.h> //包含51头文件,里面全是寄存器地址>
void Time0_Init(void);
#endif
③main.c文件的代码如下:
c
#include "Time.h"
#include "LED.h"
void main(void)
{
unsigned char Num = 0;
Time0_Init(); //定时器10ms
while(1)
{
if(TF0 == 1) //10ms定时器到了
{
TF0 = 0; //清除标志位
/* 重新设置定时计数器的初始值,定时10ms */
TH0 = ((65536 - 10000) >> 8);
TL0 = (65536 - 10000) & 0x00FF;
Num++;
if(Num == 100) //定时1s到了
{
Num = 0;
LED_Turn(1);//翻转LED1
}
}
}
}
实验②
实验:使用T0构造延时函数,让LED每隔1s闪烁
①Time.c文件的代码如下:
c
#include "Time.h"
/**
* 使用T0构造延时函数
*/
void Delay_ms(unsigned short second)
{
/* 设置T0的计数器为16,且时钟来源为晶振 TMOD = xxxx 0001 */
TMOD &= 0xF0; //将低4位置0
TMOD |= 0x01; //最低位置1
/* 设置定时计数器的初始值,定时1ms */
TH0 = ((65536 - 1000) >> 8);
TL0 = (65536 - 1000) & 0x00FF;
/* 启动T0 */
TF0 = 0; //清除标志位
TR0 = 1;
/* 循环等待 */
while(second)
{
if(TF0 == 1)
{
TF0 = 0; //清除标志位
--second; //每隔1ms进行减1
/* 设置定时计数器的初始值,定时1ms */
TH0 = ((65536 - 1000) >> 8);
TL0 = (65536 - 1000) & 0x00FF;
}
}
TR0 = 0; //关闭定时器
}
②Time.h文件的代码如下:
c
#ifndef __Time_H
#define __Time_H
#include <reg51.h> //包含51头文件,里面全是寄存器地址>
void Delay_ms(unsigned short second);
#endif
③main.c文件的代码如下:
c
#include "Time.h"
#include "LED.h"
void main(void)
{
while(1)
{
Delay_ms(1000);
LED_Turn(1);
}
}
4、定时器中断
实验③
实验:使用T0,定时器定时1s,轮询式让LED每隔1s闪烁
①Time.c文件的代码如下:
c
#include "Time.h"
/**
* 定时器T0的初始化,且开启中断
*/
void Time0It_Init(void)
{
/* 设置T0的计数器为16,且时钟来源为晶振 TMOD = xxxx 0001 */
TMOD &= 0xF0; //将低4位置0
TMOD |= 0x01; //最低位置1
/* 设置定时计数器的初始值,定时10ms */
TH0 = ((65536 - 10000) >> 8);
TL0 = (65536 - 10000) & 0x00FF;
/* 启动T0 */
TF0 = 0; //清除标志位
TR0 = 1;
/* 开启中断 */
ET0 = 1; //使能定时器溢出中断
EA = 1; //开启总开关
}
/********中断服务函数*******/
/**
* T0的中断服务函数(每隔10ms对Number加1,Number加到100,相当于隔了1s,在执行LED翻转)
*/
static unsigned char Number = 0;
void Time0_Rountine(void) interrupt 1
{
//这里不用清除标志位,硬件会自动清除
TH0 = ((65536 - 10000) >> 8);
TL0 = (65536 - 10000) & 0x00FF;
Number++;
if(Number == 100)
{
Number = 0;
LED_Turn(1);//翻转LED
}
}
②Time.h文件的代码如下:
c
#ifndef __Time_H
#define __Time_H
#include <reg51.h> //包含51头文件,里面全是寄存器地址>
#include "LED.h"
void Time0It_Init(void);
#endif
③main.c文件的代码如下:
c
#include "Time.h"
#include "LED.h"
void main(void)
{
Time0It_Init(); //定时器10ms
while(1)
{
}
}
![](https://i-blog.csdnimg.cn/direct/5b496b54786949da866d0103e4008a7a.png)
实验④
实验:
①使用T0中断,定时器定时1s,让数码管从60每隔1s减1,减到0后重新变为60,并且每次数值变化,LED就翻转一次
②使用T0中断每隔1ms对数码管进行刷新显示
①Time.c文件的代码如下:
c
#include "Time.h"
/**
* 定时器T0的初始化,且开启中断
*/
void Time0It_Init(void)
{
/* 设置T0的计数器为16,且时钟来源为晶振 TMOD = xxxx 0001 */
TMOD &= 0xF0; //将低4位置0
TMOD |= 0x01; //最低位置1
/* 设置定时计数器的初始值,定时1ms */
TH0 = ((65536 - 1000) >> 8);
TL0 = (65536 - 1000) & 0x00FF;
/* 启动T0 */
TF0 = 0; //清除标志位
TR0 = 1;
/* 开启中断 */
ET0 = 1; //使能定时器溢出中断
EA = 1; //开启总开关
}
/********中断服务函数*******/
/**
* T0的中断服务函数(刷新数码管显示,执行每隔1s数值+1,且LED的翻转)
*/
static unsigned short Number = 0;
static unsigned char Counter1 = 0;
void Time0_Rountine(void) interrupt 1
{
//这里不用清除标志位,硬件会自动清除
TH0 = ((65536 - 1000) >> 8);
TL0 = (65536 - 1000) & 0x00FF;
/* 计数:累加时间,等隔1s后在执行 */
Number++; //每隔1ms加1
if(Number == 1000) //1s了
{
Number = 0;
Counter1++;
if(Counter1 > 60)
{
Counter1 = 0;
}
LED_Turn(1); //LED翻转
}
/* 每隔1ms就刷新显示数码管 */
Show_Num(Counter1); //注意将此函数的延时函数取消
}
②Time.h文件的代码如下:
c
#ifndef __Time_H
#define __Time_H
#include <reg51.h> //包含51头文件,里面全是寄存器地址>
#include "LED.h"
#include "User_Seg.h"
void Time0It_Init(void);
#endif
③main.c文件的代码如下:
c
#include "Time.h"
void main(void)
{
Time0It_Init(); //启动定时器0
while(1)
{
}
}
![](https://i-blog.csdnimg.cn/direct/c8f4b4afb94d4ecfba73bdfad88eb99e.png)
综上:使用上面的定时器中断精髓在于,使用Number++;//每隔1ms加1,if(Number == 1000)//1s了,再去执行if里面的函数,这样相当于无形的构造了一个1s的定时器中断函数
5、定时器计数功能
定时器的计数功能就是记录外部脉冲信号的个数,此时定时器不在使用内部晶振时钟源。则寄存器TMOD中的C/T位应该置1,如下图所示:
实验⑤
实验:
①使用定时器T1对外部脉冲信号进行计数,并通过数码管显示出来
②并测量出脉冲信号的频率,并通过数码管显示出来
③使用外部中断0,按键切换数码管的显示界面(显示测量的脉冲个数/脉冲频率)
①Time.c文件的代码如下:
c
#include "Time.h"
/**
* 定时器T0的初始化,且开启中断
*/
void Time0It_Init(void)
{
/* 设置T0的计数器为16,且时钟来源为晶振 TMOD = xxxx 0001 */
TMOD &= 0xF0; //将低4位置0
TMOD |= 0x01; //最低位置1
/* 设置定时计数器的初始值,定时1ms */
TH0 = ((65536 - 1000) >> 8);
TL0 = (65536 - 1000) & 0x00FF;
/* 启动T0 */
TF0 = 0; //清除标志位
TR0 = 1;
/* 开启中断 */
ET0 = 1; //使能定时器溢出中断
EA = 1; //开启总开关
}
/**
* 定时器T1的初始化,用作计数器测量外部脉冲个数
*/
void Time1_Init(void)
{
/* 设置T1的计数器为16,且时钟来源为外部 TMOD = 0101 xxxx */
TMOD &= 0x0F; //将高4位置0
TMOD |= 0x50; //高4位为0101
/* 设置定时计数器的初始值为0 */
TH1 = 0;
TL1 = 0;
/* 启动T1*/
TF1 = 0; //清除标志位
TR1 = 1;
}
/********中断服务函数*******/
/**
* T0的中断服务函数(刷新显示数码管)
*/
static unsigned short Number = 0;
static unsigned short Freq = 0;
static unsigned int Data = 0;
extern unsigned char Flag;
void Time0_Rountine(void) interrupt 1
{
//这里不用清除标志位,硬件会自动清除
TH0 = ((65536 - 1000) >> 8);
TL0 = (65536 - 1000) & 0x00FF;
/* 计数:累加时间,等隔1s后在执行 */
Number++; //每隔1ms加1
if(Number == 1000) //1s了
{
/* 测频率法:得出脉冲频率 */
Data = (TH1 << 8) + TL1;
Freq = Data - Freq; //脉冲频率
}
/* 每隔1ms就刷新显示数码管 */
switch(Flag % 2) //外部中断改变Flag的状态值
{
case 0:
LED_On(1); //LED1点亮
Show_Num((TH1 << 8) + TL1); //显示脉冲信号个数
break;
case 1:
LED_OFF(1); //LED1熄灭
Show_Num(Freq); //显示脉冲信号频率
break;
default:break;
}
}
②Time.h文件的代码如下:
c
#ifndef __Time_H
#define __Time_H
#include <reg51.h> //包含51头文件,里面全是寄存器地址>
#include "User_Seg.h"
#include "LED.h"
void Time0It_Init(void);
void Time1_Init(void);
#endif
③EXTI.c文件的代码如下:
c
#include "EXTI.h"
/**
* 外部中断INT0的初始化(下降沿触发)
*/
void INT0_Init(void)
{
IT0 = 1; //下降沿触发标志位
EX0 = 1; //IN0中断使能
EA = 1; //中断总开关
}
/**
* 外部中断INT1的初始化(下降沿触发)
*/
void INT1_Init(void)
{
IT1 = 1; //下降沿触发标志位
EX1 = 1; //IN1中断使能
EA = 1; //中断总开关
}
/*************中断服务函数******************/
/**
* 外部中断0的服务函数
*/
unsigned char Flag = 0;
void Int0_Routine(void) interrupt 0
{
Flag++; //改变Flag的值,用于切换数码管的显示状态
if(Flag == 2)
{
Flag = 0;//防止Flag数值溢出
}
}
/**
* 外部中断1的服务函数
*/
void Int1_Routine(void) interrupt 2
{
LED_OFF(1); //熄灭LED
}
④main.c文件的代码如下:
c
#include "Time.h"
#include "EXTI.h"
void main(void)
{
Time1_Init(); //计数器1初始化
Time0It_Init(); //定时器0初始化
INT0_Init(); //外部中断0初始化
while(1)
{
}
}
综上:代码代码得出频率是使用的测频法,即在1s内测量出脉冲的个数即为频率,此方法适用于高频信号。若信号频率较低,则使用测周法,即测量周期的时间,继而转换为频率。
实验⑥
使用测周法测量信号发生器产生的频率,并通过数码管显示出来
信号发生器连接到外部中断引脚,当信号由高电平变为低电平时,触发外部中断,在外部中断服务函数里面获取定时器T1的计数器的值,通过T1计数器的值得出周期。
①Time.c文件的代码如下:
c
#include "Time.h"
/**
* 定时器T0的初始化,且开启中断
*/
void Time0It_Init(void)
{
/* 设置T0的计数器为16,且时钟来源为晶振 TMOD = xxxx 0001 */
TMOD &= 0xF0; //将低4位置0
TMOD |= 0x01; //最低位置1
/* 设置定时计数器的初始值,定时1ms */
TH0 = ((65536 - 1000) >> 8);
TL0 = (65536 - 1000) & 0x00FF;
/* 启动T0 */
TF0 = 0; //清除标志位
TR0 = 1;
/* 开启中断 */
ET0 = 1; //使能定时器溢出中断
EA = 1; //开启总开关
}
/**
* 定时器T1的初始化,用作计数器测量外部脉冲个数
*/
void Time1_Init(void)
{
/* 设置T1的计数器为16,且时钟来源为晶振 TMOD = 0001 xxxx */
TMOD &= 0x0F; //将高4位置0
TMOD |= 0x10; //高4位为0001
/* 设置定时计数器的初始值为0 */
TH1 = 0;
TL1 = 0;
/* 启动T1 */
TF1 = 0; //清除标志位
TR1 = 1;
}
/********中断服务函数*******/
/**
* T0的中断服务函数
*/
extern unsigned int Freq; //频率
void Time0_Rountine(void) interrupt 1
{
//这里不用清除标志位,硬件会自动清除
TH0 = ((65536 - 1000) >> 8);
TL0 = (65536 - 1000) & 0x00FF;
/* 数码管显示频率值 */
Show_Num(Freq);
}
②Time.h文件的代码如下:
c
#ifndef __Time_H
#define __Time_H
#include <reg51.h> //包含51头文件,里面全是寄存器地址>
#include "User_Seg.h"
void Time0It_Init(void);
void Time1_Init(void);
#endif
③EXTI.c文件的代码如下:
c
#include "EXTI.h"
/**
* 外部中断INT0的初始化(下降沿触发)
*/
void INT0_Init(void)
{
IT0 = 1; //下降沿触发标志位
EX0 = 1; //IN0中断使能
EA = 1; //中断总开关
}
/*************中断服务函数******************/
/**
* 外部中断0的服务函数
*/
unsigned int Freq = 0;
extern unsigned int timeVlau;
void Int0_Routine(void) interrupt 0
{
TR1 = 0;//关闭定时器T1
/* 获取定时器的计数器的值 */
Freq = 1000000 / (((TH1 << 8) + TL1) + timeVlau * 65535);
/* 将定时器中的计数器清0 */
TH1 = 0;
TL1 = 0;
timeVlau = 0;
TR1 = 1;//开启定时器T1
}
④main.c文件的代码如下:
c
#include "Time.h"
#include "EXTI.h"
unsigned int timeVlau = 0;
void main(void)
{
Time1_Init();
Time0It_Init();
INT0_Init();
while(1)
{
if(TF1 == 1)
{
TF1 = 0;
timeVlau++;
}
}
}
![](https://i-blog.csdnimg.cn/direct/a513ebbafeeb4db69a6cc9677735f6b7.png)