第一次接触UART和USART时,很多人会疑惑:名字这么像,到底有什么区别?我刚开始做嵌入式开发时也踩过坑,用USART模块当普通UART使,结果同步时钟线没接导致通信失败。后来才明白,这俩兄弟虽然长得像,但骨子里完全不同。
UART全称Universal Asynchronous Receiver/Transmitter,核心在"Asynchronous"(异步)这个词上。就像两个人在黑夜里打手电筒发摩斯密码,双方约定好闪烁频率(波特率),但没有共用时钟源。发送方闪一下表示开始(起始位),接着闪固定次数传递信息(数据位),最后再闪一下示意结束(停止位)。这种方式简单可靠,但传输效率有限——就像摩斯密码通信,发太快对方可能跟不上节奏。
USART的"S"代表"Synchronous"(同步),相当于给通信双方配了块共享的电子表。除了TX/RX数据线,还有根CLK时钟线实时同步节奏。我在做高速数据采集时实测过,同步模式下USART能跑到10Mbps,而同样硬件条件下UART到1Mbps就开始丢数据。不过USART的硬件设计更复杂,需要多处理一根时钟线,PCB布线时还得注意等长匹配。
最容易被忽视的是USART的硬件流控功能。去年我做工业传感器项目时,发现用普通UART在长距离传输时经常丢包。后来换成USART的RTS/CTS流控,通过硬件信号协调收发时机,传输稳定性直接提升90%。这个功能在UART上是没有的,需要外接额外芯片实现。
选型时硬件工程师最关心两个问题:要接多少线?要多花多少钱?我拆解过几十种开发板,发现UART的硬件连接堪称极简主义典范——只需要三根线:TX发送、RX接收、GND地线。这种设计在智能家居传感器中特别吃香,比如温湿度探头用UART接ESP8266,三根杜邦线一插就能工作。
USART的同步模式就麻烦多了,除了基础三件套还得加CLK时钟线。更头疼的是某些型号(比如STM32F4)的USART1时钟引脚和MCO时钟输出共用,配置时一不留神就会冲突。记得有次调试整整耗掉我两天,最后发现是CubeMX配置时钟时没注意引脚复用。
成本方面,以常见的CH340(UART转USB芯片)和CP2102(USART转USB)为例:
| 型号 | 协议支持 | 单价(1k pcs) | 外围元件 |
|---|---|---|---|
| CH340G | UART | ¥0.8 | 4个电容 |
| CP2102 | USART | ¥2.5 | 6个电容+晶振 |
对成本敏感的消费电子产品(比如电子价签),多数会选择UART方案。但工业场景下,USART多出来的硬件开销反而显得划算——我们有个PLC项目,用USART的硬件流控替代了额外的流控芯片,整体BOM成本反而降低了15%。
实际项目中该怎么选?根据我踩坑的经验,可以按这几个典型场景来决策:
调试打印首选UART:所有嵌入式开发者都熟悉的"Hello World"场景。STM32的USART模块在异步模式下完全兼容UART协议,但杀鸡焉用牛刀?用UART接个CH340就能在PC端显示调试信息。有个小技巧:在CubeMX里把USART配置成Asynchronous模式时,其实它就是在模拟UART工作。
传感器数据流分情况:低速传感器(如GPS模块)用UART足够,NMEA0183协议默认就是9600波特率。但遇到像激光雷达这种每秒要传百万字节的设备,就得祭出USART的同步模式了。我们测试过VL53L1X激光测距模块,在UART模式下最高115200bps会丢数据,切到USART同步模式跑1Mbps就稳如老狗。
多设备组网看协议:Modbus RTU这种典型UART协议就别折腾USART了。但遇到需要主从同步的场景,比如多个ADC芯片采样同步,USART的CLK时钟线就能派上大用场。曾经做过一个多轴运动控制器,用USART的同步模式同时触发8个编码器读数,同步误差控制在1us内。
以STM32F103为例,分享几个关键配置要点。先上代码片段,后面会解释为什么这样配:
c复制// USART异步模式配置(模拟UART)
void USART1_Init(void) {
// 1. 使能时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
// 2. 参数配置
USART_InitTypeDef USART_InitStruct;
USART_InitStruct.USART_BaudRate = 115200;
USART_InitStruct.USART_WordLength = USART_WordLength_8b;
USART_InitStruct.USART_StopBits = USART_StopBits_1;
USART_InitStruct.USART_Parity = USART_Parity_No;
USART_InitStruct.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_Init(USART1, &USART_InitStruct);
// 3. 使能中断
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
NVIC_EnableIRQ(USART1_IRQn);
// 4. 启动USART
USART_Cmd(USART1, ENABLE);
}
波特率精度陷阱:STM32的USART时钟源来自APB总线,当设置115200等非标准波特率时,实际波特率可能会有偏差。有个项目因此出现随机乱码,后来改用USART_OverSampling_8模式才解决。建议用这个公式校验:
code复制实际波特率 = fCK / (8 * (2 - OVER8) * USARTDIV)
DMA搭配技巧:高速传输一定要用DMA。配置时注意USART的TX/RX DMA通道不同,我习惯用CubeMX生成后,再手动修改DMA优先级。有个坑要注意:USART的TC(传输完成)标志和DMA的TC标志是两回事,等待DMA传输完成时要判断USART_FLAG_TC。
同步模式特殊配置:需要额外开启时钟输出:
c复制USART_ClockInitTypeDef USART_ClockInitStruct;
USART_ClockInitStruct.USART_Clock = USART_Clock_Enable;
USART_ClockInitStruct.USART_CPOL = USART_CPOL_Low;
USART_ClockInitStruct.USART_CPHA = USART_CPHA_1Edge;
USART_ClockInitStruct.USART_LastBit = USART_LastBit_Disable;
USART_ClockInit(USART1, &USART_ClockInitStruct);
调试时如果发现CLK引脚无输出,先检查GPIO是否配置为AF_PP模式。最崩溃的一次是发现时钟极性配反了,示波器上看波形完全正常,但数据就是不对。