1、运行环境:ESP32C3
2、编译环境:VSCode+Platform IDE
3、程序框架:arduino
4、基本背景介绍:项目中有一个耗材需要记录数据,但此耗材是活动部件,可以更换并且需要支持热拔插,我们选用的就是一个IIC通信(题外话:IIC是不持支热拔插的)的芯片再此耗材上,大致如下图;主控ESP32C3需要通过IIC不断的(10ms读一次)读取耗材芯片的ID判断耗材是否在线
问题一:
最初使用的模拟IIC读取耗材信息,出现了半波和尖峰问题,如下:
这个问题在网上搜索“IIC 半波”就可以找到相关的文章分析,然后根据网上分析需要修改SDA的引脚的配置
原来程序SDA的配置定义:
#define I2C_SDA_OUT() pinMode(SDA_PIN,OUTPUT)
#define I2C_SDA_IN() pinMode(SDA_PIN,INPUT)
修改后的代码:
#define I2C_SDA_OUT() pinMode(SDA_PIN,OUTPUT_OPEN_DRAIN)
#define I2C_SDA_IN() pinMode(SDA_PIN,INPUT_PULLUP)
以上这种把SDA的引脚配置成开漏输出+硬件外部上拉的方法在STM上确实能解决问题,但是在ESP32C3上面,发现有尖峰波形,如下:
对于这个点本人的分析是,在等待应答的函数内部SDA从开漏输出切换为上拉输入时,外加外部上拉,导致电位变高,但立马外部IIC设备响应,又给拉低了,就出现了尖峰(不对指出,还望指出),当时这点尝试了不同的输出输出模式,一直避不开上面这两个问题。既然避不开,那就不用模拟IIC,直接改用硬件IIC。
问题2:
使用硬件IIC后,确实解决上面的半波和尖峰问题,但后面发现偶发出现死机。主要出现在拔插耗材时,有一定的几率出现死机问题
在arduino框架下,
void loop()
{
}
内不允许有是循环,若出现死循环,就没法喂狗,导致死机。
通过加快读取IIC数据的速度(从上面的10ms读取一次变成3ms读取一次),拔插耗材,发现更容易出现死机,并且抓取到死机时IIC的通信波形如下:
在等待应答信号时,SAD一直没有应答信号,导致硬件超时,然后应该就是我所使用的硬件IIC的驱动里面进入了死等,导致我的整个程序死机。
由于底层封装的太多,没法找到到底是哪里的原因,这个时候有切换回模拟IIC一样的进行3ms读取一次的压力测试发现没问题,不会死机。最后还是要解决模拟IIC的尖峰或半波问题。
通过查看硬件IIC驱动中的对SDA的引脚配置找到了最后的解决方案:
#define I2C_SDA_OUT() pinMode(SDA_PIN,OUTPUT_OPEN_DRAIN)
#define I2C_SDA_IN() pinMode(SDA_PIN,INPUT_PULLUP)
//上面的两句合并成一句,如下
#define I2C_SDA_OUT() pinMode(SDA_PIN, OPEN_DRAIN | PULLUP | INPUT | OUTPUT)
最后关于SDA和CLK的配置定义
#define SET_SCL_LEVEL(a) digitalWrite(SCL_PIN, (a))
#define I2C_SCL_OUT() pinMode(SCL_PIN, OUTPUT_OPEN_DRAIN)
#define SET_SDA_LEVEL(a) digitalWrite(SDA_PIN, (a))
#define GET_SDA_LEVEL digitalRead(SDA_PIN)
#define I2C_SDA_OUT() pinMode(SDA_PIN, OPEN_DRAIN | PULLUP | INPUT | OUTPUT)