当前位置: 首页 > article >正文

STM32-I2C通信外设

目录

一:I2C外设简介 

二:I2C外设数据收发

三:I2C的复用端口

 四:主机发送和接收

五:硬件I2C读写MPU6050 

相关函数:

1.I2C_ GenerateSTART

2.I2C_ GenerateSTOP 

3.I2C_ AcknowledgeConfig 

4.I2C_ SendData 

5.I2C_ ReceiveData 

第一步:配置I2C外设,对I2C外设进行初始化

第二步:控制外设电路,实现指定地址写的时序

第三步:控制外设电路,实现指定地址读的时序

六:防止死循环卡机 


一:I2C外设简介 

STM32内部集成了硬件I2C收发电路,可以由硬件自动执行时钟生成、起始终止条件生成、应答位收发、数据收发等功能,减轻CPU的负担。也就是由硬件电路来自动翻转引脚电平,软件只需要写入控制寄存器CR和数据寄存器DR,还需要用SR读取状态。(高位先行)

二:I2C外设数据收发

1.当我们需要发送数据时,可以把一个数据写到数据寄存器DR,当移位寄存器没有数据移位时,这个数据寄存器(DATA REGISTER)的值就会进一步转到移位寄存器里,在移位的过程中,就可以把下一个数据放到数据寄存器(DATA REGISTER)里等着,一旦前一个数据完成,下一个数据就可以无缝衔接,继续发送。当数据由数据寄存器(DATA REGISTER)转到移位寄存器时,就会置状态寄存器的TXE位为1,表述发送寄存器为空。

2.接收数据时,输入的数据,一位一位地从引脚移动到移位寄存器里,当一个字节收齐之后,数据就整体从移位寄存器转到数据寄存器,同时置标志位RXNE,表示接受寄存器非空,这时就可以从数据由数据寄存器(DATA REGISTER)里把数据读出来。

三:I2C的复用端口

 四:主机发送和接收

1.主机发送:

首先,初始化之后,总线默认空闲状态STM32默认是从模式,为了产生一个起始条件,STM32需要写入控制寄存器,只要在控制寄存器START位,写1就可以产生起始条件——STM32由从模式变为主模式。

五:硬件I2C读写MPU6050 

相关函数:

1.I2C_ GenerateSTART

功能描述: 产生 I2Cx 传输 START 条件
参数1: I2Cx x 可以是 1 或者 2 ,来选择 I2C 外设
参数2:NewState: I2Cx START 条件的新状态,这个参数可以取:ENABLE 或者 DISABLE

2.I2C_ GenerateSTOP 

功能描述:产生 I2Cx 传输 STOP 条件

参数1:I2Cxx 可以是 1 或者 2,来选择 I2C 外设

参数2:NewState: I2Cx STOP 条件的新状态 ,这个参数可以取:ENABLE 或者 DISABLE

3.I2C_ AcknowledgeConfig 

功能描述:使能或者失能指定 I2C 的应答功能

参数1:I2Cxx 可以是 1 或者 2,来选择 I2C 外设

参数2:NewState: I2Cx 应答的新状态 ,这个参数可以取:ENABLE 或者 DISABLE

4.I2C_ SendData 

功能描述:通过外设 I2Cx 发送一个数据

参数1:I2Cxx 可以是 1 或者 2,来选择 I2C 外设

参数2:Data: 待发送的数据

5.I2C_ ReceiveData 

功能描述:返回通过 I2Cx 最近接收的数据

输入参数:I2Cxx 可以是 1 或者 2,来选择 I2C 外设

第一步:配置I2C外设,对I2C外设进行初始化

  • 开启I2C和GPIO时钟
  • 需要把PB10和PB11都初始化为复用开漏模式(原因:开漏是I2C协议设计要求,复用是GPIO的控制权要交给硬件外设,如果是软件I2C的话就是通用开漏模式)
  • 初始I2C2外设
  • 使能I2C255
/**
  * 函    数:MPU6050初始化
  * 参    数:无
  * 返 回 值:无
  */
void MPU6050_Init(void)
{
	/*开启时钟*/
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C2, ENABLE);		//开启I2C2的时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);		//开启GPIOB的时钟
	
	/*GPIO初始化*/
	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);					//将PB10和PB11引脚初始化为复用开漏输出
	
	/*I2C初始化*/
	I2C_InitTypeDef I2C_InitStructure;						//定义结构体变量
	I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;				//模式,选择为I2C模式
	I2C_InitStructure.I2C_ClockSpeed = 50000;				//时钟速度,选择为50KHz
	I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;		//时钟占空比,选择Tlow/Thigh = 2
	I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;				//应答,选择使能
	I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;	//应答地址,选择7位,从机模式下才有效
	I2C_InitStructure.I2C_OwnAddress1 = 0x00;				//自身地址,从机模式下才有效
	I2C_Init(I2C2, &I2C_InitStructure);						//将结构体变量交给I2C_Init,配置I2C2
	
	/*I2C使能*/
	I2C_Cmd(I2C2, ENABLE);									//使能I2C2,开始运行
	
	/*MPU6050寄存器初始化,需要对照MPU6050手册的寄存器描述配置,此处仅配置了部分重要的寄存器*/
	MPU6050_WriteReg(MPU6050_PWR_MGMT_1, 0x01);				//电源管理寄存器1,取消睡眠模式,选择时钟源为X轴陀螺仪
	MPU6050_WriteReg(MPU6050_PWR_MGMT_2, 0x00);				//电源管理寄存器2,保持默认值0,所有轴均不待机
	MPU6050_WriteReg(MPU6050_SMPLRT_DIV, 0x09);				//采样率分频寄存器,配置采样率
	MPU6050_WriteReg(MPU6050_CONFIG, 0x06);					//配置寄存器,配置DLPF
	MPU6050_WriteReg(MPU6050_GYRO_CONFIG, 0x18);			//陀螺仪配置寄存器,选择满量程为±2000°/s
	MPU6050_WriteReg(MPU6050_ACCEL_CONFIG, 0x18);			//加速度计配置寄存器,选择满量程为±16g
}

