软件I2C读写MPU6050

文章目录

  • 前言
  • 本次线路图
  • 封装I2C时序
  • 封装MPU6050,配置寄存器
  • 最后在主函数进行显示


前言

本片文章开始进行I2C在STM32的直接操作,理解时序的代码实现,理解对寄存器的配置,使用I2C读写MPU6050,读取MPU6050的各轴数据。
MPU6050详解见:https://blog.csdn.net/qq_53922901/article/details/136581780?spm=1001.2014.3001.5501
I2C内容详解见:https://blog.csdn.net/qq_53922901/article/details/136430501?spm=1001.2014.3001.5501


本次线路图

在这里插入图片描述

封装I2C时序

按照之前的时序图,来一一的实现每个时序单元
ThisI2C.c内容如下:

#include "stm32f10x.h"                  // Device header
#include "Delay.h"

// 定义一下端口,32不支持使用sbit,单纯写一下
#define ThisIICPort GPIOB
#define ThisIICSCL GPIO_Pin_10
#define ThisIICSDA GPIO_Pin_11

// 为了避免有些单片机速度过快,使用函数封装可以更好的添加延时
// 控制I2C时间总线
void ThisI2C_W_SCL(uint8_t Bit){
	GPIO_WriteBit(GPIOB,GPIO_Pin_10,(BitAction)Bit);
	Delay_us(10);
}
// 控制I2C数据总线
void ThisI2C_W_SDA(uint8_t Bit){
	GPIO_WriteBit(GPIOB,GPIO_Pin_11,(BitAction)Bit);
	Delay_us(10);
}
// 获取数据总线上的数据
uint8_t ThisI2C_R_SDA(void){
	uint8_t Bit;
	Bit = GPIO_ReadInputDataBit(ThisIICPort,ThisIICSDA);
	Delay_us(10);
	return Bit;
}


// 初始化函数
void ThisI2C_Init(void){
	// 初始化时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
	// 配置SCL,SDA所在端口
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;		// 开漏输出
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_11;	
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;		// 50Hz翻转速度
	GPIO_Init(GPIOB, &GPIO_InitStructure);

	GPIO_SetBits(GPIOB,GPIO_Pin_10 | GPIO_Pin_11);	// 将I2C总线都置为高电平
}


// 开始封装时序单元
// 起始时序
void ThisI2C_Start(void){
	// 为了兼容重复起始时序,先释放(拉高)SDA
	ThisI2C_W_SDA(1);
	ThisI2C_W_SCL(1);
	ThisI2C_W_SDA(0);
	ThisI2C_W_SCL(0);
}
// 终止时序
void ThisI2C_End(void){
	ThisI2C_W_SDA(0);		// 确保SDA能产生上升沿
	ThisI2C_W_SCL(1);
	ThisI2C_W_SDA(1);
}
// 发送一个字节时序
void ThisI2C_SendByte(uint8_t Byte){
	uint8_t i = 0;
	for(i=0;i<8;i++){
		ThisI2C_W_SDA(Byte & (0x80>>i));	// 从高位开始确定数据内容
		ThisI2C_W_SCL(1);		// SCL高电平开始读取数据
		ThisI2C_W_SCL(0);	
	}	
}
// 接收一个字节时序
uint8_t ThisI2C_ReceiveByte(void){
	uint8_t Byte = 0x00,i;
	ThisI2C_W_SDA(1);		// 先释放SDA
	for(i=0;i<8;i++){
		ThisI2C_W_SCL(1);		// SCL高电平开始读取数据
		if(ThisI2C_R_SDA()==1){
			Byte |= (0x80>>i);
		}
		ThisI2C_W_SCL(0);	
	}
	return Byte;
}
// 发送应答时序
void ThisI2C_SendACK(uint8_t ACK){
	ThisI2C_W_SDA(ACK);	
	ThisI2C_W_SCL(1);		// SCL高电平开始读取数据
	ThisI2C_W_SCL(0);		
}
// 接收应答时序
uint8_t ThisI2C_ReceiveACK(void){
	uint8_t ACK;
	ThisI2C_W_SDA(1);		// 先释放SDA
	ThisI2C_W_SCL(1);		// SCL高电平开始读取数据
	ACK = ThisI2C_R_SDA();
	ThisI2C_W_SCL(0);	
	return ACK;
}

