一、实验目标与硬件设计

实验目标

通过外部中断实现三种LED显示模式

  1. 默认模式:P2口流水灯循环

  2. INT0触发:上下4灯交替闪烁5次(K3按键)

  3. INT1触发:全灯同步闪烁5次(K4按键)

硬件连接

AT89C51单片机
├─ P3.2(INT0) → K3按键 → GND
├─ P3.3(INT1) → K4按键 → GND
└─ P2.0-P2.7 → 220Ω电阻×8 → LED×8 → GND

https://media/image1.jpeg


二、中断系统核心机制

中断响应流程

图表

代码

下载

渲染失败

关键寄存器配置
寄存器 功能说明 本实验配置值
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显示异常

排查步骤

  1. 检查P2口输出模式(需推挽输出)

  2. 测量LED两端电压(正常2.0-2.2V)

  3. 验证延时函数精度(用示波器检测)


六、进阶应用:中断嵌套

优先级配置示例

// 设置INT0为最高优先级
IP |= 0x01;  // PX0=1
IP &= 0xFD;  // PX1=0 (INT1低优先级)

// 中断服务中允许嵌套
void INT0_ISR() interrupt 0 {
    EA = 1;  // 允许中断嵌套
    // ... 处理代码
}
中断嵌套时序

时间轴: |--INT1执行--|----INT0抢占---|--INT0执行--|--INT1恢复--|

七、实验效果与总结

核心收获
  1. 中断处理原则

    • 服务函数尽量简短(本例使用标志位法)

    • 避免在中断内进行复杂运算

  2. 优先级管理

    c

    复制

    下载

    // 优先级设置公式
    高优先级可打断低优先级
    同级中断按自然优先级排序(INT0>定时器0>INT1>定时器1>串口)
  3. 抗干扰设计

    • 按键信号加滤波电容

    • I/O口配置上拉电阻

    • 关键变量使用volatile修饰


八、扩展思考

  1. 如何检测按键长按?

    • 方案:在中断中启动定时器,主循环检测持续时间

  2. 多级菜单如何结合中断?

    // 状态机示例
    enum {MODE_RUN, MODE_SET, MODE_ALARM} sys_mode;
    
    void INT0_ISR() interrupt 0 {
        if(sys_mode == MODE_RUN) //...
    }
  3. 低功耗中断唤醒

    void main() {
        PCON |= 0x01;  // 进入IDLE模式
        while(1) {
            // 中断唤醒后继续执行
        }
    }

源码下载GitHub链接
Proteus仿真工程:包含完整电路与程序


实验总结:通过本实验深入理解了中断响应机制、优先级管理和抗干扰设计,为后续开发实时控制系统奠定基础。建议在实际项目中添加看门狗定时器提高系统可靠性。

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