☰
嵌入式通信协议对比:i2c、spi、uart、i2s选型与调试实战指南
2026/9/25 8:20:54 网站建设 项目流程

引言:嵌入式工程师绕不开的四个老伙计

做嵌入式开发这几年,i2c、spi、uart 这三个词,加上音频领域必须碰的 i2s,几乎天天挂在嘴边。不管是做单片机小项目,还是折腾 RK3588 这种带复杂总线的高端平台,总得跟它们打交道。很多刚入行的朋友问我的第一个技术问题,往往就是“这几个协议到底啥区别,我该用哪个”。说实话,这个问题看着基础,真要掰扯清楚,能写一篇长文。网上讲单个协议的资料非常多,但把四个放一起、从选型和实际调试角度去对比的,还真不多见。

这篇内容我想换个讲法,不按教科书顺序平铺直叙,而是从“这三根线四根线到底怎么干活”讲起,结合我实测过的波形、踩过的坑,把 i2c、spi、uart、i2s 的电气特性、时序结构、典型应用场景、常见故障一次说透。无论你是刚准备点亮第一颗 LED 的新手,还是在 RK3588 上用 spi 挂 ADC 的老手,这篇内容应该能帮你省下不少查手册的时间。

1. 四兄弟的基本盘:从物理层就看出了性格差异

1.1 连线数量决定命运

先记住一个最直观的结论:这四个协议的性格差异,从它们用几根线就能看出来。

uart 最节俭,两根线就能干活。一根发送一根接收,收发同时进行,全双工。它不需要时钟线,收发双方各自按约定好的波特率采样。这种设计带来的问题很典型——如果两边晶振精度不够,或者波特率配置得有微小偏差,时间一长就会采样错位,出现乱码。我曾经用一个 1% 精度的内部 RC 振荡器跑 115200 波特率,短报文没事,一传 64 字节以上的包就偶发乱码,后来换了外部晶振才好。

i2c 更省,两根线同时管数据和时钟,一根 SDA 一根 SCL。它是半双工,同一时间只能一个方向传数据。靠地址寻址,一条总线上能挂一堆设备。

spi 立马阔气起来,最少三根线起步,MOSI、MISO、SCLK,再加上片选 CS。如果要接多个从机,每个从机一根 CS,线数跟着设备数涨。全双工,时钟由主机产生,速度可以拉得很高。

i2s 专为音频设计,至少有四根线,SCK 位时钟、WS 左右声道时钟、SD 数据线,还可能再加一根 MCLK 主时钟。它跟 spi 有点亲戚关系,本质上都是同步串行传输,但数据组织方式完全围绕音频帧结构来。

这四兄弟的连线差异,直接决定了它们在电路板上的布局成本。我做过一个四层板,上面同时放了三个 i2c 传感器、一个 spi Flash、一路调试串口。i2c 走线最省心,两根线挂在同一个总线上,不需要给每个设备单独拉线。spi Flash 用了四线,如果当初选了 QSPI 封装,还能省两根。uart 纯粹用于调试日志,占两个引脚就完事。说实话,引脚资源紧张的时候,优先用 i2c,能救急。

1.2 速度边界:不是跑得快就好

很多人选协议只盯着速率上限,其实这是片面的。我整理了个常用数据:

协议常规速率范围典型上限传输方向主要用途
uart9600 ~ 921600 bps几 Mbps全双工调试、低速传感器、GPS、蓝牙模块
i2c100k / 400k / 1M bps3.4M(高速模式)半双工传感器、EEPROM、PMIC 配置
spi1M ~ 数十 Mbps上百 Mbps全双工Flash、ADC、屏幕、SD 卡
i2s与音频采样率相关数 Mbps全双工但分方向ADC/DAC、音频编解码

这里我想多说一句 spi 的速率。很多新手看芯片手册说 spi 支持 50MHz,就直接把分频器设到最低,结果跑起来数据全是错的。因为 spi 速率上限不只是控制器的问题,还取决于 PCB 走线长度、从设备实际支持频率、信号完整性。我实测过在杜邦线连接的情况下,spi 超过 10MHz 就开始出现偶发错误,换到 PCB 短线,才能稳定跑到 20MHz+。杜邦线有十几厘米长的时候,波形已经不能看了。