封装MPU6050,配置寄存器

根据之前对寄存器的了解
MPU6050.c内容如下:

#include "stm32f10x.h"                  // Device header
#include "ThisI2C.h"

// 宏定义
#define MPU6050_Slave 	0xd0
// 配置滤波、传感器的初始配置
#define SMPLRT_DIV			0X19
#define CONFIG					0X1A
#define GYRO_CONFIG			0X1B
#define ACCEL_CONFIG		0X1C
// 这几个连续的寄存器存储着各个轴的值
#define ACCEL_XOUT_H		0X3B
#define ACCEL_XOUT_L		0X3C
#define ACCEL_YOUT_H		0X3D
#define ACCEL_YOUT_L		0X3E
#define ACCEL_ZOUT_H		0X3F
#define ACCEL_ZOUT_L		0X40
#define TEMP_OUT_H 			0X41
#define TEMP_OUT_L			0X42
#define GYRO_XOUT_H			0X43
#define GYRO_XOUT_L			0X44
#define GYRO_YOUT_H			0X45
#define GYRO_YOUT_L			0X46
#define GYRO_ZOUT_H			0X47
#define GYRO_ZOUT_L			0X48

#define PWR_MGMT_1			0X6B
#define PWR_MGMT_2			0X6C
#define WHO_AM_I			0X75



void MPU6050_WriteReg(uint8_t RegAddr,uint8_t Data){
	ThisI2C_Start();
	ThisI2C_SendByte(MPU6050_Slave);		// 从机地址
	// 如果没有应答直接跳出,可以用于写错误处理,不写不用管
	if(ThisI2C_ReceiveACK() != 0){
		return ;
	}
	ThisI2C_SendByte(RegAddr);					// 寄存器地址
	if(ThisI2C_ReceiveACK() != 0){
		return ;
	}
	ThisI2C_SendByte(Data);							// 数据
	if(ThisI2C_ReceiveACK() != 0){
		return ;
	}
	ThisI2C_End();
}
uint8_t MPU6050_ReadReg(uint8_t RegAddr){
	uint8_t Data;
	ThisI2C_Start();
	ThisI2C_SendByte(MPU6050_Slave);		// 从机地址
	ThisI2C_ReceiveACK();
	ThisI2C_SendByte(RegAddr);					// 寄存器地址
	ThisI2C_ReceiveACK();
	
	ThisI2C_Start();										// 转入读取时序重新起始
	ThisI2C_SendByte(MPU6050_Slave | 0x01);					// 从机地址(变为读)
	ThisI2C_ReceiveACK();
	Data = ThisI2C_ReceiveByte();
	ThisI2C_SendACK(1);										// 最后给非应答											
	ThisI2C_End();
	return Data;
}
// 初始化,配置寄存器
void MPU6050_Init(void){
	ThisI2C_Init();
	// 配置寄存器
	MPU6050_WriteReg(PWR_MGMT_1,0x01);		// 解除睡眠,选择推荐的陀螺仪x轴时钟
	MPU6050_WriteReg(PWR_MGMT_2,0x00);		// 不用待机
	MPU6050_WriteReg(SMPLRT_DIV,0x09);		// 10分频
	MPU6050_WriteReg(CONFIG,0x06);	
	MPU6050_WriteReg(GYRO_CONFIG,0x18);		// 自测不使能,使用最大量程
	MPU6050_WriteReg(ACCEL_CONFIG,0x18);
}

