3. UART 中断类型:接收中断、发送中断、线路状态中断、调制解调器中断
在嵌入式系统中,UART(通用异步收发传输器)外设通过多种中断源向 CPU 报告通信状态。正确理解每种中断类型的触发条件、应用场景及优先级关系,是编写高效中断服务函数(ISR)的基础。本章将系统梳理 UART 的四大中断类型,并给出典型配置与处理策略。
3.1 中断类型总览
标准 UART 控制器通常支持以下四类中断,它们共享同一个中断向量,但通过中断标识寄存器(IIR)区分具体来源:
| 中断类型 | 触发条件 | 典型用途 | 优先级(高→低) |
|---|---|---|---|
| 接收中断 | 接收缓冲寄存器(RBR)收到新数据;或接收 FIFO 达到触发阈值 | 逐字节或批量读取接收数据 | 1(最高) |
| 发送中断 | 发送保持寄存器(THR)空;或发送 FIFO 低于触发阈值 | 填充下一个待发送字节 | 2 |
| 线路状态中断 | 接收错误(溢出、帧错误、奇偶校验错误、中止条件) | 错误检测与恢复 | 3 |
| 调制解调器中断 | 调制解调器状态引脚变化(CTS、DSR、RI、DCD) | 流控握手、连接检测 | 4(最低) |
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);
}
}
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);
}
}
}
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;
// 可选:挂起发送任务
}
}
3.6 中断类型关系与处理流程
下图展示了 UART 中断服务函数中,如何根据中断标识寄存器(IIR)区分并分发四种中断类型:
3.7 中断配置要点总结
在实际项目中,配置 UART 中断时需注意以下要点:
| 配置项 | 推荐做法 | 避坑指南 |
|---|---|---|
| 接收中断使能 | 始终使能,配合 FIFO 阈值降低中断频率 | 不要使能接收中断却未实现 ISR,会导致 CPU 持续响应 |
| 发送中断使能 | 仅在发送缓冲区非空时使能,发送完毕后立即关闭 | 避免在 ISR 中调用 HAL_UART_Transmit 等阻塞函数 |
| 线路状态中断 | 开发阶段使能以捕获错误;量产阶段可选关闭以降低开销 | 读取 LSR 后必须读取 DR 才能清除错误标志 |
| 调制解调器中断 | 仅在使用硬件流控或外接 Modem 时使能 | MSR 的低四位是变化位,读取即清除,注意不要漏读 |
| 中断优先级 | 接收中断设为最高优先级,发送中断次之 | 在 RTOS 中,ISR 优先级不应高于 configMAX_SYSCALL_INTERRUPT_PRIORITY |