i2c 的速度相对温和,但它的 100k 和 400k 模式信号规格是有硬性要求的。我记得看过一个逻辑分析仪抓的波形,上升沿太缓,超过规格书规定的 1us,导致从机识别失败。后来把上拉电阻从 10k 换成 4.7k,波形明显改善。选上拉电阻这事,我会在后面的踩坑部分详细展开。

2. i2c 深入拆解:两根线的艺术与折磨

2.1 开漏结构为什么是 i2c 的命根子

i2c 最关键的一个设计,就是 SDA 和 SCL 都是开漏输出。这个设计让多个设备可以同时挂在一根线上,谁想拉低谁就拉低,想拉高得靠上拉电阻。这带来了仲裁机制:多个主机同时发起通信时,谁先拉低总线谁就赢了。

理解这个结构太重要了。它意味着 i2c 总线上任何一个设备出错,把 SDA 死死拉低,整条总线就瘫痪了。这种故障非常恶心,因为用万用表量 SDA 电平,永远是低,但你看不出是谁干的。

我在排查一个 gt911 触摸屏 i2c 通信失败问题时,就遇到这种情况。现象是主控读取触摸坐标一直超时,用逻辑分析仪一看,SDA 在某些状态下被异常拉低且持续了很久。最后查出原因是 gt911 的复位时序不对,导致芯片内部状态机异常,I2C 接口进入了异常模式。把复位引脚重新拉低再拉高,等足够时间再通信,问题立刻消失。

开漏结构还带来一个连带影响:i2c 的速度受限于上拉电阻和总线电容组成的 RC 延时。总线越长、设备越多,电容越大,需要的上拉电阻就越小(但太小又会增大灌电流,可能损坏设备)。这是个矛盾体,只能靠实测调平衡。

2.2 时序里藏着最难懂的细节

很多人看 i2c 时序图会懵:起始条件、停止条件、ACK、NACK、重复起始,一堆概念。我试着说得通俗点。

起始条件是 SCL 高电平时,SDA 产生一个下降沿。停止条件是 SCL 高电平时,SDA 产生一个上升沿。这很好理解,两根线空闲时都是高,谁动手拉低,就意味着要开始讲话了。

每个数据位是在 SCL 高电平期间保持稳定的。也就是说,SDA 上的数据必须在 SCL 上升沿附近准备好,然后在 SCL 高电平期间不许变化,等 SCL 低电平期间才能切换下一个位。这条规则是所有同步串行协议的通用逻辑:数据在时钟边沿采样,在时钟电平变化之间切换。

ACK 机制也很有意思。主机发送完一个字节(8位)后,第9个时钟周期释放 SDA,从机如果想表示“我收到了”,就拉低 SDA。如果从机不想收(比如寄存器地址不存在),就保持高,主机收到 NACK 后就知道出问题了。

我新手期写 i2c 驱动时,总搞不明白为什么读操作要发一个“伪写”再重复起始再读。后来理解了寻址机制就通了:读操作本质是先告诉从机“我要读你的哪个寄存器”,然后才真正读。这个“先写后读”的模式非常普遍,eeprom、传感器都这样。verilog 写 i2c eeprom 控制器的时候,这个状态机是最容易出 bug 的地方,我见过很多人把 repeat start 漏掉,或者 ACK 状态跳转写错,导致读出来全是 FF。

2.3 i2c 扩展与多路复用

i2c 地址只有 7 位,扣掉保留地址,实际可用大概一百多个,但同一型号的设备往往地址固定,冲突很常见。我做过一个项目,板上要放 8 个同样的温度传感器,地址全都一样。解决方案是加 TCA9548A 这类 i2c 多路复用器。

这类芯片本质上是一个 i2c 路由器,你向它写控制字节,选择打开某一路通道,然后后续 i2c 通信只能走到这个通道上。这样每条子总线上可以挂一组相同地址的设备,互不干扰。这颗芯片本身地址也通过引脚配置,最多支持 8 个,级联之后能扩展的设备数量非常可观。

使用多路复用器有个替代方案是给每个设备加一个使能引脚,用 GPIO 控制,平时把所有设备的地址线拉高(或拉低),需要跟谁通信就把谁使能。这种方法省钱,但占用 GPIO 且切换有延迟。我实测下来,TCA9548A 的切换速度是微秒级,比 GPIO 方式快不少。

3. spi 深入拆解:高速传输的利器,也最容易翻车

3.1 四种模式与片选的学问

