公司項目需要用到I2C從機與上位機通信。在網上搜了搜,發現大部分都是說STM32硬體有問題的,與硬體I2C從機相關的資料很少。調通之後,想着把demo分享出來,豐富一下網上的例程。STM32F1系列稍微修改下也能适用。
#include "i2c.h"
//PB6 I2C1_SCL
//PB7 I2C1_SDA
void MyI2C_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
I2C_InitTypeDef I2C_InitStructure;
NVIC_InitTypeDef NVIC_InitStructue;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);//使能GPIOA時鐘
RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1,ENABLE);//使能I2C1時鐘
//I2C_DeInit(I2C1);
I2C_InitStructure.I2C_Mode=I2C_Mode_I2C;
I2C_InitStructure.I2C_DutyCycle=I2C_DutyCycle_2;
I2C_InitStructure.I2C_OwnAddress1=0x30;//I2C_SLAVE_ADDRESS7為從機位址
I2C_InitStructure.I2C_Ack=I2C_Ack_Enable;
I2C_InitStructure.I2C_AcknowledgedAddress=I2C_AcknowledgedAddress_7bit;//指定位址的長度
I2C_InitStructure.I2C_ClockSpeed=ClockSpeed;//設定SCL時鐘頻率,
I2C_Init(I2C1,&I2C_InitStructure);
NVIC_InitStructue.NVIC_IRQChannel=I2C1_EV_IRQn;
NVIC_InitStructue.NVIC_IRQChannelPreemptionPriority =0;
NVIC_InitStructue.NVIC_IRQChannelSubPriority=2;
NVIC_InitStructue.NVIC_IRQChannelCmd=ENABLE;
NVIC_Init(&NVIC_InitStructue);
/*配置錯誤中斷,ER_IRQ的中斷隻要響應沒有應答和起始和停止條件出錯等*/
NVIC_InitStructue.NVIC_IRQChannel=I2C1_ER_IRQn;
NVIC_InitStructue.NVIC_IRQChannelSubPriority=3;
NVIC_Init(&NVIC_InitStructue);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//模拟輸入
GPIO_InitStructure.GPIO_OType=GPIO_OType_OD;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL ;//上拉
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化
GPIO_PinAFConfig(GPIOB,GPIO_PinSource6,GPIO_AF_I2C1);//PB6複用為I2C1
GPIO_PinAFConfig(GPIOB,GPIO_PinSource7,GPIO_AF_I2C1);//PB7複用為I2C1
/*使能I2C1 event and buffer interrupt*/
I2C_ITConfig (I2C1,I2C_IT_EVT|I2C_IT_BUF,ENABLE);
/*使能I2C1 Error interrupts*/
I2C_ITConfig(I2C1,I2C_IT_ERR,ENABLE);
I2C_Cmd(I2C1,ENABLE);
}
void I2C1_EV_IRQHandler(void) //事件中斷處理函數
{
__IO uint16_t SR1Register =0;
__IO uint16_t SR2Register =0;
SR1Register = I2C1->SR1;
SR2Register = I2C1->SR2;
switch (I2C_GetLastEvent(I2C1))//擷取i2c1的中斷事件
{
/* 從發送 Slave Transmitter ---------------------------------------------------*/
case I2C_EVENT_SLAVE_BYTE_TRANSMITTED: //i2c_event_slave_byte_transmitted//
/* 移位寄存器為空,資料寄存器為空,在DR中寫入Data1.
這個和下面那個都是從發送模式下發送資料的,具體兩個的差別我也不是很明白,感覺就是移位寄存器空與非 空的差別,準備好資料發送吧
***** */
I2C_SendData(I2C1, 0X88);
break;
case I2C_EVENT_SLAVE_BYTE_TRANSMITTING: /* EV3 */ //i2c_event_slave_byte_transmitting
/*
移位寄存器非空,資料寄存器為空,通過對DR執行寫操作來清零。
Transmit I2C1 data
*******/
if(I2C1_Data_Adress_Mark==1)
{
//
I2C_SendData(I2C1, I2C1_Data_Page1[I2C1_Data_Adress++]);
}else
{
I2C_SendData(I2C1, 28);
}
break;
/* 從接收 Slave Receiver ------------------------------------------------------*/
case I2C_EVENT_SLAVE_RECEIVER_ADDRESS_MATCHED: /* EV1:從接收,收到位址後響應EV1中斷 */
/* 位址比對中斷,不管從發送和接收都要比對位址,如下圖244、243發送位址之後都會響應EV1 */
break;
case I2C_EVENT_SLAVE_BYTE_RECEIVED: /* EV2 從接收,位址後開始收到的資料*/
/* Store I2C1 received data */
/* 這個中斷就是響應EV2中斷,如下圖244,每次主機發送完一個資料就會産生一個EV2的中斷 */
if(I2C1_Data_Adress_Mark==0)
{
I2C1_Data_Adress=I2C_ReceiveData(I2C1);
I2C1_Data_Adress_Mark=1;//收到資料位址,資料位址标志位置1;
}else if(I2C1_Data_Adress_Mark==1)
{
I2C1_Data_Page1[I2C1_Data_Adress++]=I2C_ReceiveData(I2C1);
}
/* 把接收到的中斷填充到數組中 */
/* 注意:位址不會填充進來的 */
break;
case I2C_EVENT_SLAVE_STOP_DETECTED: /* EV4 收到STOP停止信号*/
/* Clear I2C1 STOPF flag */
I2C1_Data_Adress_Mark=0;
/* 這個就是正常停止的時候産生的一個停止信号 */
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))
{
/* 沒有應答的中斷,發送了一串資料後的中斷,可以做清零工作 */
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);
}
}