STM32 I2C Slave (HALL Lib)
前言
這邊因有I2C slave需求,但因之前使用的LL寫法在舊款MCU中部份Lib是沒有的,因此這邊只能使用HALL Lib撰寫,但HALL Lib限制就是不指定長度接收寫法相對複雜很多,這邊因使用情境式類似sensor 方式,因次HALL Lib是相對足夠的
STM32CubeMX Setup
這邊沒有麼特別的,只有跟LL Lib相同需要留意Slave address不能為0x00
另外中斷部分需要開啟,包含ERROR部分因為Slave傳輸資料時候會使用到
Sample Code (HALL Lib)
這邊需要注意是中斷在主程式中是需要被開啟的這個Function是會常使用到的
HAL_I2C_EnableListen_IT(&hi2c1);
這邊會宣告一個陣列去接要發送的data
uint8_t I2C_REGISTERS[10] = {0,0,0,0,0,0,0,0,0,0};
另外他的Call Back也會使用到
extern void HAL_I2C_ListenCpltCallback (I2C_HandleTypeDef *hi2c)
{
HAL_I2C_EnableListen_IT(hi2c);
}
首先要先針對Address 的Callback這邊會判讀收還是發,但這邊不管是收data還是發data都一開始都會先進到I2C_DIRECTION_TRANSMIT判讀這邊可以撰寫第一筆資訊去區分是要做哪個動作也就是RxData[0],其中參考網路範例他第一筆data是用來判讀發送data的第幾個位置這邊可以依據需求修改或刪除
uint8_t bytesTransd = 0;
uint8_t txcount = 0;
uint8_t startPosition = 0;
void HAL_I2C_AddrCallback(I2C_HandleTypeDef *hi2c, uint8_t TransferDirection, uint16_t AddrMatchCode)
{
if (TransferDirection == I2C_DIRECTION_TRANSMIT) // if the master wants to transmit the data
{
RxData[0] = 0; // reset the RxData[0] to clear any residue address from previous call
rxcount =0;
HAL_I2C_Slave_Seq_Receive_IT(hi2c, RxData+rxcount, 1, I2C_FIRST_FRAME);
}
else
{
txcount = 0;
startPosition = RxData[0];
RxData[0] = 0; // Reset the start register as we have already copied it
HAL_I2C_Slave_Seq_Transmit_IT(hi2c, I2C_REGISTERS+startPosition+txcount, 1, I2C_FIRST_FRAME);
}
}
傳輸資料的CallBack比較簡單,這邊只要留意是I2C_NEXT_FRAME,這邊實際去量測會發現最後一筆都是NACK但不影響Master判讀
void HAL_I2C_SlaveTxCpltCallback(I2C_HandleTypeDef *hi2c)
{
txcount++;
HAL_I2C_Slave_Seq_Transmit_IT(hi2c, I2C_REGISTERS+startPosition+txcount, 1, I2C_NEXT_FRAME);
}
因為傳輸最後一筆會NACK所以這邊會進到ERROR CallBack這邊主要2個ERROR要注意一個是AF和BERR,其中進到ERROR後藥劑的再把中斷開啟
void HAL_I2C_ErrorCallback(I2C_HandleTypeDef *hi2c)
{
uint32_t errorcode = HAL_I2C_GetError(hi2c);
if (errorcode == 4) // AF error
{
if (txcount == 0) // error is while slave is receiving
{
bytesRrecvd = rxcount-1; // the first byte is the register address
rxcount = 0; // Reset the rxcount for the next operation
process_data();
}
else // error while slave is transmitting
{
bytesTransd = txcount-1; // the txcount is 1 higher than the actual data transmitted
txcount = 0; // Reset the txcount for the next operation
}
}
else if (errorcode == 1) // BERR Error
{
HAL_I2C_DeInit(hi2c);
HAL_I2C_Init(hi2c);
memset(RxData,'\0',RxSIZE); // reset the Rx buffer
rxcount =0; // reset the count
}
HAL_I2C_EnableListen_IT(hi2c);
}
結論
這邊會依照不同需求可以選用LL或HAL去撰寫I2C Slave code,HAL Lib比較麻煩的地方在於接收部定長度資料部分,會很容易卡住假如不知道傳輸資料長度,會比較建議使用LL Lib方式撰寫,但如果是依據sensor方式讀取這邊HAL其實會比較直覺化,另外一個小要點就是在傳資料與接收資料地方不要寫其他動作會延遲訊號傳輸,容易導致傳輸失敗或訊號判讀錯誤
另外我非常推薦下面參考部分的影片她非常好的講述流程