// 用于存储获取的加速度与陀螺仪各轴的值
struct MPU6050_DataDef{
	int16_t AccX;
	int16_t AccY;
	int16_t AccZ;
	int16_t GyroX;
	int16_t GyroY;
	int16_t GyroZ;
}MPU6050_Data;
// 获取传感器各轴的数据
void MPU6050_GetData(void){
	uint16_t DataH,DataL;
	
	DataH = MPU6050_ReadReg(ACCEL_XOUT_H);
	DataL = MPU6050_ReadReg(ACCEL_XOUT_L);
	MPU6050_Data.AccX = (DataH << 8) | DataL;
	
	DataH = MPU6050_ReadReg(ACCEL_YOUT_H);
	DataL = MPU6050_ReadReg(ACCEL_YOUT_L);
	MPU6050_Data.AccY = (DataH << 8) | DataL;
	
	DataH = MPU6050_ReadReg(ACCEL_ZOUT_H);
	DataL = MPU6050_ReadReg(ACCEL_ZOUT_L);
	MPU6050_Data.AccZ = (DataH << 8) | DataL;
	
	DataH = MPU6050_ReadReg(GYRO_XOUT_H);
	DataL = MPU6050_ReadReg(GYRO_XOUT_L);
	MPU6050_Data.GyroX = (DataH << 8) | DataL;
	
	DataH = MPU6050_ReadReg(GYRO_YOUT_H);
	DataL = MPU6050_ReadReg(GYRO_YOUT_L);
	MPU6050_Data.GyroY = (DataH << 8) | DataL;
	
	DataH = MPU6050_ReadReg(GYRO_ZOUT_H);
	DataL = MPU6050_ReadReg(GYRO_ZOUT_L);
	MPU6050_Data.GyroZ = (DataH << 8) | DataL;
}
// 获取ID
uint8_t MPU8050_GetId(void){
	return MPU6050_ReadReg(WHO_AM_I);
}

这里使用了结构体来保存获取的传感器数据,多数据时可以更方便的使用,在头文件进行了外部声明,MPU6050.h内容如下:

#ifndef __MPU6050_H
#define __MPU6050_H

struct MPU6050_DataDef{
	int16_t AccX;
	int16_t AccY;
	int16_t AccZ;
	int16_t GyroX;
	int16_t GyroY;
	int16_t GyroZ;
}extern MPU6050_Data;	
void MPU6050_Init(void);
void MPU6050_WriteReg(uint8_t RegAddr,uint8_t Data);
uint8_t MPU6050_ReadReg(uint8_t RegAddr);
void MPU6050_GetData(void);
uint8_t MPU8050_GetId(void);


#endif

最后在主函数进行显示

main.c内容如下:

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "MPU6050.h"


