这里不准备深入讨论stm32硬件IIC的锁死的原因,大概意思是应为IIC为“响应”式的通讯,通讯一方无响应则导致另一方等待或者出错。STM32的硬件IIC一直传说是有问题的,从最开始的初始化之后立刻锁死开始,一直被大家所诟病,后来官方经过多次的驱动修改或者硬件迭代,目前至少在新的mcu上,IIC可以在正常情况下正常工作了,但从设备无响应是时,可能导致整个IIC的HAL库函数处于busy或error状态,从而无法恢复。如果使用硬件模拟IIC则比较灵活,可以自行处理各种问题(如状态复位,或者使用9个脉冲复位总线等)。
但是,由于当前项目不得已使用了的fcubeMx自带的rtos,硬件上使用模拟IIC的话有诸多不便(如使用osdelay只能为ms级,使用自定义微秒延时函数时需要禁用任务调度,否则可能出现延时过长等问题),只能使用硬件IIC,从设备为SHT35。此时过程中,通过拔插从设备电源及通讯线,偶尔会出现无法恢复通通讯的情况,通过采用一下方式处理这个问题,目前经过一段时间测试还算正常,如果方便,也请各位帮忙测试或者分析下是否会有问题:
调用代码:
while(1)
{
if(SHT35Op()!=0)
{
UnlockI2C2();
}
osDelay(500);
}
函数SHT35OP中,如果出现IIC发送或者接受错误则会返回非0值,然后会调用UnlockI2C2();试图恢复总线。UnlockI2C2()代码如下:
void UnlockI2C2(void)
{
hi2c2.Instance->CR1=hi2c2.Instance->CR1&0xFFFE; //复位IIC软件
GPIOB->AFR[1]=GPIOB->AFR[1]&0xFFFF00FF;
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_11|GPIO_PIN_10,GPIO_PIN_SET);
osDelay(1);
for(uint8_t i=0;i<9;i++)
{
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_10,GPIO_PIN_RESET);
osDelay(1);
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_10,GPIO_PIN_SET);
osDelay(1);
}
GPIOB->AFR[1]=(GPIOB->AFR[1]&0xFFFF00FF)|0x00004400;
hi2c2.Instance->CR1=(hi2c2.Instance->CR1|0x01);
osDelay(1);
}
解释如下:
1. 通过直接修改寄存器值,复位IIC的各个状态(以IIC2为例参加手册的寄存器解释,不同的MCU可能不一样,当前是STM32L4xx)
2. 通过修改AFR寄存器,将所用的SCL和SDA端口变为普通IO输出。不同IIC的AFR操作数据和GPIO端口可能不一样。
3. 使用直接操作的GPIO的方式操作输出端口,发送9个脉冲使得总线复位。
4. 将SCL和SDA端口恢复为受外设控制。
5. 取消IIC的复位,回复IIC功能。