【江协STM32】10-4/5 I2C通信外设、硬件I2C读写MPU6050

1. I2C外设简介

  • STM32内部集成了硬件I2C收发电路,可以由硬件自动执行时钟生成、起始终止条件生成、应答位收发、数据收发等功能,减轻CPU的负担
  • 支持多主机模型
  • 支持7位/10位地址模式
  • 支持不同的通讯速度,标准速度(高达100 kHz),快速(高达400 kHz)
  • 支持DMA
  • 兼容SMBus协议
  • STM32F103C8T6 硬件I2C资源:I2C1、I2C2
I2C框图
I2C基本结构
I2C主机发送操作流程
I2C主机接收操作流程

硬件I2C的波形会更加规整,每个时钟的周期、占空比都非常一致。软件I2C由于操作引脚之后加了延时,这个延时有时加的多、有时加的少,所以软件时序的时钟周期、占空比可能不规整,不过由于I2C是同步时序,这些不规整也没有影响。

SCL低电平写,高电平读,虽然整个电平的任意时刻都可以读写,但一般要求保证尽早的原则,所以可以直接认为是SCL下降沿写,上升沿读。软件I2C在下降沿之后,因为操作端口之后有一些延时,所以等了一会才进行写入操作。但在硬件I2C中,数据写入都紧贴下降沿的(SCL下降沿,SDA立马切换数据),读的时刻也是紧贴上升沿进行的。还是由于I2C是同步时序,这些不标准的波形也不影响通信。

软件I2C时序波形
硬件I2C时序波形

2. 硬件I2C读写MPU6050

2.1 接线图

I2C1_SCL和I2C1_SDA引脚分别为PB6和PB7,其重映射为PB8和PB9,但由于这几个引脚都在板子右下角,被OLED占用,不方便接线,所以使用I2C2。I2C2_SCL和I2C2_SDA引脚分别为PB10和PB11。

2.2 代码

主函数和程序现象与软件I2C相同,区别在于通信的底层(MyI2C.c),所以本工程不需要MyI2C模块。

基于软件I2C代码修改,主要任务为:第一步,配置I2C外设,对I2C2外设进行初始化,替换MyI2C_Init();第二步,控制外设电路,实现指定地址写的时序,替换MPU6050_WriteReg();第三步,控制外设电路,实现指定地址读的时序,替换MPU6050_ReadReg()。

配置I2C外设:

  1. 开启I2C外设和对应GPIO口的时钟
  2. 把I2C外设对应的GPIO口初始化为复用开漏模式
  3. 使用结构体,对整个I2C进行配置
  4. 使能I2C

MPU6050.c

