3. UART 中断类型:接收中断、发送中断、线路状态中断、调制解调器中断

在嵌入式系统中,UART(通用异步收发传输器)外设通过多种中断源向 CPU 报告通信状态。正确理解每种中断类型的触发条件、应用场景及优先级关系,是编写高效中断服务函数(ISR)的基础。本章将系统梳理 UART 的四大中断类型,并给出典型配置与处理策略。

3.1 中断类型总览

标准 UART 控制器通常支持以下四类中断,它们共享同一个中断向量,但通过中断标识寄存器(IIR)区分具体来源:

中断类型 触发条件 典型用途 优先级(高→低)
接收中断 接收缓冲寄存器(RBR)收到新数据;或接收 FIFO 达到触发阈值 逐字节或批量读取接收数据 1(最高)
发送中断 发送保持寄存器(THR)空;或发送 FIFO 低于触发阈值 填充下一个待发送字节 2
线路状态中断 接收错误(溢出、帧错误、奇偶校验错误、中止条件) 错误检测与恢复 3
调制解调器中断 调制解调器状态引脚变化(CTS、DSR、RI、DCD) 流控握手、连接检测 4(最低)
关键提示: 多数 MCU 的 UART 中断优先级由硬件固定(如 16550 兼容 UART),接收中断优先级最高。在 RTOS 环境下,高优先级中断不应调用可能阻塞的 API(如信号量获取),以避免优先级反转。

3.2 接收中断(RX Interrupt)

接收中断是 UART 通信中最频繁触发的中断源。其触发条件分为两种模式:

  • 单字节模式: 每收到一个字节即触发中断。适用于低速、数据量小的场景,但中断开销较大。
  • FIFO 模式: 当接收 FIFO 中的数据达到预设阈值(如 1/4、1/2、3/4 或满)时触发。适用于高速、批量数据传输,可显著降低 CPU 中断响应次数。

接收中断 ISR 的核心职责是从 RBR 或 FIFO 中读取数据,并存入软件缓冲区。若使用 RTOS,通常通过消息队列或流缓冲区将数据传递给任务处理。

// 接收中断 ISR 示例(STM32 HAL 风格)
void USART1_IRQHandler(void) {
    uint32_t isrflags = READ_REG(usart1.Instance->SR);
    if (isrflags & USART_SR_RXNE) {  // 接收非空标志
        uint8_t data = (uint8_t)(usart1.Instance->DR & 0xFF);
        // 将数据放入环形缓冲区
        if (!ring_buffer_full(&rx_ring)) {
            ring_buffer_put(&rx_ring, data);
        }
        // 可选:通知接收任务
        BaseType_t xHigherPriorityTaskWoken = pdFALSE;
        xSemaphoreGiveFromISR(xRxSemaphore, &xHigherPriorityTaskWoken);
        portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
    }
}
优化要点: 接收中断 ISR 应尽量短小,仅完成数据搬运和标志清除。避免在 ISR 内进行协议解析或粘包处理,这些应交给任务级代码完成。

3.3 发送中断(TX Interrupt)

发送中断在发送保持寄存器(THR)空或发送 FIFO 低于阈值时触发。其典型应用场景是中断驱动发送:CPU 只需将待发送数据写入 THR 或 FIFO,然后由硬件自动逐位发送,每发送完一个字节(或一批字节)后触发中断,ISR 负责填充下一个数据。

发送中断的使能时机需要谨慎控制:

  • 初始发送: 通常先直接写入第一个字节(不依赖中断),然后使能发送中断。
  • 后续发送: 每次发送中断触发时,ISR 检查发送缓冲区是否还有数据;若有则继续填充,若无则关闭发送中断。
  • 避免空转: 若发送缓冲区为空时仍使能发送中断,会导致中断持续触发,浪费 CPU 资源。
// 发送中断 ISR 示例
void USART1_IRQHandler(void) {
    uint32_t isrflags = READ_REG(usart1.Instance->SR);
    if (isrflags & USART_SR_TXE) {  // 发送数据寄存器空
        uint8_t data;
        if (ring_buffer_get(&tx_ring, &data)) {
            WRITE_REG(usart1.Instance->DR, data);
        } else {
            // 无数据可发,关闭发送中断
            CLEAR_BIT(usart1.Instance->CR1, USART_CR1_TXEIE);
        }
    }
}
常见陷阱: 发送中断 ISR 中不应调用阻塞式发送函数(如 HAL_UART_Transmit),否则会导致递归中断或死锁。发送中断只负责“喂数据”,不负责“等数据”。

3.4 线路状态中断(Line Status Interrupt)

