51单片机中断系统详解

一、中断是什么,为什么需要中断

CPU在执行主程序的过程中,当出现某种紧急事件时,CPU会暂停当前正在执行的程序,转去执行处理该事件的程序(中断服务程序),处理完毕后再返回原来被暂停的位置继续执行。这就是中断的核心逻辑。

对比"轮询"方式,中断的优势非常明显:轮询需要CPU不断查询外设状态,大量时间浪费在等待上;而中断允许CPU在事件未发生时专注处理主任务,事件到来时自动跳转,实时性和效率都更高。在51单片机中,中断还承担着故障处理、分时操作等关键角色。

51单片机有5个中断源:

中断源 引脚/来源 触发条件
外部中断0(INT0) P3.2 低电平或下降沿
外部中断1(INT1) P3.3 低电平或下降沿
定时器0中断(T0) 内部 T0计数溢出
定时器1中断(T1) 内部 T1计数溢出
串口中断 P3.0/P3.1 接收/发送一帧完成

二、中断控制寄存器详解

与中断直接相关的寄存器有4个:TCON(中断请求标志与触发方式)、SCON(串口控制)、IE(中断允许)、IP(中断优先级)。

TCON --- 中断请求与控制

TCON字节地址88H,可位寻址。与中断相关的位如下:

  • IT0(TCON.0):INT0触发方式,1=下降沿,0=低电平
  • IE0(TCON.1):INT0请求标志,硬件置1,响应后自动清0
  • IT1(TCON.2):INT1触发方式
  • IE1(TCON.3):INT1请求标志
  • TR0/TR1:定时器启动控制位(高4位中的低2位)
  • TF0(TCON.5):T0溢出标志,响应后硬件自动清0
  • TF1(TCON.7):T1溢出标志

SCON --- 串口中断标志

SCON地址98H,其中 RI (接收中断标志)和 TI (发送中断标志)必须由软件手动清零,硬件不会自动清除。

IE --- 中断允许控制

名称 功能
IE.7 EA 总中断开关,1=开
IE.4 ES 串口中断允许
IE.3 ET1 T1中断允许
IE.2 EX1 INT1中断允许
IE.1 ET0 T0中断允许
IE.0 EX0 INT0中断允许

IP --- 中断优先级

51单片机有两个优先级,IP寄存器中对应位=1则设为高优先级。默认优先级顺序为:INT0 > T0 > INT1 > T1 > 串口。高优先级中断可以打断低优先级中断,形成二级嵌套。

三、中断向量与C51的 interrupt 关键字

每个中断源在程序存储器中有固定的入口地址,称为中断向量:

中断源 入口地址 C51中断号
外部中断0 0003H 0
定时器0 000BH 1
外部中断1 0013H 2
定时器1 001BH 3
串口中断 0023H 4

在Keil C51中,用 interrupt n 声明中断服务函数,编译器会自动将函数链接到对应的中断向量地址。例如:

c 复制代码
void External0_ISR(void) interrupt 0   // 对应外部中断0
void Timer0_ISR(void)   interrupt 1   // 对应定时器0
void UART_ISR(void)     interrupt 4   // 对应串口中断

关于 using 关键字using 用于指定中断函数使用哪个工作寄存器组(0~3)。如果不指定,C51默认在中断入口切换到寄存器组0。对于简单的单级中断场景,不写 using 完全可以正常工作;只有当主函数和中断函数都要频繁访问寄存器组、对性能有极致要求时,才需要考虑用 using 分离寄存器组,但这样需要你自行保证寄存器组使用的正确性。

四、代码实现

4.1 外部中断:按键控制LED翻转

这是最基础的外部中断应用。INT0接按键,按下时触发中断,翻转LED状态。

c 复制代码
#include <reg51.h>

sbit LED = P1^0;

void External0_ISR(void) interrupt 0
{
    LED = ~LED;          // 翻转LED状态
}

void main()
{
    IT0 = 1;             // INT0下降沿触发
    EX0 = 1;             // 使能INT0中断
    EA  = 1;             // 开总中断

    LED = 0;             // 初始熄灭(假设低电平点亮)

    while(1);
}

配置流程只有三步:设置触发方式(IT0)、使能该中断(EX0)、开总中断(EA)。中断服务函数中不要写耗时操作,简单翻转即可。

关于按键抖动:机械按键在按下和释放时会产生5~10ms的电平抖动。如果使用下降沿触发,抖动期间的多个边沿可能导致中断重复触发。在中断服务函数中做简单延时消抖即可:

c 复制代码
void External0_ISR(void) interrupt 0
{
    unsigned char i, j;
    for(i = 0; i < 20; i++)
        for(j = 0; j < 200; j++);   // 约5~10ms延时
    if(P3^2 == 0)                   // 确认按键仍处于按下状态
    {
        LED = ~LED;
    }
}

判断条件 P3^2 == 0 在下降沿触发方式下表示"确认引脚仍为低电平",能够过滤掉抖动产生的误触发。

4.2 定时器中断:精确定时与LED闪烁

