定时器基础知识
定时器相当于单片机的心脏,有了它才能让单片机知道外部的时间。
前面都是使用延时来进行LED间隔点亮等等功能,这时单片机就在傻傻的数数,就像一个大傻der。有了定时器,单片机就可以摇身一变,成为时间管理大师💪,在空闲的时间可以去干别的事情了~~~~~
定时器2中断
在前面的程序中都是顺序执行程序的,中断系统就是为使CPU具有对外界紧急时间处理能力而设置的。当CPU正在处理某件事情的时候,外界发生了紧急事件请求,要求CPU暂停当前工作,转而去处理这个紧急事件 ,处理完成以后,再回到原来被中断的地方,继续原来的工作,这种过程就称为中断。实现这种功能的不见称为中断系统,请示CPU中断请求的请求源称为中断源。微型机的中断系统一般允许多个中断源,当几个中断源同时向CPU请求中断,要求为它服务的时候,这就存在CPU优先响应哪一个中断源请求的问题,通常根据中断源的轻重缓急排队,优先处理最紧急事件的中断请求源,即规定每一个中断源有一个优先级别。CPU总市先响应优先级别最高的中断请求。比赛中不太需要管优先级其实
中断要求“快进快出”,快进中断请求快速响应,快出就是中断处理完成之后,立刻返回原事件
什么时候触发中断?
当计数器溢出时触发中断,即16位计数器计数超出65535,触发中断(当T2L=0xff T2H=0xff,再来一个脉冲,就溢出,触发中断)
声明中断函数,每一个中断函数都有一个中断编号,只有这个编号,单片机才能识别该函数是一个中断处理函数,识别它是什么中断。相应的中断编号也不需要去背,只需要在STC-ISP中就可以查找就可以。
在范例程序中选择15系列芯片的定时器2的16位自动重载模式就可以看到中断编号以及开中断等等示例程序了。
![](https://img-blog.csdnimg.cn/img_convert/a1e823172548a604693fa609db8bcfe4.png)
中断初始化函数以及中断处理函数
中断的原理在这里也不进行过多介绍,中断的初始化函数也可以不用自己背自己写,可以直接在STC-ISP中生成。
选择定时器计数器选项卡,再选择相应的定时器,以及技术方式等等选项,就可以直接生成相应的定时器初始化函数。但是需要注意的是系统频率,以及IRC频率要选择好,需要与题目要求一致,近几年蓝桥杯要求的都是 12MHZ。定时器时钟也要注意选择12T模式。(1T模式最多(65536/12)us;12T模式最多65536us!)
![](https://img-blog.csdnimg.cn/img_convert/e71ab2e6d6376856c28938b4e9b6b189.png)
//用定时器2产生1ms的中断,并用定时器的方法来实现蜂鸣器以200ms间隔响
//(优化掉while(1)里面的Delay()延时,提高CPU效率)
#include <STC15F2K60S2.H>
#include <intrins.h>
unsigned char buzzer_ctrl;
unsigned char relay_ctrl;
unsigned int buzzer_cnt=0;//必须要定义为unsigned int型的变量
//因为unsigned char是0~255ms,unsigned int是0~65535MS
void device_Ctrl(unsigned char P2data,unsigned char P0data)
{
P0=P0data;
P2=P2data;
P2=0;
}
void dvice_Init()
{
buzzer_ctrl=0;
relay_ctrl=0;
device_Ctrl(0xa0,((buzzer_ctrl<<6)|(relay_ctrl<<4)));
}
/*
使用计时器定时1ms,当需要200ms时就可以定义一个变量,每计时1ms,就将变量+1,当变量计数到200时
说明已经即使200ms,就进行相应的事件处理
*/
void Timer2Init(void) //1毫秒@12.000MHz
{
AUXR &= 0xFB; //定时器时钟12T模式
T2L = 0x18; //设置定时初始值
T2H = 0xFC; //设置定时初始值
AUXR |= 0x10; //定时器2开始计时
IE2 |= 0x04; //开定时器2中断
EA = 1; //开启总中断
}
void main()
{
dvice_Init();
Timer2Init();
while(1)
{
}
}
//中断服务程序
void t2int() interrupt 12 //中断入口
{
buzzer_cnt++;
if(buzzer_cnt==200)
{
buzzer_cnt=0;//将计数变量清0,为下一次计数做准备
buzzer_ctrl=~buzzer_ctrl;
device_Ctrl(0xa0,((buzzer_ctrl<<6)|(relay_ctrl<<4)));
}
}
定时器正确的锁存操作
有时候可能代码正确但是会发生LED微亮,蜂鸣器可能会突然响一下,数码管可能会突然乱码一下等等情况。就是因为锁存器进行锁存时出现了干扰。
中断可能在任何时刻发生,可能发生在操作译码器、锁存器的位置,那么此时转去执行中断服务程序,就可能干扰锁存器里面存入的数据,并且while(1)里面也不要进行一些耗时的操作,可能会造成定时器定时不准确的情况。尽量将外设、耗时的操作都放在定时器中断中进行处理。
下面的代码就是一种典型的错误形式
#include <STC15F2K60S2.H>
#include <intrins.h>
unsigned char buzzer_ctrl;
unsigned char relay_ctrl;
unsigned int buzzer_cnt=0;//必须要定义为unsigned int型的变量
//因为unsigned char是0~255ms,unsigned int是0~65535MS
void device_Ctrl(unsigned char P2data,unsigned char P0data)
{
P0=P0data;
P2=P2data;
P2=0;
}
void dvice_Init()
{
buzzer_ctrl=0;
relay_ctrl=0;
device_Ctrl(0xa0,((buzzer_ctrl<<6)|(relay_ctrl<<4)));
}
/*
使用计时器定时1ms,当需要200ms时就可以定义一个变量,每计时1ms,就将变量+1,当变量计数到200时
说明已经即使200ms,就进行相应的事件处理
*/
void Timer2Init(void) //1毫秒@12.000MHz
{
AUXR &= 0xFB; //定时器时钟12T模式
T2L = 0x18; //设置定时初始值
T2H = 0xFC; //设置定时初始值
AUXR |= 0x10; //定时器2开始计时
IE2 |= 0x04; //开定时器2中断
EA = 1; //开启总中断
}
void main()
{
dvice_Init();
Timer2Init();
while(1)
{
if(buzzer_cnt==200)
{
buzzer_cnt=0;//将计数变量清0,为下一次计数做准备
buzzer_ctrl=~buzzer_ctrl;
device_Ctrl(0xa0,((buzzer_ctrl<<6)|(relay_ctrl<<4)));
}
}
}
//中断服务程序
void t2int() interrupt 12 //中断入口
{
buzzer_cnt++;
}
定时器综合练习
//1.L1以0.5s间隔闪烁
//2.当L1闪烁5次后,继电器吸合;再次闪烁5次后继电器关闭
//3.L4~L8以0.1s间隔流水灯
//4.蜂鸣器一直处于关闭状态,且L2、L3一直处于熄灭状态
#include <STC15F2K60S2.H>
typedef unsigned char u8;
typedef unsigned int u16;
u16 led_link;//LED间隔闪烁控制变量
u16 led_flash;//LED流水灯控制变量
u8 led_shift;//LED流水灯移位控制变量
u16 relay_ctrl;//继电器控制变量
typedef struct
{
u8 b0:1;
u8 b1:1;
u8 b2:1;
u8 b3:1;
u8 b4:1;
u8 b5:1;
u8 b6:1;
u8 b7:1;
}bits;
typedef union
{
u8 hex;
bits b;
}HexToBin;
HexToBin led_ctrl,buzzer_ctrl;
void divice_ctrl(unsigned char P2data,unsigned char P0data)
{
P0=P0data;
P2=P2data;
P2=0;
}
void system_init()
{
divice_ctrl(0xa0,0);
led_ctrl.hex=0xff;
divice_ctrl(0x80,led_ctrl.hex); /* 关闭LED*/
}
void LED_process()
{
//L1以0.5s间隔闪烁
if(led_link==500)
{
led_link=0;
led_ctrl.b.b0=~led_ctrl.b.b0;
divice_ctrl(0x80,led_ctrl.hex);
relay_ctrl++;
}
//L4~L8以0.1s间隔流水灯
if(led_flash==100)
{
led_flash=0;
led_ctrl.b.b3=~(0x01>>led_shift);
led_ctrl.b.b4=~(0x02>>led_shift);
led_ctrl.b.b5=~(0x04>>led_shift);
led_ctrl.b.b6=~(0x08>>led_shift);
led_ctrl.b.b7=~(0x10>>led_shift);
divice_ctrl(0x80,led_ctrl.hex);
led_shift++;
if(led_shift==5)
{led_shift=0;}
}
}
void relay_process()
{
if(relay_ctrl==10)
{
buzzer_ctrl.b.b4=1;
divice_ctrl(0xa0,buzzer_ctrl.hex);
}
if(relay_ctrl==20)
{
relay_ctrl==0;
buzzer_ctrl.b.b4=0;
divice_ctrl(0xa0,buzzer_ctrl.hex);
}
}
void Timer2Init(void) //1毫秒@12.000MHz
{
AUXR &= 0xFB; //定时器时钟12T模式
T2L = 0x18; //设置定时初始值
T2H = 0xFC; //设置定时初始值
AUXR |= 0x10; //定时器2开始计时
IE2 |= 0x04; //开定时器2中断
EA = 1;
}
void main()
{
system_init();
Timer2Init();
while(1)
{
}
}
void t2int() interrupt 12
{
led_link++;
led_flash++;
LED_process();
relay_process();
}