STM32 I2C Slave(SMBUS)模式軟體參考設計
阿新 • • 發佈:2019-01-10
I2C大家都很熟悉,總共2根線,CLK和DATA,stm32的I2C相信大家就更熟悉了,採用寫控制器的方式,直接由控制器去完成I2C時序操作,使用者無需關心具體產生的邏輯。然而,大部分情況下,使用的都是I2C Master模式,即主裝置模式,很少當成slave模式即從裝置模式來用,這篇文章講的是如何把stm32 I2C當成slave模式來使用,更嚴格來說,本篇講的是smbus模式。
從官網stm32手冊上我們發現了smbus和I2C區別,大家自行理解:
真正使用時,可以把smbus等同於I2C來設定和使用,從程式碼上看,除了I2C Clock設定為20K之外,其它暫無明顯區別。以下為I2C smbus模式的設定:
void IIC_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; I2C_InitTypeDef I2C_InitStructure; // RCC_APB2PeriphClockCmd (RCC_APB2Periph_GPIOB , ENABLE); /* Configure I2C1 pins: SCL and SDA */ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7; //6:SCL 7:SDA GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;//Out_PP; GPIO_Init(GPIOB, &GPIO_InitStructure); // RCC_APB1PeriphClockCmd (RCC_APB1Periph_I2C1, ENABLE); // Initialize I2C1 clock // Reset I2C1 device clock in order to avoid non-cleared error flags //__I2C_RCC_RESET(CPAL_I2C_CLK [Device]); RCC_APB1PeriphResetCmd(RCC_APB1Periph_I2C1,ENABLE); RCC_APB1PeriphResetCmd(RCC_APB1Periph_I2C1,DISABLE); // Enable I2C1 device clock RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1,ENABLE); I2C_DeInit(I2C1); I2C_InitStructure.I2C_Mode = I2C_Mode_SMBusDevice; I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2; I2C_InitStructure.I2C_OwnAddress1 = 0x76;//slave addr I2C_InitStructure.I2C_Ack = I2C_Ack_Enable; I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit; I2C_InitStructure.I2C_ClockSpeed = 20000;//20K I2C_Init(I2C1, &I2C_InitStructure); I2C_ITConfig(I2C1, I2C_IT_EVT|I2C_IT_BUF, ENABLE); I2C_ITConfig(I2C1, I2C_IT_ERR, ENABLE); I2C_Cmd(I2C1,ENABLE); }
void NVIC_Configuration(void) { NVIC_InitTypeDef NVIC_InitStructure; /* Configure and enable I2Cx event interrupt -------------------------------*/ NVIC_InitStructure.NVIC_IRQChannel = I2C1_EV_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); }
從以上程式碼可以看出,I2C初始化的是I2C的IO對映、工作模式I2C_Mode_SMBusDevice、slave地址0x76(可以自定義)、I2C Clock、I2C中斷向量等引數。設定完成之後,每當I2C有資料產生時,就會產生IRQ中斷,同時進入中斷處理函式I2C1_EV_IRQn,如下所示:
void I2C1_EV_IRQHandler(void)
{
uint32_t I2CFlagStatus;
static uint8_t IIC_Data=0;
I2CFlagStatus = I2C_GetLastEvent(I2C1); // => (SR2<<16|SR1)
// print_info("I2CFlagStatus=0x%x \r\n",I2CFlagStatus);
IIC1_EV_INT_Flag=1;
IIC_Get_Data_Flag=1;
if ((I2CFlagStatus & I2C_SR1_ADDR) != 0)//bit1:addr matched
{
//print_info("4\r\n");
if(I2CFlagStatus & I2C_SR1_TXE) //bit7 Data register empty (transmitters)
{//read
Rx_Idx=0;
Tx_Idx = 0;
I2C_SendData(I2C1, I2C_Buffer_Tx[Tx_Idx]);
}
else
{
}
}
else if((I2CFlagStatus & I2C_SR1_RXNE) != 0)//bit6 RxNE -Data register not empty (receivers))
{
IIC_Data=I2C_ReceiveData(I2C1);
I2C_Buffer_Rx[Frame_Idx][Rx_Idx] = IIC_Data;
Rx_Idx++;
}
else if((I2CFlagStatus & I2C_SR1_STOPF) != 0)//bit4 STOPF -Stop detection (slave mode)
{
//I2C_Buffer_Rx[0] = num-1;
I2C1->CR1 |= 0x1000;//CR1_PEC_Set;
Rx_Idx_t=Rx_Idx;
Rx_Idx=0;
Frame_Idx++;
I2C1->SR1=0;
I2C1->SR2=0;
}
else
{
}
}
中斷處理函式中,產生中斷後,把標誌位IIC_Get_Data_Flag置1,根據讀到的I2CFlagStatus狀態位,來獲取當前I2C的狀態,獲取資料,然後在main函式中根據該標誌位來處理I2C產生的資料,處理完成後,再把IIC_Get_Data_Flag標誌位置0,這是一種很好的處理資料方法。(PS:中斷處理函式不能處理太複雜及費時的事務,以免影響中斷響應的實時性,所以把處理資料的過程放到main函式中)
需要STM32中文參考手冊的留下郵箱。