单片机模板六:定时器
一、定时器的基本原理
1、寄存器
顾名思义,定时器就是用来进行定时的。定时器内部有一个寄存器,每经过一个周期,寄存器会自动加一。标准的 51 单片机内部有 T0 和 T1 这两个定时器,而寄存器中TH0/TL0 用于 T0,TH1/TL1 用于T1。
对于寄存器,我们将介绍两种——TCON和TMOD。
(1) TMOD
定时器/计数器模式控制寄存器TMOD是一个逐位定义的8位寄存器,但只能使用字节寻址,其字节地址为89H。
TF1——TF1=1表示T1有中断产生;
TR1——TR1=1表示T1开始运行;
TF0——TF0=1表示T0有中断产生;
TR0——TR0=1表示T0开始运行;
IE1——IE1=1表示INT1有中断产生;
IT1——IT1=1表示INT1为下降沿触发,IT1=0表示INT1为低电平触发;
IE0——IE0=1表示INT0有中断产生;
IT0——IT0=1表示INT0为下降沿(负跳变)触发,IT0=0表示INT0为低电平触发;
(2)TCON
TCON为控制寄存器,作用是控制定时器的启、停,标志定时器溢出和中断情况。位0~位3为T0定时/计数器的设置;位4~位7为T1定时/计数器的设置。
GATE:为门控位,GATE=0时,只要在编写程序时,使TCON中的TR0或TR1为1,就可以启动定时器/计数器工作。GATE=1时,不仅要在编写程序时,使TCON中的TR0或TR1为1,且需要外部引脚也为高电平,才能工作。
C/T:定时/计数模式切换,C/T=0时为定时模式,C/T=1时为计数模式。
M1/M0:用来选择定时计/计数器的工作方式,一般使用都是采用16位的计时计数器。
对于定时器的
4
种工作模式,模式
0
是为了兼容老的
8048
系列单片机而设计的,现在的51
几乎不会用到这种模式,而模式
3
它的功能用模式2
完全可以取代,所以基本上也是不用的,那么我们就重点来学习模式
1
和模式
2
。
模式
1
,是
THn
和
TLn
组成了一个
16
位的定时器,计数范围是
0
~
65535
,溢出后,只要不对THn和
TLn
重新赋值,则从
0
开始计数。模式
2
,是
8
位自动重装载模式,只有
TLn做加1
计数,计数范围
0
~
255
,
THn
的值并不发生变化,而是保持原值,
TLn
溢出后,
TFn就直接置1
了,并且
THn
原先的值直接赋给
TLn
,然后
TLn
从新赋值的这个数字开始计数。
(3)模式1
以定时器0为例——
1
、
TR0
和下边或门电路的结果要进行与运算,
TR0
如果是
0
的话,与运算完了肯定是
0,
所以如果要让定时器工作,那么TR0
就必须置
1
。
2
、这里的与门结果要想得到
1
,那么前面的或门出来的结果必须也得是
1
才行。在
GATE位为1的情况下,经过一个非门变成
0
,或门电路结果要想是
1
的话,那
INT0
即
P3.2
引脚必须是1
的情况下,这个时候定时器才会工作,而
INT0
引脚是
0
的情况下,定时器不工作,这就是GATE
位的作用。
3
、当
GATE
位为
0
的时候,经过一个非门会变成
1
,那么不管
INT0
引脚是什么电平,经过或门电路后都肯定是1
,定时器就会工作。
4
、要想让定时器工作,就是自动加
1
,从图上看有两种方式,第一种方式是那个开关打到上边的箭头,就是C/T = 0
的时候,一个机器周期
TL
就会加
1
一次,当开关打到下边的箭头,即C/T =1
的时候,
T0
引脚即
P3.4
引脚来一个脉冲,
TL
就加
1
一次,这也就是计数器功能。
(4)定时器操作步骤
1.选择工作方式(设置M0,M1的值)
2.选择控制方式GATE(为0是只要软件设定好参数即可,为1则需要软件设定参数,且定时器/计数器的中断引脚需要为高电平)
3.确定定时器的工作模式,是定时模式还是计数模式 C/T
4.给定时器设初值(设置THX与TLX)
5.开启定时器中断(设置ET0或ET1)
6.开启总中断(设置EA的值)
7.定时器/计数器的选择T0/T1(设置TR1或TR0的值)
二、定时器的应用
1、定时器T0模式1
实现每隔1s,L1闪烁一次,即亮0.5s,灭0.5s;每隔10s,L8闪烁一次,即亮5s,灭5s。
代码:
#include <reg52.h>
sbit L1 = P0^0;
sbit L8 = P0^7;
//LED灯
void Select_HC573(unsigned char channel){
switch(channel)
{
case 4:
P2 = (P2 & 0x1f)|0x80;break;
case 5:
P2 = (P2 & 0x1f)|0xa0;break;
case 6:
P2 = (P2 & 0x1f)|0xc0;break;
case 7:
P2 = (P2 & 0x1f)|0xe0;break;
}
}
//================================
void Init_T0()
{
TMOD = 0x01; //模式1,16位无自动重装
/* TMOD只能高四位控制T1,此处不需要,所以全部置0
低四位控制T0,且使用模式1*/
TH0 = (65535-50000) / 256; //高8位
TL0 = (65535-50000) % 256; //低8位
ET0 = 1; //T0的中断允许
EA = 1; //总中断
TR0 =1; //定时器0运行控制
}
unsigned char count = 0;
unsigned char count1 = 0;
//中断
void Service_T0() interrupt 1
{
Select_HC573(4);
TH0 = (65535-50000) / 256; //模式1无自动重装,中断服务函数需重新赋初始值
TL0 = (65535-50000) % 256;
count++;
count1++;
if(count == 10)
{
L1 = ~L1;
count = 0;
}
if(count1 == 100)
{
L8 = ~L8;
count1 = 0;
}
}
//=================================
void Init_System()
{
Select_HC573(5);
P0 = 0x00;
Select_HC573(4);
P0 = 0xff;
}
void main()
{
Init_T0();
Init_System();
while(1)
{
}
}
2、实现分、秒和毫秒的显示
当按下S4时,开始计时,再按一下S4,暂停计时;按下S5,计时清零。
代码:
#include <reg52.h>
sbit S4 = P3^3;
sbit S5 = P3^2;
unsigned char SMG_duanma[12]={
0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,
0xbf,0x7f};
unsigned char min;
unsigned char sec;
unsigned char msec;
void Select_HC573(unsigned char channel)
{
switch(channel)
{
case 4:
P2 = (P2 & 0x1f) | 0x80;
break;
case 5:
P2 = (P2 & 0x1f) | 0xa0;
break;
case 6:
P2 = (P2 & 0x1f) | 0xc0;
break;
case 7:
P2 = (P2 & 0x1f) | 0xe0;
break;
}
}
void SMG_bit(unsigned char pos,unsigned char dat)
{
Select_HC573(6);
P0 = 0x01 << pos;
Select_HC573(7);
P0 = dat;
}
void SMG_Delay(unsigned int t)
{
while(t–);
}
void SMG_Display()
{
SMG_bit(7,SMG_duanma[msec%10]);
SMG_Delay(500);
SMG_bit(6,SMG_duanma[msec/10]);
SMG_Delay(500);
SMG_bit(5,SMG_duanma[10]);
SMG_Delay(500);
SMG_bit(4,SMG_duanma[sec%10]);
SMG_Delay(500);
SMG_bit(3,SMG_duanma[sec/10]);
SMG_Delay(500);
SMG_bit(2,SMG_duanma[10]);
SMG_Delay(500);
SMG_bit(1,SMG_duanma[min%10]);
SMG_Delay(500);
SMG_bit(0,SMG_duanma[min/10]);
SMG_Delay(500);
}
//==========定时器相关函数================
void Init_Time0()
{
TMOD = 0x01; //模式1,16位无自动重装
TH0 = (65535-50000) / 256; //高8位
TL0 = (65535-50000) % 256; //低8位
ET0 = 1; //T0的中断允许
EA = 1; //总中断
TR0 =1; //定时器0运行控制
}
void Service_Time0() interrupt 1
{
TH0 = (65535-50000) / 256;
TL0 = (65535-50000) % 256;
msec++;
if(msec == 20)
{
sec++;
msec = 0;
if(sec == 60)
{
min++;
sec = 0;
}
if(min == 99)
min = 0;
}
}
//=================================
void Key_Delay(unsigned int t)
{
while(t–);
}
void Key_fun()
{
if(S4 == 0)
{
Key_Delay(100); //延时消抖
if(S4 == 0)
{
TR0 = ~TR0; //暂停或启动
while(S4 == 0) //松手检测,数码管仍动态显示
{
SMG_Display();
}
}
}
if(S5 == 0)
{
Key_Delay(100); //延时消抖
if(S5 == 0)
{
min = sec = msec = 0; //复位
while(S5 == 0)
{
SMG_Display();
}
}
}
}
void Init_System()
{
Select_HC573(5);
P0 = 0x00;
Select_HC573(4);
P0 = 0xff;
}
void main()
{
Init_Time0();
Init_System();
while(1)
{
SMG_Display();
Key_fun();
}
}
作者:lmp_041018