3. I2C时序捕获实战:用逻辑分析仪抓总线波形
好,到了真正动手的环节了。前面两章我们把I2C协议的原理和逻辑分析仪的基本操作捋了一遍,这一章咱们就直接上硬菜——用逻辑分析仪去抓I2C总线上的读写波形。
说实话,很多工程师学了协议之后,一到实际抓波形就懵。为什么?因为示波器上看到的是一堆跳变的电平,你得把它翻译成Start、Address、ACK、Data、Stop这些抽象概念。我刚开始做嵌入式那会儿,对着波形数了半天,愣是没分清哪个是ACK哪个是NACK。后来被老工程师点了一句,才恍然大悟。
这一章,我就带你一步步把波形“翻译”出来。
3.1 硬件连接:别小看这一步
抓波形之前,先把物理连接搞定。逻辑分析仪的探头夹到I2C总线上,就这么简单?嗯,但有几个坑你得注意。
具体接线步骤:
- 找对通道:逻辑分析仪的通道0接SCL,通道1接SDA。我个人习惯固定这个顺序,这样波形出来一眼就能认出哪根是时钟哪根是数据。
- 共地:GND夹子夹到板子的GND测试点或螺丝孔上。
- 上拉电阻确认:I2C总线需要上拉电阻(通常4.7kΩ)。如果板子上没有,你得外接。我遇到过一块开发板,上拉电阻焊错了阻值,导致SCL上升沿特别缓,逻辑分析仪误判了好多位。
- 采样率设置:标准模式100kHz,采样率设1MHz以上就够了。快速模式400kHz,建议用4MHz以上。别设太低,否则边沿抓不准。
3.2 触发设置:让分析仪在正确的时间开始抓
逻辑分析仪不像示波器那样一直刷新,它是先等触发条件满足,然后才开始记录。所以触发设置是关键。
抓I2C波形,最常用的触发方式是Start条件触发。也就是SCL为高时,SDA从高跳变到低。这个边沿就是一次I2C传输的开始。
具体设置步骤(以Saleae Logic为例):
- 添加两个数字通道,分别命名为SCL和SDA。
- 在触发设置里,选择“I2C”协议解析器,然后选“Start”作为触发条件。
- 如果没有协议触发选项,就手动设:SDA通道触发条件为“下降沿”,SCL通道设为“高电平”。
- 设置触发位置为“中间”,这样能抓到触发点前后的波形,方便看上下文。
3.3 抓一次完整的写操作
好,连接好了,触发也设好了。现在让主设备(比如单片机)执行一次I2C写操作,往从设备(比如一个温度传感器)的某个寄存器写一个字节数据。
抓到的波形长什么样?我画个图给你看。
看到没?一次完整的写操作包含:
- Start条件:SDA在SCL高电平时下降。
- 从机地址:7位地址 + 1位读写位(写操作时R/W=0)。
- ACK:第9个SCL周期,从机将SDA拉低表示应答。
- 寄存器地址:你要写入的寄存器编号。
- ACK:从机再次应答。
- 数据字节:你要写入的数据。
- ACK:从机应答。
- Stop条件:SDA在SCL高电平时上升。
3.4 抓一次完整的读操作
读操作比写操作稍微复杂一点,因为它需要两次传输:先写寄存器地址,再读数据。中间有一个Repeated Start条件。
流程是这样的:
- Start条件
- 发送从机地址 + 写位(R/W=0)
- 等待ACK
- 发送寄存器地址
- 等待ACK
- Repeated Start条件(不是Stop,而是再发一次Start)
- 发送从机地址 + 读位(R/W=1)
- 等待ACK
- 读取数据字节(主设备接收,从机发送)
- NACK(主设备不发ACK,表示读完了)
- Stop条件
我画个读操作的波形图:
3.5 用逻辑分析仪的协议解析器自动解码
手动数波形太累了,而且容易出错。现代逻辑分析仪都带协议解析器,能自动把波形翻译成可读的协议数据。
以Saleae为例,添加I2C解析器的步骤:
- 抓完波形后,点击右侧的“+”号,选择“I2C”。
- 设置SCL和SDA对应的通道。
- 设置I2C速率(标准/快速/高速)。这个影响不大,解析器会自动适配。
- 点击“保存”。
解析器会生成一个协议列表,每一行显示一次传输的内容:
| 时间戳 | 类型 | 地址 | 方向 | 数据 | ACK |
|---|---|---|---|---|---|
| 0.000s | Start | 0x48 | 写 | - | - |
| 0.000s | 地址 | 0x48 | 写 | - | ACK |
| 0.001s | 数据 | - | 写 | 0x01 | ACK |
| 0.002s | 数据 | - | 写 | 0xA5 | ACK |
| 0.003s | Stop | - | - | - | - |
看到没?解析器直接告诉你:地址0x48,写操作,写了两个字节0x01和0xA5,都得到了ACK。一目了然。
3.6 常见波形故障定位
抓波形不是为了好看,是为了找问题。下面是我在实际项目中遇到最多的几种故障波形:
3.6.1 无ACK响应
波形上,从机地址发完后,第9个SCL周期SDA没有被拉低,而是保持高电平。原因:
- 从机地址写错了(比如7位地址左移了一位没处理)。
- 从机没上电或没初始化。
- 总线上的上拉电阻失效,SDA被拉不到高电平。
3.6.2 时钟拉伸(Clock Stretching)
有些从机(比如某些EEPROM)在收到数据后,需要时间处理,会把SCL拉低不放。主设备检测到SCL被拉低,就会等待,直到从机释放SCL。如果你看到SCL长时间为低,别慌,这可能是正常的时钟拉伸。
3.6.3 总线冲突
两个主设备同时尝试控制总线,导致SDA出现不确定的电平。波形上表现为SDA在SCL的某个边沿处出现毛刺或竞争。解决办法:确保总线上只有一个主设备,或者实现总线仲裁。
3.6.4 数据位错位
有时候波形看起来是对的,但解析出来的数据不对。比如你写0xA5,但解析器显示0x4A。这通常是SCL和SDA的通道接反了,或者采样率不够导致边沿采样点偏移。
- 看Start和Stop:确认传输的开始和结束是否正确。
- 看ACK/NACK:每个字节后有没有ACK?没有的话,问题出在从机侧。
- 看数据内容:用解析器对比你期望的数据和实际抓到的数据。
3.7 实战练习:抓一个温度传感器的寄存器读写
最后,我给你留一个完整的实战流程。假设你有一个I2C温度传感器(比如LM75),地址0x48,寄存器0x00是温度值寄存器。
写操作:配置传感器的工作模式,往寄存器0x01写入0x00。
读操作:读取温度值,从寄存器0x00读2个字节。
步骤:
- 连接逻辑分析仪到SCL和SDA。
- 设置触发为Start条件。
- 让单片机执行写操作(配置寄存器)。
- 抓波形,用解析器查看是否写成功。
- 让单片机执行读操作(读温度值)。
- 抓波形,确认读到的数据是否正确。
如果你抓到的波形和上面我画的图一致,那恭喜你,你已经掌握了I2C时序捕获的基本功。如果不对,别急,对照故障定位三步法,一步步排查。