STM32的IIC問題一直存在,在網上也被很多人吐槽,然而FAE告訴我,硬件IIC的問題在F1,F3,F4系列單片機存在,而在L0上已經解決了,然而這幾天調試加密芯片和顯示芯片,都是IIC芯片,卻再一次證明,L0系列的IIC一樣存在問題,
FAE告訴我是我的配置問題,故寫這篇文章,如果是我配置問題,希望得到高人指點,再次感激不盡。
配置代碼:
void MX_I2C1_Init(void) { hi2c1.Instance = I2C1; hi2c1.Init.Timing = 0x00707CBB; hi2c1.Init.OwnAddress1 = 0; hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT; hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE; hi2c1.Init.OwnAddress2 = 0; hi2c1.Init.OwnAddress2Masks = I2C_OA2_NOMASK; hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE; hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE; if (HAL_I2C_Init(&hi2c1) != HAL_OK) { _Error_Handler(__FILE__, __LINE__); } /**Configure Analogue filter */ if (HAL_I2CEx_ConfigAnalogFilter(&hi2c1, I2C_ANALOGFILTER_ENABLE) != HAL_OK) { _Error_Handler(__FILE__, __LINE__); } /**Configure Digital filter */ if (HAL_I2CEx_ConfigDigitalFilter(&hi2c1, 0) != HAL_OK) { _Error_Handler(__FILE__, __LINE__); } }
void HAL_I2C_MspInit(I2C_HandleTypeDef* i2cHandle) { unsigned char i; GPIO_InitTypeDef GPIO_InitStruct; if(i2cHandle->Instance==I2C1) { __HAL_RCC_I2C1_CLK_ENABLE(); __HAL_RCC_GPIOB_CLK_ENABLE(); /*I2C1 GPIO Configuration PB8 ------> I2C1_SCL PB9 ------> I2C1_SDA */ GPIO_InitStruct.Pin = I2C_SCL_Pin|I2C_SDA_Pin; GPIO_InitStruct.Mode = GPIO_MODE_AF_OD; GPIO_InitStruct.Pull = GPIO_PULLUP; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; GPIO_InitStruct.Alternate = GPIO_AF4_I2C1; HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); } } void HAL_I2C_MspDeInit(I2C_HandleTypeDef* i2cHandle) { if(i2cHandle->Instance==I2C1) { __HAL_RCC_I2C1_CLK_DISABLE(); /*I2C1 GPIO Configuration PB8 ------> I2C1_SCL PB9 ------> I2C1_SDA */ HAL_GPIO_DeInit(GPIOB, I2C_SCL_Pin|I2C_SDA_Pin); } }
現象:程序調用HAL_I2C_Master_Transmit(),如果代碼中有其它中斷,就很容易傳輸失敗:
status = HAL_I2C_Master_Transmit(&hi2c1, SLAVE_IIC_ADDR, i2c_data, 2, 2)
error_status = HAL_I2C_GetState(&hi2c1); printf("err1:%02x -- err2:%02x\r\n",status,error_status);
查看到的是status基本就是HAL_BUSY,error_status基本就是HAL_I2C_STATE_BUSY_TX,用邏輯分析儀查看I2C,即使重新調用HAL_I2C_Master_Transmit(),也沒看到有波形出來,用萬用表量出來SDA是高電平,SCL是低電平。
在網上搜索STM32硬件I2C鎖死,能搜到幾篇有參考性的文章,其中下面這篇參考性意義很大,在此非常感謝:
http://blog.csdn.net/dldw8816/article/details/51579781
由於芯片不一樣,所以方法也不一樣,但思路是一樣的,我貼上我的解決方法:
查看datasheet上寄存器的描述,清零PE,就可以清除I2C的所有錯誤狀態。
void HAL_I2C_MspInit(I2C_HandleTypeDef* i2cHandle) { unsigned char i; GPIO_InitTypeDef GPIO_InitStruct; if(i2cHandle->Instance==I2C1) { __HAL_RCC_I2C1_CLK_ENABLE(); __HAL_RCC_GPIOB_CLK_ENABLE(); GPIO_InitStruct.Pin = I2C_SCL_Pin|I2C_SDA_Pin; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; HAL_GPIO_Init(I2C_SCL_GPIO_Port, &GPIO_InitStruct); HAL_GPIO_WritePin(I2C_SCL_GPIO_Port, I2C_SCL_Pin, GPIO_PIN_SET); HAL_GPIO_WritePin(I2C_SDA_GPIO_Port, I2C_SDA_Pin, GPIO_PIN_SET); /*force to release I2C bus*/ __HAL_I2C_DISABLE(i2cHandle); for(i=0;i<100;i++); /*delay awhile*/ __HAL_I2C_ENABLE(i2cHandle); /*I2C1 GPIO Configuration PB8 ------> I2C1_SCL PB9 ------> I2C1_SDA */ GPIO_InitStruct.Pin = I2C_SCL_Pin|I2C_SDA_Pin; GPIO_InitStruct.Mode = GPIO_MODE_AF_OD; GPIO_InitStruct.Pull = GPIO_PULLUP; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; GPIO_InitStruct.Alternate = GPIO_AF4_I2C1; HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); } }
void I2C_Reset(void) { HAL_I2C_MspDeInit(&hi2c1); HAL_I2C_MspInit(&hi2c1); }
如果單單這么做,然后在I2C傳輸返回值不是HAL_OK的時候調用I2C_Reset,也不能解決問題,仍然返回HAL_BUSY,於是查看發送源碼,發現STM32的庫函數里面軟件實現了一個狀態機,需要將這個狀態機恢復到初始狀態,代碼如下:
do{ status = HAL_I2C_Master_Transmit(&hi2c1, SLAVE_IIC_ADDR, i2c_data, 2, 2); if(status != HAL_OK) { I2C_Reset(); #if 1 error_status = HAL_I2C_GetState(&hi2c1); printf("err1:%02x -- err2:%02x\r\n",status,error_status); #endif __HAL_I2C_CLEAR_FLAG(&hi2c1, I2C_FLAG_STOPF); /* Clear STOP Flag */ I2C_RESET_CR2(&hi2c1); /* Clear Configuration Register 2 */ hi2c1.State = HAL_I2C_STATE_READY; hi2c1.Mode = HAL_I2C_MODE_NONE; __HAL_UNLOCK(&hi2c1); /* Process Unlocked */ } }while(status != HAL_OK);
這樣測下來I2C發生錯誤之后,就可以重新初始化總線了,測試了一上午,查看串口打印出來的log,能看到printf("err1:%02x -- err2:%02x\r\n",status,error_status);這條消息,但我的代碼可以修復I2C。
下面兩篇文章,一篇是ST官方的解決方案,一篇是網上沒有署名的大神寫的文檔,再次非常感謝!
STM32-I2C-接口進入-Busy-狀態不能退出.pdf