UART 面试手册
复习 UART 异步帧格式、波特率与采样、STM32 HAL 收发和常见故障排查。
核心速记卡
| 概念 | 一句话定义 | 关键限制 |
|---|---|---|
| UART | 将并行数据与异步串行位流互相转换的收发外设 | 不自带时钟线、地址、仲裁或报文边界 |
8N1 | 8 个数据位、无校验、1 个停止位 | 线上每个字节至少占 10 bit:起始位 + 8 数据位 + 停止位 |
| 波特率 | 串行线每秒传输的符号数,常见 UART 中一个符号对应一个 bit | 双方配置和实际时钟必须在收发器容差内 |
| 位时间 | Tbit = 1 / baudrate | 115200 baud 时约为 8.68 µs |
| 有效吞吐 | 字节速率 ≈ 波特率 / 每帧总 bit 数 | 115200 baud、8N1 的理论上限约为 11.52 KB/s |
| 过采样 | 接收器用高于波特率的本地时钟定位并采样每个 bit | 常见为 8 倍或 16 倍,具体判决机制看芯片手册 |
TXE / TC | TXE 表示发送数据寄存器可写,TC 表示整帧已移出引脚 | RS-485 切回接收通常要等 TC,不能只等 TXE |
IDLE | RX 线在收到数据后保持了一定时间的空闲状态 | 它是线路事件,不天然等于上层协议帧结束 |
| 接收错误 | ORE 溢出、FE 帧错误、PE 校验错误、NE 噪声错误 | 要结合错误码、状态标志和实际波形排查 |
| RS-485 半双工 | UART 经收发器转换为 A/B 差分信号,同一时刻通常只能发或收 | 需控制 DE/RE,并处理终端、偏置、地址和总线调度 |
基础问答
Q: UART 为什么叫异步串行通信?
答: “串行”表示数据按 bit 依次在线上传输;“异步”表示没有额外的共享时钟线。发送端和接收端事先约定波特率和帧格式,接收端通过起始位重新对齐自己的采样时钟。
Q: UART 最基本的接线是什么?
答: 两个 TTL/CMOS UART 设备通常要交叉连接:本机 TX 接对端 RX,本机 RX 接对端 TX,并且双方共地。同时要确认 3.3 V/5 V 逻辑电平是否兼容;若启用 RTS/CTS 硬件流控,额外信号也必须正确连接并与对端配置一致。
Q: 一帧 UART 数据包含哪些部分?
答: 线路空闲时通常为高电平,一帧从低电平起始位开始,之后是数据位、可选校验位和高电平停止位。常见 UART 按 LSB 先发,8N1 就是 8 数据位、无校验、1 停止位。
Q: 波特率和 bit 速率是一回事吗?
答: 波特率是每秒符号数,bit 速率是每秒 bit 数。常见二电平 UART 中一个符号携带一个 bit,所以两者数值相同;但这不是对所有调制方式都成立的定义。
Q: 为什么 115200 baud 的 8N1 不能传 115200 byte/s?
答: 因为每个数据字节还要携带 1 个起始位和 1 个停止位,所以 8N1 传一字节至少要 10 bit。理论有效速率约为 115200 / 10 = 11520 byte/s,还没扣除上层帧头、CRC 等开销。
Q: UART、USART、TTL、RS-232 和 RS-485 有什么区别?
答: UART/USART 是芯片内部的串行收发逻辑,USART 通常还支持同步模式。TTL/CMOS、RS-232 和 RS-485 更接近电气物理层:RS-232 使用正负电压且逻辑极性不同,RS-485 使用差分信号,都需要相应收发器,不能把 MCU UART 引脚直接当成 RS-232 或 RS-485 端口。
Q: UART 的奇偶校验能替代 CRC 吗?
答: 不能。奇偶校验只能发现一部分 bit 错误,无法可靠检测多 bit 错误、丢字节、重复或帧边界错位。需要可靠报文时,上层仍应设计长度、序号、CRC 和超时等机制。
深入追问
Q: 没有共享时钟,UART 接收端如何知道在哪里采样?
答: 接收端检测到空闲高电平向起始位低电平的跳变后,用本地过采样时钟确认起始位,然后尽量在后续每个 bit 的中间区域采样。每个新字符的起始位都会提供一次重新同步。
追问: 时钟误差为什么会在一帧内累积? 答: 收发两端的实际 bit 时间略有差异,接收端的理想采样点会逐 bit 偏移。偏移过大时,帧末采样点可能靠近边沿甚至落到相邻 bit;容许多大误差要看帧长、过采样和具体芯片实现,不应脱离手册死记统一百分比。
Q: 16 倍和 8 倍过采样怎么取舍?
答: 16 倍过采样通常有更细的采样相位和更好的噪声、时钟误差裕量;8 倍过采样可以支持更高波特率,但通常会牺牲一部分容差。最终还要根据 UART 时钟、分频误差和芯片参考手册选择。
追问: 为什么高波特率更容易暴露工程问题? 答: bit 时间变短后,边沿速度、收发器带宽、线缆、噪声和时钟分频误差留下的绝对时间裕量更小。不过纯粹的相对时钟容差并不是由“波特率数值高”单独决定的。
Q: TXE 和 TC 有什么区别?
答: TXE 或 TXFNF 表示发送数据寄存器/FIFO 还能写入后续数据,此时前一字节可能仍在移位寄存器中输出。TC 才表示移位寄存器也已发完,线上最后的停止位已结束。
追问: 哪些场景必须关心 TC?
答: RS-485 半双工释放 DE、单线半双工切换方向、发送完立即进入低功耗,或关闭 UART 时都应确认真正的传输完成状态。
Q: UART 如何接收不定长数据?
答: UART 硬件只恢复字节流,不知道业务报文边界。常用做法是中断或 DMA 把数据放入环形缓冲区,上层再按固定长度、帧头 + 长度、分隔符或超时解析。STM32 的 IDLE + DMA 能降低 CPU 搬运开销,但 IDLE 只能说明线路出现空闲,仍应由长度、CRC 和解析状态机确认完整帧。
追问: 为什么不建议在 UART 中断里直接解析完整协议? 答: 复杂解析、字符串格式化或阻塞输出会延长 ISR,让后续字节来不及处理。ISR 更适合快速搬运、更新写指针或通知任务。
Q: UART 收到 ORE、FE、PE 时分别说明什么?
答: ORE 说明旧数据还没被取走,新数据已经到达;FE 说明停止位采样不符合预期,常见于波特率、帧格式或线路异常;PE 说明奇偶校验不匹配。排查时要先保留错误统计,再对照波形、时钟和缓冲区状态。
追问: 收到错误后只清标志就够吗? 答: 不一定。除了按手册正确清除错误状态,还要确认 HAL 接收状态、DMA 和中断是否仍在运行,必要时重新启动接收,并修复真正的过载、参数或电气问题。
STM32 HAL 高频点
Q: 阻塞、中断和 DMA 模式怎么选?
答: 阻塞模式逻辑最直观,适合初次调试和短数据;中断模式适合中小数据量,但每字节中断在高速下开销明显;DMA 适合大块或连续数据。引入中断和 DMA 后,还要处理回调、并发、缓冲区生命周期和错误恢复。
Q: 为什么 HAL_UART_Receive_IT() 常常只收到一次?
答: 它通常是“启动一次指定长度的接收”,达到长度后进入完成回调,不会自动永久监听。常见做法是在回调或任务中再次调用接收函数,同时处理错误和缓冲区溢出。
static uint8_t rx_byte;
void uart_rx_start(void){ HAL_UART_Receive_IT(&huart1, &rx_byte, 1);}
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart){ if (huart->Instance == USART1) { ring_buffer_push(rx_byte); HAL_UART_Receive_IT(&huart1, &rx_byte, 1); }}Q: HAL_UARTEx_ReceiveToIdle_DMA() 解决什么问题?
答: 它让 DMA 连续搬运接收数据,并在达到设定长度或出现 IDLE 等接收事件时通知软件处理,适合不定长字节流。应根据所用 HAL 版本确认回调中 Size 的含义、Normal/Circular 模式和重启规则,不能把每次回调都当成完整协议帧。DMA 缓冲区的生命周期必须覆盖整个传输,不能使用已返回函数的局部栈变量;带 D-Cache 的 MCU 还要处理缓存一致性。
Q: UART 字长和校验配置有什么 STM32 常见坑?
答: 某些 STM32 系列和 HAL 接口中,WordLength 的位数定义会把校验位算在内,所以“8 数据位 + 校验位”未必直接选看起来的 8 bit 字长。必须以对应芯片的参考手册和 HAL 头文件为准,并核对实际数据宽度。
Q: HAL 返回 HAL_BUSY 或 HAL_ERROR 时应该怎么查?
答: 先检查函数返回值、UART 句柄的 TX/RX 状态和 HAL_UART_GetError(),再确认上一次非阻塞传输是否完成、回调是否到达、中断/DMA 是否开启。不要只在循环里反复重试,否则可能掩盖 ORE、配置错误或状态机没有恢复的根因。
HAL_StatusTypeDef status = HAL_UART_Receive(&huart1, buf, len, 100);if (status != HAL_OK) { uint32_t error = HAL_UART_GetError(&huart1); printf("UART status=%d, error=0x%08lX\r\n", status, error);}工程场景题
场景:调用发送函数后,串口助手没有任何输出
分析思路: 先确认串口助手的 COM 口和参数,再用逻辑分析仪或示波器直接测 MCU TX 引脚。如果没有波形,查外设时钟、GPIO 复用、实例和初始化路径;如果引脚有波形但电脑没有,查 TX/RX 交叉、共地、USB-TTL 电平和收发器方向。
面试表达: 先用引脚波形把“MCU 没发”和“链路没传到”分开,比一上来反复改 HAL 代码更有效。
场景:能收到数据,但全是乱码
分析思路: 先统一波特率、数据位、校验位、停止位和流控,然后用逻辑分析仪测 bit 时间并尝试解码固定字节,例如 0x55。若实际波特率不对,继续检查系统时钟、UART 时钟源和分频;若逻辑分析仪能正确解码,再查串口助手的文本编码以及数据本来是否为二进制。
面试表达: 乱码不一定是字符串问题,要先用 bit 时间、帧格式和逻辑分析仪建立可测量证据。
场景:短数据正常,连续高速接收时丢字节并出现 ORE
分析思路: 先记录 ORE 次数和接收缓冲区最高水位,再检查 ISR 是否打印、阻塞或复杂解析,以及接收是否及时重启。减小 ISR 工作量,改用环形缓冲区或 DMA + IDLE,并按实际最坏流量设计缓冲容量;如果接收端本身处理不过来,还需降速、流控或协议级节流。
面试表达: ORE 的核心是软件未在下一字节到来前取走旧数据,解决时要同时看 ISR 延迟、缓冲和平均/峰值流量。
场景:RS-485 只能发不能收,或者最后一字节不完整
分析思路: 同时观察 MCU TX、DE/RE 和 A/B 差分波形。发送前使能驱动,并在 TC 表明最后停止位结束后再释放 DE;然后确认收发器已切回接收、A/B 标识与偏置/终端正确,且从机回复时间没有被本机方向切换吞掉。
面试表达: TXE 只说明还能写下一字节,RS-485 释放驱动必须关心 TC 和实际 DE/A-B 时序。
易错点
-
误区: UART 就是 RS-232。 正解: UART 是串行收发逻辑,RS-232 是使用正负电压的电气标准,MCU 引脚与 RS-232 之间需要收发器。
-
误区: 两端波特率都填 115200 就不可能有波特率问题。 正解: 软件配置相同不代表实际波特率相同,时钟源、分频、晶振误差和配置错误都会影响实际 bit 时间。
-
误区: 只要触发
TXE,数据就已经在线上发完。 正解:TXE只代表发送数据寄存器可写,最后一帧完全移出要看TC。 -
误区: 收到 IDLE 就必然收到了一个完整报文。 正解: IDLE 只是线路空闲事件;发送方中途停顿也可能触发,完整帧仍要用长度、分隔符、CRC 和状态机判断。
-
误区: DMA 可以解决所有丢包问题。 正解: DMA 只降低数据搬运开销,缓冲区太小、消费者过慢、边界计算错误或缓存不一致仍会丢数据。
-
误区: 中断回调里立即解析、打印最安全。 正解: ISR 应尽可能短,一般只搬数据、更新指针或通知任务,复杂解析和阻塞输出放在主循环或 RTOS 任务中。
-
误区: UART 收到
HAL_OK就证明业务数据正确。 正解:HAL_OK只说明这次 HAL 操作按预期完成,报文长度、CRC、字节序和业务语义仍需上层验证。
面试口述模板
如果面试官让你“讲一下 UART”,可以按这段组织:
UART 是一种异步串行收发外设,没有共享时钟线,双方要事先约定波特率和数据位、校验位、停止位。线路通常空闲为高,接收端通过起始位对齐,再用本地过采样时钟在各 bit 中间采样。常见 8N1 每字节需要 10 bit,所以有效字节速率约是波特率的十分之一。UART 和 RS-232、RS-485 不在同一层,后两者需要物理层收发器。工程上我会区分 TXE 和 TC,接收用中断或 DMA 搬到环形缓冲区,再由上层协议判断帧边界。排查时先看共地、电平和 TX/RX 接线,再用波形确认波特率与帧格式,最后查 ORE 等错误、缓冲区和协议解析。
一句话总结
UART 面试的核心是能用“起始位对齐 + 本地时钟采样”解释异步收发,并把电平、帧格式、状态标志、缓冲和上层协议串成一条工程排查链。
官方参考资料
- Microchip TB3208: Basic Operation of UART with Protocol Support
- Microchip TB3331: Debugging Serial Interfaces in Embedded Systems
- ST: Getting started with UART
- ST: HAL UART How to Use
- Analog Devices: Fundamentals of RS-232 Serial Communications
- Analog Devices AN-960: RS-485/RS-422 Circuit Implementation Guide