至我感谢亲爱的妻子大喵和孩子们
STM32F103与MS4525I压力传感器通讯中的硬件I2C的解决方案
1,问题的背景介绍
一直以来总有人说STM32F103的I2C有BUG。所以大家一般都是用GPIO模拟I2C。而实际上,这个问题还是可以解决的。
手头上有个项目,需要用103的I2C和TE的压力传感器通讯并且不允许使用操作系统。GPIO - I2C的主要问题是需要不断的轮询,并且不能使用中断、DMA。如果你还对波形的形状有所要求,比如时钟周期不要有波动,GPIO模拟的I2C的性能其实是不好的。相比之下,那么硬件I2C表现出来的性能还是不错的。
在项目的测试中发现,如果直接使用寄存器编程,103的硬件I2C的主要问题有:
- 传感器发来ACK响应,103的状态寄存器没有响应。这个问题主要发生在主机发送了ADDR以后,传感器虽然已经发送了ACK,但是103的状态寄存器没有相应置位,直接判定为Acknowledge failure并置位I2C_SR1的AF位。
- 配置CCR的分频往往不生效。
2, 原因分析
目前找到了HAL库的解决方案。根据前面的分析怀疑是I2C的GPIO时钟使能不生效。
跟I2C相关的时钟至少有两个,分别是APB1和相应的GPIO的时钟。这两个始终都必须要使能。
3, 解决方案
总的思路是,两次使能I2C时钟。
3.1, HAL库的解决方案
IDE: STM32CubeIDE(任何版本) (含STM32CubeMX) + 修改生成的函数
这里需要说明的是,使用HAL库开发103的时候推荐使用ST的官方IDE。因为在Keil的RTE上提供的是标准库。标准库有关I2C的函数其实大概就是寄存器的转写。所有使用寄存器会遇到的问题使用标准库同样会遇到。
Stm32CubeIDE中的管脚配置:
参数设置为:
并且要在NVIC配置中将I2C2的 event interupt使能。
配置好项目,点击Generate Code。在生成的代码中的i2c.h中找到void HAL_I2C_MspInit(I2C_HandleTypeDef* i2cHandle)函数,注意,必须在此函数中两次使能时钟才能让I2C正常工作,代码如下所示。
void HAL_I2C_MspInit(I2C_HandleTypeDef* i2cHandle)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
if(i2cHandle->Instance==I2C2)
{
/* USER CODE BEGIN I2C2_MspInit 0 */
__HAL_RCC_I2C2_CLK_ENABLE(); //就是这句!!!!!!
/* USER CODE END I2C2_MspInit 0 */
__HAL_RCC_GPIOB_CLK_ENABLE();
/**I2C2 GPIO Configuration
PB10 ------> I2C2_SCL
PB11 ------> I2C2_SDA
*/
GPIO_InitStruct.Pin = GPIO_PIN_10|GPIO_PIN_11;
GPIO_InitStruct.Mode = GPIO_MODE_AF_OD;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
/* I2C2 clock enable */
__HAL_RCC_I2C2_CLK_ENABLE();
/* I2C2 interrupt Init */
HAL_NVIC_SetPriority(I2C2_EV_IRQn, 3, 0);
HAL_NVIC_EnableIRQ(I2C2_EV_IRQn);
/* USER CODE BEGIN I2C2_MspInit 1 */
/* USER CODE END I2C2_MspInit 1 */
}
}
注意第一次__HAL_RCC_I2C2_CLK_ENABLE()的调用。非常关键。
3.2, CMSIS-Driver库解决方案
IDE:KEIL 5.20以后的版本可以考虑使用使用CMSIS-DRIVER中的I2C Driver。经测试没有问题。要注意的是,对于Cortex-M3的核,CMSIS-Driver是自主设计的驱动;而Cortex-M4则直接调用的Hal库。
4,结论
103的硬件I2C其实是可以使用的。人们认为的bug其实是可以通过两次使能GPIO的时钟来规避。