spi 的四种工作模式是绕不开的坑,CPOL(时钟极性)和 CPHA(时钟相位)两个参数组合出来的。

CPOL 决定 SCLK 空闲时的电平:CPOL=0 空闲低,CPOL=1 空闲高。CPHA 决定数据采样时刻:CPHA=0 时,数据在第一个时钟边沿采样(前沿),CPHA=1 时则在第二个边沿(后沿)采样。

我在 RK3588 上用 spi 接 MT6701 编码器时就被这个坑过。MT6701 数据手册要求的模式跟我想当然以为的完全不同,导致读出来的角度数据跳变、偶尔乱值。后来用逻辑分析仪对比手册时序图,才确认 CPOL=0、CPHA=1 的模式才对。所以记住:任何 spi 设备,先查手册确认模式和最高速率,再配置控制器,不要想当然。

片选信号 CS 也有讲究,分硬件片选和软件片选。硬件片选是 spi 控制器自动控制的,优点是不占用 CPU,传输开始自动拉低,结束拉高。但有些控制器的硬件片选在连续传输时会有拉高的间隔,这对于某些严格要求 CS 连续拉低的设备是致命的。

软件片选就是用普通 GPIO 控制,完全自己掌控时序。缺点是每个字节之间需要手动操作 GPIO,速度慢一些。但对 DMA 配合的批量传输来说,软件片选很灵活。我在 stm32 上做 spi 屏驱动时,发现部分屏幕 IC 在 CS 每次拉高后都需要重新初始化,用软件片选可以在所有数据发完后统一拉高,完美解决。

3.2 半双工模式怎么用

stm32 的 spi 支持半双工模式,很多人不知道这个用法。半双工模式下一根线上分时收发,引脚更省。

具体配置是 SPI_CR1 寄存器的 BIDIMODE 设为 1,然后 BIDIOE 决定当前是接收还是发送。这种模式适合那些本来就是单线协议的传感器,或者节省引脚的场合。

我做过一个案例:stm32f103 只剩一个空闲引脚,却要外挂一个 spi 接口的 ADC。我用了半双工模式,MOSI 和 MISO 短接成一个引脚(硬件上通过一个电阻隔离),通过切换 BIDIOE 位来实现方向切换。性能比全双工低一半,但对低频采样完全够用。这个改法救了我一整个项目,不然就得重新画板了。

3.3 DMA 搬运数据时才真正发挥实力

spi 和 DMA 配合是嵌入式高性能数据采集的经典组合。stm32 cubemx 里配置 spi dma 非常简单,但有几个细节必须注意。

DMA 传输分发送和接收两个通道。如果只发不收,开一个发送 DMA 就行。但如果要全双工同时收发,就得同时开两个 DMA 通道,并且保证 SPI 的 RX 和 TX 同步启动。我在调试中发现过一个坑:如果先开发送后开接收,接收端可能错过前几个字节,导致数据错位。解决办法是同时使能两个 DMA 通道,但先让 SPI 处于接收使能状态,再触发发送。

还有个关于 SPI DMA 的常见困惑:结束标志怎么判断。DMA 传输完成中断是可靠标志,不要在 SPI 的忙状态标志里等太久。实测下来,在等待 BSY 位清零时有过超时风险,尤其高速传输时 BSY 可能维持额外周期。用 DMA 完成中断更稳妥。

3.4 FPGA 侧玩 spi 的思路

FPGA 和 spi 的关系非常密切,因为 FPGA 常被用来模拟各种 spi 时序,或者作为 spi 从设备接口接收高速数据。

FPGA spi ADC 接口设计是我见过最常见的 FPGA+模拟前端组合。设计思路是:主机(通常是 MCU)通过 spi 发送配置命令给 ADC,ADC 转换完成后通过 spi 数据线返回结果。FPGA 如果是中间的桥接者,需要设计一个协议转换状态机,把 ADC 的 spi 输出整理成并行数据再给 MCU,或者反过来。

写 FPGA 的 spi 控制器,关键是把数据速率和 ADC 转换时间匹配好。很多 ADC 有转换周期要求,你发完配置命令后必须等它完成转换再读,不然读到的是旧数据。我在设计时通常会加入一个固定的延迟计数器,根据 ADC 数据手册的转换时间来设定。

