学蓝桥Chapter8:计时器
定时器是什么
定时器:对时钟信号或外部输入信号进行计数,当计数完成,定时器向CPU发出中断处理请求,从而实现定时功能。
从本质来讲:定时器就是计数器
,可以理解为特殊的计数器
51单片机定时/计数器
51单片机有两个定时/计数器T0和T1,均为16位加法计数器,由低八位TLx和高八位THx两个寄存器组成
,最大计数值为65535个计数脉冲
- 计算公式:
定时器时钟频率=内部时钟振荡器振荡频率÷分频数
定时器1个脉冲的周期=1/定时器周期
定时脉冲数=定时数÷定时器1个脉冲的周期
计数脉冲来源:
- 系统时钟振荡器(可以理解为MCU内部时钟振荡器,其
输出信号
就是内部时钟信号
)输出信号的12分频:
---- 例: 如果你的系统时钟为12mhz,那么你的定时器时钟
则为12mhz/12=1mhz,对应的定时器1个脉冲的周期
就是1/1mhz=1μs。 - T0或T1引脚输入的
外部脉冲信号
51单片机定时器T0的大体运作图
相关寄存器
IE寄存器:
IP寄存器:
TMOD寄存器:
不能进行位寻址,只能进行字节操作,也就是只能直接将数值写给TMOD从而控制其运作,也就是只能进行类似TMOD=0xFF这样的操作
自动重装模式:当定时/计数值超过最大值时,会回到0值,相当于重置定时/计数值。因此当设定自动重装功能的时候,需要在程序中设定重装后回具体初值的功能,不然定时器运行效果可能出错。
非自动重装模式:如果处于非自动重装模式,则需要对THx和TLx重新赋值
TCON寄存器:
定时/计数器程序设计思路
打开总中断——EA=1
配置工作模式——TMOD寄存器赋值
计算定时/计数初值——THx和TLx寄存器赋值
定时/计数器使能中断——ET0/1=1
启动定时器——TR0/1=1
例题
51单片机中,若我要定时50ms,TH0(高八位)和TL0(低八位)该怎么设置:
TH0=(65535-50000)/256
TL0=(65535-50000)%256(低八位取余数)
程序实现
利用定时/计数器溢出中断0实现P0^0口的LED灯1秒闪烁1次.
#include <reg52.h>
sbit L1=P0^0;
sbit L8=P0^7;
void hc(){ //选择锁存器
P2=(P2&0x1f)|0x80;
}
void initTimer0(){ //定时器T0初始化函数
EA=1;
ET0=1;
T0=0X01;//选择低四位定义定时/计数器0,并且选择TR0/TR1(具体选择要用TR0=1或者TR1=1)作为启动位
TR0=1;//最终选择TR0作为启动位,即选择定时器T0;若选择TR1作为具体启动位,则TR1=1,即选择定时器T1,那么将会执行的是interrupt 3的函数内容
TH0=(65535-50000)/256;
TL0=(65535-50000)%256;
}
unsigned int count=0; //倍数,如每500ms执行一次,则需要50ms*10,这里的10就是count充当的角色
void servicetime0() interrupt 1{ //选择定时器/计数器 T0 溢出中断;定时器中断函数:在中断过程中你需要执行的动作
//非自动重装模式,需要对THx和TLx重新赋值
TH0=(65535-50000)/256; //高8位定时器存入50ms的部分值
TL0=(65535-50000)%256; //低8位定时器存入50ms的部分值
//实际上因为51单片机是8位的单片机 它的寄存器一般都是8位,而它的定时器是16位,所以只能分为两个寄存器来存储,因而要同时定义高八位和低八位从而组成1个16位值。如果遇到了16位的单片机,则无需定义高8位和低8位直接就能存入16位的值。
count++; //倍数自增
if(count==10){ //当倍数达到10,即50ms的10倍时(此时为500ms)执行动作
L1 =~ L1; //P0^0口LED灯动作
count = 0; //重置倍数,跳出条件动作。
}
}
void main(){
hc();
initTimer0(); //中断初始化函数执行后会自动执行初始化内指定的中断函数,我选中执行的是定时器/计数器溢出中断0(对应中断号为1),所以会执行interrupt 1的函数内容
//经测试,当后面while()存在时,initTimer0在循环体内或体外都可以实现灯泡的500ms动作一次的功能,即1秒内动作两次(即闪烁1次)。
while(1){ //循环执行中断函数,实现灯泡的500ms动作一次的功能,即1秒内动作两次(即闪烁1次)
//initTimer0(); //中断初始化函数执行后会自动执行初始化内指定的中断函数,我选中执行的是定时器/计数器溢出中断0(对应中断号为1),所以会执行interrupt 1的函数内容
}
}
综合训练
使用板子的BTN独立按键模式(J5短接23脚),当按下S4暂停/启动秒表计时,当按下S5则清零。并且在8位共阳数码管上分别以两位的形式显示分时,毫秒,毫秒以0.05ms作为单位,当显示到20自动令秒进1
#include <reg52.h>
sbit s4=P3^3;
sbit s5=P3^2;
unsigned char code dx[]={0xc0,0xf9,0xa4,0xb0,0x99,0x12,0x82,0xf8,0x80,0x90,0xbf}; //共阳极数码管段码
unsigned char min=0; //分钟变量定义
unsigned char sec=0; //秒变量定义
unsigned char msec=0; //毫秒变量定义
void hc573(unsigned char num){ //hc573锁存器初始化:选择指定的hc573锁存器
switch(num)
{
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 displaysw(unsigned int value,unsigned int pos){ //数码管显示函数
hc573(6);
P0=0x01 << pos; //P0初始值设为0x01,根据pos值决定点亮第几位。若pos为3,则P0值左移3位,此时P0=00001000=0x08,因为0x08对应4位数码管从左往右的第4位,所以会亮从左往右的第4位数码管
hc573(7);
P0=value; //这里的value与dx[]形成了链接的关系
}
void delay(unsigned char dd){ //延时函数
while(dd){
dd--;
}
}
void displaytime(){ //该函数只负责显示时间的方法。核心计算函数是initT0()和workT0()组合在一起的中断函数
displaysw(dx[msec%10],7);//数码管显示毫秒的从右数第1位
delay(50);
displaysw(dx[msec/10],6);//数码管显示毫秒的从右数第2位
delay(50);
displaysw(dx[10],5); //数码管显示”-“
displaysw(dx[sec%10],4);//数码管显示秒的从右数第1位
delay(50);
displaysw(dx[sec/10],3);//数码管显示秒的从右数第2位
delay(50);
displaysw(dx[10],2); //数码管显示”-“
displaysw(dx[min%10],1);//数码管显示分钟的从右数第1位
delay(50);
displaysw(dx[min/10],0);//数码管显示分钟的从右数第2位
delay(50);
}
void scankey(){ //去抖动操作,防止误触操作
if(s4==0){ //S4被按下,秒表启动、暂停
delay(10);
if(s4==0){
TR0 =~ TR0;
}
}
if(s5==0){ //S5按下秒表清零
delay(10);
if(s5==0){
min=0;
sec=0;
msec=0;
}
}
}
void initT0(){ //定时器0初始化函数
EA=1;
ET0=1;
TMOD=0X01;
TR0=1; //选用T0定时器
TH0=(65535-50000)/256;
TL0=(65535-50000)%256;
}
void workT0() interrupt 1{ //中断号1对应定时器T0,这是T0的动作函数
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 main(){
initT0(); //启用中断函数
while(1){
displaytime();
scankey();
}
}