第二步:控制外设电路,实现指定地址写的时序

  • 生成起始条件(对于非阻塞式的程序,在函数结束之后,我们都要等待相应的标志位来确保这个函数的操作执行到位了,所以在程序中我们要等待EV5事件的到来),检测EV5事件:I2C_ CheckEvent——检查最近一次 I2C 事件是否是输入的事件
  • 起始条件发送之后,就要发送从机地址、接收应答  
  • 按这个流程写代码:
/**
  * 函    数:MPU6050写寄存器
  * 参    数:RegAddress 寄存器地址,范围:参考MPU6050手册的寄存器描述
  * 参    数:Data 要写入寄存器的数据,范围:0x00~0xFF
  * 返 回 值:无
  */
void MPU6050_WriteReg(uint8_t RegAddress, uint8_t Data)
{
	I2C_GenerateSTART(I2C2, ENABLE);										//硬件I2C生成起始条件
	MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT);					//等待EV5
	
	I2C_Send7bitAddress(I2C2, MPU6050_ADDRESS, I2C_Direction_Transmitter);	//硬件I2C发送从机地址,方向为发送
	MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED);	//等待EV6
	
	I2C_SendData(I2C2, RegAddress);											//硬件I2C发送寄存器地址
	MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTING);			//等待EV8
	
	I2C_SendData(I2C2, Data);												//硬件I2C发送数据
	MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED);				//等待EV8_2
	
	I2C_GenerateSTOP(I2C2, ENABLE);											//硬件I2C生成终止条件
}

第三步:控制外设电路,实现指定地址读的时序

/**
  * 函    数:MPU6050读寄存器
  * 参    数:RegAddress 寄存器地址,范围:参考MPU6050手册的寄存器描述
  * 返 回 值:读取寄存器的数据,范围:0x00~0xFF
  */
uint8_t MPU6050_ReadReg(uint8_t RegAddress)
{
	uint8_t Data;
	
	I2C_GenerateSTART(I2C2, ENABLE);										//硬件I2C生成起始条件
	MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT);					//等待EV5
	
	I2C_Send7bitAddress(I2C2, MPU6050_ADDRESS, I2C_Direction_Transmitter);	//硬件I2C发送从机地址,方向为发送
	MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED);	//等待EV6
	
	I2C_SendData(I2C2, RegAddress);											//硬件I2C发送寄存器地址
	MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED);				//等待EV8_2
	
	I2C_GenerateSTART(I2C2, ENABLE);										//硬件I2C生成重复起始条件
	MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT);					//等待EV5
	
	I2C_Send7bitAddress(I2C2, MPU6050_ADDRESS, I2C_Direction_Receiver);		//硬件I2C发送从机地址,方向为接收
	MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED);		//等待EV6
	
	I2C_AcknowledgeConfig(I2C2, DISABLE);									//在接收最后一个字节之前提前将应答失能
	I2C_GenerateSTOP(I2C2, ENABLE);											//在接收最后一个字节之前提前申请停止条件
	
	MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_RECEIVED);				//等待EV7
	Data = I2C_ReceiveData(I2C2);											//接收数据寄存器
	
	I2C_AcknowledgeConfig(I2C2, ENABLE);									//将应答恢复为使能,为了不影响后续可能产生的读取多字节操作
	
	return Data;
}

六:防止死循环卡机 

/**
  * 函    数:MPU6050等待事件
  * 参    数:同I2C_CheckEvent
  * 返 回 值:无
  */
void MPU6050_WaitEvent(I2C_TypeDef* I2Cx, uint32_t I2C_EVENT)
{
	uint32_t Timeout;
	Timeout = 10000;									//给定超时计数时间
	while (I2C_CheckEvent(I2Cx, I2C_EVENT) != SUCCESS)	//循环等待指定事件
	{
		Timeout --;										//等待时,计数值自减
		if (Timeout == 0)								//自减到0后,等待超时
		{
			/*超时的错误处理代码,可以添加到此处*/
			break;										//跳出等待,不等了
		}
	}
}


http://www.kler.cn/a/578817.html

相关文章:

  • 解决JDK 序列化导致的 Redis Key 非预期编码问题
  • npm install 报错ERESOLVE
  • Django工程获取请求参数的几种方式
  • Scala 中的访问修饰符
  • WebGPT: 基于浏览器辅助的问答系统,结合人类反馈优化答案质量
  • sysbench手动测试OceanBase v4.2.4集群
  • 推荐一个好用的在线文本对比网站 - diffchecker
  • 如何使用Webpack打包React项目?
  • 系统思考与问题诊断
  • Ubuntu 24.04安装搜狗出现闪烁,使用 Fcitx5 平替并添加自定义字典教程
  • 如何应对Maven中的依赖导入挑战?
  • 广州哪家公司做柔性装备?富唯智能以科技重构镀膜生产新范式
  • Linux查询内存使用率 并使用命令计算出来
  • MySQL:CRUD(增删查改)
  • 【C#】详解C#中的内存管理机制
  • 【C语言】--- 动态内存管理详解
  • Spring Boot与Axon Framework整合教程
  • Java EE 进阶:Spring IoCDI
  • RISC-V医疗芯片工程师复合型转型的路径与策略
  • 知识图谱相关的Terse RDF Triple Language 文件格式介绍