另外,FPGA 做高速 spi 时,跨时钟域问题必须处理。如果 FPGA 内部系统时钟是 100MHz,而 spi 时钟是 33MHz,需要用异步 FIFO 或者双端口 RAM 做缓冲,直接打拍的方式会有数据丢失风险。

4. uart 深入拆解:最老的协议,最稳的伙伴

4.1 波特率误差才是乱码之源

uart 没有时钟线,全靠收发双方约定采样时间点。发送方按波特率一个位一个位往外吐,接收方用自己内部的时钟去采样。这就产生一个数学问题:如果双方时钟频率有偏差,经过多次采样后,累计误差会越来越大。

以 115200 波特率、8N1 格式来说,一帧共 10 位(起始1 + 数据8 + 停止1),每位约 8.68us。如果接收方时钟偏差 2%,10 位后累计偏差约 1.74us,接近半位宽。如果偏差到 3%,停止位采样就可能出错,直接导致帧错误。

我实测过用 stm32f103 内部 RC 时钟跑 115200,连续发 64 字节会出现偶发乱码,每 100 次传输约 1~2 次错误。换成外部晶振后,连续发几 MB 数据都没有问题。这个案例充分说明:不要依赖内部 RC 做高波特率通信。

但高速批量传输时,外部晶振也未必扛得住,还需要关注线路质量。我测过 FT232R 的 USB 转串口模块,在 921600 波特率下,用劣质杜邦线拉长距离,也会出错。换绞线或者短线后恢复稳定。

4.2 阻塞和非阻塞接收怎么选

uart 通信里一个永远躲不开的话题:阻塞式接收好还是非阻塞好?

阻塞式接收最简单,程序死等一个字节接收完成,然后处理。适合数据量极小、实时性要求不高的场合。坏处很明显:接收一个完整的 64 字节报文,CPU 大部分时间都在傻等。

非阻塞接收通常配合 DMA 或中断。stm32 标准库时代最经典的做法是 uart dma 空闲中断接收:启用 DMA 循环接收,当检测到总线空闲(一帧结束),触发空闲中断,然后 DMA 传输完成中断告诉你“有一包数据来了”。

我用 stm32f103 标准库做过这个方案:配置 USART1 开启 DMA 接收,通道配置为循环模式,同时在 USART_CR1 里打开 IDLE 中断。每次接收到一包数据后,在中断里计算 DMA 当前计数,减去上次处理的计数,就知道这包数据有多长。然后从缓冲区里提取数据,重新调整 DMA 指针继续接收。这套代码跑了两年没出过问题。

不过注意,这个方案里 DMA 缓冲区的长度必须大于最大报文长度。如果报文长度超过缓冲区,DMA 会自动覆盖前面数据,导致数据丢包。我的做法是缓冲区设成最大报文的 4 倍,并且在处理时尽快把数据拷贝到应用层。

4.3 流控:全双工不是免费的

uart 的全双工只是物理线上双向同时传输,但如果对端处理不过来,数据还是会丢。真正解决背压问题的是流控机制。

硬件流控(RTS/CTS)是一种可靠的解耦方式:接收方拉低 RTS 表示“忙,先别发”,拉高表示“可以接收”。双方通过额外两根线沟通,效率高。

有些人图省事直接关掉硬件流控,但工程上不建议。尤其是在一个繁忙系统里,主控可能同时响应十几个模块的消息,如果串口数据涌进来速度过快,软流控(XON/XOFF)在传输二进制数据时容易出问题,因为 XON/XOFF 字符可能混在数据包里。

我处理过一个 uart 转 GPIB 的适配器固件:PC 用 uart 发指令给适配器,适配器转成 GPIB 协议控制仪器。PC 端用 Python 一次丢过来几百条命令,如果适配器没做好流控,缓冲区一满就开始丢命令,仪器根本反应不过来。最后打开 RTS/CTS 硬件流控,问题彻底解决。这算是 uart “看似简单实则要细想”的一个典型场景。

5. i2s 深入拆解:音频波形背后的时钟秘密

5.1 为什么 i2s 需要那么多时钟

i2s 的作用很简单:把音频数据(PCM 采样值)从 ADC 搬到主控,或者从主控搬到 DAC。但音频数据有特殊的时序约束——左声道和右声道交替传输、采样率固定、位深固定。

