公司项目需要用到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);
}
}