STM32——I2C通信协议

I2C通信(Inter IC BUS)

        两根通信线:SCL(Serial Clock)、SDA(Serial Data)

        同步、半双工(一根数据线兼顾数据收发)

        带数据应答

        支持总线挂载多设备(一主多从、多主多从)

I2C硬件电路设计

 所有I2C设备的SCL连在一起,SDA连在一起

 设备的SCL,SDA都要配置成开漏输出(强下拉,和浮空。所以外部接一个上拉电阻)

(1)杜绝电源短路的现象(被同时强下拉和强上拉)。

(2)避免来回切换GPIO模式,统一设置成开漏。

(3)可以执行多主机模式下的时钟同步和总线仲裁        

SCL和SDA各加一个上拉电阻,4.7k左右

图1.SCL、SDA连线和配置(I2C硬件电路设计)

        异步时序对时间要求严格,依赖硬件电路

        同步时序对时间要求不严格,不依赖硬件电路(可以模拟时序)

I2C软件(时序)设计

1.起始和终止都是由主机完成的(一主多从)

图2.起始条件、终止条件

2.发送一个字节(主机放数据到SDA)

八位数据(一个字节)。串口是低位先行。

如果主机进中断,时序(SCL和SDA)会暂停,等待恢复。

图3.发送一个字节

3.接收一个字节(从机放数据到SDA)

        因为总线是线与,所以任何一个外设低电平都能拉低SDA,所以要释放SDA。

图4.接收一个字节(实线是主机控制的,虚线是从机控制的)
​​​

4.发送应答,接收应答(一位的数据,相当于发送一字节和接收一字节中取一位)

图5.发送应答,接收应答

I2C时序

1.指定地址写(字节)

图6的波形(MPU6050的I2C):S起始位;   一个字节(前七位是从机地址,第八位书读/写(r/w)0:写入。1:读);   应答位(RA); 一个字节(地址); 应答(RA); 一个字节(发送的数据);应答(RA);停止位(P)

图6.
​​​​

2.当前地址读(不能选地址读,用的不多)(一个字节)

图7

3.指定地址读(一个字节)

前半部分相当于指定地址写(指定地址),后半部分相当于当前地址读

S起始位; 七位地址和一个读/写位;   RA应答; 指定地址(写入从机的地址指针);  Sr(重复其实条件);  七位地址和一个读/写位; RA应答; 接收一个字节数据;  SA(非应答);P(停止)

主机给应答(RA)了,从机就会接着发。主机非应答(SA),从机就不发,就可以P(没有非应答的话,可能P会被拉下去,不会被停止)

图8

软件I2C代码(GPIO模拟时序)

SCL高电平的时候,SDA能动的只有开始(S)和结束(P)  

数据传输的时候,SCL高电平,SDA不能动

     1.   起始(Start)

void MyI2C_Start(void)
{
	MyI2C_W_SDA(1);
	MyI2C_W_SCL(1); // 1:释放    0:拉低
	MyI2C_W_SDA(0);
	MyI2C_W_SCL(0);
}

2.停止(Stop)

void MyI2C_Stop(void)
{
	MyI2C_W_SDA(0);
	MyI2C_W_SCL(1);
	MyI2C_W_SDA(1);	
}

3.发送一个字节

void MyI2C_SendByte(uint8_t Byte)
{
	for(uint8_t i = 0; i<8; ++i)
	{
		MyI2C_W_SDA(Byte & (0x80 >> i));
		MyI2C_W_SCL(1);
		MyI2C_W_SCL(0);	
	}
}

4.接收一个字节

uint8_t MyI2C_ReceiveByte(void)
{
	uint8_t Byte = 0x00;
	MyI2C_W_SDA(1);	
	for(uint8_t i = 0; i<8; i++)
	{
		MyI2C_W_SCL(1);
		if(MyI2C_R_SDA() == 1){Byte |= (0x80 >>i);}
		MyI2C_W_SCL(0);	
	}
	return Byte;
}

5.发送应答

void MyI2C_SendAck(uint8_t AckBit)
{
	MyI2C_W_SDA(AckBit);
	MyI2C_W_SCL(1);
	MyI2C_W_SCL(0);	
}

6.接收应答

uint8_t MyI2C_ReceiveAck(void)
{
	uint8_t AckBit ;
	MyI2C_W_SDA(1);	
	MyI2C_W_SCL(1);
	AckBit = MyI2C_R_SDA() ;
	MyI2C_W_SCL(0);	
	return AckBit;
}

硬件I2C代码(STM32外设,调库)

