stm32入门-----软件I2C读写MPU6050

 目录

前言

MPU6050 

1.简介

2.相关参数

3.硬件电路

 4.MPU6050框图

C编程实现I2C读写MPU6050步骤

1.MyI2C.c文件

(1)引脚的宏定义

(2)对SCL和SDA的操作以及初始化

(3)起始信号标志

(4)终止信号标志

(5)发送一个字节

(6)接收一个字节

(7)发送应答

 (8)接收应答

2.MPU6050.c文件

MPU6050_reg.h

(1)指定地址写

(2)指定地址读

(3)获取转换后的数据

(4)初始化

项目实践


前言

        上一期我发布了关于I2C通讯的原理(链接:stm32入门-----I2C通讯-CSDN博客),本期我们就来学习通过软件编程来去实现stm32与MPU6050之间的I2C读写操作(视频:[10-3] 软件I2C读写MPU6050_哔哩哔哩_bilibili)

本期项目工程代码我已上传至百度网盘,可自行下载

链接: https://pan.baidu.com/s/1-HVrt5ROsxLqMWCnUgxXCw?pwd=0721

提取码: 0721 

MPU6050 

(MPU6050介绍视频:[10-2] MPU6050简介_哔哩哔哩_bilibili)

1.简介

  • MPU6050是一个6轴姿态传感器,可以测量芯片自身XYZ轴的加速度、角速度参数,通过数据融合,可进一步得到姿态角,常应用于平衡车、飞行器等需要检测自身姿态的场景
  • 3轴加速度计(Accelerometer):测量XYZ轴的加速度
  • 3轴陀螺仪传感器(Gyroscope):测量XYZ轴的角速度

         下面第一张图是MPU6050元器件的芯片,上面讲到MPU6050是一种6轴姿态传感器,是可以去测量芯片自身X、Y、Z轴的加速度、角速度参数,那么我们对于加速度可以想象成第二张图这种形式,也就是中间放一个滑块,如果前后上下左右这六个面都接上一个弹簧电压传感器,然后去通过这个滑块的姿势不同的受力情况来去感应出电压的大小进而求出受力大小,其实这个就是我们高中学过的,然后通过F=ma,测出加速度a的值就行了。同样的对于角速度我们可以想象成下面第三张图的情况,就是中间有一个平衡环,当感应到旋转的时候也会感应出不同的电压进而得到角速度的值。但是实际上MPU6050中间并没有图二和图三这种东西,这只不过是一种用来理解的模型,其内部是通过自身电路去实现这些模型的。

2.相关参数

  • 16ADC采集传感器的模拟信号,量化范围:-32768~32767
  • 加速度计满量程选择:±2±4±8±16g
  • 陀螺仪满量程选择: ±250±500±1000±2000°/sec
  • 可配置的数字低通滤波器
  • 可配置的时钟源
  • 可配置的采样分频

  • I2C从机地址:1101000AD0=0
  •   1101001AD0=1

3.硬件电路

引脚

功能

VCCGND

电源

SCLSDA

I2C通信引脚

XCLXDA

主机I2C通信引脚

AD0

从机地址最低位

INT

中断信号输出

 4.MPU6050框图

下面从这个框图就可以看出,MPU6050这个芯片去测量加速度和角速度都是去通过ADC模数转换器来去实现的。

C编程实现I2C读写MPU6050步骤

如果相关原理不熟悉的可以去看看上一期的内容:stm32入门-----I2C通讯-CSDN博客

工程主要文件如下,其中MyI2C.c 和MyI2C.h 文件是用来编写I2C接口读取功能的,MPU6050的文件是用来封装前面写好的I2C模块来去读写MPU6050寄存器的操作。

下面我就对这些模块进行一一讲解。

1.MyI2C.c文件

对于这个文件我们需要实现6个功能,实际上就是我们上一期讲到的I2C通讯的六大功能,分别是开始、停止、发送、接受、发送应答、接收应答。

(1)引脚的宏定义

