一、简单说明
本例子参考了ST官方历程,官方历程的链接如下
关于i2c的协议这里就不做描述了
关于STM32 i2c的模式可以在中文数据手册中查看
手册中已经描述,该模块默认工作在从模式,要想变为主模式,主要生产一个起始条件。(主模式的代码可以参考野火开发板的硬件i2c历程,本例子中也是使用野火开发板硬件i2c作为主机的)
二、i2c从机的配置
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从机中断的配置
/* 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的中断只要响应没有应答和起始和停止条件出错等
四、使能中断
/* 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);
五、中断处理函数
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官网的历程还是挺多的,没遇到的知识可以去官网找找。