背景介绍
通过镍锌电池和铅酸电池分别给系统板供电,主控单片机使用STM32F4芯片,外部EEPROM使用AT24C08芯片,通过STM32F4芯片驱动硬件IIC读写AT24C08芯片。当铅酸电池被拔掉之后,镍锌电池会直接给系统供电。由于镍锌电池容量比较低,镍锌电池只能维持2小时的系统供电,电压低于3.3V时STM32芯片复位,但是此时系统板还有存电(这是由于电路设计的缺陷导致)。
IIC总线基础
IIC总线传输包络起始信号、数据传输、应答位、结束信号组成如下图所示,
起始信号是主设备在SCL保持高电平的同时SDA产生一个下降沿;
结束信号是主设备在SCL保持高电平的同时SDA产生一个上升沿;
数据传输是主设备在SCL产生上升沿并驱动SDA数据线高/低电平;
应答位是是主设备在SCL产生上升沿并拉高SDA数据线,从设备驱动SDA回复一个低电平;
IIC总线是一个主从结构的协议,主设备需要主动发起起始信号、数据传输、结束信号,从设备不可以主动发起通信。
现象
在镍锌电池供电的状态下,系统会一直操作外部设备(包括EEPROM、NORFLASH等),当电压低于3.3V时会STM32芯片复位,系统板上的外设(包括EEPROM)还一直供着电。因为EEPROM还未断电,所以EEPROM会一直保持STM32芯片复位前的状态。下次接上铅酸电池,STM32检测到铅酸电池会重启STM32芯片,此时偶尔会出现EEPROM异常,无法通过IIC总线控制eeprom,系统板不断电EEPROM就会一直处于失控状态。只要系统板完全断电EEPROM就会恢复正常。
问题分析
1.当镍锌电池电量低于3.3V,STM32芯片异常复位。如果此时STM32芯片正在读写EEPROM芯片,有可能会造成EEPROM无法控制(锁死的状态)。
2.如下图所示STM32芯片完成起始信号和数据传输之后拉低SCL时钟线(即下图复位处),此时STM32芯片复位,SCL时钟线被拉高(即下图复位后),EEPROM将SDA数据线拉低,EEPROM会一直等待主设备将SCL时钟线拉低。由于此时EEPROM一直拉低SDA数据线,STM32芯片检测到SDA拉低,认为IIC总线一直被占用,就会一直等待SDA数据线和SCL时钟线被拉高。从而造成STM32芯片认为IIC总线被占用,而EEPROM因为STM32芯片未将SCL时钟线拉低一直驱动SDA数据线(低电平)。这样就会造成IIC总线处于一种死锁的状态。
3.根据以上阐述STM32芯片在操作EEPROM时(发送起始信号和传输数据之后)被异常复位之后,只要EEPROM不断电,STM32芯片就无法读写EEPROM芯片。
解决对策
1.根据以上的分析,STM32芯片复位后无法操作EEPROM芯片是因为STM32芯片异常复位造成IIC总线处于一种死锁的状态。解决办法如下
(1)当STM32芯片检测到IIC总线被一直占用的情况下(或是系统初始化时),需要主动拉低SCL时钟线(代码如下),让EEPROM释放SDA数据线(EEPROM不驱动SDA数据线),从而达到释放IIC总线的目的。
//拉低SCK,释放EEPROM的SDA数据线
GPIO_InitStruct.Pin = GPIO_PIN_8;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_8, GPIO_PIN_RESET);
(2)在软件框架中,检测到电池不足以给MCU供电时不可以操作IIC总线,或者直接让MCU进入低功耗,等待下次唤醒源唤醒。
(3)在系统电路设计上,给EEPROM芯片增加一个电源控制的功能,允许MCU软件复位EEPROM芯片。
(4)最后这里想说明一下,如果没有特殊需求,建议使用GPIO模拟IIC总线。