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

【stm32】标准库学习——I2C

目录

一、I2C

1.I2C简介

2.MPU6050参数 

3.I2C时序基本单元

二、I2C外设

1.I2C外设简介

2.配置I2C基本结构

3.初始化函数模板

4.常用函数 


一、I2C

1.I2C简介


本节课使用的是MPU6050硬件外设

2.MPU6050参数 


3.I2C时序基本单元

这里发送应答是指主机发送,即STM32作为主机,MPU6050或其他I2C通信外设作为从机,我们接收到从机的数据后要给从机发送应答位以供从机判断我们是不是还需要数据。

接收应答则是判断从机有没有接收到数据,并且我们要将SDA的控制权转交给从机,即释放SDA(拉高)。

结合以下两种时序理解上面6种基本时序单元

 在起始条件后紧跟着的一个字节就是七位从机地址和一位读写位,0代表写,1代表读。

不同的芯片厂商对第二位字节数据的作用要求不同,MPU6050规定第二个字节数据是指定寄存器地址

在对寄存器进行读写时,地址指针会自动加1,比如这里地址一开始指向0x19,写入数据后指向0x1A,所以想要对特定地址进行读操作,就要先写入寄存器地址,再重复起始条件发送从机地址和读操作,这时候没有指定寄存器的操作,从机会自动将数据发送给你。 

以上就是I2C通信的时序,可以通过GPIO口软件模拟I2C通信,本节主要学习STM32的硬件I2C外设


二、I2C外设

1.I2C外设简介


2.配置I2C基本结构


由于硬件I2C不同于软件模拟,我们不知道一个字节的数据是否发送完毕等等,因此STM32设置了许多代表不同事件的标志位


3.初始化函数模板

void MPU6050_Init(void)
{	RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C2,ENABLE);//I2C1和2都是APB1的设备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);//初始化I2CI2C_InitTypeDef I2C_InitStructure;I2C_InitStructure.I2C_Mode=I2C_Mode_I2C;//选择I2C模式I2C_InitStructure.I2C_ClockSpeed=50000;//时钟速度 可配置SCL时钟频率I2C_InitStructure.I2C_DutyCycle=I2C_DutyCycle_2;//配置占空比I2C_InitStructure.I2C_Ack=I2C_Ack_Enable;//应答位配置I2C_InitStructure.I2C_AcknowledgedAddress=I2C_AcknowledgedAddress_7bit;//作为从机时能响应多少位的地址I2C_InitStructure.I2C_OwnAddress1=0x00;//STM32作为从机时的地址,长度和上面参数对应I2C_Init(I2C2,&I2C_InitStructure);I2C_Cmd(I2C2,ENABLE);
}

这里GPIO口模式选择复用开漏输出,虽然是输出,但仍然可以输入,应为这里默认高电平是一种弱上拉,从机要发送数据只需要进行拉低电平或释放这一种操作,“拉”或“不拉”,主机读取电平就能接收从机发送的数据。PB10和PB11是I2C2的引脚。


初始化I2C后,封装指定地址写和指定地址读两个函数

//指定地址写
void MPU6050_WriteReg(uint8_t RegAddress,uint8_t Data)//参数为寄存器地址和要写入的数据
{
//	MyI2C_Start();
//	MyI2C_SendByte(0xD0);从机地址
//	MyI2C_ReceiveAck();
//	MyI2C_SendByte(RegAddress);
//	MyI2C_ReceiveAck();
//	MyI2C_SendByte(Data);
//	MyI2C_ReceiveAck();
//	MyI2C_Stop(); I2C_GenerateSTART(I2C2,ENABLE);//起始条件//判断EV5事件while(I2C_CheckEvent(I2C2,I2C_EVENT_MASTER_MODE_SELECT)!=SUCCESS);I2C_Send7bitAddress(I2C2,0xD0,I2C_Direction_Transmitter);//发送从机地址,选择“写”//判断EV6事件,这里的EV6选择“发送事件已选择”while(I2C_CheckEvent(I2C2,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)!=SUCCESS);I2C_SendData(I2C2,RegAddress);//发送寄存器地址//判断EV8事件,“字节正在发送”while(I2C_CheckEvent(I2C2,I2C_EVENT_MASTER_BYTE_TRANSMITTING)!=SUCCESS);I2C_SendData(I2C2,Data);//发送数据//判断EV8_2事件,即移位寄存器空,且没有新数据要发while(I2C_CheckEvent(I2C2,I2C_EVENT_MASTER_BYTE_TRANSMITTED)!=SUCCESS);I2C_GenerateSTOP(I2C2,ENABLE);//终止条件
}

这里判断各个标志位用I2C_CheckEvent ()函数,完成各个标志位会返回SUCCESS,这里判断等于SUCCESS跳出循环,以免数据没有发送完全等。硬件I2C和软件I2C不同的是:软件I2C因为是手动模拟,发送字节数据都是完整的,是通过发送和接收应答位来判断双方交流是否正常;而硬件I2C则是判断标志位,并不需要管应答位,只需要使能ACK应答位即可。

