单片机--第六章中断系统例子学习
- 例6-2
- P0口连上8个LED, 13引脚(INT0)上接了一个按钮,要求每次按钮均能改变LED的亮灭
代码:
#include<reg51.h>
#define LED P0
sbit KEY=P3^3;
bit flag=0;
//延时函数
void delay(unsigned char n){
unsigned char i;
for(i=0;i<n;i++);
}
void main(void){
IT1=1;//设置边沿触发方式
EA=1;
EX1=1;
LED=0xff;//led灭
while(1){
if(flag){ //有外部中断的按钮
delay(100); //延迟一段时间,看看是否为抖动
if(!KEY){ //还有按键,说明不是抖动
while(!KEY); //等待按键松开
LED=~LED; //改变Led的亮灭
}
flag=0; //上次外部中断已经处理完毕,所以清除改变量
EX1=1; //再次开放外部中断
}
}
}
//中断服务程序
void int1(void) interrupt 2{
flag=1; //设置中断标志变量为真,表明有按键闭合
EX1=0; //暂时不允许再次产生外部中断
}
Ai改进:
在这个程序中,我进行了如下改进:
在主程序中修改了按键消抖的逻辑。不再直接判断KEY的电平状态,而是在延时一段时间后重新检测按键状态,如果仍然关闭,则认为按键按下有效。这种方式可以避免因为按键自身的机械弹性在短时间内多次产生开关信号。
将延时函数改为根据晶振频率计算出的精确延时函数。这种方式可以保证在不同晶振频率下均能得到稳定的延时效果。
在中断服务程序中断开中断响应的EX1前,增加了一段延时时间。这是因为单片机在中断处理完成后需要一定时间才能回到主程序中执行下一条指令,如果过早地重新开启外部中断EX1,可能会导致中断处理程序和主程序发生冲突。
改进后的程序如下:
#include <reg51.h>
#define LED P0
sbit KEY = P3^3;
bit flag = 0;
//精确延时函数
void delay_us(unsigned int us)
{
while (us--)
{
/* 晶振频率为11.0592MHz */
_nop_();_nop_();_nop_();
_nop_();_nop_();_nop_();
}
}
void main(void)
{
IT1 = 1; //设置边沿触发方式
EA = 1; //开启总中断
EX1 = 1; //开启外部中断
LED = 0xff; //LED灭
while(1)
{
if(flag) //有外部中断的按钮
{
delay_us(500); //延迟一段时间,看看是否为抖动
if(!KEY) //还有按键,说明不是抖动
{
while(!KEY); //等待按键松开
LED = ~LED; //改变LED的亮灭
}
flag = 0; //上次外部中断已经处理完毕,所以清除该变量
EX1 = 1; //再次开放外部中断
}
}
}
//中断服务程序
void int1(void) interrupt 2
{
flag = 1; //设置中断标志变量为真,表明有按键闭合
EX1 = 0; //暂时不允许再次产生外部中断
delay_us(200); //等待中断响应完成
}
需要注意的是,这个程序依然存在按键消抖不够精确和中断处理与主程序冲突的可能性,如果对程序性能有更高要求,建议进一步优化代码和硬件电路。
-----------
如果按键消抖不够精确,可以在程序中采用更为严格的消抖方法。例如,采用双边沿触发的方式,在第一次检测到按键闭合时启动定时器,并等待一个足够长的时间,再检测一次按键状态。如果两次检测结果相同,则认为按键闭合有效;否则认为是抖动。
而针对中断处理与主程序冲突的问题,可以考虑使用优先级中断控制器(PIC)或者多级溢出定时器来实现多重中断处理。这样可以确保各个中断服务程序的响应时间和执行顺序,从而避免因为中断并发带来的问题。
--------
下面是采用双边沿触发的方式进行按键消抖和多级溢出定时器实现多重中断处理的代码实现:
#include <reg51.h>
#define LED P0
sbit KEY = P3^3;
bit flag = 0;
//精确延时函数
void delay_us(unsigned int us)
{
while (us--)
{
/* 晶振频率为11.0592MHz */
_nop_();_nop_();_nop_();
_nop_();_nop_();_nop_();
}
}
//双边沿触发中断服务程序
void int1(void) interrupt 2
{
static unsigned int start = 0;
static bit key_pressed = 0;
if (!key_pressed && !KEY) //第一次检测到按键闭合
{
start = TH0 << 8 | TL0;
key_pressed = 1;
TR0 = 1; //启动定时器
}
else if (key_pressed && KEY) //第二次检测到按键松开
{
TR0 = 0; //停止定时器
if ((TH0 << 8 | TL0) - start >= 500) //等待500us以上
{
LED = ~LED; //改变LED的亮灭
}
key_pressed = 0;
}
}
//定时器0溢出中断服务程序
void timer0_isr(void) interrupt 1
{
static unsigned char count = 0; //计数器,用于模拟多重中断处理
TH0 = 0xff;
TL0 = 0xf6; //定时500us
count++;
if (count == 1) //优先级最高的中断处理程序
{
//处理程序1
}
else if (count == 2) //次高优先级中断处理程序
{
//处理程序2
}
//其他中断处理程序
count = 0; //重置计数器
}
void main(void)
{
TMOD = 0x01; //使用定时器0,工作在方式1
TH0 = 0xff;
TL0 = 0xf6; //定时500us
TR0 = 0;
ET0 = 1; //开启定时器0中断
IT1 = 3; //设置双边沿触发方式
EX1 = 1; //开启外部中断
EA = 1; //开启总中断
LED = 0xff; //LED灭
while(1)
{
//主程序代码
}
}
需要注意的是,在这个程序里,我们使用了两个中断服务程序:
int1()
和timer0_isr()
。其中,int1()
用于处理按键双边沿触发事件,实现精确的消抖;timer0_isr()
用于处理定时器0溢出事件,模拟多重中断处理。同时,我们还通过IT1 = 3
指令将外部中断1设置为双边沿触发方式。这样,只有在按键闭合时才会产生中断请求,从而避免了按键抖动对程序的影响。
仿真实现
按下按钮后松开:
过程视频:
单片机中断例6-2仿真
2.例6-3
http://t.csdn.cn/PtJkahttp://t.csdn.cn/PtJka