3、I2C/SMBus通信协议详解

各位同学,咱们今天聊聊I2C和SMBus。说实话,这俩协议在电源管理芯片调试中,几乎是绕不开的坎。我见过不少工程师,芯片上电后读不到寄存器,第一反应就是“芯片坏了”。其实呢,十有八九是I2C通信出了问题。

这一章,我会把I2C物理层、协议层、SMBus的区别,以及NXP PMIC的寄存器读写操作,还有那些让人头疼的总线调试问题,一次性讲清楚。嗯,咱们开始吧。

3.1 I2C物理层:两根线的事儿

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

开漏输出与上拉电阻

I2C的引脚都是开漏结构。什么意思?就是引脚只能拉低,不能主动拉高。高电平全靠外部上拉电阻。我刚开始做项目时,有次板子调不通,量SDA波形全是乱的。后来发现,上拉电阻焊错了,用了10kΩ,总线电容又大,上升沿慢得像蜗牛爬。

上拉电阻怎么选?给你个经验值:

  • 标准模式(100kHz):4.7kΩ ~ 10kΩ
  • 快速模式(400kHz):2.2kΩ ~ 4.7kΩ
  • 高速模式(3.4MHz):1kΩ ~ 2.2kΩ

总线电容限制

I2C规范要求总线电容不超过400pF。你想想看,一条线上挂十几个设备,每根走线再长一点,电容很容易超标。我遇到过最夸张的一次,客户把PMIC放在板子一头,MCU放在另一头,中间隔了30cm的排线。结果400kHz死活跑不起来,降到100kHz才勉强能用。

注意:调试时如果发现波形上升沿太缓,先检查上拉电阻,再检查总线电容。别一上来就怀疑芯片。

3.2 I2C协议层:起始、停止、数据、应答

I2C的协议其实挺简单的。四个基本动作:起始条件、停止条件、数据传输、应答信号。

起始条件(START):SCL高电平时,SDA从高变低。

停止条件(STOP):SCL高电平时,SDA从低变高。

数据传输:每个时钟周期传输1位数据,高位在前。SCL低电平时SDA可以变化,SCL高电平时SDA必须稳定。

应答信号(ACK/NACK):每传输完一个字节,接收方在第9个时钟周期拉低SDA表示应答。

这里有个坑,我栽过。有些PMIC在写寄存器时,如果写入的值超出范围,它会回复NACK。但很多工程师以为NACK就是通信失败,其实不是。NACK是设备在说:“你给我的数据我不接受。” 这时候应该检查寄存器地址或数据值对不对。

3.3 SMBus与I2C的区别

SMBus(系统管理总线)是基于I2C发展来的,但两者不完全兼容。NXP的PMIC很多都支持SMBus,所以你得搞清楚区别。

特性 I2C SMBus
时钟频率 100kHz / 400kHz / 3.4MHz 10kHz ~ 100kHz
最小时钟低电平时间 无严格限制 4.7μs(SMBus 1.0)
超时机制 35ms超时(时钟低电平超过35ms视为超时)
地址格式 7位或10位 7位
应答策略 每个字节后应答 每个字节后应答,支持NACK终止
分组协议 无标准分组 SMBus读写、块读写等标准分组

我个人习惯,调试PMIC时优先用SMBus模式。为什么?因为有超时机制。如果从设备死机了,总线不会一直被拉死。我曾经遇到过PMIC进入异常状态,SCL被拉低超过100ms,如果是纯I2C,整个总线就瘫痪了。但SMBus的超时机制让主设备能检测到异常并复位总线。

小技巧:如果你的MCU只支持I2C,但PMIC是SMBus设备,通常也能通信。只要注意时钟频率别超过100kHz,并且主设备能处理超时情况就行。

3.4 NXP PMIC常用寄存器读写操作

NXP的PMIC,比如PF系列、PCA系列,寄存器读写格式基本一致。我以PF8100为例,给你演示一下。

写寄存器操作

// 伪代码示例:写寄存器0x12,值为0xAB
START
发送设备地址(0x08 << 1 | 0)  // 写地址
等待ACK
发送寄存器地址(0x12)
等待ACK
发送数据(0xAB)
等待ACK
STOP

读寄存器操作

