第一次接触51单片机中断功能时,我被它的"打断-响应-返回"机制深深吸引。想象一下,单片机正在执行主程序,突然有个紧急事件发生(比如按键按下),它能立即暂停手头工作去处理这个事件,完成后又回到原来的地方继续执行——这种机制在工业控制、智能家居等领域无处不在。本文将用最直观的流水灯案例,带你从电路搭建到代码调试,完整掌握外部中断的应用技巧。
准备清单:
关键电路设计要点:
实际调试中发现,机械按键容易产生抖动,建议在开关两端并联0.1μF电容消除抖动干扰
使用万用表检测关键点电压:
常见问题排查表:
| 现象 | 可能原因 | 解决方案 |
|---|---|---|
| LED全亮不灭 | P1口未正确初始化 | 检查代码中P1赋值语句 |
| 按键无反应 | 中断引脚接触不良 | 重新焊接或更换杜邦线 |
| 灯组响应延迟 | 未启用下降沿触发 | 设置IT0/IT1=1 |
外部中断涉及三个关键寄存器:
c复制// 中断初始化函数示例
void Init_INT0() {
EA = 1; // 总中断使能
EX0 = 1; // INT0使能
IT0 = 1; // 下降沿触发
}
中断函数有固定格式要求:
c复制// INT0中断服务程序
void INT0_ISR() interrupt 0 {
static unsigned char pattern = 0x01;
P1 = ~pattern; // 输出取反使LED亮
pattern <<= 1; // 左移实现流水效果
if(!pattern) pattern = 0x01; // 归位
}
中断服务程序应尽量简短,实测显示当ISR执行时间超过50μs时可能影响主程序实时性
c复制if(KEY==0) {
delay_ms(20); // 等待抖动过去
if(KEY==0) { // 确认按键状态
// 执行操作
}
}
c复制enum {IDLE, DEBOUNCE, PRESSED} key_state;
void check_key() {
switch(key_state) {
case IDLE: if(!KEY) key_state = DEBOUNCE; break;
case DEBOUNCE:
if(!KEY) { key_state = PRESSED; /* 触发操作 */ }
else key_state = IDLE;
break;
case PRESSED: if(KEY) key_state = IDLE; break;
}
}
当多个中断同时存在时需注意:
c复制volatile unsigned int counter; // 多中断共享变量
void Timer0_ISR() interrupt 1 {
EA = 0; // 关中断保护临界区
counter++;
EA = 1; // 恢复中断
}
c复制#include <reg52.h>
#define uint unsigned int
#define uchar unsigned char
sbit LED = P1^0; // 测试用单独LED
sbit KEY = P3^2; // 中断按键
uchar mode = 0; // 0-单次 1-自动 2-停止
uint hold_time = 0; // 按键保持时间
void delay_ms(uint ms) {
while(ms--) {
uint x = 114;
while(x--);
}
}
void Init_INT0() {
EA = 1;
EX0 = 1;
IT0 = 1;
}
void main() {
P1 = 0xFF; // 初始灯全灭
Init_INT0();
while(1) {
if(mode == 1) { // 自动流水模式
static uchar pat = 0x01;
P1 = ~pat;
pat = (pat << 1) | (pat >> 7);
delay_ms(200);
}
}
}
void INT0_ISR() interrupt 0 {
static uchar click_cnt = 0;
static uint last_time = 0;
// 双击检测(间隔<500ms)
if(TMOD & 0x30) { // 借用定时器值作粗略计时
if(++click_cnt >= 2) {
mode = 2; // 紧急停止
P1 = 0xFF;
click_cnt = 0;
return;
}
}
// 长按检测
while(!KEY) {
delay_ms(10);
if(++hold_time > 300) { // 3秒长按
mode = (mode == 1) ? 0 : 1;
hold_time = 0;
break;
}
}
// 单次触发处理
if(mode == 0) {
static uchar pat = 0x01;
P1 = ~pat;
pat <<= 1;
if(!pat) pat = 0x01;
}
}
c复制void main() {
// ...初始化代码...
while(1) {
PCON |= 0x01; // 进入空闲模式
_nop_(); // 等待中断唤醒
}
}
在最近的一个智能台灯项目中,正是利用这种中断架构实现了触摸切换与环境光感应的无缝配合。当手指接触金属面板时,INT0触发立即响应;同时光敏电阻通过ADC采样在后台调整亮度,两者互不干扰却又协同工作——这正是中断机制的精妙之处。