定时器中断是51单片机中最常用的中断类型。以12MHz晶振、T0工作在方式1(16位定时)为例,实现500ms定时翻转LED。

初值计算:方式1是16位计数器,最大计数值65536。12MHz晶振的机器周期为1μs,要实现50ms定时,需要计数50000次,初值 = 65536 − 50000 = 15536 = 0x3CB0。

c 复制代码
#include <reg51.h>

sbit LED = P1^0;

void Timer0_ISR(void) interrupt 1
{
    static unsigned char count = 0;
    TH0 = 0x3C;              // 重装初值 0x3CB0
    TL0 = 0xB0;

    count++;
    if(count >= 10)          // 10 × 50ms = 500ms
    {
        count = 0;
        LED = ~LED;
    }
}

void main()
{
    TMOD &= 0xF0;            // 清除T0控制位
    TMOD |= 0x01;            // T0方式1(16位定时)
    TH0 = 0x3C;              // 定时50ms初值
    TL0 = 0xB0;
    ET0 = 1;                 // 使能T0中断
    EA  = 1;                 // 开总中断
    TR0 = 1;                 // 启动定时器

    while(1);
}

注意 TH0/TL0 的初值重装写在中断服务函数内部,因为方式1不是自动重装载模式,每次溢出后计数器归零,必须手动重装。

4.3 串口中断:中断接收 + 查询发送

串口中断标志位RI和TI必须软件清零。以下是一个典型的串口收发框架(9600波特率、11.0592MHz晶振):

c 复制代码
#include <reg51.h>

#define BUF_SIZE 64
unsigned char rxBuf[BUF_SIZE];
unsigned char rxCount = 0;

void UART_Init()
{
    TMOD = 0x20;             // T1方式2(8位自动重装)
    TH1  = 0xFD;             // 9600bps @ 11.0592MHz
    TL1  = 0xFD;
    TR1  = 1;                // 启动T1
    SM0  = 0;                // 串口方式1(8位UART)
    SM1  = 1;
    REN  = 1;                // 允许接收
    ES   = 1;                // 使能串口中断
    EA   = 1;                // 开总中断
}

void UART_ISR(void) interrupt 4
{
    if(RI)
    {
        RI = 0;              // 软件清接收标志
        if(rxCount < BUF_SIZE - 1)
        {
            rxBuf[rxCount++] = SBUF;
        }
    }
    if(TI)
    {
        TI = 0;              // 软件清发送标志(若使用查询发送,此处可不处理)
    }
}

void UART_SendByte(unsigned char dat)
{
    SBUF = dat;
    while(!TI);              // 查询等待发送完成
    TI = 0;                  // 清发送标志
}

接收逻辑放在中断中,每收到一个字节就存入缓冲区并清RI标志;发送则用查询方式,写入SBUF后等待TI置位再清除。这种"中断接收 + 查询发送"的组合在大多数应用场景下足够高效,也避免了收发中断互相干扰的问题。

五、总结

51单片机中断系统的核心可以归纳为"三看":

一看触发条件 :外部中断通过TCON的IT0/IT1选择电平或边沿触发;定时器中断靠T0/T1计数溢出;串口中断靠RI/TI标志。二看使能开关 :IE寄存器中先开对应中断位,再开EA总开关,两者缺一不可。三看标志清零:外部中断和定时器中断的标志位由硬件自动清除,而串口的RI和TI必须软件手动清0,这是最容易踩的坑。

中断函数用 interrupt n 声明,n对应中断号。中断服务函数应当尽量短小,避免长时间占用CPU。对于按键等有抖动的信号源,在中断内加入简单消抖判断即可有效防止误触发。掌握这三点,51单片机的中断编程就抓住了主线。

相关推荐
Brilliantwxx1 小时前
【STM32】 I2C 外设内部硬件结构和状态位
开发语言·stm32·单片机·嵌入式硬件
CHHH_HHH1 小时前
【Linux系统篇】进程间通信揭秘:从管道到共享内存
linux·服务器·c语言·开发语言·后端·ubuntu
天空'之城9 小时前
单片机基础核心知识点汇总(三十五)
单片机·嵌入式硬件
secondyoung10 小时前
I2C通信协议详解
arm开发·驱动开发·stm32·单片机·嵌入式硬件·mcu·51单片机
sunshine22 girl10 小时前
Java学习二 基本语法2, 算术运算符
java·学习
anew___10 小时前
蜂鸣器播放音乐——让 Arduino 唱出旋律
c++·stm32·单片机·嵌入式硬件·c
派葛穆10 小时前
Lerobot -飞特舵机ST-3215双舵机联动(ESP_NOW)
c语言
jianqiang.xue11 小时前
ESP-IDF保姆级入门40|量产烧录与产线测试全解:批量烧录方案/校准流程/功能测试/不良分拣/产线追溯,掌握工业级量产交付全流程
单片机·物联网·esp32
零依赖极客11 小时前
Day 6·1 ARMv8.2 dotprod——vdotq_s32 一条指令做 4 个点积
c语言·开发语言·人工智能·矩阵·arm·vllm