// 伪代码示例:读寄存器0x12
START
发送设备地址(0x08 << 1 | 0)  // 写地址
等待ACK
发送寄存器地址(0x12)
等待ACK
REPEATED START
发送设备地址(0x08 << 1 | 1)  // 读地址
等待ACK
读取数据(0xAB)
发送NACK  // 告诉设备我读完了
STOP

这里要注意,读操作时,写完寄存器地址后要发一个重复起始条件(Repeated START),不能直接发停止再发起始。为什么?因为如果发了停止,总线就释放了,其他主设备可能抢占总线,导致读操作被中断。

NXP PMIC寄存器特点

  • 大部分寄存器是8位宽
  • 有些寄存器是只读的(比如状态寄存器)
  • 有些寄存器需要解锁才能写(比如关键配置寄存器)
  • 写操作通常需要先读-改-写,避免影响其他位

避坑指南:我曾经调试PF3000时,发现写寄存器0x1F总是失败。查了半天手册,才发现这个寄存器需要先写0x5A到解锁寄存器0x00,才能修改。很多NXP PMIC都有这种保护机制,千万别硬写。

3.5 总线调试常见问题

调试I2C/SMBus总线,说白了就是看波形、抓时序。我总结了三个最常见的问题。

3.5.1 时钟拉伸(Clock Stretching)

时钟拉伸是从设备的一种“慢速请求”。当从设备还没准备好处理下一个数据时,它会拉低SCL,告诉主设备:“你等等,我还没忙完。”

很多MCU的I2C外设不支持时钟拉伸。我遇到过用某款国产MCU驱动PMIC,读寄存器时偶尔会卡死。用示波器一看,PMIC在发完数据后拉低了SCL,但MCU没检测到,还在等SCL变高,结果死锁了。

解决办法:

  • 用支持时钟拉伸的MCU(比如STM32的I2C外设支持)
  • 或者用GPIO模拟I2C,自己控制时序
  • 降低时钟频率,给从设备更多时间

3.5.2 仲裁丢失(Arbitration Loss)

多主设备共享总线时,如果两个主设备同时发起通信,就会发生仲裁。仲裁的规则是:谁先拉低SDA,谁就赢得总线。

仲裁丢失不是错误,是正常的总线管理机制。但如果你只有一个主设备,却频繁出现仲裁丢失,那就要查查了。

我遇到过的情况:板子上有两个I2C主设备,一个MCU,一个PMBus控制器。两个设备都试图访问同一个PMIC,结果总线乱成一锅粥。后来加了总线缓冲器(如PCA9517),才解决问题。

3.5.3 总线挂死(Bus Lock)

总线挂死是最让人头疼的问题。SDA被拉低,SCL也拉低,整个总线一动不动。

常见原因:

  • 从设备死机,一直拉着SDA不放
  • 主设备发送了不完整的字节,从设备在等第9个时钟
  • 噪声干扰导致状态机错乱

我曾经在产线上遇到过一批板子,上电后I2C总线就挂死。查了三天,发现是PMIC的复位引脚没接好,上电时序不对,PMIC进入了一种“半死不活”的状态。解决办法很简单:给PMIC的复位引脚加一个RC延时电路,确保上电稳定后再释放复位。

调试技巧:如果总线挂死了,可以尝试发送9个时钟脉冲(SCL上产生9个上升沿),让从设备退出异常状态。很多PMIC支持这种“总线恢复”操作。

3.6 实战建议

最后,给你几个实战建议:

  1. 先看波形,再读代码。 示波器是调试I2C最好的工具。先看起始条件、停止条件、地址、数据、应答,哪个环节出问题一目了然。
  2. 用逻辑分析仪抓长序列。 示波器只能看几个字节,逻辑分析仪可以抓几百个字节,适合分析复杂的读写序列。
  3. 写一个I2C扫描程序。 上电后先扫描总线上有哪些设备,确认地址对不对。我每次调试新板子,第一件事就是跑I2C扫描。
  4. 注意上拉电阻的供电电压。 I2C总线的上拉电阻要接到正确的电源上。如果MCU是3.3V,PMIC是1.8V,上拉电阻接3.3V的话,PMIC的引脚可能会过压。

嗯,这一章的内容就到这里。I2C/SMBus看似简单,但实际调试中坑不少。记住一句话:时序对了,一切都对了。 下一章咱们聊聊PMIC的上电时序配置,那才是真正考验系统级调试能力的地方。