I2C的接口模式可以选择4种
在本次实验中需要用到两块STM32单片机,其中一块的I2C1做主机,另一块的I2C2做从机,分别进行I2C1写I2C2实验、I2C1读I2C2实验,两次实验I2C1均为主机。
在I2C1写I2C2实验中,先检测总线占用情况,确定在总线空闲的情况下,由主机发送起始信号然后发送从机地址,选择写命令,从机接收到地址与自身匹配后回应ACK信号,主机接收到ACK信号后开始发送数据,从机收到数据后回应ACK,当主机发送到最后一个字节并且收到ACK后,主机发出停止信号结束本次通信并且释放总线,从机收到停止信号后也退出与主机的通信。
注意:在从机I2C初始化的时候要设置从机地址,在主机发送从机地址的时候,一般最后一位是读写位,0写1读,假设从机地址为0x30,则发送0x30|0x00为写,0x30|0x01为读。
/**
* @brief 主机I2C1写数据
* @param None
* @retval None
*/
static void I2C1_Write_Byte(uint32_t ReadAddr)
{
uint32_t i;
uint32_t tbuf[10] = {0X01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a};
while(I2C1->SR2&((u16)0x1<<1)); //等待总线空闲
I2C1->CR1|=(u16)0x1<<8; //置位START,产生起始条件
/* SB = 1: EV5 */
while(!(I2C1->SR1&(u16)0x1<<0)); //等待SB置位,当发送出起始条件时该位被置1
I2C1->SR1; //读SR1
I2C1->DR = ReadAddr; //发送写指令,写DR寄存器清除SB事件
/* ADDR = 1: EV6 */
while(!(I2C1->SR1&(u16)0x1<<1)); //等待从设备应答
/* TxE = 1: EV8-1 */
while(!(I2C1->SR1&(u16)0x1<<7)); //等待TxE为1,数据寄存器空
for(i=0;i<SIZE;i++)
{
/* TxE = 1: EV8 */
I2C1->DR = tbuf[i];
/* TxE = 1, BTF = 1: EV8-2 */
while(!(I2C1->SR1&(u16)0x1<<2));//BTF为1,字节发送结束
}
I2C1->CR1|=(u16)0x1<<9; //产生停止条件
while(!(I2C1->CR1&(u16)0x1<<9));
}
/**
* @brief 从机I2C2 读数据
* @param None
* @retval None
*/
static void I2C2_Read_Byte(void)
{
uint32_t i;
uint32_t rbuf[SIZE];
while(!(I2C2->SR2&(u16)0x1<<1)); //1:BUSY忙
while(I2C2->SR2&(u16)0x1); //0:从模式
/* ADDR = 1: EV1 */
while(!(I2C2->SR1&(u16)0x1<<1)); //接收到地址
I2C2->SR1;
I2C2->SR2; //清除ADDR
for(i=0; i<10; i++)
{
/* RxNE = 1: EV2 */
while(!(I2C2->SR1&(u16)0x1<<6));//接收到数据
rbuf[i] = I2C2->DR;
}
/* STOPF = 1: EV4 */
while(!(I2C2->SR1&(u16)0x1<<4)); //等待STOPF为1
}
在I2C1读I2C2实验中,先检测总线占用情况,确定在总线空闲的情况下,由主机发送起始信号然后发送从机地址,选择读命令,从机接收到地址与自身匹配后回应ACK信号,接着由从机开始发送数据,主机接收到数据后回应ACK信号,当主机完成所有数据接收后发送一个NACK信号,从机收到NACK后停止发送数据,主机发送完NACK后再发出停止信号,释放总线结束本次通信。
/**
* @brief 主机I2C1读数据
* @param ReadAddr :开始读数的地址
* @retval None
*/
static void I2C1_Read_Byte(uint32_t ReadAddr)
{
uint32_t i;
uint32_t rbuf[SIZE];
while(I2C1->SR2&((u16)0x1<<1)); //等待总线空闲
I2C1->CR1|=(u16)0x1<<8; //置位START,产生起始条件
/* SB = 1: EV5 */
while(!(I2C1->SR1&(u16)0x1<<0)); //等待SB置位,当发送出起始条件时该位被置1
I2C1->SR1; //读SR1
I2C1->DR = ReadAddr|0x01; //发送读指令,写DR寄存器清除SB事件
/* ADDR = 1: EV6 */
while(!(I2C1->SR1&(u16)0x1<<1)); //等待从设备应答
I2C1->SR1;
I2C1->SR2; //清除ADDR
for(i=0;i<SIZE;i++)
{
/* RxNE = 1: EV7 */
while(!(I2C1->SR1&(u16)0x1<<6));//等待RXNE=1,接收到数据
rbuf[i] = I2C1->DR;
}
/* STOP = 1: EV7-1 */
I2C1->CR1&=~((u16)0x1<<10); //ACK=0
I2C1->CR1|=(u16)0x1<<9; //产生停止条件
}
/**
* @brief 从机I2C2 写数据
* @param None
* @retval None
*/
static void I2C2_Write_Byte(void)
{
uint32_t i;
uint32_t tbuf[10] = {0X01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a};
while(!(I2C2->SR2&(u16)0x1<<1)); //1:BUSY忙
while(I2C2->SR2&(u16)0x1); //0:从模式
/* ADDR = 1: EV1 */
while(!(I2C2->SR1&(u16)0x1<<1)); //接收到地址
I2C2->SR1;
I2C2->SR2;
/* TxE = 1: EV3-1 */
while(!(I2C2->SR1&(u16)0x1<<7)); //1:TXE 数据寄存器空
for(i=0; i<10; i++)
{
/* TxE = 1: EV3 */
I2C2->DR = tbuf[i];
while(!(I2C2->SR2&(u16)0x1<<2));//1:TRA 数据已发送
while(!(I2C2->SR1&(u16)0x1<<2));//1:BTF 字节发送结束
}
/* AF = 1: EV3-2 */
while(!(I2C2->SR1&(u16)0x1<<10)); //1:AF 应答失败,发送结束
}