跳到正文

UART、SPI 与 I2C

本文性质声明:本机无开发板,全部内容未实测,为手册级框架。站内对照:net-socket(TCP 的字节流与粘包实测)、dbg-log(日志缓冲成本实测)、conc-task-queue(队列模式)。

一、三条总线的性格

UARTSPII2C
信号线TX/RX(两根,点对点)SCK/MOSI/MISO + 每从机一根 CSSDA/SCL(两根,总线)
拓扑点对点一主多从(片选选人)多主多从(地址选人)
速率9600–Mbps 级MHz–几十 MHz100k/400k/1M(3.4M 高速)
复杂度最低(两边约定好波特率)中(模式、片选、DMA)最高(仲裁、拉伸、NACK)
距离米级(RS-485 更远)板级(<30cm)板级(<1m)

选型默认:只要两根线、一个从机、简单省事 → UART;要速度、全双工、从机少 → SPI;要从机多/线少、能忍受复杂度 → I2C。

二、UART:波特率误差与 DMA 环形收发

UART 没有时钟线——双方各自按约定的波特率采样。所以波特率不是精确值而是分频逼近:72MHz 主频下 115200 的分频常是 39.0625 而非整数,误差 <2% 能容忍,>3% 就开始偶发帧错误(表现为高位字节偶发丢)。换主频后原来正常的波特率突然乱码,第一嫌疑就是它。

收发三档实现:

  1. 轮询:发一个字节等 TXE、收一个字节等 RXNE——简单但 CPU 全耗在等上。
  2. 中断:每字节一次中断——省 CPU 但中断频率 = 波特率/10(115200 下每秒 11520 次,高波特率下吃不消)。
  3. 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(从机在错误的边沿采样)。实践:

  1. 片选(CS)必须软件管理:硬件自动 CS 的快模式在多数从机上半自动模式有时序坑;标准姿势是:GPIO 拉低 CS → SPI 传输 N 字节 → GPIO 拉高 CS。CS 拉高前的延迟(从机需要时间处理)按手册来。
  2. DMA 传输一次事务:发 N 收 N 用 DMA + 传输完成中断,别逐字节中断。全双工意味着每发一字节必收一字节(想只发不收就发 dummy 读回来丢掉)。
  3. 多从机 = 多 CS 共享 SCK/MOSI/MISO:从机之间切换就是换 CS——注意切换时的 CS 空闲电平(多数从机要求高电平空闲,拉低时间别超过手册的上限)。
  4. 信号完整性:MHz 级速率下线长就是问题:SCK 上过冲加串阻、MISO 读回偶发错位——降速是最快的诊断手段。

四、I2C:地址、拉伸与仲裁

I2C 的麻烦都来自”两根线的总线”:

  1. 7 位地址 + 读写位:手册给的地址常是 7 位(如 0x68)或 8 位(0xD0,含读写位)——搞错的表现是 ACK 不到(总线扫描时该设备不存在)。多个同型号设备用地址脚(A0-A2)改地址。
  2. 时钟拉伸(clock stretching):从机可以把 SCL 拉低逼主机等——ADC/EERPOM 转换期间常见。主机侧要有超时(从机拉死不放 = 总线挂住),超时后发 STOP 恢复。
  3. 仲裁(多主):多主同时发,SDA 与发送不符者判负退出——单主系统用不到,但别把”仲裁丢失”当中断 bug 查。
  4. 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 开发机上的项目外设——该机不可达,未涉及。