STM32Cube_FW_F0_V1.10.0 官方庫的I2C 調試

   又來寫I2C通信了......真是換一個新庫就要花時間重新調試一下,這次用的是最新的官方庫STM32Cube_FW_F0_V1.10.0 ,開發平臺用的也是新的STM32CubeIDE。

    需要完成的任務是模擬一個電池包被動發送信息給充電器,調試階段我用STM32F030R8板子上的I2C2做主模擬充電器,I2C1做從模擬電池包。充電器讀取電池包信息時通信時序是:先發送從機地址(模擬的電池包地址)+ Write標誌,將需要讀取信息的寄存器值發送給從機,發送完成後再發送一次從機地址+ Read標誌,等待從機發送相關的信息回來。

    主機我用的的讀取函數是

    HAL_I2C_Mem_Read(&hi2c2, (uint16_t)BAT_IC_ADDRESS, BAT_REG_TEMP, I2C_MEMADD_SIZE_8BIT, Tempe, 2, 500);

    因爲我需要根據不同的寄存器值來發送不同的信息,所以我在第二次地址匹配時應該先判斷上次讀取的寄存器值,再選擇發送哪種類型的數據 。因此,從機採用中斷接收模式。

    研究了下官方庫,總的來說,I2C如果要實現中斷,有一個基本的函數調用順序:

    1、配置I2C初始化設置,使能I2C中斷

    2、當發生事件中斷時會調用EV_IRQHandler,而如果發生錯誤會調用ER_IRQHandler。EV_IRQHandler中,會判斷中斷ISR以及中斷源類型,並根據I2C是主機還是從機來決定調用I2C_Matser_IT還是I2C_Slave_IT

    3、在I2C_Matser/Slave_IT函數中,有具體的區分不同的中斷類型:地址匹配中斷(ADDR/ADDRIT)、發送中斷(TXIS/TXI)、接收中斷(RXNE/RXI)、AF/NACKI、STOPF/STOPI,不同的中斷類型有不同的中斷處理回調函數

   

   然而,實際調試中發現,如果需要在地址匹配時自己做另外處理,則需要重寫HAL_I2C_AddrCallback函數,而這個函數只有在滿足 ((hi2c->State & HAL_I2C_STATE_LISTEN) == HAL_I2C_STATE_LISTEN)條件時纔會調用,找到源頭確定滿足該條件即需要使用HAL_I2C_Slave_Sequential_Receive_IT函數進行中斷接收。而如果需要該函數能正常發生中斷,在初始化時還需使能ListenIT即HAL_I2C_EnableListen_IT(&hi2c1);

   在完成這些配置後即可重寫地址匹配回調函數HAL_I2C_AddrCallback,如下,我在地址匹配中斷髮生後判斷主機的讀寫方向,只有當主機發送的是讀時,從機纔開始發送數據。

void HAL_I2C_AddrCallback(I2C_HandleTypeDef *hi2c, uint8_t TransferDirection, uint16_t AddrMatchCode)
{
	uint8_t Direction = TransferDirection;
	uint8_t Temp[2] = {0xFE,0x03};

	__HAL_I2C_CLEAR_FLAG(hi2c, I2C_FLAG_ADDR);


	if(hi2c == &hi2c1)
	{
	  if(Direction == 1)//Read
	  {
		 HAL_I2C_Slave_Sequential_Transmit_IT(&hi2c1, Temp, 2, I2C_FIRST_FRAME);
	  }
	}
}

   實際調試過程中發現由有問題,定位到調用HAL_I2C_AddrCallback()的函數I2C_ITAddrCplt(),有一段可以看到當7位地址匹配時,會先Disable_IRQ(hi2c, I2C_XFER_LISTEN_IT),這樣會導致第一地址匹配後,第二次再發地址就接收不到中斷了。一開始我的解決辦法是在後面重新使能中斷,即調用I2C_Enable_IRQ(hi2c, I2C_XFER_LISTEN_IT);這樣操作後,我發現如果我發兩個數據(比如0xFE、0x03),但實際接收到的是0xB6、0x00,DEBUG後發現I2C1中的hi2c->pBuffPtr 的數據在調用I2C_Enable_IRQ(hi2c, I2C_XFER_LISTEN_IT)後會發生變化(具體爲什麼還未知),這樣導致TXDR中的數據變化,發出去的數據也就是錯的。最後,我將I2C_Disable_IRQ(hi2c, I2C_XFER_LISTEN_IT);和I2C_Enable_IRQ(hi2c, I2C_XFER_LISTEN_IT)都註釋掉,即解決了問題。

    else
    {
      /* Disable ADDR Interrupts */
      I2C_Disable_IRQ(hi2c, I2C_XFER_LISTEN_IT);

      /* Process Unlocked */
      __HAL_UNLOCK(hi2c);

      /* Call Slave Addr callback */
      HAL_I2C_AddrCallback(hi2c, transferdirection, slaveaddrcode);

    }
  }
  /* Else clear address flag only */
  else
  {
    /* Clear ADDR flag */
    __HAL_I2C_CLEAR_FLAG(hi2c, I2C_FLAG_ADDR);

    /* Process Unlocked */
    __HAL_UNLOCK(hi2c);
  }

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章