这里的核心是 WS(声道选择时钟,也叫 LRCK)。WS 的频率等于音频采样率,比如 44.1kHz。WS 高电平代表左声道数据在线上传输,低电平代表右声道。每个声道的数据位由 SCK(位时钟)控制,比如 44.1kHz 采样率、16 位位深、双声道情况下,SCK 频率等于 44.1k × 16 × 2 = 1.4112MHz。

i2s 还有个精心设计的细节:数据位比 SCK 延迟一个时钟,这样接收方可以在 SCK 上升沿稳定采样。换句话说,数据在 MSB 之前先跳一位“哑巴位”。很多新手用逻辑分析仪抓 i2s 波形,看到第一位数据总是在 SCK 边沿后才出现,不理解,其实就是这个延迟设计。

5.2 i2s 与 spi 的“亲戚关系”及调试要点

如果只考虑硬件时序,i2s 就是 spi 的一种特殊用法:SCK 对应 SCLK,WS 相当于一个低频的片选信号,SD 是 MOSI/MISO 的角色。但控制方式差得远。

调试 i2s 时最常遇到的问题之一:WS 极性选择和 SCK 空闲极性没配对。不同音频芯片厂家的定义略有差异,有些 IC 用 WS 高表示左声道,有的相反。必须在初始化时检查芯片手册的时序图,配上对应的 i2s 模式。

我调试 ES8388 音频编解码器时,就是因为默认初始化代码里的 i2s 模式跟硬件连接不符,导致播放声音全是“哒哒哒”的杂音。后来用逻辑分析仪抓 WS 和 SD 的对应关系,才发现主控发出的数据出现在 WS 高电平期间,而 ES8388 却把这个高电平当作右声道,左右声道正好反了。修改 i2s 模式配置后声音才正常。

另一个常见坑是 MCLK。很多中高端音频编解码器要求外部提供 MCLK(主时钟),通常是采样率的 256 倍或 512 倍。比如 48kHz 采样率需要 12.288MHz 的 MCLK。如果主控没有多余的时钟输出引脚,有些芯片可以从 SCK 上倍频,但倍频电路实现相对复杂。我建议做音频项目时,优先确认主控时钟树里能不能生成特定频率的 MCLK。

5.3 左右声道的硬件细节与特殊模式

标准 i2s 用两根数据线分时传左右声道,但有些简化方案只用一根 SD 线,半双工轮流传左右声道。这种模式下时钟频率要求不变,但主控端数据处理方式要跟着改。

还有个概念叫 DSP 模式(也叫 PCM 模式),它跟标准 i2s 的区别是:WS 信号变成了帧同步信号,不再分左右声道。这种模式常用于语音处理芯片,比标准 i2s 更高效。如果你用的是 DSP 模式,初始化配置千万不能照搬 i2s 那套参数。

回到逻辑分析仪的实操。抓 i2s 波形时我习惯同时抓 SCK、WS、SD 三根线,然后根据 WS 频率除以 SCK 频率,能算出声道位宽。举个例子,如果 SCK 1.4112MHz、WS 44.1kHz,相除得到 32,说明每个声道占据 32 个位时钟,但实际有效数据可能是 16 位或 24 位,靠波形里数据位后的填充位判断。

6. 高速对比与选型决策:到底该用哪个

6.1 一张表看穿选型逻辑

我个人做了个决策流程,每次新项目选通信接口都走一遍:

  1. 数据传输方向:如果是一对一全双工,优先 uart 或 spi;如果多设备共享总线,优先 i2c。
  2. 速率要求:只传状态、配置信息,i2c 完全够;传波形、图片、大块数据,spi 或高速 uart 更合适。
  3. 引脚资源:紧张优先 i2c(2根线可带多个设备),其次 uart,spi 线数较多。
  4. 实时性要求:spi 带 DMA 通常延迟最低,i2c 有 ACK 机制但速度受限。
  5. 硬件复杂度:uart 最简单,但长距离传输需要电平转换;i2c 开漏结构需要注意上拉;spi 需要关注信号完整性。

在实际项目里最常用的组合是:传感器数据走 i2c,存储走 spi Flash,调试输出走 uart,音频走 i2s。这个搭配在现代嵌入式系统里几乎是标配。RK3588 这类应用处理器的开发板,底板上往往同时集成这几个接口,驱动代码写好就能用。

6.2 从总线视角理解层级关系

如果把 CPU 内部的高速总线比作高速公路,i2c、spi、uart 这些就是连接各个小区与主路之间的城市道路。它们各管一段,互不替代。

