一、簡單說明
本例子參考了ST官方歷程,官方歷程的鏈接如下
http://www.st.com/content/st_com/zh/products/embedded-software/mcus-embedded-software/stm32-embedded-software/stm32-standard-peripheral-library-expansion/stsw-stm32094.html
關于i2c的協議這里就不做描述了
關于STM32 i2c的模式可以在中文數據手冊中查看
手冊中已經描述,該模塊默認工作在從模式,要想變為主模式,主要生產一個起始條件。(主模式的代碼可以參考野火開發板的硬件i2c歷程,本例子中也是使用野火開發板硬件i2c作為主機的)
二、i2c從機的配置
[cpp] view plain copy
I2C_DeInit(I2C1);
/* I2C1 configuration ------------------------------------------------------*/
I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;//模式
I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;
I2C_InitStructure.I2C_OwnAddress1 = I2C_SLAVE_ADDRESS7;//這個就是作為從機的地址,一定要配置正確
I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;
I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;//7位的地址
I2C_InitStructure.I2C_ClockSpeed = ClockSpeed;
I2C_Init(I2C1, &I2C_InitStructure);
上面配置注意的就是從機地址,這就是主機要查詢的從機地址
三、i2c從機中斷的配置
[cpp] view plain copy
/* 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);
/* Configure and enable I2C1 error interrupt -------------------------------*/
NVIC_InitStructure.NVIC_IRQChannel = I2C1_ER_IRQn;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;
NVIC_Init(&NVIC_InitStructure);
標準庫中的i2c一共有兩個中斷
一個是事件中斷(EV_IRQ)和一個錯誤中斷(ER_IRQ)
EV_IRQ的中斷只要響應EV1 EV2 EV4 之類的,后面會說明
ER_IRQ的中斷只要響應沒有應答和起始和停止條件出錯等
四、使能中斷
[cpp] view plain copy
/* Enable I2C1 event and buffer interrupts */
I2C_ITConfig(I2C1, I2C_IT_EVT | I2C_IT_BUF, ENABLE);
/* Enable I2C1 Error interrupts */
I2C_ITConfig(I2C1, I2C_IT_ERR, ENABLE);
使能了I2C1的這三個中斷,每個中斷的作用下面說明
五、中斷處理函數
[cpp] view plain copy
void I2C1_EV_IRQHandler(void)
//事件中斷處理函數
{
switch (I2C_GetLastEvent(I2C1))
//獲取i2c1的中斷事件
{
/* Slave Transmitter ---------------------------------------------------*/
case I2C_EVENT_SLAVE_BYTE_TRANSMITTED:
/* 這個和下面那個都是從發送模式下發送數據的,具體兩個的區別我也不是很明白,感覺就是移位寄存器空與非 空的區別,準備好數據發送吧 */
I2C_SendData(I2C1, I2C1_Buffer_Tx[Tx_Idx++]);
break;
case I2C_EVENT_SLAVE_BYTE_TRANSMITTING: /* EV3 */
/* Transmit I2C1 data */
I2C_SendData(I2C1, I2C1_Buffer_Tx[Tx_Idx++]);
break;
/* Slave Receiver ------------------------------------------------------*/
case I2C_EVENT_SLAVE_RECEIVER_ADDRESS_MATCHED: /* EV1 */
/* 地址匹配中斷,不管從發送和接收都要匹配地址,如下圖244、243發送地址之后都會響應EV1 */
break;
case I2C_EVENT_SLAVE_BYTE_RECEIVED: /* EV2 */
/* Store I2C1 received data */
/* 這個中斷就是響應EV2中斷,如下圖244,每次主機發送完一個數據就會產生一個EV2的中斷 */
I2C1_Buffer_Rx[Rx_Idx++] = I2C_ReceiveData(I2C1);
/* 把接收到的中斷填充到數組中 */
/* 注意:地址不會填充進來的 */
break;
case I2C_EVENT_SLAVE_STOP_DETECTED: /* EV4 */
/* Clear I2C1 STOPF flag */
/* 這個就是正常停止的時候產生的一個停止信號 */
I2C_Cmd(I2C1, ENABLE);
/* 我也不清楚這個為什么要這樣,如果接收完一串數據之后,不響應主機的情況可以 關閉i2c,然后在處理完數據后再 從新配置i2c,記得是從新配置 */
Rx_Idx=0;
i2c_event = EVENT_OPCOD_NOTYET_READ;
break;
default:
break;
}
}
void I2C1_ER_IRQHandler(void)
{
/* Check on I2C1 AF flag and clear it */
if (I2C_GetITStatus(I2C1, I2C_IT_AF))
{
/* 這個就是圖243中最后那個沒有應答的中斷,也就是發送了一串數據后的中斷,可以做清零工作 */
I2C_ClearITPendingBit(I2C1, I2C_IT_AF);
Tx_Idx = 0;
i2c_event = EVENT_OPCOD_NOTYET_READ;
}
/* Check on I2C1 AF flag and clear it */
if (I2C_GetITStatus(I2C1, I2C_IT_BERR)) //這個就是起始和停止條件出錯了
{
I2C_ClearITPendingBit(I2C1, I2C_IT_BERR);
}
}
注意:i2c中斷中不要printf打印信息,
否則會出錯
主要是參考了官方的歷程,然后自己再調整一下邏輯即可使用。
最后的效果感覺還是挺穩定的,這篇文章主要是給大家拋磚引玉的作用,也記錄一下自己調試幾天的成果。
ST官網的歷程還是挺多的,沒遇到的知識可以去官網找找。
上一篇:stm32 hal i2c 庫讀寫sd3088時鐘
下一篇:關于STM32的硬件IIC使用問題解決方案
推薦閱讀
史海拾趣