4. UART驱动与调试接口
UART,说白了就是串口。做嵌入式开发的人,没人离得开它。我入行那会儿,第一个调试手段就是拿串口线怼上去,看打印信息。直到现在,UART 依然是我最信赖的调试接口——简单、可靠、哪里都能用。
这一章,咱们把 UART 从协议到驱动,再到调试工具链,彻底捋一遍。
4.1 UART协议基础
UART 是异步串行通信。什么叫异步?就是没有单独的时钟线。收发双方靠约定的波特率来同步。
一个完整的数据帧长这样:
| 起始位 | 数据位(5~8位) | 校验位(可选) | 停止位(1/1.5/2位) |
|---|---|---|---|
| 0(低电平) | LSB 先发 | 奇/偶/无 | 1(高电平) |
我最常用的配置是:115200 波特率、8 数据位、无校验、1 停止位,也就是常说的 115200-8N1。这个组合在大多数 MCU 上都能稳定工作。
4.2 轮询模式驱动
轮询模式最简单。CPU 死等,直到数据收发完成。适合裸机调试,或者对实时性要求不高的场景。
下面是一个典型的轮询发送函数:
void uart_send_byte(uint8_t data) {
// 等待发送缓冲区空
while (!(UART->SR & UART_SR_TXE));
// 写入数据
UART->DR = data;
// 等待发送完成
while (!(UART->SR & UART_SR_TC));
}
接收也类似:
uint8_t uart_recv_byte(void) {
// 等待接收数据就绪
while (!(UART->SR & UART_SR_RXNE));
return (uint8_t)(UART->DR & 0xFF);
}
嗯,这里要注意。轮询模式有个大坑:如果对方不发数据,你的程序就卡死在 while 循环里了。我早期做 Bootloader 时,就因为这个原因,导致整个系统在等待串口数据时无法响应看门狗,直接复位了。
4.3 中断模式驱动
中断模式才是正经的驱动写法。CPU 不用傻等,数据来了自动触发中断。
中断接收的典型流程:
- 使能 UART 接收中断(RXNEIE)
- 在中断服务函数里读取 DR 寄存器
- 把数据存入缓冲区
- 清除中断标志
代码骨架:
void UART_IRQHandler(void) {
if (UART->SR & UART_SR_RXNE) {
uint8_t data = (uint8_t)(UART->DR & 0xFF);
// 把 data 存入环形缓冲区
ringbuf_put(&rx_ring, data);
}
}
中断发送稍微讲究一点。我习惯用 发送缓冲区空中断(TXEIE) 来驱动发送。初始化时先发第一个字节,然后使能 TXEIE,后续字节在中断里逐个发送。全部发完后,关闭 TXEIE。
4.4 环形缓冲区实现
环形缓冲区,也叫循环队列。它是中断驱动和主循环之间的桥梁。
为什么用环形缓冲区?说白了,就是解决 生产者和消费者速度不匹配 的问题。中断是生产者,主循环是消费者。中断随时可能来数据,主循环不能保证立刻处理。
一个简单的环形缓冲区结构体:
typedef struct {
uint8_t *buffer;
uint32_t head; // 写指针
uint32_t tail; // 读指针
uint32_t size; // 缓冲区大小
} ringbuf_t;
核心操作就两个:
- 写操作: 把数据放到 head 位置,head 递增。如果 head 追上 tail,说明缓冲区满了——要么覆盖旧数据,要么丢弃新数据。
- 读操作: 从 tail 位置取数据,tail 递增。如果 tail 追上 head,说明缓冲区空了。
我见过很多人把环形缓冲区写复杂了。其实关键就一点:判断满和空。我的做法是:
- 缓冲区空:head == tail
- 缓冲区满:(head + 1) % size == tail
这种实现会浪费一个字节的空间,但判断逻辑极其清晰,不容易出 bug。
head = (head + 1) & (size - 1),效率高不少。
4.5 串口调试工具链
工具用对了,调试效率翻倍。我这些年攒了一套串口调试工具链,分享给你。
| 工具 | 用途 | 我的评价 |
|---|---|---|
| PuTTY | 基础串口终端 | 轻量、稳定,适合快速看打印 |
| SecureCRT | 专业串口终端 | 支持脚本、日志、多会话,收费但值 |
| Serial Port Monitor | 抓取串口数据 | 调试协议栈时必备,能看到原始字节流 |
| 逻辑分析仪 | 硬件级抓波形 | 波特率不对、电平不匹配时,一抓就知 |
我个人最常用的组合是:SecureCRT + 逻辑分析仪。SecureCRT 看应用层数据,逻辑分析仪看物理层波形。两个视角一结合,大部分串口问题都能定位。
举个例子。有一次 Bootloader 下载固件总是校验失败。用 SecureCRT 看,数据好像没问题。但用逻辑分析仪一抓,发现停止位宽度不对。原来是时钟分频算错了,导致实际波特率偏差。这种问题,光靠软件是看不出来的。
- TX/RX 有没有接反?——我犯过至少三次这个错
- 电平是否匹配?3.3V 和 5V 不能直连
- 共地了吗?——不共地,数据全是乱码
- 波特率误差在范围内吗?——用逻辑分析仪量一下实际波形
4.6 本章知识结构
下面这张图,把 UART 驱动与调试的核心脉络串起来了:
从协议到驱动,再到缓冲区和工具链,这是一条完整的链路。每个环节都踩过坑,也都有优化空间。掌握了这些,你在 Bootloader 开发中遇到串口相关的问题,基本都能快速定位。