i2c 优势是总线拓扑,适合接大量低速外设,比如温湿度传感器、光照传感器、多个 EEPROM。这类数据传输量小,但设备数量多,用 i2c 可以省 GPIO。

spi 优势是吞吐,适合传输大块连续数据,比如 W25Q128 Flash、SD 卡、TFT 屏。它是点对点(通常一个片选对应一个设备)的,总线利用率很高。

uart 是异步串口的代表,适合机器与机器之间长距离或简单连接。工业控制柜里设备之间的连接很多就是 uart/RS232/RS485 这类异步串口,虽然速率不高但极度成熟可靠。

i2s 完全面向音频流,它的时钟设计让 DAC/ADC 可以直接硬件同步,不用在软件里做复杂的采样率转换。

6.3 接口转换也很常见

实际项目中接口转换无处不在,比如 USB 转 uart(FT232R、FT231X 这类芯片)就非常常用。这类芯片把 USB 串口虚拟成一个 COM 口,操作系统自动枚举出来,应用层直接读写即可。

驱动方面,FT231X 的驱动在 Windows、Linux、macOS 上都有官方支持,一般插上就能用。但我遇到过一次“i2c hid该设备找不到足够资源可以使用 (代码 12)”的问题。这个报错通常出现在 Windows 上,与特定 USB 设备资源分配异常有关,跟某个 i2c 或 HID 设备冲突。重启电脑或更换 USB 口通常能解决,但如果反复出现,就要查设备管理器里是否有资源抢占。

另一个方向是 uart 转 GPIB,老式仪器测试系统里经常用它。GPIB 是并行总线,uart 是串行,中间需要一个协议转换设备。我最近写过一个 python 调用 usb 模拟 spi 接口的脚本,就是通过 uart 转发 spi 命令给远方设备,相当于做了一个透明的协议桥。用 Python 的好处是快速验证上层逻辑,底层转换交给硬件完成。

7. 实操技巧与踩坑实录:这是我用代码换来的

7.1 片选抖动的真相

做过屏幕、Flash、音频编解码的朋友,一定遇到过片选引脚抖动的问题。现象是通信偶尔失败,用示波器看 CS,发现它在传输开始或结束时有多余的脉冲。

这种问题多数来自 GPIO 复用配置没做好,或者控制器的片选逻辑在连续传输中产生了 CS 拉高再拉低的间隙。比如 stm32 的 SPI 在 NSS 硬件模式下,如果传输的字节之间不连续,会自动把 CS 拉高。有些芯片要求 CS 在整个操作序列中保持低电平,此时就会出错。解决方案是切换到软件片选,用 GPIO 手动控制 CS,保证整个序列中 CS 一直拉低。

我处理 stm32 做 SPI Flash 编程器的时候遇到过:at25sf128 的编程命令要求 CS 在整个读 状态寄存器循环中保持低,结果硬件片选每读一字节就拉高一次,导致状态寄存器永远读不对。换软件片选后,一次性发送完整的命令序列,问题消失。

7.2 逻辑分析仪抓时序的三板斧

调试这四个协议,逻辑分析仪是必备工具,我几乎每次调试都会用到。分享三个实用操作:

第一,采样率至少设为协议时钟的 10 倍。比如 i2c 400k 时采样率至少 4M,建议 10M。spi 10MHz 时就得上 100M 采样率的分析仪,普通 24M 8通道的就不太够用。我平时调高速 spi 会直接用 400M 采样率的专业设备。

第二,抓信号时要加合适的触发。抓 i2c 起始条件,用 SDA 下降沿触发;抓 spi,用 CS 下降沿触发;抓 uart,用 RX 下降沿(空闲是高的)触发。这样可以稳定抓到通信起始的一帧数据。

第三,解码设置必须对应协议参数。i2c 要设 7/10 位地址模式;spi 要选对的 CPOL/CPHA;uart 要设波特率和数据格式;i2s 要设置 WS 极性、位宽。这些搞错了,波形对但解出来全是乱的,排查半天发现是设置问题。

7.3 时钟拉伸:i2c 从机的自救

i2c 里有个“时钟拉伸”机制,很多新手不知道,但它能救命。某些从机在内部处理数据时,会主动把 SCL 拉低,让主机等一等。主机检测到 SCL 被拉低,就会暂停发送,直到从机释放时钟。这个机制可以让慢速从机与高速主机共存。

