4、UART串口通信基础:UART协议原理、波特率设置、STM32串口配置、串口助手调试
说到嵌入式开发,串口绝对是咱们打交道最多的外设之一。我刚开始做项目那会儿,第一个调试手段就是串口打印。说白了,UART就是设备之间对话的"电话线",简单、可靠、到处都能用。
这一章咱们就把UART串口通信彻底讲透。从协议原理到STM32的配置,再到实际调试,一条龙搞定。
4.1 UART协议原理
UART的全称是Universal Asynchronous Receiver/Transmitter,通用异步收发器。注意"异步"这两个字——收发双方不需要共享时钟信号,全靠约定的波特率来同步。
我当年刚学的时候有个疑问:没有时钟怎么对齐数据?其实答案就在起始位和停止位上。
一个标准的UART数据帧长这样:
起始位(1位) + 数据位(5~8位) + 校验位(可选) + 停止位(1~2位)
具体来说:
- 空闲状态:总线保持高电平
- 起始位:拉低电平,告诉接收方"我要发数据了"
- 数据位:从低位到高位逐位发送,通常是8位
- 校验位:奇校验或偶校验,用来简单检错
- 停止位:拉回高电平,表示一帧结束
我在项目中遇到过一个问题:两个设备明明波特率一样,但通信就是乱码。查了半天发现,一个用的是8位数据+1位停止,另一个用的是7位数据+2位停止。你想想看,帧格式不匹配,数据肯定对不上。
核心要点:UART通信双方必须约定完全相同的帧格式——波特率、数据位、校验位、停止位,一个都不能错。
4.2 波特率设置
波特率就是每秒传输的比特数。常见的波特率有9600、19200、38400、115200等。115200是目前最常用的,速度够快,稳定性也不错。
波特率怎么算?对于STM32来说,公式是这样的:
波特率 = 时钟频率 / (16 × USARTDIV)
其中USARTDIV是一个分频系数,写在USART_BRR寄存器里。嗯,这里要注意:USARTDIV可以带小数,小数部分用4位二进制表示。
举个例子,如果时钟是72MHz,想要115200的波特率:
USARTDIV = 72000000 / (16 × 115200) ≈ 39.0625
整数部分 = 39 = 0x27
小数部分 = 0.0625 × 16 = 1 = 0x1
BRR值 = (39 << 4) | 1 = 0x271
我个人习惯直接用STM32CubeMX生成配置,它会自动算好。但如果你在做底层驱动或者Bootloader,手动算波特率是基本功。
避坑指南:我曾经在项目里直接用115200的波特率,结果通信距离超过2米后频繁丢包。后来降到38400,问题就解决了。波特率越高,对线路质量的要求也越高。长距离通信时,适当降低波特率是明智的选择。
4.3 STM32串口配置
STM32的USART外设功能很强大,支持同步模式、智能卡、IrDA等。但咱们做支付模块,最常用的就是异步模式,也就是标准的UART。
配置步骤其实就这几步:
- 使能时钟:GPIO时钟和USART时钟都要打开
- 配置GPIO:TX设为复用推挽输出,RX设为浮空输入
- 配置USART参数:波特率、数据位、停止位、校验位
- 使能USART:先使能发送器,再使能接收器
- 配置中断(可选):如果需要中断接收,就配置NVIC
下面是一个标准的初始化代码,我用的是HAL库:
void MX_USART1_UART_Init(void)
{
huart1.Instance = USART1;
huart1.Init.BaudRate = 115200;
huart1.Init.WordLength = UART_WORDLENGTH_8B;
huart1.Init.StopBits = UART_STOPBITS_1;
huart1.Init.Parity = UART_PARITY_NONE;
huart1.Init.Mode = UART_MODE_TX_RX;
huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart1.Init.OverSampling = UART_OVERSAMPLING_16;
if (HAL_UART_Init(&huart1) != HAL_OK)
{
Error_Handler();
}
}
你看,代码其实很简洁。但有个细节我提醒一下:OverSampling这个参数,默认是16倍过采样。如果时钟频率不够高,可以改成8倍,这样能支持更高的波特率。
发送数据用这个:
HAL_UART_Transmit(&huart1, (uint8_t *)"Hello\r\n", 7, 1000);
接收数据用这个:
HAL_UART_Receive(&huart1, rx_buffer, 1, HAL_MAX_DELAY);
个人经验:我建议在实际项目中用中断方式接收,而不是轮询。轮询会阻塞CPU,浪费资源。中断接收配合环形缓冲区,是串口通信的黄金搭档。
4.4 串口助手调试
硬件配好了,代码写完了,怎么验证对不对?这时候就需要串口助手了。
常用的串口助手有:
- SSCOM:经典款,轻量好用
- Putty:支持串口和SSH,功能全面
- SecureCRT:商业软件,功能强大
- 串口调试助手:网上各种版本,选一个顺手的就行
调试步骤很简单:
- 用USB转串口模块连接STM32的TX和RX
- 打开串口助手,设置好波特率、数据位、停止位、校验位
- 打开串口,发送数据看接收区有没有回显
- 如果乱码,先检查波特率是否一致
- 如果没反应,检查TX和RX有没有接反
我遇到过最坑的一次:串口助手显示正常,但设备就是没反应。折腾了半天才发现,USB转串口模块的TX接了STM32的TX,RX接了RX——这是典型的"直连"错误。UART是交叉连接的,TX接RX,RX接TX。
调试口诀:TX接RX,RX接TX,GND接GND。波特率一致,帧格式一致,通信就通了。
还有一个实用技巧:用串口助手发送十六进制数据,可以精确控制每个字节。比如发送0xAA 0x55来测试通信链路,这两个字节的二进制是10101010和01010101,交替的0和1能暴露很多时序问题。
好了,UART串口通信的基础就这些。下一章咱们会讲怎么用串口和支付模块的读卡器通信,到时候会用到中断和DMA,那才是真正的实战。