//指定地址读
uint8_t MPU6050_ReadReg(uint8_t RegAddress)//参数为寄存器地址
{uint8_t Data;//	MyI2C_Start();
//	MyI2C_SendByte(0xD0);从机地址
//	MyI2C_ReceiveAck();
//	MyI2C_SendByte(RegAddress);
//	MyI2C_ReceiveAck();
//	
//	MyI2C_Start();
//	MyI2C_SendByte(0xD0|0x01);
//	MyI2C_ReceiveAck();
//	Data=MyI2C_ReceiveByte();
//	MyI2C_SendAck(1);
//	MyI2C_Stop();//这里和指定地址写的前三步一样,即指定寄存器地址I2C_GenerateSTART(I2C2,ENABLE);//起始条件while(I2C_CheckEvent(I2C2,I2C_EVENT_MASTER_MODE_SELECT)!=SUCCESS);I2C_Send7bitAddress(I2C2,0xD0,I2C_Direction_Transmitter);//发送从机地址,选择“写”while(I2C_CheckEvent(I2C2,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)!=SUCCESS);I2C_SendData(I2C2,RegAddress);//发送寄存器地址while(I2C_CheckEvent(I2C2,I2C_EVENT_MASTER_BYTE_TRANSMITTING)!=SUCCESS);I2C_GenerateSTART(I2C2,ENABLE);//重复起始条件//判断EV5while(I2C_CheckEvent(I2C2,I2C_EVENT_MASTER_MODE_SELECT)!=SUCCESS);I2C_Send7bitAddress(I2C2,0xD0,I2C_Direction_Receiver);//发送从机地址,选择“读”//等待EV6事件,选择“接收事件已选择”while(I2C_CheckEvent(I2C2,I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED)!=SUCCESS);//在接收最后一个字节时,要提前将ACK置0,并且产生终止条件I2C_AcknowledgeConfig(I2C2,DISABLE);//ACK=0,不给应答I2C_GenerateSTOP(I2C2,ENABLE);//STOP=1,申请产生终止条件//判断EV7事件,意味着数据移到DR寄存器,可以读走了while(I2C_CheckEvent(I2C2,I2C_EVENT_MASTER_BYTE_RECEIVED)!=SUCCESS);Data=I2C_ReceiveData(I2C2);//读取数据//最后将应答位置1I2C_AcknowledgeConfig(I2C2,ENABLE);return Data;
}

 指定地址读的前三步和指定地址写一样,起始条件后发送从机地址,写寄存器地址,然后重复起始条件,发送从机地址选择“读”,这里判断EV6事件就不同于指定地址写。

因为我们写和读都是只有一个字节的数据,所以指定地址读后就是接收最后一个字节,如果接收多个字节则是判断EV7事件,这里接收一个字节也是最后一个字节,要提前将ACK置0(失能),同时申请终止条件。最后终止条件产生后判断EV7事件,读走DR寄存器的数据。最后将ACK置1(使能)(我的理解是因为要在主循环里不断调用读的操作,在初始化函数里通过结构体参数I2C_Ack使能ACK,意味着正常读写ACK都是处于使能的状态,而函数I2C_AcknowledgeConfig可以单独配置ACK的状态,为了后续正常运行,所以最后还要将ACK使能)

附:在主发送和主接收的序列图中,发送从机地址后,我们会判断EV6事件,可以看到后面还接了一个EV8_1事件,这个事件是提醒我们该写入数据发送出去了,因此我们不需要判断EV8_1事件

参数列表里也没有EV8_1事件。


4.常用函数 

I2C_GenerateSTART,生成起始条件

I2C_GenerateSTOP,生成终止条件

l2C_SendData,发送数据

I2C_ReceiveData,接收数据

I2C_AcknowledgeConfig,使能或失能ACK,给ENABLE就是就是给从机应答

http://www.lqws.cn/news/449515.html

相关文章:

  • 指标解读——113页企业信息化成熟度评估指标【附全文阅读】
  • 算法第38天|322.零钱兑换\139. 单词拆分
  • C语言:二分搜索函数
  • 【Java学习笔记】线程基础
  • 【RTSP从零实践】2、使用RTP协议封装并传输H264
  • JetBrains IDE v2025.1 升级,AI 智能+语言支持齐飞
  • Maven 之工程化开发核心指南:插件配置、pom 文件与依赖管理
  • GreatSQL加入openKylin社区,共推开源生态建设
  • keep-alive缓存文章列表案例完整代码(Vue3)
  • Symbol.iterator 详解
  • 《被讨厌的勇气》详细概述
  • 【大数据】java API 进行集群间distCP 报错unresolvedAddressException
  • vue3打包后,图片丢失
  • 【unity游戏开发——热更新】什么是Unity热更新
  • 【精选】基于SpringBoot的宠物互助服务小程序平台开发 微信小程序宠物互助系统 宠物互助小程序平台设计与实现 支持救助发布+领养申请+交流互动功能
  • 无人机不再“盲飞”!用Python搞定实时目标识别与跟踪
  • 【Linux驱动开发 ---- 4.2_平台设备(Platform Devices)概述】
  • 1.容器技术与docker环境部署
  • phpstudy无法启动mysql,一启动就关闭,完美解决
  • PLuTo 编译器示例9-12
  • 从生活场景学透 JavaScript 原型与原型链
  • 力扣-62.不同路径
  • 【MySQL篇08】:undo log日志和MVCC实现事务隔离
  • 【小程序】如何生成特定页面的小程序码
  • apifox接收流式响应无法自动合并json内容
  • 组态王工程运行时间显示
  • 数据库从零开始:MySQL 中的 DDL 库操作详解【Linux版】
  • 大话软工笔记—业务功能分类
  • 青少年编程与数学 01-012 通用应用软件简介 03 图像处理软件
  • VR 看房:突破成长痛点,展望未来趋势