news 2026/4/16 18:03:22

从零开始学UART协议:串行通信基本结构详解

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
从零开始学UART协议:串行通信基本结构详解

从一个“乱码”说起:为什么你的UART通信总是出问题?

上周,一位刚入门嵌入式开发的朋友在群里发了一张图——串口助手屏幕上满屏的“??”,他苦笑着说:“我明明按照例程写的代码,接线也对了,怎么就是收不到正确的数据?”

这个问题太典型了。

几乎每个接触单片机的人都会在某个深夜,盯着电脑屏幕上那一串看不懂的字符发呆:信号线没接错,电源正常,程序也能跑,可为什么就是通信失败?

答案往往就藏在最基础的地方——UART协议的理解是否到位

今天,我们就抛开教科书式的讲解,用工程师的视角,带你真正“看懂”UART是怎么工作的。不讲空话,只说实战中踩过的坑、调过的波形、改过的配置。


UART不是“插上线就能通”的黑盒子

很多人以为UART就是两根线(TX和RX)交叉一连,设个波特率,然后调个发送函数就完事了。但现实是:哪怕一个参数配错,结果就是“静默”或“乱码”

我们先来打破一个误解:

UART不是一个物理接口,而是一种通信机制

你看到的DB9插座、USB转TTL模块、RS-485端子……这些都是物理层标准。而UART是在这些硬件之上运行的一套“语言规则”——它定义了数据如何打包、何时开始、怎样结束。

就像两个人打电话,即使用了同一部手机,如果一个说普通话、一个说粤语,依然无法沟通。UART要通,必须双方“说同一种话”。


数据帧:UART通信的基本单位

想象一下,你要通过电报给远方的朋友传一句话。为了确保对方能准确接收,你们事先约定好格式:

  1. 先敲一下铃铛(提醒准备听)
  2. 然后逐字报出内容
  3. 报完再说一句“完毕”

UART的数据传输也是这样一套流程,只不过它的“铃铛”叫起始位,“字”是数据位,“完毕”则是停止位

一个完整的UART数据帧长这样:

[起始位][D0][D1][D2][D3][D4][D5][D6][D7][校验位][停止位]

我们一个个拆解。

起始位:唯一的同步信号

  • 固定为低电平(0)
  • 宽度 = 1 bit 时间
  • 功能:告诉接收方“我要发数据了,请立刻启动采样!”

这是整个异步通信中唯一一次强制同步。因为没有共用时钟,接收端只能靠这个下降沿来“对齐时间轴”。

一旦错过或误判,后面所有数据都会错位。

数据位:真正要传的信息

  • 长度可选:5~9位,常用8位
  • 顺序:低位先行(LSB First)

举个例子:你想发送字符'A',ASCII码是0x41,二进制为01000001

那么实际在线路上的传输顺序是:

第1位:1 (D0) 第2位:0 (D1) 第3位:0 (D2) ... 第8位:0 (D7)

如果你用逻辑分析仪抓包,看到的就是这个反向序列。很多初学者在这里栽跟头:以为高位先发,结果解析全错。

奇偶校验位(可选):简单的错误检测

虽然不能纠正错误,但可以发现部分传输异常。

  • 偶校验:保证数据位 + 校验位中“1”的总数为偶数
  • 奇校验:总数为奇数

比如数据位中有3个1(奇数),启用偶校验时,校验位就要补1,凑成偶数。

接收端收到后重新计算,如果不一致,就会标记为帧错误(Framing Error)

⚠️ 注意:校验只是锦上添花,不能替代CRC等强校验机制。高噪声环境下建议关闭或配合软件协议使用。

停止位:留给系统的喘息时间

  • 固定为高电平(1)
  • 持续1、1.5 或 2个bit时间
  • 作用:标志一帧结束,并允许接收端恢复状态

常见的是1位停止位。但在老旧工业设备或低速通信中(如≤600bps),可能会用1.5或2位,以提高容错性。


波特率:异步通信的生命线

如果说数据帧是“说什么”,那波特率就是“多快说”

波特率 = 每秒传输的符号数(bps)。例如9600bps,意味着每bit持续约104.17μs。

关键来了:发送和接收双方必须使用几乎相同的波特率,否则采样点会逐渐偏移。

假设发送方每100μs发一位,接收方却按105μs采样,到了第8位数据末尾,偏差已达40%,很可能把高电平误判为低电平。

一般要求误差不超过±2%。怎么做到?

方法说明
使用高精度晶振如7.3728MHz,可被常见波特率整除
分数分频器现代MCU支持小数分频,提升匹配精度
动态补偿在软件中根据反馈微调

💡 实战经验:调试阶段优先选用标准波特率(9600、115200、460800)。别试图自定义120000这种非标值,除非你知道自己在做什么。


全双工 vs 半双工:你真的需要两条线吗?

UART天生支持全双工通信——独立的TX和RX线路,允许同时收发。

