
直接进入主题,首先说一下背景:
我们使用 ESP32 上的 I2C 和一片 LED 控制芯片进行通讯,SCL 频率为 10K。ESP32 I2C 作为 master,在控制 LED 渐变时(会频繁的向设备写 12 字节的数据),会导致灯全部熄灭,芯片不能正常工作,除非重新初始化。
使用示波器抓取的 I2C 总线上异常的波形,如图1所示:
从波形可以看到,master 在写完 12 字节的数据后并没有发送停止位,而是继续发送 SCL 时钟向 LED 控制芯片一直写入数据直到软件超时复位 master。这就导致 LED 控制芯片的初始化参数被修改而无法工作。master 需要写的数据已经全部发送完成,如果没有发送停止位就会导致状态机出错。但为什么 ESP32 的 I2C 会丢失 stop 信号呢

我们怀疑就是这些高频的噪声影响了ESP32 I2C master 的时序。因为 I2C master 在向外输出信号时,也会回采输出的信号,这些干扰很容易导致 master 出现故障。但从示波器抓取的波形来看,SCL 的上升沿没有发现 20ns 的干扰。不过 I2C 总线上接了较大的滤波电容,导致 SCL 的上升沿很缓慢。我们判断很有可能是 SCL 信号电平在中间电平上下抖动。 ESP32 I2C 以 80M 的频率采样 SCL 总线电平时就很有可能采到了这种都抖动。我们尝试将逻辑分析仪的采样频率设置为 400K, 发现采集到的数据正常。如图3 所示:
所以基本可以肯定是 SCL 上升沿太缓慢导致 ESP32 采集到了干扰信号。我们提供的解决办法是滤除这些高频的脉冲序列。ESP32 采样周期是 12.5ns, 而线上的这些干扰基本都是 20ns, 所以我们只要将这些 20ns 的脉冲信号滤除就可以了。ESP32 I2C 支持硬件滤波功能,当采样到的脉冲周期小于配置的阈值时,这些脉冲会被忽略。I2C_SCL_FILTER_CFG_REG 和 I2C_SDA_FILTER_CFG_REG 用于使能硬件滤波功能和配置滤除的信号周期。我们向这两个寄存器写入 0xf 后,经过长时间测试, ESP32 I2C 与 LED 控制芯片的通讯很稳定。
在使用 ESP32 上 I2C 时,需要注意以下两点:
1. 当总线上有 高频干扰时最好使能硬件滤波。
2. 在 I2C 总线上不要接太大的滤波电容,如果 SCL 上升沿非常缓慢,在中间电平附近持续时间过长,则硬件滤波也是无法滤除这种信号的。