4. 利用逻辑分析仪分析时序问题
任务目标
使用逻辑分析仪捕获I²C总线上的SCL与SDA信号,定位因时钟延展(Clock Stretching)不足或应答时序违规导致的通信不稳定问题,并通过调整主机时序参数解决。
必要环境与依赖
- 逻辑分析仪:支持≥20MHz采样率,至少2通道(推荐Saleae Logic 8或兼容设备)
- 待测目标:I²C从设备(如传感器或EEPROM),主机为MCU(本例以STM32 HAL库为例)
- 软件:逻辑分析仪配套软件(Saleae Logic 2 / PulseView),支持I²C协议解码器
- 连接:逻辑分析仪CH0→SCL,CH1→SDA,共地
问题场景与定位思路
通信不稳定的典型表现是偶发NACK或数据错位。通过逻辑分析仪捕获波形后,重点检查:
- SCL高电平期间的SDA变化——I²C规定SDA只能在SCL低电平时变化,否则视为START或STOP条件,导致帧同步丢失。
- 应答位(ACK)时序——从设备在第9个时钟脉冲释放SDA,主机应保持SCL低电平足够时间让从设备拉低SDA;若主机提前释放SCL,可能采样到高电平而误判NACK。
- 时钟延展——从设备在需要时拉低SCL以延长低电平时间,主机必须检测并等待SCL真正释放为高后再继续。
关键完整代码(STM32主机时序修正)
以下代码展示如何通过调整I²C时序参数解决因SCL低电平时间不足导致的ACK采样失败问题。核心是增加SCL低电平持续时间,确保从设备有足够时间拉低SDA完成应答。
// 使用STM32 HAL库,修正I²C时序
// 问题:默认时序下从设备ACK采样不稳定
// 解决:增大SCL低电平时间(对应I2C_TIMINGR的SCLL字段)
#include "stm32f4xx_hal.h"
// I2C句柄(已在MX_I2C1_Init中初始化)
extern I2C_HandleTypeDef hi2c1;
// 自定义时序配置:SCL低电平时间由默认的0x13延长至0x2A
// 对应SCLL = 42个时钟周期(在APB1=42MHz下,低电平约1μs)
#define I2C1_TIMING_CUSTOM 0x20A02A2A // PRESC=0x20, SCLL=0x2A, SCLH=0x2A, SDADEL=0x0, SCLDEL=0x2
void I2C1_ApplyCustomTiming(void)
{
// 关闭I2C外设以修改时序寄存器
__HAL_I2C_DISABLE(&hi2c1);
// 写入自定义时序(仅修改TIMINGR寄存器)
hi2c1.Init.Timing = I2C1_TIMING_CUSTOM;
MODIFY_REG(hi2c1.Instance->TIMINGR, I2C_TIMINGR_TIMINGR, I2C1_TIMING_CUSTOM);
// 重新使能I2C
__HAL_I2C_ENABLE(&hi2c1);
}
// 使用修正后时序进行单字节写操作
HAL_StatusTypeDef I2C1_WriteByte_WithFix(uint16_t DevAddr, uint8_t RegAddr, uint8_t Data)
{
HAL_StatusTypeDef status;
uint8_t buf[2] = {RegAddr, Data};
// 应用自定义时序
I2C1_ApplyCustomTiming();
// 执行写操作(100kHz模式)
status = HAL_I2C_Master_Transmit(&hi2c1, DevAddr, buf, 2, 100);
return status;
}
运行与验证方法
- 捕获设置:逻辑分析仪采样率设为20MHz(每个SCL周期至少采样20点),触发条件设为SDA下降沿(START条件)。
- 执行通信:运行上述代码向从设备写入1字节数据,重复100次。
- 协议解码:在逻辑分析仪软件中启用I²C解码器,观察每次传输的ACK位。
- 判定通过标准:
- 100次传输中,解码器显示全部为ACK(无NACK);
- 波形中SDA仅在SCL低电平时变化,无高电平跳变;
- SCL低电平时间≥1μs(从波形测量),且从设备无时钟延展超时。
常见问题与调整方向
- 若仍有NACK:继续增大SCLL值(如0x3C),但需确保SCL频率不低于规格要求(100kHz模式允许低电平最长约4μs)。
- 若SDA在SCL高电平时变化:检查是否误将SDA/SCL接反,或从设备地址错误导致无ACK响应。
- 若波形显示时钟延展:确认主机代码使用HAL库时已正确等待SCL释放(HAL库自动处理),无需额外修改。
上图对比了修正前后的SCL低电平持续时间差异。修正前SCL低电平过短(约0.5μs),从设备来不及在ACK位拉低SDA,导致主机误判NACK;修正后低电平延长至约1μs,从设备有充足时间完成应答,通信恢复稳定。