STM32 内部集成了硬件 I2C 收发电路,可以由硬件自动执行时钟生成、起始终止条件生成、应答位收发、数据收发等功能,减轻 CPU 的负担
支持多主机模型(多主机:固定多主机,可变多主机。stm32是可变多主机)
支持 7 /10 位地址模式
支持不同的通讯速度,标准速度 ( 高达 100 kHz) ,快速 ( 高达 400 kHz)
支持 DMA
兼容 SMBus 协议
STM32F103C8T6 硬件 I2C 资源:I2C1(SCL->PB6、SDA->PB7)、
                                                     I2C2(SCL->PB10、SDA->PB11)

I2C功能框图

发送

数据写入→数据寄存器(DATA REGISTER ,DR)→数据移位寄存器(上一个数据移位完成后)。置状态寄存器的TXE = 1(表示发送寄存器为空)

接收

SDA→移位寄存器(收齐一个字节)→数据寄存器(DR),置状态寄存器的RXNE (表示接收寄存器非空)→可以从DR读数据

简化流程图(一主多从)
(GPIO都要配置成复用开漏输出模式)

1.硬件I2C初始化

        硬件I2C的SDA,SCL是固定的引脚,而且都要设置为复用开漏模式(F_OD)

	RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C2, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_11;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);
	
	I2C_InitTypeDef I2C_InitStructure;
	I2C_InitStructure.I2C_Ack = I2C_Ack_Enable; //ACK使能,ACK标志位
	I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;//选择七位地址或十位地址
	I2C_InitStructure.I2C_ClockSpeed = 50000;  //时钟速度 <400k
	I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;//占空比,高速时比较明显
	I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;
	I2C_InitStructure.I2C_OwnAddress1 = 0x00;  //自己的地址,一主动从不用这个
	I2C_Init(I2C2, &I2C_InitStructure);
	I2C_Cmd(I2C2, ENABLE);

2.硬件I2C读写MPU6050寄存器

        

void MPU6050_W_Reg(uint8_t RegAddress, uint8_t Data )
{
	I2C_GenerateSTART(I2C2, ENABLE);//S
	MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT);
	
	I2C_Send7bitAddress(I2C2, MPU6050_ADDRESS, I2C_Direction_Transmitter);//八位数据(MPU6050设备地址 + 一位读写(R/W))
	MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED);
	
	I2C_SendData(I2C2, RegAddress);//八位数据(MPU6050内部寄存器地址)
	MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTING);
	
	I2C_SendData(I2C2, Data);//八位数据(要发送的数据Data)
	MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED) ;
	
	I2C_GenerateSTOP(I2C2, ENABLE);//停止(Stop)
}

/*	MPU6050读寄存器
*/
uint8_t MPU6050_R_Reg(uint8_t RegAddress)
{	
	uint8_t Data;
	
	I2C_GenerateSTART(I2C2, ENABLE);//S
	MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT) ;
	
	I2C_Send7bitAddress(I2C2, MPU6050_ADDRESS, I2C_Direction_Transmitter);//八位数据(MPU6050设备地址 + 一位读写(R/W))
	MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED);
	
	I2C_SendData(I2C2, RegAddress);//八位数据(MPU6050内部寄存器地址)
	MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED) ;
	
	I2C_GenerateSTART(I2C2, ENABLE);//S
	MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT) ;
	
	/*#define  I2C_Direction_Transmitter      ((uint8_t)0x00)
	  #define  I2C_Direction_Receiver         ((uint8_t)0x01)*/
	I2C_Send7bitAddress(I2C2, MPU6050_ADDRESS, I2C_Direction_Receiver);// 使用Receiver函数会自动把MPU6050_ADDRESS置1,相当一地址+1
	MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED) ;
	
	/*先置Ack和p,然后在判断状态寄存器*/
	I2C_AcknowledgeConfig(I2C2, DISABLE);
	I2C_GenerateSTOP(I2C2, ENABLE);
	
	MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_RECEIVED) ;
	Data = I2C_ReceiveData(I2C2); //数据寄存器
	
	I2C_AcknowledgeConfig(I2C2, ENABLE);
	
	return Data;
	
}

相关推荐

最近更新

  1. TCP协议是安全的吗?

    2024-04-01 21:54:01       18 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-04-01 21:54:01       19 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-04-01 21:54:01       18 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-04-01 21:54:01       20 阅读

热门阅读

  1. 医疗器械测试面试准备—质量部总监二面

    2024-04-01 21:54:01       30 阅读
  2. 蓝桥杯考前复习二

    2024-04-01 21:54:01       17 阅读
  3. 前端CSS样式(image)

    2024-04-01 21:54:01       18 阅读
  4. 2084: [蓝桥杯2023初赛] 整数删除

    2024-04-01 21:54:01       18 阅读
  5. Stable Diffusion 本地部署教程

    2024-04-01 21:54:01       18 阅读
  6. 学习记录之数学表达式(3)

    2024-04-01 21:54:01       14 阅读
  7. echarts初始化时只显示100px的问题

    2024-04-01 21:54:01       12 阅读
  8. Qt 总结

    2024-04-01 21:54:01       14 阅读