情況簡述
最近在調試與兼容 STM32F103C8T6 的 GD32F103C8T6 時使用了STM32 HAL庫,MCU作爲從機。
在調試到 HAL_I2C_Slave_Receive_IT() 函數,使用中斷方式接收I2C數據時,只要調用了該函數,開啓了中斷,單片機就會一直被 ADDR 寄存器的中斷觸發,循環進入中斷函數中,甚至會嵌套進入,導致我的堆棧爆炸。
排錯歷程
使用調試打印的函數,發現程序不斷執行了HAL庫I2C中斷處理函數中的 I2C_Master_ADDR() 函數,由此我判斷是 ADDR 標誌位未被HAL庫代碼清零。查看HAL庫代碼,發現處理從機收到匹配地址的內容如下:
/* ADDR set --------------------------------------------------------------*/
if ((I2C_CHECK_FLAG(sr1itflags, I2C_FLAG_ADDR) != RESET) && (I2C_CHECK_IT_SOURCE(itsources, I2C_IT_EVT) != RESET))
{
/* Now time to read SR2, this will clear ADDR flag automatically */
if (hi2c->ErrorCode != HAL_I2C_ERROR_NONE)
{
sr2itflags = READ_REG(hi2c->Instance->SR2);
}
I2C_Slave_ADDR(hi2c, sr2itflags);
// rt_kprintf("I2C_Slave_ADDR\n");
}
而在I2C_Slave_ADDR()函數中,清除ADDR寄存器的代碼居然時這樣子的
{
/* Clear ADDR flag */
__HAL_I2C_CLEAR_FLAG(hi2c, I2C_FLAG_ADDR);
/* Process Unlocked */
__HAL_UNLOCK(hi2c);
}
官方的程序員居然用寫寄存器的操作來清除 ADDR 標誌位,而芯片的操作手冊中明確標明瞭 ADDR 標誌位爲只讀模式, 且清除方式爲按順序讀取 SR1、SR2寄存器。
於是修改添加如下代碼, 按順序讀取 SR1 SR2:
else
{
/* Clear ADDR flag */
/* 此清除ADDR操作存在問題,ADDR應該爲read only,只有多SR1 SR2順序讀取的方式清除 */
// __HAL_I2C_CLEAR_FLAG(hi2c, I2C_FLAG_ADDR);
/* 改寫的清除ADDR代碼 */
__attribute__((unused)) uint32_t tmp = READ_REG(hi2c->Instance->SR1);
tmp = READ_REG(hi2c->Instance->SR2);
/* Process Unlocked */
__HAL_UNLOCK(hi2c);
}
再次測試後,I2C slave 中斷模式終於正常運行, 問題被解決。
總結
經過這一番折騰,我就實在是搞不明白ST對他們芯片的硬件I2C的態度了,莫不是官方自己覺得硬件I2C太廢了,以至於在HAL庫中這麼明顯的錯誤都會出現?