这里我们可以去根据接线图来去宏定义SDA与SCL的引脚,通过宏定义可以去提高代码的修改复用效率,到时候如果想修改为其他引脚,我们只需要去改这个宏定义对应的引脚就行了。下面我是定义到pin6 和pin7的引脚,跟接线图的是不一样的,注意一下。

#include "stm32f10x.h"                  // Device header
#include "Delay.h"#define SDA_PORT         GPIOA
#define SCL_PORT         GPIOA
#define SCL_PIN         GPIO_Pin_6
#define SDA_PIN         GPIO_Pin_7
(2)对SCL和SDA的操作以及初始化

下面我就去通过函数的形式去对GPIO口读写SCL和SDA的操作进行封装,到时候直接去拿出来使用就行了,就不需要用GPIO口的函数读写。

//对SCL和SDA进行操作
void I2C_W_SCL(uint8_t val) { //写入SCLGPIO_WriteBit(SCL_PORT, SCL_PIN, (BitAction)val);Delay_us(10);
}
void I2C_W_SDA(uint8_t val) { //写SDAGPIO_WriteBit(SDA_PORT, SDA_PIN, (BitAction)val);Delay_us(10);
}
uint8_t I2C_R_SDA() { //读取SDAuint8_t value;  value = GPIO_ReadInputDataBit(SDA_PORT, SDA_PIN);Delay_us(10);return value;
}//初始化
void I2C_init() {//配置SCL和SDA RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);GPIO_InitTypeDef GPIO_initstruct; GPIO_initstruct.GPIO_Mode=GPIO_Mode_Out_OD; //开漏输出GPIO_initstruct.GPIO_Pin=SCL_PIN | SDA_PIN;GPIO_initstruct.GPIO_Speed=GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_initstruct);GPIO_SetBits(GPIOA, SCL_PIN | SDA_PIN);}
(3)起始信号标志
起始条件: SCL 高电平期间, SDA 从高电平切换到低电平

// 1.开始
void I2C_Start() {//这里需要先SDA再放手SCL,因为避免SDA在SCL高电平的时候变化,这是结束的意思I2C_W_SDA(1);I2C_W_SCL(1);//先拉低SDAI2C_W_SDA(0);I2C_W_SCL(0);
}
(4)终止信号标志
        终止条件:SCL高电平期间,SDA从低电平切换到高电平

//2.终止
void I2C_Stop() {I2C_W_SDA(0);I2C_W_SCL(1);I2C_W_SDA(1);
}
(5)发送一个字节
发送一个字节: SCL 低电平期间,主机将数据位依次放到 SDA 线上(高位先行),然后释放 SCL ,从机将在 SCL 高电平期间读取数据位,所以 SCL 高电平期间 SDA 不允许有数据变化,依次循环上述过程 8 次,即可发送一个字节

// 发送一个字节
void I2C_sendbyte(uint8_t byte) {uint8_t i;for (i = 0;i < 8;i++) {//在SCL低电平期间给SDA放入这个字节的一位,高位先放I2C_W_SDA(byte & (0x80 >> i));//按位与1000 0000//下面这里走一个SCL时钟,从机就会把上面放入的数据读取走I2C_W_SCL(1);I2C_W_SCL(0);}
}
(6)接收一个字节
接收一个字节: SCL 低电平期间,从机将数据位依次放到 SDA 线上(高位先行),然后释放 SCL ,主机将在 SCL 高电平期间读取数据位,所以 SCL 高电平期间 SDA 不允许有数据变化,依次循环上述过程 8 次,即可接收一个字节(主机在接收之前,需要释放 SDA

//接受一个字节
uint8_t I2C_recivebyte() {uint8_t result = 0x00;//存储读取到的结果uint8_t i;I2C_W_SDA(1);//主机释放SDA,这时候从机就会拿到SDA放入数据for (i = 0;i < 8;i++) {I2C_W_SCL(1);//SCL为高电平,下面主机就可以开始读取操作了if (I2C_R_SDA() == 1) {//如果读取到最高位为1result |= (0x80 >> i);//此时获取到的最高位置1}I2C_W_SCL(0);//读取结束}return result;
}
(7)发送应答
发送应答:主机在接收完一个字节之后,在下一个时钟发送一位数据,数据 0 表示应答,数据1表示非应答

//发送应答
void I2C_sendack(uint8_t ackbit) {I2C_W_SDA(ackbit);I2C_W_SCL(1);I2C_W_SCL(0);}
 (8)接收应答
接收应答:主机在发送完一个字节之后,在下一个时钟接收一位数据,判断从机是否应答,数据 0 表示应答,数据 1 表示非应答(主机在接收之前,需要释放 SDA

//接收应答
uint8_t I2C_reciveack() {uint8_t ackbit;I2C_W_SDA(1);//主机释放SDA,这时候从机就会拿到SDA放入数据I2C_W_SCL(1);//SCL为高电平,下面主机就可以开始读取操作了ackbit = I2C_R_SDA(); //读取到了0就表示接受到了应答OKI2C_W_SCL(0);//读取结束return ackbit;
}

2.MPU6050.c文件

MPU6050_reg.h

这里还有一个MPU6050_reg.h的文件,这个文件是用来定义好MPU6050芯片相关寄存器地址的头文件。如下所示:

#ifndef __MPU6050_REG_h
#define __MPU6050_REG_h//这里存放的是MPU6050的寄存器地址
#define	MPU6050_SMPLRT_DIV		0x19
#define	MPU6050_CONFIG			0x1A
#define	MPU6050_GYRO_CONFIG		0x1B
#define	MPU6050_ACCEL_CONFIG	0x1C#define	MPU6050_ACCEL_XOUT_H	0x3B
#define	MPU6050_ACCEL_XOUT_L	0x3C
#define	MPU6050_ACCEL_YOUT_H	0x3D
#define	MPU6050_ACCEL_YOUT_L	0x3E
#define	MPU6050_ACCEL_ZOUT_H	0x3F
#define	MPU6050_ACCEL_ZOUT_L	0x40
#define	MPU6050_TEMP_OUT_H		0x41
#define	MPU6050_TEMP_OUT_L		0x42
#define	MPU6050_GYRO_XOUT_H		0x43
#define	MPU6050_GYRO_XOUT_L		0x44
#define	MPU6050_GYRO_YOUT_H		0x45
#define	MPU6050_GYRO_YOUT_L		0x46
#define	MPU6050_GYRO_ZOUT_H		0x47
#define	MPU6050_GYRO_ZOUT_L		0x48#define	MPU6050_PWR_MGMT_1		0x6B
#define	MPU6050_PWR_MGMT_2		0x6C
#define	MPU6050_WHO_AM_I		0x75#endif // !__MPU6050_REG_h
(1)指定地址写
指定地址写
对于指定设备( Slave Address ),在指定地址( Reg Address )下,写入指定数据( Data

这里我们就对应着上面这个时序来去编程。 

//指定地址写
void MPU6050_writereg(uint8_t regaddress, uint8_t data) {I2C_Start();I2C_sendbyte(MPU6050_ADDRESS); I2C_reciveack(); //返回值这边就不做处理了,这里就直接去进行数据的收发I2C_sendbyte(regaddress);I2C_reciveack();I2C_sendbyte(data);I2C_reciveack();I2C_Stop();
}
(2)指定地址读
指定地址读
对于指定设备( Slave Address ),在指定地址( Reg Address )下,读取从机数据( Data

//指定地址读
uint8_t MPU6050_readreg(uint8_t regaddress) {uint8_t result;//当前指针指向这个地址I2C_Start();I2C_sendbyte(MPU6050_ADDRESS); I2C_reciveack();I2C_sendbyte(regaddress);I2C_reciveack();//转入读的时序I2C_Start();I2C_sendbyte(MPU6050_ADDRESS | 0x01); //MPU6050_ADDRESS默认是写,这里要或上0x01表示读取操作I2C_reciveack();result = I2C_recivebyte(); //获取到接收的数据I2C_sendack(1); //读取完成之后要给从机回复,如果回复0那么就表示从机继续发送数据,如果回复1就表示读取结束,主机拿回SDA控制权I2C_Stop();return result;
}
(3)获取转换后的数据

这里是通过传入数据的地址来去存储数据,这种操作学过C语音的都很常见的了。

//获取MPU6050转换出加速度和角速度的值
void MPU6050_getdata(int16_t* accX, int16_t* accY, int16_t* accZ,int16_t* gyroX, int16_t* gyroY, int16_t* gyroZ)
{uint16_t dataH, dataL;//加速度值数据dataH = MPU6050_readreg(MPU6050_ACCEL_XOUT_H);dataL = MPU6050_readreg(MPU6050_ACCEL_XOUT_L);*accX = (dataH << 8) | dataL;dataH = MPU6050_readreg(MPU6050_ACCEL_YOUT_H);dataL = MPU6050_readreg(MPU6050_ACCEL_YOUT_L);*accY = (dataH << 8) | dataL;dataH = MPU6050_readreg(MPU6050_ACCEL_ZOUT_H);dataL = MPU6050_readreg(MPU6050_ACCEL_ZOUT_L);*accZ = (dataH << 8) | dataL;//陀螺仪数据dataH = MPU6050_readreg(MPU6050_GYRO_XOUT_H);dataL = MPU6050_readreg(MPU6050_GYRO_XOUT_L);*gyroX = (dataH << 8) | dataL;dataH = MPU6050_readreg(MPU6050_GYRO_YOUT_H);dataL = MPU6050_readreg(MPU6050_GYRO_YOUT_L);*gyroY = (dataH << 8) | dataL;dataH = MPU6050_readreg(MPU6050_GYRO_ZOUT_H);dataL = MPU6050_readreg(MPU6050_GYRO_ZOUT_L);*gyroZ = (dataH << 8) | dataL;
}
(4)初始化
//初始化
void MPU6050_init() {I2C_init();MPU6050_writereg(MPU6050_PWR_MGMT_1, 0x01); //电源管理器1MPU6050_writereg(MPU6050_PWR_MGMT_2, 0x00); //电源管理器2MPU6050_writereg(MPU6050_SMPLRT_DIV, 0x09); //分频器MPU6050_writereg(MPU6050_CONFIG, 0x06); //配置寄存器MPU6050_writereg(MPU6050_GYRO_CONFIG, 0x18); //陀螺仪配置寄存器MPU6050_writereg(MPU6050_ACCEL_CONFIG, 0x18);//加速度计配置寄存器
}//获取当前的ID号,也就是MPU6050的I2C地址
uint8_t MPU6050_getID() {return MPU6050_readreg(MPU6050_WHO_AM_I);
}

项目实践

上面讲解了相关模块的功能,那么这里我们就可以去写一个主函数来去进行对MPU6050读取操作了。

 接线图:

main.c文件: 

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "MPU6050.h"
uint8_t ID;
int16_t aX, aY, aZ, gX, gY, gZ;
int main(void)
{	OLED_Init();MPU6050_init();OLED_ShowString(1, 1, "ID:");ID = MPU6050_getID();OLED_ShowHexNum(1, 4, ID, 2);while (1) {MPU6050_getdata(&aX, &aY, &aZ, &gX, &gY, &gZ);OLED_ShowSignedNum(2, 1, aX, 5);OLED_ShowSignedNum(3, 1, aY, 5);OLED_ShowSignedNum(4, 1, aZ, 5);OLED_ShowSignedNum(2, 8, gX, 5);OLED_ShowSignedNum(3, 8, gY, 5);OLED_ShowSignedNum(4, 8, gZ, 5);}
}

实验效果:

 

以上就是本期的全部内容了,我们下次见!

今日壁纸:

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

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

相关文章

数据结构(面试)

目录 线索二叉树哈夫曼树并查集最小生成树最短路径拓扑排序二叉排序树平衡二叉树红黑树折半查找散列表堆排序归并排序 线索二叉树 原理&#xff1a;利用树节点的n1个左右空指针指向其遍历序列的前驱和后继&#xff08;线索&#xff09; 优点&#xff1a;简化遍历&#xff0c;不…

7.2 单变量(多->多),attention/informer

继续上文书写&#xff1a; 1 GRU Attention 收敛速度稳定的很多&#xff0c;你看这些模型是不是很容易搭&#xff0c;像积木一样&#xff1b; def create_model(input_shape, output_length,lr1e-3, warehouse"None"):input Input(shapeinput_shape)conv1 Conv…

【C++标准模版库】模拟实现vector+迭代器失效问题

模拟实现vector 一.vector成员变量二.构造函数1.无参&#xff08;默认&#xff09;构造2.有参构造3.拷贝构造1.传统写法2.现代写法 三.vector对象的容量操作1.size2.capacity3.clear4.empty5.reserve6.resize 四.vector对象的访问及遍历操作1.operator[]2.实现迭代器&#xff1…

免费【2024】springboot 大学生志愿者管理系统的设计与实现

博主介绍&#xff1a;✌CSDN新星计划导师、Java领域优质创作者、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和学生毕业项目实战,高校老师/讲师/同行前辈交流✌ 技术范围&#xff1a;SpringBoot、Vue、SSM、HTML、Jsp、PHP、Nodejs、Python、爬虫、数据可视化…

windows下设置java环境变量

1.打开window的环境变量设置 右键开始菜单选择系统 选择高级系统设置&#xff1a; 点击环境变量 2.在系统变量 新增 JAVA_HOME&#xff1b;该变量的值 选择jdk所在的目录即可。 JAVA_HOME: D:\Program Files\Java\jdk1.8.0_131 3. 在系统变量新增 classpath; 该变量的值设置…

GoLang 安装

golang学习笔记 goland 安装 To use Go programming language in Visual Studio Code (VSCode), you can follow these steps: 1. Install Go: Download and install the latest version of Go from the official Go website (https://golang.org/dl/). 2. Install VSCode:…

llama-3.1下载部署

llama-3.1 下载部署 下载 huggingface 详情页填写申请后等待审核 点击 头像->setting->access token 创建token 配置环境变量 下载模型 pip install -U huggingface_hubhuggingface-cli download --resume-download meta-llama/Meta-Llama-3.1-8B-Instruct --loca…

IDEA的疑难杂症

注意idea版本是否与maven版本兼容 2019idea与maven3.6以上不兼容 IDEA无法启动 打开idea下载安装的目录:如:Idea\IntelliJ IDEA 2024.1\bin 在bin下面找到 打开在最后一行添加暂停 pause 之后双击运行idea.bat 提示找不到一个jar包,切记不要有中文目录 IDEA缓存 …

Java与Python谁更适合后端开发?

在软件开发的世界里&#xff0c;选择合适的编程语言就像为建筑选择合适的材料一样重要。 对于后端开发而言&#xff0c;Java和Python都是流行的选择&#xff0c;但它们各自拥有独特的优势和劣势&#xff0c;“谁更适合”就成为一个被议论的话题。 事实上&#xff0c;并不存在…

每日学术速递8.2

1.A Scalable Quantum Non-local Neural Network for Image Classification 标题&#xff1a; 用于图像分类的可扩展量子非局部神经网络 作者&#xff1a; Sparsh Gupta, Debanjan Konar, Vaneet Aggarwal 文章链接&#xff1a;https://arxiv.org/abs/2407.18906 摘要&#x…

[BJDCTF2020]Easy MD51

抓包看一下信息&#xff0c;发现有sql注入字段 输入 注入发现 查看源码 然后get传参?aQNKCDZO&bs214587387a 最后 MD5函数的弱类型比较 发现PHP代码&#xff0c;分析仍为 PHP md5绕过。 使用数组绕过POST传入param1[]1&param2[]2&#xff0c;得到flag。

RIP综合练习

要求&#xff1a; 1.合理使用IP地址划分网络&#xff0c;各自创建循环接口 2.R1创建环回172.16.1.1/24 172.16.2.1/24 172.16.3.1/24 3.要求R3使用R2访问R1环回 4.减少路由条目数量&#xff0c;R1,R2之间增加路由传递安全性 5.R5创建一个环回模拟运营商&#xff0c;不能…

打卡第31天------贪心算法

每天抓紧时间刷题,争取尽快上岸,不能再耽误一分一秒了,2024年已经过去大半年了。这个算法编程题是我的痛点。要尽快弥补。 卡尔在讲算法题的时候,思路比较清晰,通俗易懂,以前看见算法题就害怕,因为啥都不会,看懵了,跟了一个月了,每天坚持刷题,偶尔会回顾思路,也会…

开源Spring Boot版本WebSSH:轻松在浏览器中管理SSH和FTP

介绍 WebSSH 是一个轻量级的开源ssh工具&#xff0c;只需安装在服务端&#xff0c;就可以通过浏览器访问SSH和FTP。它支持文件和日志高亮显示&#xff0c;Vim 和 Top 命令&#xff0c;实时查看日志&#xff0c;并且操作体验与标准的 Shell 基本相同。WebSSH 支持多会话、文件上…

【Git】git 从入门到实战系列(二)—— git 介绍以及安装方法 (文末附带视频录制操作步骤)

文章目录 一、前言二、git 是什么三、版本控制系统是什么四、本地 vs 集中式 vs 分布式本地版本控制系统集中式版本控制系统分布式版本控制系统 五、安装 git 一、前言 本系列上一篇文章【Git】git 从入门到实战系列&#xff08;一&#xff09;—— Git 的诞生&#xff0c;Lin…

【2024蓝桥杯/C++/B组/小球反弹】

题目 分析 Sx 2 * k1 * x; Sy 2 * k2 * y; &#xff08;其中k1, k2为整数&#xff09; Vx * t Sx; Vy * t Sy; k1 / k2 (15 * y) / (17 * x)&#xff1b; 目标1&#xff1a;根据k1与k2的关系&#xff0c;找出一组最小整数组&#xff08;k1, k2&#xff09;&#xff…

NLP-使用Word2vec实现文本分类

Word2Vec模型通过学习大量文本数据&#xff0c;将每个单词表示为一个连续的向量&#xff0c;这些向量可以捕捉单词之间的语义和句法关系。本文做文本分类是结合Word2Vec文本内容text&#xff0c;预测其文本标签label。以下使用mock商品数据的代码实现过程过下&#xff1a; 1、…

PCL从理解到应用【08】 点云特征 | 法线估计 | 主曲率估计

前言 在PCL中&#xff0c;有多种方法和函数可以用来提取点云特征&#xff0c;本文介绍几何特征。 其中&#xff0c;几何特征主要包括法线估计和主曲率估计。 这些特征能够描述点云表面的几何形状&#xff0c;常用于进一步的点云处理和分析&#xff0c;如配准、分割和物体识别…

利用canvas 实现图片的标注,把标注像素点传入到后端

背景&#xff1a;我们有一个摄像的产品&#xff0c;拍照传统的水表盘面&#xff0c;我们需要框选水表读数&#xff0c;标注点传到后端&#xff0c;后端根据标注点自动去截取摄像表拍摄回来的图片&#xff0c;然后拿到大模型里面进行训练。由于同一只表拍摄的画面都是一样的&…

【时时三省】unity test 测试框架 使用 code blocks 移植

山不在高&#xff0c;有仙则名。水不在深&#xff0c;有龙则灵。 ----CSDN 时时三省 目录 1&#xff0c;使用 Code::Blocks 17.12 创建工程 2&#xff0c;移植文件至该工程下&#xff1a; 移入的文件为: 被移入的文件介绍&#xff1a; 更改代码&#xff1a; 向工程添加文…