#include "stm32f10x.h"                  // Device header
#include "MPU6050_Reg.h"#define MPU6050_ADDRESS 0xD0//  封装I2C_CheckEvent和超时退出循环
void MPU6050_WaitEvent(I2C_TypeDef* I2Cx, uint32_t I2C_EVENT)
{uint32_t Timeout;Timeout = 10000;while(I2C_CheckEvent(I2Cx, I2C_EVENT) != SUCCESS)//   监测EVx事件是否发生{ // 循环超时退出机制,防止意外卡死Timeout--;if(Timeout == 0){break;//    跳出循环}}
}// 指定地址写
void MPU6050_WriteReg(uint8_t RegAddress, uint8_t Data)
{
//    MyI2C_Start();
//    MyI2C_SendByte(MPU6050_ADDRESS);// 发送从机地址+读写位
//    MyI2C_ReceiveAck();//   接收应答
//    MyI2C_SendByte(RegAddress);//   指定寄存器地址
//    MyI2C_ReceiveAck();
//    MyI2C_SendByte(Data);//   发送写入指定寄存器地址下的数据
//    MyI2C_ReceiveAck();
//    MyI2C_Stop();I2C_GenerateSTART(I2C2, ENABLE);//  生成起始条件MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT);//   监测EV5事件是否发生I2C_Send7bitAddress(I2C2, MPU6050_ADDRESS, I2C_Direction_Transmitter);//   发送从机地址,第3个参数是读写位//  库函数中发送数据都自带了接收应答的过程。同样,接收数据也自带了发送应答的过程。//  如果应答错误,硬件会通过标志位和中断提示。所以发送地址后应答位就不需要处理了MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED);// 等待EV6事件I2C_SendData(I2C2, RegAddress);//   发送寄存器地址MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTING);//   检查EV8事件I2C_SendData(I2C2, Data);// 发送数据MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED);//   发送完最后一个字节,等待EV8_2事件I2C_GenerateSTOP(I2C2, ENABLE);//   终止时序
}//  指定地址读
uint8_t MPU6050_ReadReg(uint8_t RegAddress)
{uint8_t Data;//    MyI2C_Start();
//    MyI2C_SendByte(MPU6050_ADDRESS);// 发送从机地址+读写位
//    MyI2C_ReceiveAck();//   接收应答
//    MyI2C_SendByte(RegAddress);//   指定寄存器地址
//    MyI2C_ReceiveAck();
//    
//    MyI2C_Start();//    重复起始条件
//    MyI2C_SendByte(MPU6050_ADDRESS | 0xD1);//   读写位1,读出数据
//    MyI2C_ReceiveAck();
//    Data = MyI2C_ReceiveByte();//   从机发送数据,主机接收数据
//    MyI2C_SendAck(1);//  这里只想要1个字节,所以不给从机应答
//    MyI2C_Stop();I2C_GenerateSTART(I2C2, ENABLE);//  生成起始条件MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT);//   监测EV5事件是否发生I2C_Send7bitAddress(I2C2, MPU6050_ADDRESS, I2C_Direction_Transmitter);//   发送从机地址,第3个参数是读写位MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED);// 等待EV6事件I2C_SendData(I2C2, RegAddress);//   发送寄存器地址MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED);//   检查EV8事件I2C_GenerateSTART(I2C2, ENABLE);//  重复起始条件MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT);// 等待EV5事件I2C_Send7bitAddress(I2C2, MPU6050_ADDRESS, I2C_Direction_Receiver);//   发送从机地址,第3个参数是读写位MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED);// 等待EV6事件//  这里需要在接收完成前提前设置Ack非应答和终止时序I2C_AcknowledgeConfig(I2C2, DISABLE);// 配置ACK位,临时给非应答I2C_GenerateSTOP(I2C2, ENABLE);//   终止时序MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_RECEIVED);//   等待EV7事件产生后,一个字节的数据就已经在DR内了Data = I2C_ReceiveData(I2C2);//    读取DRI2C_AcknowledgeConfig(I2C2, ENABLE);// 配置ACK位,给应答,为了方便指定地址收多个字节//  虽然现在程序中只有收一个字节,但写一下方便之后改进return Data;
}void MPU6050_Init(void)
{
//    MyI2C_Init();RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C2, ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;//	复用开漏模式。开漏是I2C协议的设计要求,复用是GPIO的控制权要交给硬件外设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;// 应答位I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;// 指定STM32作为从机可以响应几位的地址,STM32作为从机模式才会用到I2C_InitStructure.I2C_ClockSpeed = 50000;//  时钟频率I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;//   时钟占空比。此参数只有在时钟频率>100kHz(进入快速状态)时才生效,在<=100kHz(标准速度)下为固定的1:1I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;I2C_InitStructure.I2C_OwnAddress1 = 0x00;// 自身地址1。STM32作为从机使用,用于指定STM32的自身地址。如果I2C_AcknowledgedAddress选择了响应7位地址,这里就要指定一个自身的7位地址I2C_Init(I2C2, &I2C_InitStructure);I2C_Cmd(I2C2, ENABLE);MPU6050_WriteReg(MPU6050_PWR_MGMT_1, 0x01);//   配置PWR_MGMT_1寄存器,解除睡眠,选择X轴陀螺仪时钟MPU6050_WriteReg(MPU6050_PWR_MGMT_2, 0x00);MPU6050_WriteReg(MPU6050_SMPLRT_DIV, 0x09);//   采样率分频,决定了数据输出的快慢。10分频MPU6050_WriteReg(MPU6050_CONFIG, 0x06);//   数字低通滤波器给110MPU6050_WriteReg(MPU6050_GYRO_CONFIG, 0x18);//  陀螺仪配置,选择最大量程11MPU6050_WriteReg(MPU6050_ACCEL_CONFIG, 0x18);// 加速度计配置,选择最大量程11}uint8_t MPU6050_GetID(void)
{return MPU6050_ReadReg(MPU6050_WHO_AM_I);
}//  由于需要返回6个变量,使用指针的地址传递
void MPU6050_GetData(int16_t *AccX, int16_t *AccY, int16_t *AccZ,int16_t *GyroX, int16_t *GyroY, int16_t *GyroZ)
{uint8_t Data_H, Data_L;Data_H = MPU6050_ReadReg(MPU6050_ACCEL_XOUT_H);//    加速度寄存器X轴高8位Data_L = MPU6050_ReadReg(MPU6050_ACCEL_XOUT_L);*AccX = (Data_H << 8) | Data_L;//   加速度计X轴的16位数据Data_H = MPU6050_ReadReg(MPU6050_ACCEL_YOUT_H);Data_L = MPU6050_ReadReg(MPU6050_ACCEL_YOUT_L);*AccY = (Data_H << 8) | Data_L;Data_H = MPU6050_ReadReg(MPU6050_ACCEL_ZOUT_H);Data_L = MPU6050_ReadReg(MPU6050_ACCEL_ZOUT_L);*AccZ = (Data_H << 8) | Data_L;Data_H = MPU6050_ReadReg(MPU6050_GYRO_XOUT_H);//    陀螺仪寄存器X轴高8位Data_L = MPU6050_ReadReg(MPU6050_GYRO_XOUT_L);*GyroX = (Data_H << 8) | Data_L;//   陀螺仪X轴的16位数据Data_H = MPU6050_ReadReg(MPU6050_GYRO_YOUT_H);Data_L = MPU6050_ReadReg(MPU6050_GYRO_YOUT_L);*GyroY = (Data_H << 8) | Data_L;Data_H = MPU6050_ReadReg(MPU6050_GYRO_ZOUT_H);Data_L = MPU6050_ReadReg(MPU6050_GYRO_ZOUT_L);*GyroZ = (Data_H << 8) | Data_L;
}

代码其他部分见(【江协STM32】10-2/3 MPU6050简介、软件I2C读写MPU6050,第2.2节)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/66899.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Web开发中页面出现乱码的解决(Java Web学习笔记:需在编译时用 -encoding utf-8)

目录 1 引言2 乱码表现、原因分析及解决2.1 乱码表现2.2 原因分析2.3 解决 3 总结 1 引言 Web开发的页面出现了乱码&#xff0c;一直不愿写出来&#xff0c;因为网上的解决方案太多了。但本文的所说的页面乱码问题&#xff0c;则是与网上的大多数解决方案不一样&#xff0c;使…

分类模型为什么使用交叉熵作为损失函数

推导过程 让推理更有体感&#xff0c;进行下面假设&#xff1a; 假设要对猫、狗进行图片识别分类假设模型输出 y y y&#xff0c;是一个几率&#xff0c;表示是猫的概率 训练资料如下&#xff1a; x n x^n xn类别 y ^ n \widehat{y}^n y ​n x 1 x^1 x1猫1 x 2 x^2 x2猫1 x …

【AUTOSAR 基础软件】软件组件的建立与使用(“代理”SWC)

基础软件往往需要建立一些“代理”SWC来完成一些驱动的抽象工作&#xff08;Complex_Device_Driver_Sw或者Ecu_Abstraction_Sw等&#xff09;&#xff0c;或建立Application Sw Component来补齐基础软件需要提供的功能实现。当面对具体的项目时&#xff0c;基础软件开发人员还可…

【Linux】sed编辑器二

一、处理多行命令 sed编辑器有3种可用于处理多行文本的特殊命令。 N&#xff1a;加入数据流中的下一行&#xff0c;创建一个多行组进行处理&#xff1b;D&#xff1a;删除多行组中的一行&#xff1b;P&#xff1a;打印多行组中的一行。 1、next命令&#xff1a;N 单行next命…

HTML5 网站模板

HTML5 网站模板 参考 HTML5 Website Templates

数据链路层-STP

生成树协议STP&#xff08;Spanning Tree Protocol&#xff09; 它的实现目标是&#xff1a;在包含有物理环路的网络中&#xff0c;构建出一个能够连通全网各节点的树型无环逻辑拓扑。 选举根交换机&#xff1a; 选举根端口&#xff1a; 选举指定端口&#xff1a; 端口名字&…

前端学习-事件流,事件捕获,事件冒泡以及阻止冒泡以及相应案例(二十八)

目录 前言 事件流与两个阶段说明 说明 事件捕获 目标 说明 事件冒泡 目标 事件冒泡概念 简单理解 阻止冒泡 目标 语法 注意 综合示例代码 总结 前言 梳洗罢&#xff0c;独倚望江楼。过尽千帆皆不是&#xff0c;斜晖脉脉水悠悠。肠断白蘋洲 事件流与两个阶段说明…

Cognitive architecture 又是个什么东东?

自Langchain&#xff1a; https://blog.langchain.dev/what-is-a-cognitive-architecture/ https://en.wikipedia.org/wiki/Cognitive_architecture 定义 A cognitive architecture refers to both a theory about the structure of the human mind and to a computational…

CVE-2025-22777 (CVSS 9.8):WordPress | GiveWP 插件的严重漏洞

漏洞描述 GiveWP 插件中发现了一个严重漏洞&#xff0c;该插件是 WordPress 最广泛使用的在线捐赠和筹款工具之一。该漏洞的编号为 CVE-2025-22777&#xff0c;CVSS 评分为 9.8&#xff0c;表明其严重性。 GiveWP 插件拥有超过 100,000 个活跃安装&#xff0c;为全球无数捐赠平…

【Linux】网络层

目录 IP协议 协议头格式 网段划分 2中网段划分的方式 为什么要进行网段划分 特殊的IP地址 IP地址的数量限制 私有IP地址和公有IP地址 路由 IP协议 在通信时&#xff0c;主机B要把数据要给主机C&#xff0c;一定要经过一条路径选择&#xff0c;为什么经过路由器G后&…

HarmonyOS:@LocalBuilder装饰器: 维持组件父子关系

一、前言 当开发者使用Builder做引用数据传递时&#xff0c;会考虑组件的父子关系&#xff0c;使用了bind(this)之后&#xff0c;组件的父子关系和状态管理的父子关系并不一致。为了解决组件的父子关系和状态管理的父子关系保持一致的问题&#xff0c;引入LocalBuilder装饰器。…

Elasticsearch—索引库操作(增删查改)

Elasticsearch中Index就相当于MySQL中的数据库表 Mapping映射就类似表的结构。 因此我们想要向Elasticsearch中存储数据,必须先创建Index和Mapping 1. Mapping映射属性 Mapping是对索引库中文档的约束&#xff0c;常见的Mapping属性包括&#xff1a; type&#xff1a;字段数据类…

MySQL进阶突击系列(05)突击MVCC核心原理 | 左右护法ReadView视图和undoLog版本链强强联合

2024小结&#xff1a;在写作分享上&#xff0c;这里特别感谢CSDN社区提供平台&#xff0c;支持大家持续学习分享交流&#xff0c;共同进步。社区诚意满满的干货&#xff0c;让大家收获满满。 对我而言&#xff0c;珍惜每一篇投稿分享&#xff0c;每一篇内容字数大概6000字左右&…

金融项目实战 02|接口测试分析、设计以及实现

目录 ⼀、接口相关理论 二、接口测试 1、待测接口&#xff1a;投资业务 2、接口测试流程 3、设计用例理论 1️⃣设计方法 2️⃣工具 4、测试点提取 5、测试用例&#xff08;只涉及了必测的&#xff09; 1️⃣注册图⽚验证码、注册短信验证码 2️⃣注册 3️⃣登录 …

指令的修饰符

指令的修饰符 参考文献&#xff1a; Vue的快速上手 Vue指令上 Vue指令下 Vue指令的综合案例 文章目录 指令的修饰符指令修饰符 结语 博客主页: He guolin-CSDN博客 关注我一起学习&#xff0c;一起进步&#xff0c;一起探索编程的无限可能吧&#xff01;让我们一起努力&…

[DO374] Ansible 配置文件

[DO374] Ansible 配置文件 1. 配置文件位置2. 配置文件3. Ansible 配置4. Ansible的Ad-hoc5. Ansible 模块6. playbook段落7. 任务执行后续8. Ansible 变量8.1 ansible 变量的定义8.1.1 主机变量8.1.2 主机组变量 8.2 vars的循环 9. Ansible Collection10. Ansible-galaxy 安装…

STM32如何测量运行的时钟频率

前言 环境&#xff1a; 芯片&#xff1a;STM32F103C8T6 Keil&#xff1a;V5.24.2.0 一、简介STM32F103C8T6的时钟源 ①HSI 内部高速时钟,RC振荡器&#xff0c;频率为8MHz&#xff0c;精度不高。②HSE 外部高速时钟,可接石英/陶瓷谐振器&#xff0c;频率范围为4MHz~16MHz&…

【Web安全】SQL 注入攻击技巧详解:UNION 注入(UNION SQL Injection)

【Web安全】SQL 注入攻击技巧详解&#xff1a;UNION 注入&#xff08;UNION SQL Injection&#xff09; 引言 UNION注入是一种利用SQL的UNION操作符进行注入攻击的技术。攻击者通过合并两个或多个SELECT语句的结果集&#xff0c;可以获取数据库中未授权的数据。这种注入技术要…

什么是卷积网络中的平移不变性?平移shft在数据增强中的意义

今天来介绍一下数据增强中的平移shft操作和卷积网络中的平移不变性。 1、什么是平移 Shift 平移是指在数据增强&#xff08;data augmentation&#xff09;过程中&#xff0c;通过对输入图像或目标进行位置偏移&#xff08;平移&#xff09;&#xff0c;让目标在图像中呈现出…

C#中通道(Channels)的应用之(生产者-消费者模式)

一.生产者-消费者模式概述 生产者-消费者模式是一种经典的设计模式&#xff0c;它将数据的生成&#xff08;生产者&#xff09;和处理&#xff08;消费者&#xff09;分离到不同的模块或线程中。这种模式的核心在于一个共享的缓冲区&#xff0c;生产者将数据放入缓冲区&#x…