I2C vs SPI vs UART
判别规则(核心,唯一要记的)
- 选型看"挂多少 + 快不快 + 干什么":挂一堆低速小器件、要省线 → I2C;大块数据要快(Flash/屏)→ SPI;两个设备对连/调试 → UART
- 有时钟线 = 同步(I2C/SPI),没时钟线 = 异步(UART,靠波特率对表)
- SPI 全双工最少 4 根线(只读场景可省 MISO 到 3 根);I2C 靠地址 + ACK、SPI 靠 CS 片选、UART 靠波特率配对
一句话:"挂一堆慢器件省线用 I2C,大块快数据用 SPI,两设备对连用 UART;有时钟=同步,没时钟=异步。"
双工(先懂这个)
"双工" = 数据能不能同时两个方向走:
| 名称 |
同一时刻 |
像什么 |
谁是这样 |
| 单工 |
只能一个方向 |
收音机广播 |
少见 |
| 半双工 |
只能一个方向,但可换方向 |
对讲机 |
I2C |
| 全双工 |
两个方向同时 |
打电话 |
SPI、UART |
- I2C 半双工:只有一根 SDA 数据线,主机发的时候从机没法同时回,发完才能换方向
- SPI 全双工:MOSI + MISO 两根独立线,一个时钟换一位、两边同时
- UART 全双工:TX + RX 两根独立线,各走各的
抽象描述(一句话本质)
三种都是"芯片之间传数据"。区别在三个硬指标:几根线、有没有时钟线(同步/异步)、每秒能传多少位。
I2C:2 根线,同步,慢,一总线挂多设备
物理
- SDA(数据)+ SCL(时钟),两根
- 开漏输出 + 外部上拉:只能"拉低"或"释放"(释放被电阻拉高),空闲两线都是高
数据怎么交换(逐步)
① 起始:SCL 高时 SDA 由高→低 → 告诉所有从机"要开始了"
② 寻址:SCL 打 8 个时钟,SDA 送出 7 位地址 + 1 位读写位
③ ACK:匹配的从机拉低 SDA 一个时钟 → "我在"
④ 数据:按"8 位数据 + 1 位 ACK"重复
⑤ 停止:SCL 高时 SDA 由低→高 → 传输结束
数据位规则:SCL 高时 SDA 必须稳定(接收方在高电平采样),SCL 低时才允许变化。
特点
- 半双工、慢(每字节 9 时钟 + 起止开销;100K~400K bit/s)
- 靠地址找设备,一根总线挂几十上百个从机
- 省线(2 根),引脚紧张首选
SPI:4 根线,同步,快,主从靠片选
物理
- SCLK(时钟)+ MOSI(主出从入)+ MISO(主入从出)+ CS(片选,低有效)
- 每从机单独一根 CS,其余三根共享
数据怎么交换(逐步)
① 主机把某从机的 CS 拉低 → 该从机上线,其余 CS 高不参与
② 主机打时钟,双方各一个移位寄存器,被 MOSI/MISO 连成环:
每个时钟边沿,主机从 MOSI 移出 1 位,同时从 MISO 收回 1 位
8 个时钟 = 换 1 字节(两方向同时)
③ 传完,CS 拉高 → 从机下线
特点
- 全双工:一个时钟同时双向换一位
- 快:无地址、无 ACK、无起止条件,每字节就 8 个时钟;SCLK 可达几十 MHz
- 无地址、无应答:发出去不知道从机收到没(协议层自己加)——换来速度
- CPOL/CPHA(时钟极性和相位)主从必须对上,否则错位
- 线多(每从机一根 CS),但带宽大
UART:2 根线,异步,靠波特率对表,点对点
物理
- TX + RX 两根,A 的 TX 接 B 的 RX(交叉),共地
- 没有时钟线——和 I2C/SPI 的本质区别
数据怎么交换(逐步)
① 空闲 TX 为高
② 起始位:TX 拉低 1 位时间 → 接收方靠下降沿对齐开始
③ 数据位:依次 8 位(低位在前),每 1 位占 1 个位时间
④ 停止位:TX 拉高 1 位时间 → 一帧结束
帧 = 起始位(1) + 数据位(8) + 停止位(1)(可加校验位)
没时钟怎么对(三个机制)
- 约定波特率:双方各自用自己的时钟,约定"每秒传多少位"(如 115200)
- 起始位对齐:检测到 TX 高→低的下降沿 = 开始,从此对齐
- 中点采样:按波特率计时,在每个位的正中间采样(不在边沿),时钟误差 ±3% 内能扛住
特点
- 全双工、无主从(点对点平等)
- 速度中(9600 / 115200 常见,高速几 Mbps)
- 没时钟兜底 → 高速长线易受干扰丢帧,不适合大块高速数据
为什么 I2C 慢、SPI 快(机制三连)
I2C 慢:
- 每字节开销大:8 数据 + 1 ACK = 9 时钟,还有起始/寻址/停止
- 开漏 + 上拉:只能拉低,拉高靠电阻慢充 → 频率上不去
- 半双工:同一根线,发完才能收
SPI 快:
- 开销极小:无地址/ACK/起止,每字节就 8 时钟纯数据
- 推挽驱动:既能主动拉高又能拉低,边沿陡翻转快 → 几十 MHz
- 全双工:一个时钟换一位,效率翻倍
为什么 UART 没时钟线还能传(总结)
约定波特率 + 起始位下降沿对齐 + 每位中点采样,三个机制兜底,没时钟线也能准确收发。
为什么大块高速数据选 SPI 不选 UART
- 波特率上限低:常见 115200,高速几 Mbps;SPI 几十 MHz
- 异步没时钟兜底:速度一高、线一长就时钟误差 + 干扰 → 丢帧错位
- 每字节有额外开销:起始 + 停止 ≈ 10~11 位/字节,浪费带宽
硬数据对比表(背这张)
| 指标 |
I2C |
SPI |
UART |
| 线数 |
2(SDA+SCL) |
4(MOSI/MISO/SCLK/CS,每从机多 1 CS) |
2(TX/RX) |
| 同步/异步 |
同步 |
同步 |
异步 |
| 双工 |
半双工 |
全双工 |
全双工 |
| 找对家 |
7 位地址 + ACK |
CS 片选(无地址) |
无寻址,直接连 |
| 速度量级 |
K 级(100K~1M) |
MHz 级(几十 M) |
K~M 级波特率 |
| 典型器件 |
传感器/EEPROM/OLED |
Flash/显示屏/SD 卡 |
调试口/GPS/蓝牙模块 |
正反例(建立直觉)
- 正例1:5 个传感器 + 引脚紧张 → I2C(2 根线 + 地址,省线)
- 正例2:2MB 数据快速烧写外部 Flash → SPI(MHz 级)
- 正例3:GPS 模块接单片机 → UART(两设备对连)
- 反例1:高速显示屏用 I2C → 太慢,带宽不够(I2C 是 K 级)
- 反例2:SPI 从机既要收配置又要回状态 → 必须 4 根线(全双工),别省 MISO
- 反例3:以为 UART 有主从 → 没有,点对点平等
变体验证(3 题,全过=学会)
- 5 个传感器 + 引脚紧张 → I2C(2 根线共享总线 + 地址寻址,省线)
- 陷阱题:"SPI 最少 3 根线" → 不完整。全双工最少 4 根(MOSI+MISO+SCLK+CS);只读场景才可 3 根(省 MISO)。面试答"全双工 4 根、只读 3 根"
- 快速烧 2MB 到外部 Flash → 不能用 I2C(K 级太慢),用 SPI(MHz 级)
口述要点(面试怎么讲)
- 结论先行:挂多/省线 → I2C;大块快 → SPI;对连调试 → UART
- 双工:I2C 半双工(一根数据线换向),SPI/UART 全双工(两根独立线同时)
- I2C 为什么慢:每字节 9 时钟 + 寻址 ACK 开销 + 开漏上拉拉高慢 + 半双工
- SPI 为什么快:纯数据无开销 + 推挽陡边沿 + 全双工
- UART 没时钟怎么传:约定波特率 + 起始位下降沿对齐 + 中点采样(误差 ±3% 内扛得住)
- 大块高速为何不用 UART:波特率上限低 + 异步无时钟兜底易丢帧 + 每字节多起始停止位
- 易错点:SPI 最少几根线只答 3 根;把 UART 当有主从;搞混同步/异步(有无时钟线)
关系网络
| 相邻概念 |
关系 |
孤立理解会犯的错 |
| 时钟树 |
串口/SPI 的波特率/时钟都由 RCC 提供 |
波特率算错不知查时钟源 |
| DMA |
串口/SPI 大批量收发用 DMA 提高效率 |
不知 DMA 能配到串口/SPI |
| 中断 |
UART 不定长接收用中断方式 |
全用轮询阻塞主循环 |
| HAL vs LL |
HAL 三种模式(轮询/中断/DMA)配合这三协议 |
选错模式 |
学习日期
2026-08-18
每主题一页,复习时只翻本目录。