UART 串口通信
UART 串口通信
一、概述
UART(Universal Asynchronous Receiver/Transmitter,通用异步收发器)是最常用的异步、串行、全双工通信接口。它用两根数据线(TX 发送、RX 接收)即可实现点对点通信,结构简单、应用极广(调试日志、GPS 模块、蓝牙串口模块、AT 指令模组等)。
一句话核心:把并行数据按位、按预设波特率逐位串行发送/接收,收发双方靠波特率对齐和帧格式(起始位/数据位/停止位)同步,不需要时钟线。
二、为什么用 UART
| 优点 | 说明 |
|---|---|
| 简单 | 只需 2 根线(TX/RX)+ 共地,硬件极简 |
| 全双工 | 收发可同时进行 |
| 通用 | 几乎所有 MCU 都有,兼容性好 |
| 无时钟同步 | 靠波特率约定,无需额外时钟线 |
| 缺点 | 说明 |
|---|---|
| 距离短 | 标准电平(TTL/CMOS)下通常 < 10m |
| 速率有限 | 受波特率精度和线材限制 |
| 点对点 | 一根总线只能连 2 个设备(多机需 RS-485) |
| 抗干扰差 | 单端信号,易受噪声干扰 |
三、UART 帧格式(异步通信的关键)
3.1 帧由哪些部分构成
先想一个生活场景:两个人打电话,通话内容要"开始 → 说话 → 结束"三个环节才完整。UART 发一个字节也一样,不能上来直接甩电平,必须告诉对方"我要开始发了"、然后发数据、最后说"我发完了"。
类比:把一帧当成一句话。
- 一句完整的话 = "喂(提醒对方我要说了)→ 正文 → 嗯/好(表示我说完了)"
- 一帧完整的数据 = "起始位(提醒对方我要发了)→ 数据位(正文)→ 停止位(表示发完了)"
UART 异步通信没有时钟线,对方不知道你什么时候开始发,所以必须用"起始位 + 数据位 + 停止位"这一套固定结构来同步——这套结构就是帧(Frame)。一次传输一帧,每帧结束后 TX 回到空闲高电平,下一帧的起始位正是靠"从高到低的下降沿"被识别。至于帧与帧之间是否再额外插入高电平空闲位,取决于是否连续发送——紧贴连发时 P 之后可直接接下一帧的 S(详见后文多帧图)。
一个完整帧由以下部分组成:
| 字段 | 说明 | 典型值 |
|---|---|---|
| 起始位(START) | 一个低电平位,宣告一帧开始 | 1 位 |
数据位(D0~D7) | 真正的数据,先发 LSB | 8 位(常用) |
| 校验位(Parity,可选) | 简单检错,位于数据位之后、停止位之前 | 无/奇/偶 |
| 停止位(STOP) | 一个高电平位,宣告一帧结束 | 1 位 |
先认识两个概念:LSB 与 MSB。 一个字节(如 0x41)有 8 个数据位 D0~D7。按二进制权重大小分:
- LSB(Least Significant Bit,最低有效位)=
D0,权重最小(数值是 1),就是字节的"最右边一位"; - MSB(Most Significant Bit,最高有效位)=
D7,权重最大(数值是 128),是字节的"最左边一位"。
UART 的特别之处在于先发 LSB(最低位)。下面图中 D0 就紧跟在起始位之后发出。
怎么读这张图:下面用纯 ASCII 字符(
H=高电平 1、L=低电平 0),确保各列严格对齐。每 1 列 = 1 个 bit,同一列上下对齐表示同一个 bit:起始位 1 列 + 数据位 8 列 + 停止位 1 列 = 10 列,正好是一帧的 10 个 bit。列名缩写含义:I=IDLE(帧间空闲间隙,不属于帧)、S=START(起始位)、D0~D7=8 个数据位、P=STOP(停止位)。图中以发送'A'(0x41)为例,数据位电平即0x41各位的实际值(先发 LSB)。
位号: I S D0 D1 D2 D3 D4 D5 D6 D7 P I
电平: H L H L L L L L H L H H
对应bit: - - 1 0 0 0 0 0 1 0 - -
──────────────────────────────────────────────────────────────────
空闲 └────── 8 个数据位(先发 LSB)───────┘ 帧结束
↑下降沿 ↑回到空闲高读图对照:
I列:线上空闲(保持高电平),表示帧与帧之间的间隙,不属于帧;S列:电平从H跳变到L(下降沿),宣告一帧开始;D0 ~ D7列:8 个数据位,先发最低位(LSB)。这里的电平是发送'A'(0x41)的真实值:0x41 = 0100_0001(MSB 在左),先发 LSB 后各位依次为D0=1, D1=0, D2=0, D3=0, D4=0, D5=0, D6=1, D7=0,所以电平为H L L L L L H L;换成其他字节,这些电平会随之改变;P列:电平回到H(帧结束),之后恢复线上空闲。
一个完整帧就是上面从「S」到「P」这一整段:起始位 1 bit(低)+ 数据位 8 bit + 停止位 1 bit(高),正好 10 列 = 10 bit。I(IDLE)不算帧,只是帧间的高电平间隙。
帧内各字段的职责如下(空闲电平不算帧内字段,是帧之间的间隙,一并列出便于对照):
| 字段 | 说明 | 典型值 |
|---|---|---|
| 起始位 | 一个低电平位,标志一帧开始 | 1 位 |
| 数据位 | 实际数据,先发 LSB(最低位) | 8 位(常用) |
| 校验位(可选) | 奇/偶校验,用于简单检错 | 无/奇/偶 |
| 停止位 | 一个或多个高电平位,标志帧结束 | 1 位 |
| 空闲电平 | 无数据传输时 TX 保持高电平,不算帧内字段 | 高 |
典型配置:8 数据位 + 无校验 + 1 停止位,即 8N1。
上面认识了单帧的组成与职责,接下来看多帧之间如何衔接。先看带空闲间隙的多帧情形:帧与帧之间通常会有一段空闲高电平把它们分开(本例展示两个相邻帧,每帧 10 bit):
怎么读这张图:与上面的完整帧图同款格式,
位号与电平两行逐列对齐,每 1 列 = 1 个 bit。列名缩写与上一张图一致(I=IDLE 帧间隙、S=START 起始位、D0~D7=数据位、P=STOP 停止位),数据位电平沿用'A'(0x41)。第一帧(S~P,10 bit)结束后回到空闲高电平I,再下降沿进入第二帧。
位号: I S D0 D1 D2 D3 D4 D5 D6 D7 P I S D0 D1 D2 D3 D4 D5 D6 D7 P I
电平: H L H L L L L L H L H H L H L L L L H L H H
对应bit: - - 1 0 0 0 0 0 1 0 - - - 1 0 0 0 0 0 1 0 - -
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
└─────────────── 第 1 帧(10 bit)─────────────┘└─间隙─┘└────────────── 第 2 帧(10 bit)──────────────┘
↑下降沿 ↑下降沿读图对照:
- 第 1 帧结束:
P列电平回到H(上升沿),TX 进入空闲;- 帧间隙:
I列保持高电平,接收端据此知道"上一帧已完、下一帧未开始";- 第 2 帧开始:
I之后的S列再次从H跳到L(下降沿),宣告新一帧开始;- 前后两帧的下降沿之间,正好隔了 10 个 bit 的整数倍,接收端可据此逐帧对齐、连续采样。
如果发送端紧挨着连发字节,可以在第 1 帧停止位 P 结束后不插入额外空闲,直接进入下一帧的起始位 S。此时 P(高)到 S(低)的跳变本身就是下一帧的下降沿(数据位电平沿用 'A',即 0x41):
位号: I S D0 D1 D2 D3 D4 D5 D6 D7 P S D0 D1 D2 D3 D4 D5 D6 D7 P I
电平: H L H L L L L L H L H L H L L L L H L H H
对应bit: - - 1 0 0 0 0 0 1 0 - - 1 0 0 0 0 0 1 0 - -
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
└────────────── 第 1 帧(10 bit)─────────────┘ └────────────── 第 2 帧(10 bit)─────────────┘
↑下降沿 ↑下降沿读图对照:与上图对比,区别只在帧交界处——上图在
P后插入了 1 个空闲位I,本图没有,P直接接S。两帧的下降沿之间仍是恰好 10 bit(第 1 帧S~P),所以接收端依然能逐帧对齐。
注意:紧密连发要求收发双方波特率精度高、时序稳定;若波特率容差大,接收端容易因缺少空闲位而采样漂移。带间隙的版本更稳妥(见上一张图)。
小结:帧间隙实际中是怎么来的(工作中不用手动控制)
上面两张图对应两种真实情况,但实际工程中不需要也不应该手动去"插入"或"去掉"帧间隙——帧与帧之间是紧贴还是留空,取决于数据是否连续进入发送通道,而不是代码里刻意控制电平:
| 发送方式 | 帧间表现 | 原因 |
|---|---|---|
| 逐字节调用发送函数(如循环里一字节一字节发) | 自动有不固定的高电平间隙 | 函数调用、中断响应、循环判断等耗时,自然形成空闲 |
| 连续写入发送 FIFO / 硬件自动发送 | 紧贴连发,P 后直接接 S | 数据连续排队,硬件按波特率逐个发,无空隙 |
为什么实际中"带间隙"更常见、更稳妥:接收端靠检测"空闲高电平之后的下降沿"来定位帧起点(见 3.2 节),帧间隙相当于给接收端一次"重新对齐"的机会。波特率存在 ±2~3% 容差(RC 振荡器更差),帧越密、连发越久,采样点累积偏移越大;中间若有高电平间隙,等于把累计误差"清零"重来一次。因此长时、大批量传输时,很多驱动会在包与包之间刻意留一点间隔,或用更宽的停止位(如 8N2)来抗漂移。
| 实际场景 | 常见做法 |
|---|---|
| 调试打印(逐字节) | 天然带间隙,无需干预 |
| 发送一帧协议数据(几到几十字节) | 帧内连续发,帧间由上层逻辑自然间隔 |
| 大批量文件/固件传输 | 硬件流控 + 紧贴连发,靠波特率精度保证 |
| 波特率不稳定的低成本 MCU | 宁可每字节留间隙或用 8N2,避免采样漂移 |
一句话总结:帧间隙由"数据是否连续进发送通道"决定,而非手动控制——逐字节发送→自动有间隙,填满 FIFO 连发→自动紧贴。带间隙更稳、更常见;紧贴连发吞吐更高但要求时序稳定。上文两张图正好对应这两种真实情况。
3.2 为什么必须有起始位和停止位(同步的关键)
异步 = 没有时钟线。收发双方唯一能依靠的就是 TX 线上的电平跳变,因此必须用"约定好的、确定性的电平跳变"来界定一帧的边界:
| 位 | 电平 | 作用 | 为什么需要它 |
|---|---|---|---|
| 空闲 | 高 | 表示"线上无数据" | 给出一个稳定已知的基线,接收端才能识别出"变化" |
| 起始位 | 高→低跳变(下降沿) | 宣布"一帧开始" | 这是唯一一个能由电平变化来确定的事件,接收端靠检测下降沿来对齐帧起点 |
| 停止位 | 拉高 | 宣布"一帧结束" | 保证帧与帧之间至少有 1 个 bit 的高电平间隙,使下一帧的下降沿能被再次识别 |
关键理解:为什么必须有起始位?因为没有时钟线,接收端不知道发送端"什么时候开始发"。起始位(高→低的下降沿)是接收端唯一的"同步锚点"——一旦检测到这个跳变,它就以此为零点,按波特率依次采样后续每一位。若没有起始位,接收端永远无法确定数据从哪个 bit 开始。
为什么停止位必须是高电平?因为帧结束后必须回到"空闲高电平"状态,下一帧的起始位(下降沿)才能被识别。若结束在低电平,下一帧就没有下降沿了,同步就失效。
3.3 为什么先发 LSB(最低位)
UART 数据位约定先发最低位(LSB)。这是历史沿革(早期计算机与 ASCII 电传打字机沿用的习惯),与 16550/8250 这类经典 UART 芯片的硬件移位寄存器设计一致。接收端也按同样顺序接收,所以只要收发双方一致即可,无需关心对错,但约定俗成统一为 LSB 先发。
注意:与 SPI/I2C(通常先发 MSB)不同,UART 是LSB 先发。两者方向恰好相反,移植代码时最容易踩坑。
3.4 接收端如何在每个数据位中间采样
发送端按波特率逐位把电平放在 TX 线上。接收端则在每个 bit 的中间时刻采样(因为波特率存在 ±2~3% 容差,位中间是最稳的采样点)。以下仍以发送 'A'(0x41)为例,0x41 的 D0~D7 = 1 0 0 0 0 0 1 0:
位号: S D0 D1 D2 D3 D4 D5 D6 D7 P
电平: L H L L L L L H L H
采样: ● ● ● ● ● ● ● ● ●
时刻: 1.5 2.5 3.5 4.5 5.5 6.5 7.5 8.5 9.5
下降沿: ↑(0 时刻)读图:时刻以"起始位下降沿"为 0 起算(单位 = 1 个 bit 时长)。
●表示接收端在该时刻采样:D0 中点 = 1.5 bit、D1 中点 = 2.5 bit、……、停止位 P 中点 = 9.5 bit。每个●采到H记为 1、L记为 0,采到的序列1 0 0 0 0 0 1 0正好还原出0x41。
接收端流程:
- 检测起始位下降沿:把 TX 从高拉低的那一瞬间当作零点(0 时刻);
- 等待 1.5 个 bit 时长:从下降沿起,跳过起始位的整个宽度,再跨半个数据位,到达 D0 的中间;
- 每间隔 1 个 bit 采样一次:在 D0、D1、...、D7 的中间各采样一次电平,读到什么就是该位的数据;
- 读停止位:校验最后一拍应是高电平,据此判断帧是否完整。
为什么必须取位中间采样,而不是位的开头/结尾:位边界(上一个 bit 翻转处)的电平处于过渡区,波特率容差或噪声稍大就会误判;位中间离两次翻转最远,最稳:
D0=1 D1=0 D2=0 D3=0 D4=0 D5=0 D6=1 D7=0
───────|──────|──────|──────|──────|──────|──────|──────
● ● ● ● ● ● ● ●其中每个 | 是两个相邻 bit 的位边界,此处电平正在翻转,处于过渡区(不稳、易采错);每个 ● 是采样点,落在当前 bit 的正中间,离左右两个位边界都最远(最稳)。接收端只在 ● 处采样,所以能避开边界处的电平抖动。
小结:接收端在位中间采样,离两次电平翻转(位边界)都最远,抗波特率容差和线路噪声能力最强;位开头/结尾恰是翻转过渡区,最容易采错。
3.5 完整字节发送演算(发送 'A' = 0x41)
沿用全局统一假设:波特率 115200、8N1(10 bit/帧)。
发送 0x41,其二进制为 0100_0001(MSB 在左)。UART 先发 LSB,所以发送顺序从 D0(最低位)开始:
| 发送序号 | 帧位 | 对应 bit | 电平 | 说明 |
|---|---|---|---|---|
| 0 | 起始位 | — | 0(低) | 高→低下降沿,宣布帧开始 |
| 1 | D0 | 0x41 的 bit0 | 1(高) | 最低位先发 |
| 2 | D1 | bit1 | 0(低) | |
| 3 | D2 | bit2 | 0(低) | |
| 4 | D3 | bit3 | 0(低) | |
| 5 | D4 | bit4 | 0(低) | |
| 6 | D5 | bit5 | 0(低) | |
| 7 | D6 | bit6 | 1(高) | 0x41 的 bit6 = 1 |
| 8 | D7 | bit7 | 0(低) | 最高位最后发 |
| 9 | 停止位 | — | 1(高) | 回到空闲高电平 |
发送波形(起始 0 + 数据位倒序 + 停止 1):
位号: 起始 D0 D1 D2 D3 D4 D5 D6 D7 停止
位值: 0 1 0 0 0 0 0 1 0 1
高线: ┌──┐ ┌──┐ ┌──┐
低线: ────┘ └─────────────────────────────────┘ └────────┘ └波形高=1、低=0:起始位拉低 → D0 拉高 → D1~D5 保持低 → D6 拉高 → D7 拉低 → 停止位回高。每位在该 bit 时长内保持对应电平,跳变(竖线)只发生在位边界。
即发送的完整 bit 序列为:0 1 0 0 0 0 0 1 0 1(起始 0 + 数据位倒序 + 停止 1)
关键理解:发送一帧时,先把 TX 拉低(起始位),然后从 LSB 到 MSB 逐位发送数据位,最后拉高(停止位)。接收端检测到"空闲高电平后的下降沿"即识别为起始位,然后按波特率在每个数据位中间采样。
3.6 常见帧格式配置速查
| 配置缩写 | 数据位 | 校验 | 停止位 | 每帧 bit 数 | 典型场景 |
|---|---|---|---|---|---|
| 8N1 | 8 | 无 | 1 | 10 | 最常用(默认) |
| 8E1 | 8 | 偶校验 | 1 | 11 | 老式 Modbus 部分实现 |
| 8O1 | 8 | 奇校验 | 1 | 11 | 部分工业设备 |
| 8N2 | 8 | 无 | 2 | 11 | 慢速、抗时钟偏差场景 |
| 7E1 | 7 | 偶校验 | 1 | 10 | 早期 ASCII 传输 |
典型配置:8 数据位 + 无校验 + 1 停止位,即 8N1——收发双方必须逐项完全一致(数据位、校验、停止位任一不同都会导致乱码)。
四、波特率(Baud Rate)
波特率 = 每秒传输的码元(bit)个数,单位 bps。收发双方必须使用相同波特率才能正确通信。
常用波特率:9600、19200、38400、115200(最常用)、230400、921600。
数值演算(一帧耗时):
全局统一假设数据:本文所有波特率相关演算统一采用波特率
115200 bps、帧格式8N1(1 起始 + 8 数据 + 1 停止 = 10 bit/帧)。
设波特率 115200 bps,帧格式 8N1,逐项演算如下:
| 计算项 | 代入计算 | 结果 |
|---|---|---|
| 每比特时长 | 1 / 115200 | ≈ 8.68 μs |
| 每帧耗时 | 10 × 8.68 μs | ≈ 86.8 μs |
| 每秒帧数 | 115200 / 10 | = 11520 帧/秒 |
| 有效数据率 | 8 / 10 × 115200 | = 92160 bps |
| 传输开销 | 1 - 8 / 10 | = 20%(每字节 10 bit 中有 2 个开销位:起始 + 停止) |
关键理解:波特率不是纯数据率。8N1 下每个字节实际传 10 个 bit(含起始/停止位),所以有效吞吐 = 波特率 × 80%。
五、校验位(Parity)
校验位用于简单检错(检测奇数个位翻转,不能纠错,且可能误判偶数个位翻转):
| 校验方式 | 规则 |
|---|---|
| 无校验(N) | 无校验位,帧最短 |
| 奇校验(O) | 数据位 + 校验位中 1 的个数为奇数 |
| 偶校验(E) | 数据位 + 校验位中 1 的个数为偶数 |
数值演算(以 0x53 为例):先算校验位,再看它如何拼进帧。
第 1 步:展开数据位,数出 1 的个数
数据 0x53 = 0101_0011(8 个数据位,MSB 在左)。逐位标出每一位的值,并统计其中 1 的个数:
| 位号 | D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 |
|---|---|---|---|---|---|---|---|---|
| 位值 | 0 | 1 | 0 | 1 | 0 | 0 | 1 | 1 |
| 是否为 1 | ✗ | ✓ | ✗ | ✓ | ✗ | ✗ | ✓ | ✓ |
其中 1 出现在 D6、D4、D1、D0,共 4 个,所以 0x53 中 1 的个数 = 4(偶数)。
第 2 步:按校验规则算校验位
- 偶校验(E):要求"数据位 + 校验位"中 1 的总数为偶数。当前已有 4 个 1(已是偶数),为了让总数保持偶数,校验位必须补 0 → 校验位 = 0。此时
4 + 0 = 4(偶数)✓; - 奇校验(O):要求"数据位 + 校验位"中 1 的总数为奇数。当前已有 4 个 1(偶数),为了让总数变成奇数,校验位必须补 1 → 校验位 = 1。此时
4 + 1 = 5(奇数)✓。
第 3 步:把校验位拼进完整帧
校验位位于数据位之后、停止位之前。若用偶校验,发送 0x53 的帧为(按 UART 实际发送顺序,D0 先发):
起始位 D0 D1 D2 D3 D4 D5 D6 D7 校验位 停止位
L 1 1 0 0 1 0 1 0 0 H校验位只在"8 数据位 + 校验"的配置下存在,8N1 无校验位;加校验后每帧变为 11 bit。
关键理解:校验位只做检测,出错时接收端通常丢弃该字节(靠更高层协议重发)。真正的纠错/可靠性需靠数据链路层协议(如 CRC)。
六、UART 与 USART
| 特性 | UART | USART |
|---|---|---|
| 全称 | 通用异步收发器 | 通用同步/异步收发器 |
| 异步 | ✓ | ✓ |
| 同步(带时钟) | ✗ | ✓ |
| 额外时钟线 | 无 | 可选 SCLK |
| 典型用途 | 串口通信 | 串口 + 同步外设 |
USART 到底是什么:USART(Universal Synchronous/Asynchronous Receiver-Transmitter,通用同步/异步收发器)可以理解为 UART + 同步模式。它既能像 UART 那样做异步串口通信,也额外支持带时钟的同步传输。
异步模式 vs 同步模式(核心区别)
| 模式 | 是否需要时钟线 | 如何对齐采样 | 典型场景 |
|---|---|---|---|
| 异步(UART 方式) | 否 | 靠波特率约定 + 起始位下降沿 | 普通串口调试、GPS、蓝牙串口模块 |
| 同步(USART 独有) | 是,额外 1 根 SCLK | 时钟信号边沿决定采样时刻,数据跟着时钟走 | 智能卡(ISO7816)、LIN、同步外设 |
同步模式怎么工作:USART 在同步模式多输出一根时钟线 SCLK。发送方把数据放在数据线上,同时用 SCLK 的上升沿/下降沿"告诉"接收方"此刻去读数据位"。什么时候采、采几位,都由时钟边沿决定,不再依赖波特率去估位中间——所以同步传输对波特率精度的要求更低、时序更稳,也能更简单地让多设备共用同一时钟。
例子:用同步模式发送 0x53(8 位,D0 先发)
发送方把 0x53 的 8 位按 D0 先发放到 TXD,同时 SCLK 产生 8 个时钟脉冲,接收方在每个上升沿采样一位:
时钟: 第1个 第2个 第3个 第4个 第5个 第6个 第7个 第8个
SCLK: ┌┐ ┌┐ ┌┐ ┌┐ ┌┐ ┌┐ ┌┐ ┌┐
─┘└─────┘└─────┘└─────┘└─────┘└─────┘└─────┘└─────┘└─
TXD: 1 1 0 0 1 0 1 0
采样: ↑采 ↑采 ↑采 ↑采 ↑采 ↑采 ↑采 ↑采
位: D0 D1 D2 D3 D4 D5 D6 D7逐周期读到的位序列是 1 1 0 0 1 0 1 0,正好是 0x53 按 D0 先发的顺序。关键差异在采样时刻:异步模式下接收方要自己"估"每个 bit 的中间(1.5bit、2.5bit……,见 3.4 节);同步模式下SCLK 的上升沿就是采样时刻,接收方照边沿采即可,因此只要时钟稳定,就不会因为波特率偏差采错位。
上升沿 / 下降沿是怎么确定的:边沿指 SCLK 电平跳变的那一瞬间,方向由跳变决定:
- 上升沿(↑):SCLK 从低电平跳到高电平的时刻;
- 下降沿(↓):SCLK 从高电平跳到低电平的时刻。
SCLK 是周期性"高↔低"翻转的方波,每个周期各经历一次上升沿和下降沿:
SCLK: ┌─┐ ┌─┐ ┌─┐
─┘ └──┘ └──┘ └─
↑ ↑ ↑
(低→高 = 上升沿,本例在此采样)用哪个沿采样,取决于收发双方约定好的配置(对应芯片里的 CPOL/CPHA 设置)——有的协议在上升沿采、有的在下降沿采,只要双方一致即可。接收方硬件通过检测 SCLK 电压的跳变方向来识别边沿并触发采样。
"只要双方一致"是怎么保证的?
"一致"指收发双方的采样沿必须相同(要么都上升沿、要么都下降沿),否则一方在低→高采、另一方在高→低采,会错位一位。它不是硬件自动"商量"出来的,而是靠"主设备配置 + 从设备跟随同一配置"来保证:
- 主设备决定采样沿:USART 芯片通常用寄存器位(对应 SPI 的 CPOL=时钟极性、CPHA=时钟相位)设定"空闲电平和在哪条边沿采样"。主设备软件写好这两项,就确定了采样沿;
- 从设备配成相同参数:通信前,把从设备(外设)配置成与主设备完全相同的 CPOL 和 CPHA,双方对"哪个沿采"就有同样的理解;
- 主设备发起时钟,从设备跟随:同步模式下通常由主设备产生 SCLK,从设备沿主设备给的时钟边沿采样,因此只要从设备按主设备约定好的沿采即可。
CPOL 与 CPHA 的组合共有 4 种(即常说的 SPI 模式 0~3):
| CPOL | CPHA | 空闲电平 | 采样边沿 | 对应模式 |
|---|---|---|---|---|
| 0 | 0 | 低 | 上升沿 | 0 |
| 0 | 1 | 低 | 下降沿 | 1 |
| 1 | 0 | 高 | 下降沿 | 2 |
| 1 | 1 | 高 | 上升沿 | 3 |
关键:所谓"一致",就是主设备选定一组(CPOL、CPHA)锁定采样沿,从设备配置成完全相同的一组。只要这两项相同、时钟稳定,收发就锁定在同一条采样沿上,不会错位。
实际用哪个:做串口调试、AT 指令、普通设备间通信,走异步模式即可(等同于 UART),这是最常用、最方便的方式;只有当目标外设明确要求"带时钟同步传输"(如智能卡读卡器)时,才用到 USART 的同步能力。
关键理解:
USART = UART + 同步模式。平时当串口用时两者无差别(都走异步);USART 多出的同步模式 = 额外 1 根时钟线 SCLK,用时钟边沿采样,用于连接智能卡、LIN 等带时钟的同步外设。
七、电平标准(不同电压/接口)
UART 是逻辑协议,但电气电平有多种:
| 电平标准 | 电压 | 逻辑 | 典型场景 |
|---|---|---|---|
| TTL | 0~3.3V/5V | 高=逻辑1,低=逻辑0 | MCU 板级直连(MCU 之间直连用 TTL) |
| CMOS | 0~3.3V/5V | 高=逻辑1,低=逻辑0 | MCU 板级直连 |
| RS-232 | ±3~15V | 负逻辑:-15V=逻辑1、+15V=逻辑0 | 电脑串口(DB9),远距离,需电平转换芯片(如 MAX3232) |
| RS-485 | 差分 | 差分:A 高于 B=逻辑1、A 低于 B=逻辑0 | 工业多机,见 RS-485 章节 |
八、常见坑与排查
| 问题 | 可能原因 | 排查 |
|---|---|---|
| 完全无数据 | TX/RX 接反、未共地、波特率错 | 交叉 TX↔RX,确认共地 |
| 乱码 | 波特率不匹配、校验位设置不一致 | 收发双方配置必须完全一致 |
| 丢字节 | 接收缓冲溢出 | 加大缓冲/降波特率 |
| 电平不匹配 | TTL 直连 RS-232 | 加电平转换 |
| 高字节错 | 停止位配置不一致 | 统一 8N1 |