UART、SPI 与 I2C
本文性质声明:本机无开发板,全部内容未实测,为手册级框架。站内对照:net-socket(TCP 的字节流与粘包实测)、dbg-log(日志缓冲成本实测)、conc-task-queue(队列模式)。
一、三条总线的性格
| UART | SPI | I2C | |
|---|---|---|---|
| 信号线 | TX/RX(两根,点对点) | SCK/MOSI/MISO + 每从机一根 CS | SDA/SCL(两根,总线) |
| 拓扑 | 点对点 | 一主多从(片选选人) | 多主多从(地址选人) |
| 速率 | 9600–Mbps 级 | MHz–几十 MHz | 100k/400k/1M(3.4M 高速) |
| 复杂度 | 最低(两边约定好波特率) | 中(模式、片选、DMA) | 最高(仲裁、拉伸、NACK) |
| 距离 | 米级(RS-485 更远) | 板级(<30cm) | 板级(<1m) |
选型默认:只要两根线、一个从机、简单省事 → UART;要速度、全双工、从机少 → SPI;要从机多/线少、能忍受复杂度 → I2C。
二、UART:波特率误差与 DMA 环形收发
UART 没有时钟线——双方各自按约定的波特率采样。所以波特率不是精确值而是分频逼近:72MHz 主频下 115200 的分频常是 39.0625 而非整数,误差 <2% 能容忍,>3% 就开始偶发帧错误(表现为高位字节偶发丢)。换主频后原来正常的波特率突然乱码,第一嫌疑就是它。
收发三档实现:
- 轮询:发一个字节等
TXE、收一个字节等RXNE——简单但 CPU 全耗在等上。 - 中断:每字节一次中断——省 CPU 但中断频率 = 波特率/10(115200 下每秒 11520 次,高波特率下吃不消)。
- DMA + 环形缓冲(推荐):发送 DMA 自动搬空缓冲,接收 DMA 循环写入环形 buffer,协议层从环形 buffer 取——CPU 只在”够一个包了”时被唤醒一次。两个坑:环形缓冲的读写指针要 volatile + 单字访问(中断与主循环共享,conc-mutex 的临界区纪律适用);半满中断阈值设太大延迟高、太小中断频繁,按协议最大包长折中。
帧错误诊断:ORE(溢出——没及时读)、FE(帧错误——波特率不匹配或线太长)、NE(噪声)。串口日志偶发乱码先查这三个标志,别先怀疑线。
三、SPI:四种模式与片选时序
SPI 用**极性(CPOL)+ 相位(CPHA)**定义四种模式(0,0 / 0,1 / 1,0 / 1,1),主从必须一致——从机手册会写,配错了的现象是全 0xFF 或全 0x00(从机在错误的边沿采样)。实践:
- 片选(CS)必须软件管理:硬件自动 CS 的快模式在多数从机上半自动模式有时序坑;标准姿势是:GPIO 拉低 CS → SPI 传输 N 字节 → GPIO 拉高 CS。CS 拉高前的延迟(从机需要时间处理)按手册来。
- DMA 传输一次事务:发 N 收 N 用 DMA + 传输完成中断,别逐字节中断。全双工意味着每发一字节必收一字节(想只发不收就发 dummy 读回来丢掉)。
- 多从机 = 多 CS 共享 SCK/MOSI/MISO:从机之间切换就是换 CS——注意切换时的 CS 空闲电平(多数从机要求高电平空闲,拉低时间别超过手册的上限)。
- 信号完整性:MHz 级速率下线长就是问题:SCK 上过冲加串阻、MISO 读回偶发错位——降速是最快的诊断手段。
四、I2C:地址、拉伸与仲裁
I2C 的麻烦都来自”两根线的总线”:
- 7 位地址 + 读写位:手册给的地址常是 7 位(如 0x68)或 8 位(0xD0,含读写位)——搞错的表现是 ACK 不到(总线扫描时该设备不存在)。多个同型号设备用地址脚(A0-A2)改地址。
- 时钟拉伸(clock stretching):从机可以把 SCL 拉低逼主机等——ADC/EERPOM 转换期间常见。主机侧要有超时(从机拉死不放 = 总线挂住),超时后发 STOP 恢复。
- 仲裁(多主):多主同时发,SDA 与发送不符者判负退出——单主系统用不到,但别把”仲裁丢失”当中断 bug 查。
- NACK 是唯一错误通道:从机不回 ACK = 地址没人认/从机忙/总线断——I2C 排障 90% 在看谁 NACK 了。诊断三板斧:总线扫描(逐地址探 ACK)、示波器/逻辑分析仪看 SDA/SCL 实际波形(拉伸和毛刺一眼可见)、上拉电阻值(阻值大信号沿慢,1k–10k 按总线和速率算)。
I2C 状态机实现:别用”发完一步查个标志再发下一步”的阻塞写法(一次拉伸就卡死整个循环),按字节事件(START 发完/地址 ACK/数据 ACK)推进状态机——和 emb-adc 的传感器驱动同一个模式。
五、排障索引
| 症状 | 入口 |
|---|---|
| 串口乱码 | 波特率误差(第二节);两端配置不一致 |
| SPI 读全 0xFF | 模式(CPOL/CPHA)错;CS 没拉;MOSI/MISO 接反 |
| SPI 读回错位 | SCK 太快/线太长;降速验证再查信号 |
| I2C 扫描不到设备 | 地址 7/8 位搞错;上拉缺失;从机没供电 |
| I2C 偶发卡死 | 从机时钟拉伸没做超时;总线被拉死——发 9 个时钟脉冲 + STOP 恢复 |
| 高波特率丢数据 | 换 DMA 环形缓冲(第二节);查 ORE 标志 |
六、未实测清单
- 本文所有总线行为——无开发板,未测。
- 波特率误差的具体数值——取决于主频配置,未算。
- 各从机手册的时序细节——以你器件的数据手册为准。
- 用户 Linux 开发机上的项目外设——该机不可达,未涉及。