定时器
- 1、定时器如何定时
- 2、寄存器简介
- 3、定时器定时
- 实验①
- 实验②
- 4、定时器中断
- 实验③
- 实验④
- 5、定时器计数功能
- 实验⑤
- 实验⑥
1、定时器如何定时
通过晶振(晶体振荡器)发出脉冲,记录分频后的脉冲的个数来进行定时。51单片机的定时器时钟源分频系数为6T和12T。在下载器的STC-ISP中选择。默认为12T(12分频)。
如图: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文件的代码如下:
#include "Time.h"/*** 定时器T0的初始化*/
void Time0_Init(void)
{/* 设置T0的计数器为16,且时钟来源为晶振 TMOD = xxxx 0001 */TMOD &= 0xF0; //将低4位置0TMOD |= 0x01; //最低位置1/* 设置定时计数器的初始值,定时10ms */TH0 = ((65536 - 10000) >> 8);TL0 = (65536 - 10000) & 0x00FF;/* 启动T0 */TF0 = 0; //清除标志位TR0 = 1; //启动定时器0
}
②Time.h文件的代码如下:
#ifndef __Time_H
#define __Time_H
#include <reg51.h> //包含51头文件,里面全是寄存器地址>void Time0_Init(void);#endif
③main.c文件的代码如下:
#include "Time.h"
#include "LED.h"void main(void)
{unsigned char Num = 0;Time0_Init(); //定时器10mswhile(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文件的代码如下:
#include "Time.h"/*** 使用T0构造延时函数*/
void Delay_ms(unsigned short second)
{/* 设置T0的计数器为16,且时钟来源为晶振 TMOD = xxxx 0001 */TMOD &= 0xF0; //将低4位置0TMOD |= 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文件的代码如下:
#ifndef __Time_H
#define __Time_H
#include <reg51.h> //包含51头文件,里面全是寄存器地址>void Delay_ms(unsigned short second);#endif
③main.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文件的代码如下:
#include "Time.h"/*** 定时器T0的初始化,且开启中断*/
void Time0It_Init(void)
{/* 设置T0的计数器为16,且时钟来源为晶振 TMOD = xxxx 0001 */TMOD &= 0xF0; //将低4位置0TMOD |= 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文件的代码如下:
#ifndef __Time_H
#define __Time_H
#include <reg51.h> //包含51头文件,里面全是寄存器地址>
#include "LED.h"void Time0It_Init(void);#endif
③main.c文件的代码如下:
#include "Time.h"
#include "LED.h"void main(void)
{Time0It_Init(); //定时器10mswhile(1){}
}
实验④
实验:
①使用T0中断,定时器定时1s,让数码管从60每隔1s减1,减到0后重新变为60,并且每次数值变化,LED就翻转一次
②使用T0中断每隔1ms对数码管进行刷新显示
①Time.c文件的代码如下:
#include "Time.h"/*** 定时器T0的初始化,且开启中断*/
void Time0It_Init(void)
{/* 设置T0的计数器为16,且时钟来源为晶振 TMOD = xxxx 0001 */TMOD &= 0xF0; //将低4位置0TMOD |= 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加1if(Number == 1000) //1s了{Number = 0;Counter1++;if(Counter1 > 60){Counter1 = 0;}LED_Turn(1); //LED翻转}/* 每隔1ms就刷新显示数码管 */Show_Num(Counter1); //注意将此函数的延时函数取消
}
②Time.h文件的代码如下:
#ifndef __Time_H
#define __Time_H
#include <reg51.h> //包含51头文件,里面全是寄存器地址>
#include "LED.h"
#include "User_Seg.h"void Time0It_Init(void);#endif
③main.c文件的代码如下:
#include "Time.h"void main(void)
{Time0It_Init(); //启动定时器0while(1){}
}
综上:使用上面的定时器中断精髓在于,使用Number++;//每隔1ms加1,if(Number == 1000)//1s了,再去执行if里面的函数,这样相当于无形的构造了一个1s的定时器中断函数
5、定时器计数功能
定时器的计数功能就是记录外部脉冲信号的个数,此时定时器不在使用内部晶振时钟源。则寄存器TMOD中的C/T位应该置1,如下图所示:
实验⑤
实验:
①使用定时器T1对外部脉冲信号进行计数,并通过数码管显示出来
②并测量出脉冲信号的频率,并通过数码管显示出来
③使用外部中断0,按键切换数码管的显示界面(显示测量的脉冲个数/脉冲频率)
①Time.c文件的代码如下:
#include "Time.h"/*** 定时器T0的初始化,且开启中断*/
void Time0It_Init(void)
{/* 设置T0的计数器为16,且时钟来源为晶振 TMOD = xxxx 0001 */TMOD &= 0xF0; //将低4位置0TMOD |= 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位置0TMOD |= 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加1if(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文件的代码如下:
#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文件的代码如下:
#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文件的代码如下:
#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文件的代码如下:
#include "Time.h"/*** 定时器T0的初始化,且开启中断*/
void Time0It_Init(void)
{/* 设置T0的计数器为16,且时钟来源为晶振 TMOD = xxxx 0001 */TMOD &= 0xF0; //将低4位置0TMOD |= 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位置0TMOD |= 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文件的代码如下:
#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文件的代码如下:
#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文件的代码如下:
#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++;}}
}