这在与WiFi模块(ESP8266)、蓝牙(HC-05)、GPS等外设交互时非常有用。比如你可以一边发AT指令,一边实时监听响应。

但注意:全双工需要四根线(GND共地 + TX/RX双向)。

有些场景下为了节省引脚,会改成半双工模式(如RS-485),但这已经不属于原生UART范畴,而是加上了使能控制的变种。


STM32实战:HAL库下的UART初始化详解

下面这段代码,可能是你在无数例程里见过的:

UART_HandleTypeDef huart1; void 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; if (HAL_UART_Init(&huart1) != HAL_OK) { Error_Handler(); } }

看起来很简单?其实每一行都藏着玄机。

关键配置解读

参数含义推荐设置
BaudRate通信速率115200(高速调试)、9600(兼容旧设备)
WordLength数据位长度8位最通用
StopBits停止位数量1位足够,除非对接老设备
Parity是否启用校验初期建议关掉,避免干扰判断
Mode工作模式TX_RX双工
HwFlowCtl硬件流控无(除非外设有CTS/RTS引脚)

⚠️ 特别提醒:如果你启用了校验位(如Odd/Even),MCU会在发送时自动计算并插入校验位,接收时也会验证。一旦失败,可能直接丢帧。初学阶段建议一律设为UART_PARITY_NONE


发送字符串:别让CPU卡死在这里

再看这个函数:

void Send_String(char *str) { uint16_t len = strlen(str); HAL_UART_Transmit(&huart1, (uint8_t*)str, len, HAL_MAX_DELAY); }

它能工作,但有个致命问题:阻塞式发送

这意味着CPU会一直等待每一个字节发完才继续执行。如果发1KB的日志,主循环就卡住10ms以上——对于实时系统来说不可接受。

✅ 正确做法:
- 小量调试信息 → 仍可用HAL_UART_Transmit,但加超时(如100ms)
- 大量数据或频繁通信 → 启用DMA + 中断

示例(非阻塞方式):

HAL_UART_Transmit_DMA(&huart1, (uint8_t*)"Hello\n", 6);

配合回调函数处理完成事件,释放CPU资源。


接收更难搞:如何不错过任何一字节?

发送只要芯片能发就行,但接收面临两个挑战:

  1. 数据源源不断来,CPU不一定及时处理
  2. 中断服务函数里不能做复杂操作

常见错误写法:

// 错误示范:在主循环轮询接收 while (1) { HAL_UART_Receive(&huart1, &ch, 1, 1); // 超时1ms buffer[i++] = ch; }

这种方式极易丢包,尤其当其他任务耗时较长时。

✅ 正确做法:开启接收中断 + 环形缓冲区

#define RX_BUFFER_SIZE 128 uint8_t rx_buffer[RX_BUFFER_SIZE]; volatile uint16_t rx_head = 0, rx_tail = 0; // 启动一次中断接收 HAL_UART_Receive_IT(&huart1, &rx_temp, 1); // 中断回调 void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { if (huart == &huart1) { rx_buffer[rx_head] = rx_temp; rx_head = (rx_head + 1) % RX_BUFFER_SIZE; HAL_UART_Receive_IT(huart, &rx_temp, 1); // 重新启用 } }

这样一来,无论主程序在干什么,UART都能持续接收数据而不丢失。


电平不匹配?这才是“乱码”的元凶!

回到开头的问题:为什么会出现乱码?

除了波特率不对,最大的可能是电平不兼容

常见的三种电平标准:

类型电压范围应用场景
TTL UART0V / 3.3V 或 5VMCU之间短距离通信
RS-232±3V ~ ±15V(典型±12V)老式PC串口、工业设备
RS-485差分信号(A/B线)远距离、抗干扰、多点通信

📌 重点:TTL和RS-232不能直连!必须通过转换芯片,如:

  • MAX3232:TTL ↔ RS-232,内置电荷泵升压
  • SP3485:TTL ↔ RS-485,适合工业现场总线

否则,轻则通信失败,重则烧毁IO口。

🔧 设计建议:
- 不同电源系统互联时加光耦隔离
- 长线传输使用屏蔽双绞线
- 接口处加TVS管防静电(ESD)


实际项目中的典型架构

在一个典型的物联网终端中,UART往往是“中枢神经”:

+--------+ AT指令 [MCU] --- | ESP32 | <---> Wi-Fi 上云 +--------+ | v NMEA语句 +--------+ | GPS | +--------+ ^ | +-------------+ | 串口打印日志 | +-------------+ ↓ PC上位机(串口助手)

MCU通过多个UART通道分别连接不同模块,各自独立工作。

此时要注意:
- 不同模块可能支持的波特率不同(GPS常为9600,WiFi可达115200)
- 初始化顺序很重要(先初始化MCU UART,再唤醒外设)
- 添加超时重试机制应对临时通信失败


故障排查清单:快速定位问题

