近日在做项目的时候,遇到了一个问题让我百思不得其解。为了尽可能复现问题,我说的尽量详细一些,并附上一些尝试。
IIC协议是一种很常用的通信协议,一般分为硬件IIC和软件IIC两种实现方式。在实际使用过程中可能会出现IIC锁死[1]的问题。其具体表现为:SCL始终为高,SDA始终为低。但这种现象往往出现在硬件IIC,软件IIC并不会出现。但我在使用过程中却意外发现了,软件IIC也会出现IIC死锁的现象。
我使用的板子是STM32F103RCT6。资源配置如下:
NVIC分组为2bits抢占2bits响应
串口:①USART1用于红外,串口波特率为9600,使用DMA接受数据。②USART2用于语音芯片,波特率9600,使用DMA发送数据。③USART3用于陀螺仪模块,波特率为57600,陀螺仪的采样率为50Hz,采用中断处理数据,抢占2,响应0。④UART4、UART5用于两个激光测距模块,波特率为115200,采样率100Hz,采用中断处理数据,抢占2响应2.
定时器:①TIM4,CH1-CH4均用于PWM输出驱动电机。②TIM2、TIM3采用了编码器模式,用于采集电机编码器的值 ③TIM6为抢占0响应1的10ms中断,用于处理当前电机的编码器的值通过PID运算得出PWM输出值作用于TIM4 ④TIM7为抢占1响应0的中断,用于刷新处理陀螺仪的数据。
主函数:①硬件初始化,初始化OLED以及上述定时器串口。OLED采用软件IIC,运用延时函数delay_us处理IIC时序。 ②死循环里是:
- LED1 = ~LED1;
-
- OLED_ShowNum(0,18,flag_water_cd,8,12);
- OLED_FloatShow(0,36,speed_L,12);
- OLED_FloatShow(0,52,speed_R,12);
- OLED_Refresh_Gram();
复制代码 即跑马灯和OLED数据更新。由于OLED刷新需要一定时间,虽然没有delay但可以看到灯是在闪的。
问题描述:当系统运行一段时间后,会出现OLED数据不更新,灯不再闪烁,SCL为高,SDA为低的现象。可以判定为是IIC死锁的问题。但经过查阅资料,IIC死锁只会在硬件IIC出现,软件IIC不出现。
一些尝试:
①注释掉UART4, UART5初始化后运行五分钟,测试三次均未出现死锁问题。 ②注释掉定时器6的初始化,运行五分钟,三次测试均不出现死锁。 ③不注释任何代码,系统运行7s后IIC死锁,灯不再闪烁,OLED不更新。 ④注释掉UART4,UART5后,修改与陀螺仪的波特率为115200,运行数十秒后死锁。⑤注释掉陀螺仪的初始化代码,运行约3min后出现死锁问题。
猜测:高波特率的中断会打断IIC时序使主机不能收到IIC的Ack信号。
想过的解决方案:
①采用输入捕获和定时器对SCL进行检测,当长时间为高电平时再给SCL一个脉冲。实测可以解决但在此系统不能实装。因为TIM6要进行PID运算,如果有比他优先级更高的中断会导致PID更新不稳定,电机转动不顺畅。②目前临时办法是不使用OLED,或者不使用激光测距模块。不知道论坛的大神有没有遇到过类似的问题,希望能有所指点。
[1] I2C死锁原因及解决方法_fengel_cs的专栏-CSDN博客_i2c死锁
|