问题描述
在学习STM32F407硬件I2C时,上电复位就会出现程序卡死现象,调试发现程序卡死在I2C的检测busy位。
解决过程:
首先对比了网上开源的STM32F1的硬件I2C代码(寄存器版),无果。网上大部分示例是基于hal库或者标准库,跟踪调试可能会比较繁琐。最后对比了AI生成的寄存器版初始化代码,发现它多了两行重置I2C的代码:
RCC->APB1RSTR |= RCC_APB1RSTR_I2C1RST;
RCC->APB1RSTR &= ~RCC_APB1RSTR_I2C1RST; //配置之前重置I2C
加上这两行代码之后程序勉强可以跑起来,但还是偶尔会出现上电卡死现象。后面查阅STM32F4参考手册(RM0090)时偶然发现其寄存器中有专门的一位用于错误锁死复位,将上述两行代码替换为下述代码:
I2C1->CR1 |= I2C_CR1_SWRST;
I2C1->CR1 &= ~I2C_CR1_SWRST; //配置之前重置I2C 避免上电锁死
替换之后发现程序就不会出现程序卡死现象了,下面是修改之后I2C初始化代码:
void I2C_Init(void)
{
//开启时钟
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOBEN;
RCC->APB1ENR |= RCC_APB1ENR_I2C1EN;
//GPIO配置
GPIOB->MODER |= (GPIO_MODER_MODE8_1 | GPIO_MODER_MODE9_1);
GPIOB->MODER &= ~(GPIO_MODER_MODE8_0 | GPIO_MODER_MODE9_0); //10:复用模式
GPIOB->OTYPER |= (GPIO_OTYPER_OT8 | GPIO_OTYPER_OT9); //1:开漏输出
GPIOB->OSPEEDR |= (GPIO_OSPEEDR_OSPEED8_1 | GPIO_OSPEEDR_OSPEED9_1);
GPIOB->OSPEEDR &= ~(GPIO_OSPEEDR_OSPEED8_0 | GPIO_OSPEEDR_OSPEED9_0); //10:高速模式
GPIOB->PUPDR &= ~(GPIO_PUPDR_PUPD8 | GPIO_PUPDR_PUPD9); //00:外接上拉电阻 此次设置浮空
GPIOB->AFR[1] &= ~(GPIO_AFRH_AFRH0 | GPIO_AFRH_AFRH1);
GPIOB->AFR[1] |= (GPIO_AFRH_AFRH0_2 | GPIO_AFRH_AFRH1_2); ///100:引脚复用为I2C1
I2C1->CR1 |= I2C_CR1_SWRST;
I2C1->CR1 &= ~I2C_CR1_SWRST; //配置之前重置I2C 避免上电锁死
//IIC配置
I2C1->CR1 &= ~I2C_CR1_SMBUS;
I2C1->CCR &= ~I2C_CCR_FS; //标准I2C模式
I2C1->CR2 |= 42;
I2C1->CCR |= 210;
I2C1->TRISE |= 43; //I2C时钟配置
I2C1->CR1 |= I2C_CR1_PE; //使能I2C
}
为避免I2C在工作过程中受到外界干扰而出现卡死现象,可以进一步写一个I2C重置函数(重置之后需要重新配置I2C相关寄存器),当I2C错误或者处于锁死状态时,调用该函数自动重置I2C。
void I2C_ErrorReset(void)
{
I2C1->CR1 &= ~I2C_CR1_PE;
I2C1->CR1 |= I2C_CR1_SWRST;
I2C1->CR1 &= ~I2C_CR1_SWRST; //重置I2C
I2C1->CR2 |= 42;
I2C1->CCR |= 210;
I2C1->TRISE |= 43; //重新配置I2C相关寄存器
I2C1->CR1 |= I2C_CR1_PE;
}