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的超时机制让主设备能检测到异常并复位总线。
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延时电路,确保上电稳定后再释放复位。
3.6 实战建议
最后,给你几个实战建议:
- 先看波形,再读代码。 示波器是调试I2C最好的工具。先看起始条件、停止条件、地址、数据、应答,哪个环节出问题一目了然。
- 用逻辑分析仪抓长序列。 示波器只能看几个字节,逻辑分析仪可以抓几百个字节,适合分析复杂的读写序列。
- 写一个I2C扫描程序。 上电后先扫描总线上有哪些设备,确认地址对不对。我每次调试新板子,第一件事就是跑I2C扫描。
- 注意上拉电阻的供电电压。 I2C总线的上拉电阻要接到正确的电源上。如果MCU是3.3V,PMIC是1.8V,上拉电阻接3.3V的话,PMIC的引脚可能会过压。
嗯,这一章的内容就到这里。I2C/SMBus看似简单,但实际调试中坑不少。记住一句话:时序对了,一切都对了。 下一章咱们聊聊PMIC的上电时序配置,那才是真正考验系统级调试能力的地方。