线路状态中断用于报告接收路径上的异常情况。当 UART 检测到以下错误之一时触发:

  • 溢出错误(OE): 接收 FIFO 已满,新数据覆盖旧数据。
  • 帧错误(FE): 接收到的停止位无效(通常为低电平)。
  • 奇偶校验错误(PE): 接收数据的奇偶校验与设置不匹配。
  • 中止条件(BI): 接收线保持低电平超过一个帧长度。

线路状态中断的优先级通常低于接收中断,但高于调制解调器中断。ISR 应读取线路状态寄存器(LSR)以获取具体错误类型,并执行相应处理:

  • 记录错误计数,用于通信质量统计。
  • 清空接收 FIFO 以恢复同步(针对帧错误或中止条件)。
  • 通知上层协议栈进行重传或复位。
// 线路状态中断处理片段
if (isrflags & USART_SR_FE) {  // 帧错误
    error_stats.frame_error_count++;
    // 读取 DR 清除错误标志
    (void)READ_REG(usart1.Instance->DR);
    // 可选:复位接收状态机
}
if (isrflags & USART_SR_ORE) { // 溢出错误
    error_stats.overrun_count++;
    // 读取 DR 清除溢出标志
    (void)READ_REG(usart1.Instance->DR);
}
调试建议: 在开发阶段,可将线路状态中断的触发次数打印到调试端口,帮助定位波特率不匹配、电气噪声或接地不良等问题。

3.5 调制解调器中断(Modem Interrupt)

调制解调器中断用于监测 RS-232 接口的硬件流控信号变化,包括:

  • CTS(Clear to Send): 对端允许发送。
  • DSR(Data Set Ready): 对端设备就绪。
  • RI(Ring Indicator): 振铃指示(通常用于拨号调制解调器)。
  • DCD(Data Carrier Detect): 载波检测。

在嵌入式系统中,调制解调器中断最常用于实现硬件流控(RTS/CTS)。当 CTS 信号变为无效时,发送方应暂停发送;当 CTS 恢复有效时,发送方继续发送。ISR 应读取调制解调器状态寄存器(MSR)以获取变化位,并更新流控状态机。

// 调制解调器中断处理片段
if (isrflags & USART_SR_CTS) {  // CTS 状态变化
    uint16_t msr = READ_REG(usart1.Instance->MSR);
    if (msr & USART_MSR_CTS) {
        // CTS 有效,允许发送
        flow_ctrl.cts_asserted = 1;
    } else {
        // CTS 无效,暂停发送
        flow_ctrl.cts_asserted = 0;
        // 可选:挂起发送任务
    }
}
现代趋势: 在 MCU 内部 UART 中,调制解调器中断的使用频率已大幅降低。多数芯片支持自动硬件流控(Auto RTS/CTS),由硬件直接控制 RTS 引脚,无需软件干预。但在外接 RS-232 收发器或工业通信场景中,调制解调器中断仍不可或缺。

3.6 中断类型关系与处理流程

下图展示了 UART 中断服务函数中,如何根据中断标识寄存器(IIR)区分并分发四种中断类型:

UART 中断分发流程(基于 IIR 寄存器) UART ISR 入口 读取中断标识寄存器 (IIR) 接收中断 RXNE / FIFO 阈值 发送中断 TXE / FIFO 空 线路状态中断 OE / FE / PE / BI 调制解调器中断 CTS / DSR / RI / DCD 读取 RBR / FIFO 存入软件缓冲区 通知接收任务 从发送缓冲区取数据 写入 THR / FIFO 无数据则关中断 读取 LSR 获取错误类型 记录错误计数 清 FIFO / 通知协议层 读取 MSR 获取变化位 更新流控状态机 暂停/恢复发送 ISR 退出 注:IIR 寄存器编码决定优先级顺序,高优先级中断会覆盖低优先级中断的 IIR 位。

3.7 中断配置要点总结

在实际项目中,配置 UART 中断时需注意以下要点:

配置项 推荐做法 避坑指南
接收中断使能 始终使能,配合 FIFO 阈值降低中断频率 不要使能接收中断却未实现 ISR,会导致 CPU 持续响应
发送中断使能 仅在发送缓冲区非空时使能,发送完毕后立即关闭 避免在 ISR 中调用 HAL_UART_Transmit 等阻塞函数
线路状态中断 开发阶段使能以捕获错误;量产阶段可选关闭以降低开销 读取 LSR 后必须读取 DR 才能清除错误标志
调制解调器中断 仅在使用硬件流控或外接 Modem 时使能 MSR 的低四位是变化位,读取即清除,注意不要漏读
中断优先级 接收中断设为最高优先级,发送中断次之 在 RTOS 中,ISR 优先级不应高于 configMAX_SYSCALL_INTERRUPT_PRIORITY