我在调一个低速传感器时就遇到过:如果主机按 400k 速率一直发,传感器来不及处理数据,返回的数据就是错误的。后来查手册发现这个传感器支持时钟拉伸,但需要主机在硬件/软件层面支持。stm32 的 I2C 硬件模块在时钟拉伸时能自动等待,但有些 GPIO 模拟 i2c 的代码不会处理这种场景,会一直卡死在等待状态。所以用 GPIO 模拟 i2c 的代码,务必加超时机制,否则一旦遇到支持时钟拉伸的从机,程序就会死等。

7.4 PMBus 与 i2c 的关系

很多人问 PMBus 和 i2c 到底啥关系。简单说,PMBus 是在 i2c 物理层之上定义的一套电源管理命令协议。它复用 i2c 的物理传输机制,但定义了标准化的命令字、格式和语义,让不同的电源芯片可以用同一套代码去配置和监视。

我曾经在一个服务器电源板上调试 PMBus 芯片,使用 i2c 总线访问,但所有寄存器地址、命令格式跟普通 i2c 设备的接口完全不同。后来拿到 PMBus 协议手册按规范编写驱动,才顺利读取电压、电流和温度寄存器。这个经验说明,物理层一样不意味着应用层一样,拿到设备时先确认它的协议版本。

8. 串口调试的补遗:从驱动到波形再到工具链

8.1 USB 转串口驱动那些事

FT232R、FT231X 这类芯片的驱动安装,看起来简单,但 Windows 下偶尔会翻车。

常见问题是设备管理器里显示感叹号,或者能识别但无法打开 COM 口。我的建议:去 FTDI 官网下载最新 WHQL 驱动,不要用 Windows 自带的旧版。如果装完还不行,试试“更新驱动程序” → “浏览我的电脑查找驱动” → “从列表中选取”,手动选择 FTDI 设备。

Linux 下一般不用装驱动,内核自带 ftdi_sio 模块,插入就能看到 /dev/ttyUSB0。要注意的是权限问题,需要把用户加入 dialout 组,否则打不开设备。

我还有一个习惯:接到 USB 转串口模块后,先用示波器或逻辑分析仪验证一下输出引脚的电平和数据波形。有些模块标注的 TX/RX 在硬件上是反的(TTL 电平版本会反),接反了通信必失败。批量生产时这问题非常普遍,做个上电自检固件,在调试串口上循环发一个测试字符串,能快速排查。

8.2 从波形看懂 uart 帧结构

用逻辑分析仪抓 uart 波形,空闲状态是高电平,起始位是突然拉低的一个低电平,然后 8 个数据位、1 个校验位(可选)、最后停止位是高电平。这个帧结构非常直观,新手看一次波形就记住了。

我建议调试串口问题时先看波形,再看数据内容。如果波形正常但数据乱码,大概率是波特率或帧格式配错。如果波形都异常,那就是电气层面或驱动没配置好的问题。

8.3 工具链推荐与自检清单

调试这几类总线时我常备这些工具:

  • 逻辑分析仪:推荐支持解码的型号,抓波形直接出解码结果。性能够用的就行,贵的不一定更适合你。
  • 万用表:测电平、电阻、上拉电压。
  • 示波器:高速 spi 和 i2s 需要它看边沿、过冲、振铃。
  • 串口助手:调试 uart 时快速收发验证。
  • i2c 总线检测器:扫描总线地址、检测设备是否在线。

给新手的自检清单:

  • 引脚接的是不是对的?TX 对 RX 还是 TX 对 TX?
  • 共地了吗?这是最常被忽略的坑。
  • 波特率/时钟极性/相位设置对不对?
  • 上拉电阻阻值是否合适?设备和总线电容是否过大?
  • 逻辑分析仪采样率够不够?
  • DMA 有没有正确配置中断和缓冲区?

结束语:我的四点心得

写到这,最后分享一点我自己的体会。第一,这四个协议没有绝对的优劣,只有匹配场景与否。第二,纸上谈兵永远不如逻辑分析仪实测,很多故障看波形一眼就明白,凭空猜能猜一天。第三,选接口时多考虑后续扩展,宁可多留一路备用接口,也别等加功能时发现引脚全用完了。第四,调试总线信号时保持耐心,每次解一个疑难 Bug 后,对协议的理解都会上一个台阶——这些积累,比背多少手册都管用。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询