1. 串口基础与硬件原理

大家好,我是你们这趟串口与 I2C 性能调优之旅的向导。咱们第一节课,先把串口的老底儿翻出来晒晒。别觉得基础就简单,很多坑其实都埋在这些“基础”里。

1.1 UART 协议帧格式:数据是怎么“坐车”的?

UART,全称 Universal Asynchronous Receiver/Transmitter。说白了,就是让两个设备能“异步”聊天。什么叫异步?就是没有专门的时钟线,全靠双方约定好一个“语速”(波特率),然后在一根数据线上你一句我一句。

那每一句话(数据帧)长什么样呢?我习惯把它想象成一列火车:

  • 起始位(Start Bit): 火车头。平时数据线是“高电平”(空闲状态),一旦拉低,就告诉对方:“注意,我要开始说话了!” 固定为 1 位。
  • 数据位(Data Bits): 车厢。这是真正的货物,通常是 5 到 8 位。最常见的是 8 位,刚好一个字节。传输时一般是低位在前(LSB first)。
  • 校验位(Parity Bit): 可选的车厢。用来做简单的错误检查。可以选无校验(None)、奇校验(Odd)或偶校验(Even)。
  • 停止位(Stop Bit): 火车尾。必须拉高电平,表示“我说完了”。长度可以是 1、1.5 或 2 位。

核心配置: 我们常说的“9600 8N1”,意思就是:波特率 9600,8 位数据位,无校验(None),1 位停止位。这是嵌入式世界里最通用的配置。

为什么会需要停止位?其实是为了给接收端一点时间来处理刚收到的字节。我在一个老项目里遇到过,把停止位从 1 改成 2,一个棘手的丢数据问题就解决了。你想想看,有时候就是这么微妙。

1.2 波特率计算:语速到底有多快?

波特率(Baud Rate),就是每秒传输的符号数。在 UART 里,一个符号就是一个 bit,所以波特率就等于比特率(bps)。

那怎么算传输一个字节需要多久?公式很简单:

传输时间 = (1 / 波特率) * (1 + 数据位数 + 校验位数 + 停止位数)

举个例子,9600 8N1:

  • 传输 1 位的时间:1/9600 ≈ 104.17 微秒
  • 传输 1 帧(1 起始 + 8 数据 + 1 停止)的时间:104.17 * 10 ≈ 1.04 毫秒
  • 理论最大吞吐量:1 / 1.04ms ≈ 961 字节/秒

我的经验: 实际吞吐量通常只有理论值的 70%-80%。因为还有中断处理、系统调度、协议开销(比如 Modbus 的帧间隔)等损耗。别把理论值当上限去设计系统。

波特率是怎么产生的?通常由 MCU 的主时钟经过分频得到。比如一个 16MHz 的晶振,要得到 9600 波特率,分频系数就是 16,000,000 / (16 * 9600) ≈ 104.17。这个“16”是过采样率,大部分 UART 都用 16 倍过采样来保证采样精度。

避坑指南: 我曾经在一个 115200 波特率的项目里,因为晶振精度不够(用了 1% 的陶瓷谐振器),导致长时间通信后出现帧错误。后来换成 50ppm 的晶振,问题消失。记住,波特率误差必须小于 2%,最好控制在 1% 以内。

1.3 RS232 / RS485 电平转换:TTL 电平的“翻译官”

MCU 的 UART 引脚输出的是 TTL 电平(0V 为逻辑 0,3.3V 或 5V 为逻辑 1)。但这个电平抗干扰能力差,传不了几米。所以就有了 RS232 和 RS485 这两种“翻译官”。

特性 TTL (UART) RS232 RS485
电平标准 0V / 3.3V 或 5V -15V ~ -3V (逻辑1) / +3V ~ +15V (逻辑0) A-B 电压差:< -200mV (逻辑1) / > +200mV (逻辑0)
传输方式 单端 单端 差分(双绞线)
最大距离 < 1 米 约 15 米 约 1200 米
最大节点数 点对点 点对点 最多 256 个(标准)
典型芯片 MCU 内置 MAX232, SP3232 MAX485, SP3485

RS232 用正负电压来传输,抗干扰能力比 TTL 强,适合短距离(十几米)的工业设备调试。RS485 则是差分信号,两根线(A 和 B)互为参考,共模干扰被抵消,所以能传上千米,而且支持多节点组网。

我记得有一次在工厂调试,现场电机一启动,RS232 就乱码。换成 RS485 后,世界清净了。差分信号在工业环境里就是王道。

1.4 硬件流控 RTS/CTS:别让数据“堵车”

当两个设备速度不匹配时,比如一个快的 MCU 给一个慢的蓝牙模块发数据,就会发生数据溢出。软件流控(XON/XOFF)靠发特殊字符,但效率低,而且如果数据里恰好有这些字符就麻烦了。

硬件流控 RTS/CTS 就靠谱得多。它用了两根额外的线:

  • RTS (Request To Send): 我准备好了,你可以发数据给我。
  • CTS (Clear To Send): 我准备好了,你可以发数据过来。

流程是这样的:

  1. 设备 A 要发数据前,先拉高 RTS,问设备 B:“我要发了,你准备好了吗?”
  2. 设备 B 如果缓冲区有空位,就拉高 CTS 回应:“来吧,我准备好了。”
  3. 设备 A 看到 CTS 为高,才开始发送数据。
  4. 如果设备 B 的缓冲区快满了,它就拉低 CTS。设备 A 看到后,必须立即停止发送,直到 CTS 再次变高。

实战建议: 如果你的系统里,发送端和接收端的处理速度有明显差异(比如 MCU 向一个低速无线模块发数据),强烈建议开启硬件流控。我曾经在一个 4G 模块的项目里,因为没开流控,导致模块频繁重启。开了之后,稳如老狗。

不过要注意,不是所有 UART 都支持硬件流控。有些 MCU 的 UART 只有 TX/RX 两根线,那就只能用软件流控或者降低波特率来妥协。

1.5 知识体系总览

下面这张图,把咱们这一章的核心知识点串起来了。你可以把它当作一个快速索引。

串口通信基础核心知识体系 UART 串口通信 帧格式 波特率计算 电平转换 硬件流控 起始/数据/校验/停止 分频系数 & 误差 RS232 / RS485 RTS / CTS 握手 核心要点 1. 通信双方必须约定完全相同的帧格式和波特率 2. 电平转换芯片是长距离通信的“翻译官”