I2C Slave mode
前言
I2C有2種不同模式可以操作使用master 與 slave模式,大部分在操控sensor MCU都是以master模式去做操控,部分EEPROM會需要搭配MCU作為slave操作,但當slave HAL會需要已知長度才能正常work,這邊會特別介紹LL模式修正不特定長度
STM32CubeMX Setting
Stm32cubeMX上的設定Master與Slave基本上沒有太大差異,唯一要注意的是slave address不能為0x00
至於為甚麼Slave address為什麼不能為0目前猜測可能為下
ST I2C slave addr 是用AND GATE,來跟OAR1作比對相同就成立中斷,而設定成0X00會造成永遠不中斷
STM32CubeIDE(HAL)
這邊(HAL)slave會用到函數如下
HAL_I2C_Slave_Transmit()
HAL_I2C_Slave_Receive()
HAL_I2C_Slave_Transmit_IT()
HAL_I2C_Slave_Receive_IT()
HAL_I2C_Slave_Seq_Transmit_IT()
HAL_I2C_Slave_Seq_Receive_IT()
HAL_I2C_Slave_Transmit_DMA()
HAL_I2C_Slave_Receive_DMA()
HAL_I2C_Slave_Seq_Transmit_DMA()
HAL_I2C_Slave_Seq_Receive_DMA()
HAL_I2C_SlaveTxCpltCallback()
HAL_I2C_SlaveRxCpltCallback()
這邊用HAL_I2C_Slave_Receive做簡單示範一般會搭配中斷做撰寫
while(1)
{
HAL_I2C_Slave_Receive(&hi2c1, testArr, 4, 50);
decode();
if(old_vel != vel){
__HAL_TIM_SET_COMPARE(&htim2,TIM_CHANNEL_1,vel);//ccw
__HAL_TIM_SET_COMPARE(&htim2,TIM_CHANNEL_2,0);//cw
old_vel = vel;
}
}
另外針對中段部分會使用到callback(HAL_I2C_SlaveTxCpltCallback()/HAL_I2C_SlaveRxCpltCallback())
void HAL_I2C_AddrCallback(I2C_HandleTypeDef *hi2c, uint8_t TransferDirection, uint16_t AddrMatchCode)
{
Transfer_Direction = TransferDirection;
if (Transfer_Direction != 0)
{
/*##- Start the transmission process #####################################*/
/* While the I2C in reception process, user can transmit data through
"aTxBuffer" buffer */
if (HAL_I2C_Slave_Seq_Transmit_IT(&hi2c1, (uint8_t *)aTxBuffer, TXBUFFERSIZE, I2C_FIRST_AND_LAST_FRAME) != HAL_OK)
{
/* Transfer error in transmission process */
Error_Handler();
}
}
else
{
/*##- Put I2C peripheral in reception process ###########################*/
if (HAL_I2C_Slave_Seq_Receive_IT(&hi2c1, (uint8_t *)aRxBuffer, RXBUFFERSIZE, I2C_FIRST_AND_LAST_FRAME) != HAL_OK)
{
/* Transfer error in reception process */
Error_Handler();
}
}
}
STM32CubeMX(LL) Setting
這邊因為要編輯底層所以不選用HAL在設定上有以下2個地方的不同
- 把I2C修改成LL編輯
- 把.c/.h檔案分離出來方便單獨編輯
STM32CubeIDE(LL)
首先要開啟中斷並在中斷stm32g0xx_it.c後面_it.c內找到void I2C1_IRQHandler(void)編輯中斷要執行的事情,基本上會用到的是中斷flag判讀等等範例如下
if(LL_I2C_IsActiveFlag_ADDR(I2C1))
{
I2C_Start_Flag = 1;
//printf("S");
/* Verify the Address Match with the OWN Slave address */
if(LL_I2C_GetAddressMatchCode(I2C1) == 0xA0)
{
//printf("R");
/* Verify the transfer direction, a read direction, Slave enters transmitter mode */
if(LL_I2C_GetTransferDirection(I2C1) == LL_I2C_DIRECTION_READ)
{
//printf("R");
/* Clear ADDR flag value in ISR register */
LL_I2C_ClearFlag_ADDR(I2C1);
if(I2C_R_W_Flag == I2C_W_Mode)
I2C_R_W_Flag = I2C_WR_Mode;
else
I2C_R_W_Flag = I2C_R_Mode;
}
else
{
//printf("W");
/* Clear ADDR flag value in ISR register */
LL_I2C_ClearFlag_ADDR(I2C1);
I2C_R_W_Flag = I2C_W_Mode;
I2C_WriteAddrByte_Counter = 1;
}
}
else
{
/* Clear ADDR flag value in ISR register */
LL_I2C_ClearFlag_ADDR(I2C1);
}
}
/* Check NACK flag value in ISR register */
else if(LL_I2C_IsActiveFlag_NACK(I2C1))
{
//printf("N");
/* End of Transfer */
LL_I2C_ClearFlag_NACK(I2C1);
}
/* Check TXIS flag value in ISR register */
else if(LL_I2C_IsActiveFlag_TXIS(I2C1))
{
/* Call function Slave Ready to Transmit Callback */
//printf("d");
LL_I2C_TransmitData8(I2C1,A0_Ram_Table[A0_Ram_Pointer++]);
}
/* Check STOP flag value in ISR register */
else if(LL_I2C_IsActiveFlag_STOP(I2C1))
{
/* Clear STOP flag value in ISR register */
LL_I2C_ClearFlag_STOP(I2C1);
/* Check TXE flag value in ISR register */
if(!LL_I2C_IsActiveFlag_TXE(I2C1))
{
/* Flush the TXDR register */
LL_I2C_ClearFlag_TXE(I2C1);
}
I2C_Start_Flag = 0;
I2C_R_W_Flag = I2C_NONE_Mode;
//printf("P\n\r");
}
else if(LL_I2C_IsActiveFlag_RXNE(I2C1))
{
//printf("D");
if(I2C_WriteAddrByte_Counter == 1)
{
A0_Ram_Pointer = LL_I2C_ReceiveData8(I2C1);
I2C_WriteAddrByte_Counter--;
}
else
{
A0_Ram_Table[A0_Ram_Pointer++] = LL_I2C_ReceiveData8(I2C1);
}
}
/* Check TXE flag value in ISR register */
else if(!LL_I2C_IsActiveFlag_TXE(I2C1))
{
//printf("e");
/* Do nothing */
/* This Flag will be set by hardware when the TXDR register is empty */
/* If needed, use LL_I2C_ClearFlag_TXE() interface to flush the TXDR register */
}
else if(!LL_I2C_IsActiveFlag_BERR(I2C1))
{
//printf("e");
LL_I2C_ClearFlag_BERR(I2C1);
I2C1_SoftwareReset();
I2C_Start_Flag = 0;
I2C_R_W_Flag = I2C_NONE_Mode;
}
else if(!LL_I2C_IsActiveFlag_ARLO(I2C1))
{
//printf("e");
LL_I2C_ClearFlag_ARLO(I2C1);
I2C1_SoftwareReset();
I2C_Start_Flag = 0;
I2C_R_W_Flag = I2C_NONE_Mode;
}
else if(!LL_I2C_IsActiveFlag_OVR(I2C1))
{
//printf("e");
LL_I2C_ClearFlag_OVR(I2C1);
I2C1_SoftwareReset();
I2C_Start_Flag = 0;
I2C_R_W_Flag = I2C_NONE_Mode;
}
else
{
//printf("e");
}
/* USER CODE END I2C1_IRQn 0 */
/* USER CODE BEGIN I2C1_IRQn 1 */
/* USER CODE END I2C1_IRQn 1 */
}
注意事項
在執行中斷程式時要注意必須把I2C中斷打開與使用前建議會讓他重新開起reset確保訊號正常,其範例如下
void Activate_I2C1_IT(void)
{
LL_I2C_EnableIT_TX(I2C1);
LL_I2C_EnableIT_RX(I2C1);
LL_I2C_EnableIT_ADDR(I2C1);
LL_I2C_EnableIT_NACK(I2C1);
LL_I2C_EnableIT_ERR(I2C1);
LL_I2C_EnableIT_STOP(I2C1);
LL_I2C_Enable(I2C1);
}
void I2C1_SoftwareReset(void)
{
/* Disable peripheral */
LL_I2C_Disable(I2C1);
/* Perform a dummy read to delay the disable of peripheral for minimum
3 APB clock cycles to perform the software reset functionality */
*(__IO uint32_t *)(uint32_t)I2C1;
/* Enable peripheral */
LL_I2C_Enable(I2C1);
}
Pingback: Inter-Integrated Circuit(I2C) - AMS and STM32