第三章 I2C/SPI总线实战:时序分析、多设备挂载、速率优化、常见故障排查

各位同学,今天我们来聊聊嵌入式开发中最常用的两种总线——I2C和SPI。说实话,我见过太多工程师在这上面栽跟头了。我自己刚入行那会儿,也曾经被一个I2C的ACK信号折磨了整整三天。嗯,今天就把这些经验一次性讲透。

3.1 时序分析:别被示波器骗了

先说说I2C的时序。I2C总线就两根线:SCL(时钟)和SDA(数据)。但别小看这两根线,里面的门道可不少。

I2C的起始条件和停止条件:

  • 起始条件:SCL高电平时,SDA从高变低
  • 停止条件:SCL高电平时,SDA从低变高
  • 数据采样:SCL低电平时改变SDA,高电平时采样

关键点:我见过有人把起始和停止条件搞反了,结果总线一直卡在某个状态。记住一个口诀——「高时变,低时稳」。

SPI的时序相对简单一些,但要注意四种模式(CPOL和CPHA的组合)。我个人习惯用模式0(CPOL=0, CPHA=0),因为大多数传感器都支持这个模式。

// SPI模式0的典型配置
spi_config_t config = {
    .mode = SPI_MODE_0,      // CPOL=0, CPHA=0
    .freq = 1000000,        // 1MHz
    .bit_order = MSB_FIRST,
    .cs_polarity = LOW_ACTIVE
};

小技巧:调试时序时,别光看示波器上的波形。我建议先用逻辑分析仪抓一下,它能帮你看到完整的协议帧,包括地址、数据、ACK/NACK。

3.2 多设备挂载:地址冲突和片选管理

I2C和SPI都能挂多个设备,但方式完全不同。

I2C多设备:每个设备有唯一的7位或10位地址。我遇到过最坑的事——两个传感器用了同一个地址,结果总线上一片混乱。解决办法是检查数据手册,看有没有地址引脚可以调整。

地址位 典型值 说明
7位地址 0x68 MPU6050默认地址
AD0=0 0x68 地址引脚接地
AD0=1 0x69 地址引脚接VCC

SPI多设备:每个设备有独立的片选(CS)引脚。这里有个坑——片选信号的电平极性。有些设备是高电平有效,有些是低电平有效。我曾经因为搞反了极性,导致两个设备同时响应,数据全乱了。

警告:SPI总线上挂多个设备时,一定要确保未选中的设备处于高阻态。否则它们会干扰总线,造成数据错误。

3.3 速率优化:别一味追求高频

很多人觉得总线速率越高越好,其实不然。我做过一个项目,把SPI频率从1MHz提到10MHz,结果数据反而出错了。为什么?因为信号完整性出了问题。

I2C速率优化:

  • 标准模式:100kHz
  • 快速模式:400kHz
  • 高速模式:3.4MHz

我个人建议,除非你的走线很短(<10cm),否则别用高速模式。上拉电阻也很关键——速率越高,上拉电阻要越小。一般100kHz用4.7kΩ,400kHz用2.2kΩ。

SPI速率优化:

  • 检查从设备的最大支持频率
  • 考虑PCB走线的寄生电容
  • 适当增加驱动能力
// 动态调整SPI速率
void spi_set_freq(uint32_t freq_hz) {
    uint32_t prescaler = get_prescaler(freq_hz);
    SPI1->CR1 = (SPI1->CR1 & ~SPI_CR1_BR_MASK) | prescaler;
}

经验之谈:调试速率时,先用低速跑通功能,再逐步提高频率。每次提高后,连续读100次数据,看看有没有错误。这样能快速定位速率瓶颈。

3.4 常见故障排查:从波形到代码

这部分是我最想分享的。这些年我排查过的总线故障,少说也有上百个了。总结下来,无非就这几类:

故障1:总线卡死(I2C常见)

现象:SCL一直为低,SDA也拉不上去。

原因:从设备没有释放总线,或者主设备产生了错误的停止条件。

解决办法:

  1. 检查从设备是否正常工作(供电、复位)
  2. 手动发送9个时钟脉冲,让从设备释放总线
  3. 重新初始化I2C外设

故障2:数据错位(SPI常见)

现象:读回来的数据总是少一位或多一位。

原因:时钟极性和相位不匹配,或者片选信号时序不对。

解决办法:

  1. 用逻辑分析仪抓取波形,对比数据手册
  2. 检查CPOL和CPHA的配置
  3. 确认片选信号在数据传输前后有足够的建立时间

我曾经...遇到过一个问题:SPI读温度传感器,数据总是跳变。查了两天,最后发现是片选信号毛刺导致的。加了一个10nF的电容就解决了。嗯,有时候硬件问题比软件更隐蔽。

故障3:地址无响应(I2C常见)

现象:发送从设备地址后,没有收到ACK。

原因:地址错误、设备未上电、总线被拉死。

排查步骤:

  1. 用万用表测量SDA和SCL的电压
  2. 确认上拉电阻是否正常
  3. 用示波器看起始条件是否完整
  4. 换个地址再试(有些设备支持多个地址)

3.5 实战技巧:我的调试工具箱

最后分享一些我常用的调试方法:

  • 逻辑分析仪:必备工具,建议买24MHz采样率以上的
  • 示波器:看信号质量,特别是上升沿和下降沿
  • 串口打印:在关键节点打印调试信息,比看波形快
  • 软件模拟:用GPIO模拟I2C/SPI,可以精确控制每个时序
// 软件模拟I2C的起始条件
void i2c_start(void) {
    SDA_HIGH();
    delay_us(5);
    SCL_HIGH();
    delay_us(5);
    SDA_LOW();      // SCL高时,SDA从高变低
    delay_us(5);
    SCL_LOW();      // 准备发送数据
}

个人建议:刚开始学的时候,先用软件模拟的方式跑通一个传感器。虽然慢,但你能清楚地看到每个时序细节。等理解了原理,再用硬件外设提高效率。

好了,关于I2C和SPI总线的实战内容就讲到这里。记住,调试总线问题,耐心比技术更重要。很多时候,问题就出在一个不起眼的细节上——比如少了一个上拉电阻,或者多了一段长走线。

I2C/SPI总线实战知识体系 I2C总线 • 两根线:SCL + SDA • 起始/停止条件 • 7位/10位地址 • 速率:100kHz ~ 3.4MHz • 上拉电阻:4.7kΩ ~ 2.2kΩ SPI总线 • 四根线:MOSI/MISO/SCLK/CS • 四种模式:CPOL + CPHA • 独立片选(CS) • 速率:1MHz ~ 50MHz+ • 全双工通信 常见故障排查 🔴 总线卡死 → 检查从设备状态,发送9个时钟脉冲释放总线 🟡 数据错位 → 检查CPOL/CPHA配置,确认片选时序 🟢 地址无响应 → 测量电压,检查上拉电阻,确认起始条件 🔵 速率问题 → 从低速开始,逐步提高,连续验证数据正确性

可可精品资料库