int main(void)
{
	uint8_t ID;
	OLED_Init();
	OLED_ShowString(1,1,"ID:");
	MPU6050_Init();
	ID = MPU8050_GetId();
	OLED_ShowHexNum(1,7,ID,2);
	while (1)
	{
		MPU6050_GetData();
		OLED_ShowSignedNum(2,1,MPU6050_Data.AccX,5);
		OLED_ShowSignedNum(2,9,MPU6050_Data.AccY,5);
		OLED_ShowSignedNum(3,1,MPU6050_Data.AccZ,5);
		OLED_ShowSignedNum(3,9,MPU6050_Data.GyroX,5);
		OLED_ShowSignedNum(4,1,MPU6050_Data.GyroY,5);
		OLED_ShowSignedNum(4,9,MPU6050_Data.GyroZ,5);

	}
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

vue 自定义组件绑定model+弹出选择支持上下按键选择

参考地址v-modelhttps://v2.cn.vuejs.org/v2/guide/components-custom-events.html#%E8%87%AA%E5%AE%9A%E4%B9%89%E7%BB%84%E4%BB%B6%E7%9A%84-v-model 原文代码 Vue.component(base-checkbox, {model: {prop: checked,event: change},props: {checked: Boolean},template: `…

Java异常分类(二)

RuntimeException 运行时异常&#xff1a; 派生于 RuntimeException 的异常&#xff0c;如被 0 除、数组下标越界、空指针等&#xff0c;其产生比较频繁&#xff0c;处理麻烦&#xff0c;如果显式的声明或捕获将会对程序可读性和运行效率影响很大。因此由系统自动检测并将它们交…

Caffeine缓存

本地缓存基于本地环境的内存&#xff0c;访问速度非常快&#xff0c;对于一些变更频率低、实时性要求低的数据&#xff0c;可以放在本地缓存中&#xff0c;提升访问速度 使用本地缓存能够减少和Redis类的远程缓存间的数据交互&#xff0c;减少网络 I/O 开销&#xff0c;降低这…

使用EasyRec快速构建推荐模型

随着移动app的普及&#xff0c;个性化推荐和广告成为很多app不可或缺的一部分。他们在改善用户体验和提升app的收益方面带来了巨大的提升。深度学习在搜广推领域的应用也已经非常深入&#xff0c;并且给各种场景的效果带来了巨大的提升。针对推荐流程的各个阶段&#xff0c;业界…

基于YOLOv8/YOLOv7/YOLOv6/YOLOv5的石头剪刀布手势识别系统详解(深度学习模型+UI界面代码+训练数据集)

摘要&#xff1a;本篇博客深入探讨了使用深度学习技术开发石头剪刀布手势识别系统的过程&#xff0c;并分享了完整代码。该系统利用先进的YOLOv8、YOLOv7、YOLOv6、YOLOv5算法&#xff0c;并对这几个版本进行性能对比&#xff0c;如mAP、F1 Score等关键指标。文章详细阐述了YOL…

【算法题解】Java算法题目解析

1. 数字三角形 import java.util.Scanner;public class Main {public static void main(String[] args) {Scanner scan new Scanner(System.in);int n scan.nextInt();int[][] dp new int[n 1][n 1];int max 0;for (int i 1; i < dp.length; i) {for (int j 1; j &l…

Android 生成SO - 基础工程创建

最近需要给小伙伴扫盲一下如何使用Android Studio 生成一个SO文件&#xff0c;网上找了很多都没有合适的样例&#xff0c;那只能自己来写一个了。 原先生成SO是一个很麻烦的事情&#xff0c;现在Android Studio帮忙做了很多的事情&#xff0c;基本只要管好自己的C代码即可。 …

第2篇【Docker项目实战】使用Docker部署Raneto知识库平台(转载)

【Docker项目实战】使用Docker部署Raneto知识库平台 一、Raneto介绍 1.1 Raneto简介 Raneto是一个免费、开放、简单的 Markdown 支持的 Node.js 知识库。 1.2 知识库介绍 知识库 知识库是指存储和组织知识的系统或库&#xff0c;它包括了各种类型的信息和知识&#xff0c;如…

【数学建模】熵权法 Python代码

熵权法是一种客观的赋权方法&#xff0c;它可以靠数据本身得出权重。 依据的原理&#xff1a;指标的变异程度越小&#xff0c;所反映的信息量也越少&#xff0c;其对应的权值也应该越低。 import numpy as np#自定义对数函数mylog&#xff0c;用于处理输入数组中的0元素 def m…

OpenCV和Mediapipe实现摸嘴或鼻检测

目录 引言 1.过程简介 2. 代码结构 2.1 导入库 2.2 初始化模型 2.3 读取视频流或摄像头 2.4 初始化FPS计算 2.5 主循环 2.6 转换BGR图像为RGB图像 2.7 运行姿势检测模型和手部检测模型 2.8 绘制姿势关键点及连接线 2.9 检测手部关键点 2.10 判断手部与鼻子、嘴的相对…

300分钟吃透分布式缓存-28讲:如何构建一个高性能、易扩展的Redis集群?

Redis 集群的分布式方案主要有 3 种。分别是 Client 端分区方案&#xff0c;Proxy 分区方案&#xff0c;以及原生的 Redis Cluster 分区方案。 Client 端分区 Client 端分区方案就是由 Client 决定数据被存储到哪个 Redis 分片&#xff0c;或者由哪个 Redis 分片来获取数据。…

DVWA靶场-暴力破解

DVWA是一个适合新手锻炼的靶机&#xff0c;是由PHP/MySQL组成的 Web应用程序&#xff0c;帮助大家了解web应用的攻击手段 DVWA大致能分成以下几个模块&#xff0c;包含了OWASP Top 10大主流漏洞环境。 Brute Force——暴力破解 Command Injection——命令注入 CSRF——跨站请…

职场中的创新思维与执行力

在职场中&#xff0c;创新思维和执行力是两个关键要素。创新思维能够帮助员工在工作中找到更好的解决方案&#xff0c;而执行力则是将想法付诸实践的能力。本文将探讨如何在职场中培养创新思维和提升执行力。 一、创新思维的重要性 在职场中&#xff0c;创新思维是推动企业发展…

Docker容器化技术(互联机制实现便捷互访)

容器的互联是一种让多个容器中的应用进行快速交互的方式。它会在源和接收容器之间创建连接关系&#xff0c;接收容器可以通过容器名快速访问到源容器&#xff0c;而不用指定具体的IP地址。 1.自定义容器命名 连接系统依据容器的名称来执行。因此&#xff0c;首先需要自定义一…

Django 模版基本语法

Django学习笔记 模版语法 本质&#xff1a;在HTML中写一些占位符&#xff0c;由数据对这些占位符进行替换和处理。 views.py def page2(request):#定义一些变量将变量传送给templates中的html文件name1 sallyname2 yingyinghobbys [swimming,badminton,reading]person {…

惬意上手Redis

Redis介绍 Redis&#xff08;全称为REmote Dictionary Server&#xff09;是一个开源的、内存中的数据存储结构&#xff0c;主要用作应用程序缓存或快速相应数据库。 REmote Dictionary Server: 有道翻译Redis是“远程字典服务”&#xff0c;远程不过是远程访问&#xff0c;而…

Ingress 实战:从零到一构建高可用服务

Ingress 是 Kubernetes 中一种用于控制流量进入集群的资源。它可以为集群内的服务提供统一的访问入口&#xff0c;并提供一些额外的功能&#xff0c;例如&#xff1a; 路由流量到不同的服务 提供基于路径的路由 提供基于主机的路由 提供 TLS 加密 使用身份验证和授权 Ing…

SQL: 触发器/存储过程/游标的操作

目录 触发器存储过程创建存储过程修改存储过程删除存储过程执行存储过程 游标待续、更新中 触发器 待更新存储过程 定义 是一组TSQL语句的预编译集合&#xff0c;能实现特定的功能 是一种独立的数据库对象&#xff0c;在服务器上创建和运行 类似于编程语言中的过程或函数分类…

SublimeText4 安装

Sublime Text 可以编写html&#xff0c;css&#xff0c;js&#xff0c;php等等&#xff0c;是一个轻量、简洁、高效、跨平台的编辑器。 图1&#xff1a;SublimeText官网 Sublime Text具有漂亮的用户界面和强大的功能&#xff0c;例如代码缩略图&#xff0c;Python的插件&#…

Java学习记录(十九)多线程(一)

线程 线程是操作系统能进行调度的最小单位&#xff0c;他是被包含在进程中的&#xff0c;一个运行的软件可以看作为一个进程&#xff0c;而在该软件中执行的各种功能自身可以理解为一个线程&#xff0c;可以理解为在软件中互相独立又可以同时进行的功能&#xff0c;他是进程中…