51单片机外部中断深度解析:按键控制LED的三种状态切换
中断处理原则服务函数尽量简短(本例使用标志位法)避免在中断内进行复杂运算优先级管理c复制下载// 优先级设置公式高优先级可打断低优先级同级中断按自然优先级排序(INT0>定时器0>INT1>定时器1>串口)抗干扰设计按键信号加滤波电容I/O口配置上拉电阻关键变量使用volatile修饰。
·
一、实验目标与硬件设计
实验目标
通过外部中断实现三种LED显示模式:
-
默认模式:P2口流水灯循环
-
INT0触发:上下4灯交替闪烁5次(K3按键)
-
INT1触发:全灯同步闪烁5次(K4按键)
硬件连接
AT89C51单片机 ├─ P3.2(INT0) → K3按键 → GND ├─ P3.3(INT1) → K4按键 → GND └─ P2.0-P2.7 → 220Ω电阻×8 → LED×8 → GND
二、中断系统核心机制
中断响应流程
图表
代码
下载
渲染失败
关键寄存器配置
| 寄存器 | 位 | 功能说明 | 本实验配置值 |
|---|---|---|---|
| IE | EA | 全局中断使能 | 1 |
| EX0 | INT0中断使能 | 1 | |
| EX1 | INT1中断使能 | 1 | |
| TCON | IT0 | INT0触发方式(0:电平/1:边沿) | 1 |
| IT1 | INT1触发方式 | 1 | |
| IP | PX0/PX1 | 中断优先级 | 0(同级) |
三、优化后的完整代码实现
#include <reg52.h>
#include <intrins.h>
// 引脚定义
sbit K3 = P3^2; // INT0引脚
sbit K4 = P3^3; // INT1引脚
// 全局变量
volatile unsigned char interrupt_flag = 0; // 中断标志
// 精准延时函数(12MHz晶振)
void delay_ms(unsigned int ms) {
unsigned int i, j;
for(i=0; i<ms; i++)
for(j=0; j<120; j++);
}
// INT0中断服务函数
void INT0_ISR() interrupt 0 {
if(interrupt_flag == 0) interrupt_flag = 1; // 设置INT0标志
delay_ms(20); // 简易消抖(实际项目用定时器)
}
// INT1中断服务函数
void INT1_ISR() interrupt 2 {
if(interrupt_flag == 0) interrupt_flag = 2; // 设置INT1标志
delay_ms(20); // 简易消抖
}
// LED流水灯效果
void running_led() {
static unsigned char pos = 0;
P2 = ~(0x01 << pos); // 单灯流动
pos = (pos + 1) % 8;
delay_ms(150);
}
int main() {
// 中断初始化
EA = 1; // 全局中断使能
EX0 = 1; // INT0中断使能
EX1 = 1; // INT1中断使能
IT0 = 1; // INT0边沿触发
IT1 = 1; // INT1边沿触发
P2 = 0xFF; // 初始LED全灭
while(1) {
// 无中断时运行流水灯
if(interrupt_flag == 0) {
running_led();
}
// 处理INT0中断(K3按下)
else if(interrupt_flag == 1) {
for(unsigned char i=0; i<5; i++) {
P2 = 0xF0; // 上半区亮
delay_ms(200);
P2 = 0x0F; // 下半区亮
delay_ms(200);
}
P2 = 0xFF; // 恢复全灭
interrupt_flag = 0; // 清除标志
}
// 处理INT1中断(K4按下)
else if(interrupt_flag == 2) {
for(unsigned char i=0; i<5; i++) {
P2 = 0x00; // 全亮
delay_ms(200);
P2 = 0xFF; // 全灭
delay_ms(200);
}
interrupt_flag = 0; // 清除标志
}
}
}
四、关键优化点解析
1. 中断服务优化
// 原始代码(问题:未消抖)
void interrupt_init0() interrupt 0 {
interrupt_flag = 1;
}
// 优化后(增加消抖)
void INT0_ISR() interrupt 0 {
if(interrupt_flag == 0) interrupt_flag = 1;
delay_ms(20); // 20ms消抖
}
2. 中断标志处理策略
| 策略 | 优点 | 缺点 |
|---|---|---|
| 直接操作硬件 | 响应快 | 易导致时序冲突 |
| 标志位+主循环 | 安全可靠,避免长中断 | 响应略有延迟 |
| 状态机 | 适合复杂逻辑 | 实现复杂度高 |
五、常见问题及解决方案
1. 按键抖动导致多次触发
现象:单次按键触发多次中断
解决方案:
// 方案1:硬件消抖(推荐)
└─ 并联104电容(0.1μF)到按键两端
// 方案2:软件消抖
void INT0_ISR() interrupt 0 {
static unsigned int last_time = 0;
if(系统时间 - last_time > 50) { // 50ms防抖
interrupt_flag = 1;
last_time = 系统时间;
}
}
2. 中断冲突问题
现象:两按键同时按下时程序异常
解决方案:
// 修改IP寄存器设置优先级
IP = 0x01; // 设置INT0为高优先级
// 在中断服务中添加优先级判断
void INT1_ISR() interrupt 2 {
if(interrupt_flag != 1) { // 仅当无INT0中断时响应
interrupt_flag = 2;
}
}
3. LED显示异常
排查步骤:
-
检查P2口输出模式(需推挽输出)
-
测量LED两端电压(正常2.0-2.2V)
-
验证延时函数精度(用示波器检测)
六、进阶应用:中断嵌套
优先级配置示例
// 设置INT0为最高优先级
IP |= 0x01; // PX0=1
IP &= 0xFD; // PX1=0 (INT1低优先级)
// 中断服务中允许嵌套
void INT0_ISR() interrupt 0 {
EA = 1; // 允许中断嵌套
// ... 处理代码
}
中断嵌套时序
时间轴: |--INT1执行--|----INT0抢占---|--INT0执行--|--INT1恢复--|
七、实验效果与总结
核心收获
-
中断处理原则:
-
服务函数尽量简短(本例使用标志位法)
-
避免在中断内进行复杂运算
-
-
优先级管理:
c
复制
下载
// 优先级设置公式 高优先级可打断低优先级 同级中断按自然优先级排序(INT0>定时器0>INT1>定时器1>串口)
-
抗干扰设计:
-
按键信号加滤波电容
-
I/O口配置上拉电阻
-
关键变量使用volatile修饰
-
八、扩展思考
-
如何检测按键长按?
-
方案:在中断中启动定时器,主循环检测持续时间
-
-
多级菜单如何结合中断?
// 状态机示例 enum {MODE_RUN, MODE_SET, MODE_ALARM} sys_mode; void INT0_ISR() interrupt 0 { if(sys_mode == MODE_RUN) //... } -
低功耗中断唤醒
void main() { PCON |= 0x01; // 进入IDLE模式 while(1) { // 中断唤醒后继续执行 } }
源码下载:GitHub链接
Proteus仿真工程:包含完整电路与程序
实验总结:通过本实验深入理解了中断响应机制、优先级管理和抗干扰设计,为后续开发实时控制系统奠定基础。建议在实际项目中添加看门狗定时器提高系统可靠性。
更多推荐



所有评论(0)