当你遇到UART不通时,按这个顺序检查:

  1. TX/RX是否交叉连接?
    MCU-TX → 模块-RX;MCU-RX ← 模块-TX

  2. 共地了吗?
    必须连接GND,否则无参考电平

  3. 波特率一致吗?
    双方都设为115200试试,排除配置差异

  4. 电平匹配吗?
    3.3V MCU不要直接连5V设备

  5. 有没有开启接收中断?
    如果只初始化发送,自然收不到数据

  6. 串口助手设置正确吗?
    数据位、停止位、校验位都要匹配

  7. 用示波器或逻辑分析仪看过波形吗?
    真实世界的问题,要用真实工具验证

👉 推荐工具组合:
-CH340G USB-TTL模块:低成本调试利器
-Saleae Logic Analyzer:可视化查看帧结构
-Tera Term / XCOM / PuTTY:稳定可靠的串口助手


写在最后:UART为何经久不衰?

尽管SPI、I2C速度更快,USB功能更强,但UART依然活跃在一线,原因很简单:

  • 🛠️实现简单:两个引脚+几行代码就能通信
  • 🔍调试友好:一句printf拯救整个项目
  • 💡跨平台通用:从8位单片机到Linux ARM板都支持
  • 🔄可扩展性强:搭上Modbus就是工业总线,配上LoRa就是远距离通信

它是嵌入式世界的“第一语言”。

所以,下次当你又看到串口助手里跳出一堆“``”时,别慌。静下心来,从起始位开始,一步步回溯信号路径——你会发现,问题从来都不神秘,只是细节没到位。

动手做个实验吧:让STM32读取DHT11温湿度,通过UART发送到PC显示。当你第一次看到“Temperature: 25°C”清晰出现在屏幕上时,你就真正入门了。

欢迎在评论区分享你的第一个UART成功案例。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/16 14:51:18

Qwen3-14B-AWQ:让AI智能切换思维模式的秘诀

Qwen3-14B-AWQ&#xff1a;让AI智能切换思维模式的秘诀 【免费下载链接】Qwen3-14B-AWQ 项目地址: https://ai.gitcode.com/hf_mirrors/Qwen/Qwen3-14B-AWQ 导语 Qwen3-14B-AWQ作为Qwen系列最新一代大语言模型的量化版本&#xff0c;首次实现了单一模型内"思考模…

作者头像 李华
网站建设 2026/4/16 13:00:32

超详细版:CANoe中自定义NRC返回值的配置步骤

如何在 CANoe 中精准模拟 ECU 的“拒绝艺术”——自定义 NRC 响应全解析你有没有遇到过这样的场景&#xff1a;测试诊断工具时&#xff0c;发现它对异常请求的处理总像“失明”一样&#xff1f;点击一个不存在的数据项&#xff0c;本该弹出“请求越界”的提示&#xff0c;结果却…

作者头像 李华
网站建设 2026/4/16 14:50:20

ERNIE 4.5突破:2比特量化实现单GPU部署大模型

百度ERNIE团队宣布推出ERNIE-4.5-300B-A47B-2Bits-Paddle模型&#xff0c;通过创新的2比特量化技术&#xff0c;首次实现了3000亿参数级大模型在单GPU上的高效部署&#xff0c;这一突破显著降低了大模型的应用门槛。 【免费下载链接】ERNIE-4.5-300B-A47B-2Bits-Paddle 项目…

作者头像 李华
网站建设 2026/4/16 13:02:00

PyTorch-CUDA-v2.6镜像如何助力AI初创公司降低算力成本?

PyTorch-CUDA-v2.6 镜像如何重塑 AI 初创公司的算力经济模型&#xff1f; 在 AI 初创公司最常听到的一句话是&#xff1a;“我们不缺想法&#xff0c;只缺时间和 GPU。” 这句玩笑背后藏着真实的困境&#xff1a;一个算法工程师入职三天&#xff0c;两天半花在配环境上&…

作者头像 李华
网站建设 2026/4/16 12:56:54

emwin入门必看:手把手教你搭建第一个GUI界面

emWin 入门实战&#xff1a;从零开始点亮你的第一个图形界面你有没有遇到过这样的场景&#xff1f;项目需要一块显示屏&#xff0c;原本打算用数码管或段码屏凑合一下&#xff0c;结果产品经理甩过来一张高颜值的UI设计图&#xff1a;“我们要做交互体验一流的设备。”——这时…

作者头像 李华
网站建设 2026/4/16 12:49:11

抖音无水印下载完整教程:三步快速获取高清视频

抖音无水印下载完整教程&#xff1a;三步快速获取高清视频 【免费下载链接】douyin_downloader 抖音短视频无水印下载 win编译版本下载&#xff1a;https://www.lanzous.com/i9za5od 项目地址: https://gitcode.com/gh_mirrors/dou/douyin_downloader 还在为抖音视频的水…

作者头像 李华