赞
踩
17.1.
定时器介绍:51单片机的定时器属于单片机的内部资源,其电路的连接和运转均在单片机内部完成,无需占用CPU外围IO接口;
定时器作用:
(1)用于计时系统,可实现软件计时,或者使程序每隔一固定时间完成一项操作;
(2)替代长时间的Delay,提高CPU的运行效率和处理速度,因为delay程序占用CPU资源,同时导致CPU无法处理外部诸如按键的操作;
(…)
17.2. STC89C52 定时器相关扩展阅读:STC89C52的介绍手册
官方链接地址: https://www.stcmcu.com/datasheet/stc/STC-AD-PDF/STC89C51RC-RD+_GUIDE-CHINESE.pdf
17.3. STC89C52定时器资源
•定时器个数:3个(T0、T1、T2),T0和T1与传统的51单片机兼容,T2是此型号单片机增加的资源
•注意:定时器的资源和单片机的型号是关联在一起的,不同的型号可能会有不同的定时器个数和操作方式,但一般来说,T0和T1的操作方式是所有51单片机所共有的
17.4. 51单片机定时器内部工作原理
•定时器在单片机内部就像一个小闹钟一样,根据时钟的输出信号,每隔“一秒”,计数单元的数值就增加一,当计数单元数值增加到“设定的闹钟提醒时间”时,计数单元就会向中断系统发出中断申请,产生“响铃提醒”,使程序跳转到中断服务函数中执行
17.5. 定时器工作模式:
•STC89C52的T0和T1均有四种工作模式:
模式0:13位定时器/计数器
模式1:16位定时器/计数器(常用)
模式2:8位自动重装模式
模式3:两个8位计数器
•工作模式1框图:
•SYSclk:系统时钟,即晶振周期,本开发板上的晶振为12MHz
17.6. 中断系统
17.7. 中断程序流程:
17.8. 中断资源:
•中断源个数:8个(外部中断0、定时器0中断、外部中断1、定时器1中断、串口中断、定时器2中断、外部中断2、外部中断3)
•中断优先级个数:4个
•中断号:
•注意:中断的资源和单片机的型号是关联在一起的,不同的型号可能会有不同的中断资源,例如中断源个数不同、中断优先级个数不同等等
17.9. 定时器和中断系统;
17.10. 定时器相关寄存器:
#include <REGX52.h> void TimerR0_Init() //定时器初始化子函数 { //TMOD=0x01; //0000 0001;如果同时使用T0与T1则此TMOD赋值方式有问题 //TMOD=TMOD&0xf0;//把TMOD低4位清0,高4位不变;与清0; //TMOD=TMOD|0x01;//把TMOD最低位置1,高4位不变;或置1; TMOD&=0xf0;//上述简写 TMOD|=0x01;//上述简写 TF0=0; TR0=1; TH0=64535/256; //高8位数值,从64535到65535需要1000us,超出65535后溢出 TL0=64535%256; //低8位数值 ET0=1; EA=1; PT0=0; } void main() //主程序 { TimerR0_Init(); while(1) { } } unsigned int T0_Count; void TimerR0_Routine() interrupt 1 //中断子函数 { TH0=64535/256; //重新赋予初值,防止从0开始计数 TL0=64535%256; //重新赋予初值,防止从0开始计数 T0_Count++; if(T0_Count>=1000) { T0_Count=0; P2_0=~P2_0; } }
此时的C代码为:
void Timer0_Init(void) //1毫秒@12.000MHz
{
//AUXR &= 0x7F; //定时器时钟12T模式,新版本问题此语句删除
TMOD &= 0xF0; //设置定时器模式
TMOD |= 0x01; //设置定时器模式
TL0 = 0x18; //设置定时初始值 计算后与64535%256=17不同;
TH0 = 0xFC; //设置定时初始值 计算后与64535/256相同;
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时
}
#include <REGX52.h> void TimerR0_Init() //定时器0初始化子函数 { TMOD&=0xf0;//上述简写 TMOD|=0x01;//上述简写 TF0=0; TR0=1; TH0=0xfc; //高8位数值,从64535到65535需要1000us,超出65535后溢出 TL0=0x18; //低8位数值 ET0=1; EA=1; PT0=0; } void main() //主程序 { TimerR0_Init(); while(1) { } } unsigned int T0_Count; void TimerR0_Routine() interrupt 1 //中断子函数 { TH0=64535/256; //重新赋予初值,防止从0开始计数 TL0=64535%256; //重新赋予初值,防止从0开始计数 T0_Count++; if(T0_Count>=1000) { T0_Count=0; P2_0=~P2_0; } }
#include <REGX52.H> /** * @brief 定时器初始化子函数,1ms,12MHz * @param 无 * @retval 无 */ void TimeR0_Init() //定时器初始化子函数 { //AUXR &= 0x7F; //定时器时钟12T模式 TMOD &= 0xF0; //设置定时器模式 TMOD |= 0x01; //设置定时器模式 TL0 = 0x18; //设置定时初始值 TH0 = 0xFC; //设置定时初始值 TF0 = 0; //清除TF0标志 TR0 = 1; //定时器0开始计时 ET0=1; EA=1; // PT0=0; //中断优先级 } //以下程序全部注释方便后续调用,定时器中断函数模板 // //void TimeR0_Routine() interrupt 1 //中断子函数 //{ // static unsigned int T0Count //设置静态子函数用T0Count,防止T0Count丢失 // TL0=0x18; //设置定时初始值 // TH0=0xFC; //设置定时初始值 // T0Count++; // if(T0Count>=1000) // { // T0Count=0; // // } //}
#ifndef _TIMER0_H_
#define _TIMER0_H_
void TimeR0_Init();
#endif
#include <REGX52.h> #include "delay_xms.h" /** * @brief 4个独立按键按下判断程序,按键松手返回KeyNum * @param 无 * @retval KeyNum 的返回值,0~4,无按键按下时返回值为0 */ unsigned char Four_Key() { unsigned char KeyNum=0; if(P3_1==0){delay_xms(20);while(P3_1==0);delay_xms(20);KeyNum=1;} if(P3_0==0){delay_xms(20);while(P3_0==0);delay_xms(20);KeyNum=2;} if(P3_2==0){delay_xms(20);while(P3_2==0);delay_xms(20);KeyNum=3;} if(P3_3==0){delay_xms(20);while(P3_3==0);delay_xms(20);KeyNum=4;} return KeyNum; }
#ifndef __H_
#define __H_
unsigned char Four_Key();
#endif
#include <REGX52.h> #include "TimeR0.h" #include "Four_Key.h" unsigned char keynum; void main() //主程序 { TimeR0_Init(); while(1) { keynum=Four_Key(); if(keynum) { if (keynum==1){P2_0=!P2_0;} if (keynum==2){P2_1=!P2_1;} if (keynum==3){P2_2=!P2_2;} if (keynum==4){P2_3=!P2_3;} } } } void TimerR0_Routine() interrupt 1 //中断子函数 { static unsigned int T0Count; //设置静态子函数用T0Count,防止T0Count丢失 TL0=0x18; //设置定时初始值 TH0=0xFC; //设置定时初始值 T0Count++; if(T0Count>=1000) { T0Count=0; } }
测试程序Proteus中测试没有问题,按键按一下相应LED灯亮起,再按一下熄灭;同时测试了独立按键检测程序和中断函数模块程序没有问题。
18.6. LED流水灯控制的实现
通过包含 INTRINS.h的函数库中的_cror_与_crol_循环左移与循环右移函数来实现;
#include <REGX52.h> #include "TimeR0.h" #include "Four_Key.h" #include <INTRINS.h> unsigned char keynum,LED_Mode; //增加LED_Mode变量控制LED灯模式 void main() //主程序 { TimeR0_Init(); P2=0xfe; //P2口LED灯赋予初始值,保证至少1个LED灯亮起; while(1) { keynum=Four_Key(); if(keynum) { if (keynum==1) //当S1按下松开后 { LED_Mode++; //LED_Mode数值自动更新 if (LED_Mode>=2) LED_Mode=0; } } } } void TimerR0_Routine() interrupt 1 //中断子函数 { static unsigned int T0Count; //设置静态子函数用T0Count,防止T0Count丢失 TL0=0x18; //设置定时初始值 TH0=0xFC; //设置定时初始值 T0Count++; if(T0Count>=500) { T0Count=0; if (LED_Mode==0) P2=_crol_(P2,1);//通过_crol_(P2,1)函数控制LED左移 if (LED_Mode==1) P2=_cror_(P2,1);//通过_cror_(P2,1)函数控制LED右移 } }
#include <REGX52.h> #include "LCD1602.h" #include "TimeR0.h" unsigned char sec=55,min=59,hour=23; void main() { TimeR0_Init(); LCD_Init(); LCD_ShowString(1,1,"Clock:");//1行1列显示Clock: LCD_ShowString(2,1," : :");//2行中需要显示的分割冒号 while(1) { LCD_ShowNum(2,7,sec,2);//秒显示 LCD_ShowNum(2,4,min,2);//分钟显示 LCD_ShowNum(2,1,hour,2);//小时显示 } } void TimeR0_Routine() interrupt 1 //中断子函数 { static unsigned int T0Count; //设置静态子函数用T0Count,防止T0Count丢失 TL0=0x18; //设置定时初始值 TH0=0xfc; //设置定时初始值 T0Count++; if(T0Count>=1000) { T0Count=0; sec++; if(sec>=60) { sec=0; min++; if(min>=60) { min=0; hour++; if(hour>=24) { hour=0; } } } } }
proteus测试结果没问题;
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。