STM32 学习笔记【补充】(十)硬件I2C读写MPU6050

该系列为笔者在学习STM32过程(主线是江科大的视频)中的记录与发散思考。

初学难免有所纰漏、错误,还望大家不吝指正,感谢~


一、I2C 外设简介

I2C(Inter-Integrated Circuit)是一种多主多从的串行通信协议,广泛应用于短距离、低速率的设备通信中,例如传感器、存储器等。I2C 具有以下特点:

  • 双线通信:需两根信号线,分别是 SDA(数据线)和 SCL(时钟线)。
  • 支持多主多从:通过设备地址实现主从设备通信。
  • 简单的通信协议:包括起始信号、停止信号、数据传输、ACK/NACK 等。
  • 灵活的数据速率:标准模式(100kHz)、快速模式(400kHz)

二、I2C 框图

SDA 数据控制

  • 数据寄存器

    • 数据寄存器是 I2C 数据传输的接口,用于存储即将发送或刚接收的数据。
  • 数据移位寄存器

    • 数据移位寄存器在实际传输时逐位处理数据,配合 SDA 线完成字节级数据的发送与接收。
  • PEC(帧错误校验)计算与存储

    • PEC模块,硬件自动进行CRC校验计算,用于计算帧错误校验运算,得到一个字节的校验位,附在数据帧后。

SCL 时钟控制

  • 时钟控制模块

    • 时钟控制寄存器(CCR):配置 I2C 的通信速率(如标准模式 100kHz、快速模式 400kHz)。
    • 时钟控制器:生成同步时钟信号,保证主从设备数据同步。
  • 控制寄存器(CR1 和 CR2)

    • CR1:用于配置基本 I2C 功能,如使能 I2C 外设、生成启动和停止条件等。
    • CR2:用于配置 I2C 时钟频率和 DMA 功能。
  • 状态寄存器(SR1 和 SR2)

    • SR1:实时记录 I2C 通信状态,如发送成功、接收完成等。
    • SR2:提供 I2C 外设的详细状态信息,包括主从模式、总线忙碌状态等。。
  • DMA 请求与响应

    • I2C 可通过 DMA(直接存储器访问)模块高效传输大量数据,减少 CPU 的负载。
  • 中断处理

    • I2C 支持多种中断源(如起始条件检测、停止条件检测、数据发送完成等),通过中断处理提高响应速度。

三、I2C基本结构

内部简化结构如下: 

  • 时钟控制器:负责生成时钟信号,确保主从设备同步。
  • 数据控制器:管理数据的接收和发送。
  • 移位寄存器:在数据传输过程中存储当前发送或接收的字节。向左移位,发送时高位先行。接收时从右边依次移入。
  • 数据寄存器(DR):作为应用程序与外设之间的数据接口。
  • GPIO 接口:配置成复用(交由片上外设控制)开漏输出(I2C协议要求)的模式,通过 SDA 和 SCL 引脚实现物理通信。
  • 开关控制:配置好了相关设置就使能外设。

四、主机发送

I2C协议规定:起始条件发生后第一个字节为地址,后面数据由各个芯片来规定。(MPU6050:地址后一位为指定寄存器地址,后续为写入寄存器的数据。)

  • Start:由CR(控制寄存器)控制,产生起始条件。
  • SB:状态寄存器的一位,表明硬件状态。
  • TxE:标志位,TxE=1,表明TDR(数据寄存器)为空。
  • ADDR = 1:地址发送结束
  • BTF =1: 字节发送结束(一个新数据将被发送且DR中未写入新数据)

五、主机接收

ACK=0:控制寄存器响应位置零,给出非应答。

其他逻辑与主机发送差不多,对应着看看=-=

六、代码部分

MPU6050配置

#include "stm32f10x.h"                  // Device header
#include "MPU6050_Reg.h"#define MPU6050_ADDRESS		0xD0		//MPU6050的I2C从机地址/*** 函    数: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;										//跳出等待,不等了}}
}/*** 函    数: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);					//等待EV5I2C_Send7bitAddress(I2C2, MPU6050_ADDRESS, I2C_Direction_Transmitter);	//硬件I2C发送从机地址,方向为发送MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED);	//等待EV6I2C_SendData(I2C2, RegAddress);											//硬件I2C发送寄存器地址MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTING);			//等待EV8I2C_SendData(I2C2, Data);												//硬件I2C发送数据MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED);				//等待EV8_2I2C_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);					//等待EV5I2C_Send7bitAddress(I2C2, MPU6050_ADDRESS, I2C_Direction_Transmitter);	//硬件I2C发送从机地址,方向为发送MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED);	//等待EV6I2C_SendData(I2C2, RegAddress);											//硬件I2C发送寄存器地址MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED);				//等待EV8_2I2C_GenerateSTART(I2C2, ENABLE);										//硬件I2C生成重复起始条件MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT);					//等待EV5I2C_Send7bitAddress(I2C2, MPU6050_ADDRESS, I2C_Direction_Receiver);		//硬件I2C发送从机地址,方向为接收MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED);		//等待EV6I2C_AcknowledgeConfig(I2C2, DISABLE);									//在接收最后一个字节之前提前将应答失能I2C_GenerateSTOP(I2C2, ENABLE);											//在接收最后一个字节之前提前申请停止条件MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_RECEIVED);				//等待EV7Data = I2C_ReceiveData(I2C2);											//接收数据寄存器I2C_AcknowledgeConfig(I2C2, ENABLE);									//将应答恢复为使能,为了不影响后续可能产生的读取多字节操作return Data;
}/*** 函    数: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;				//时钟速度,选择为50KHzI2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;		//时钟占空比,选择Tlow/Thigh = 2I2C_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);					//配置寄存器,配置DLPFMPU6050_WriteReg(MPU6050_GYRO_CONFIG, 0x18);			//陀螺仪配置寄存器,选择满量程为±2000°/sMPU6050_WriteReg(MPU6050_ACCEL_CONFIG, 0x18);			//加速度计配置寄存器,选择满量程为±16g
}/*** 函    数:MPU6050获取ID号* 参    数:无* 返 回 值:MPU6050的ID号*/
uint8_t MPU6050_GetID(void)
{return MPU6050_ReadReg(MPU6050_WHO_AM_I);		//返回WHO_AM_I寄存器的值
}/*** 函    数:MPU6050获取数据* 参    数:AccX AccY AccZ 加速度计X、Y、Z轴的数据,使用输出参数的形式返回,范围:-32768~32767* 参    数:GyroX GyroY GyroZ 陀螺仪X、Y、Z轴的数据,使用输出参数的形式返回,范围:-32768~32767* 返 回 值:无*/
void MPU6050_GetData(int16_t *AccX, int16_t *AccY, int16_t *AccZ, int16_t *GyroX, int16_t *GyroY, int16_t *GyroZ)
{uint8_t DataH, DataL;								//定义数据高8位和低8位的变量DataH = MPU6050_ReadReg(MPU6050_ACCEL_XOUT_H);		//读取加速度计X轴的高8位数据DataL = MPU6050_ReadReg(MPU6050_ACCEL_XOUT_L);		//读取加速度计X轴的低8位数据*AccX = (DataH << 8) | DataL;						//数据拼接,通过输出参数返回DataH = MPU6050_ReadReg(MPU6050_ACCEL_YOUT_H);		//读取加速度计Y轴的高8位数据DataL = MPU6050_ReadReg(MPU6050_ACCEL_YOUT_L);		//读取加速度计Y轴的低8位数据*AccY = (DataH << 8) | DataL;						//数据拼接,通过输出参数返回DataH = MPU6050_ReadReg(MPU6050_ACCEL_ZOUT_H);		//读取加速度计Z轴的高8位数据DataL = MPU6050_ReadReg(MPU6050_ACCEL_ZOUT_L);		//读取加速度计Z轴的低8位数据*AccZ = (DataH << 8) | DataL;						//数据拼接,通过输出参数返回DataH = MPU6050_ReadReg(MPU6050_GYRO_XOUT_H);		//读取陀螺仪X轴的高8位数据DataL = MPU6050_ReadReg(MPU6050_GYRO_XOUT_L);		//读取陀螺仪X轴的低8位数据*GyroX = (DataH << 8) | DataL;						//数据拼接,通过输出参数返回DataH = MPU6050_ReadReg(MPU6050_GYRO_YOUT_H);		//读取陀螺仪Y轴的高8位数据DataL = MPU6050_ReadReg(MPU6050_GYRO_YOUT_L);		//读取陀螺仪Y轴的低8位数据*GyroY = (DataH << 8) | DataL;						//数据拼接,通过输出参数返回DataH = MPU6050_ReadReg(MPU6050_GYRO_ZOUT_H);		//读取陀螺仪Z轴的高8位数据DataL = MPU6050_ReadReg(MPU6050_GYRO_ZOUT_L);		//读取陀螺仪Z轴的低8位数据*GyroZ = (DataH << 8) | DataL;						//数据拼接,通过输出参数返回
}

感谢阅读

跟着光

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

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

相关文章

.Net Core webapi 实现JWT认证

文章目录 需求准备创建JWT配置创建JWTService注册JWT创建中间件读取jwt的token在需要的接口上添加属性启动认证启动swagger的授权认证使用 需求 实现一个记录某个用户所有操作的功能 准备 创建你的webapi项目从nuget下载安装JWT资源包根据你的项目使用.net版本下载对应的jwt…

leetcode203-移除链表元素

leetcode203 什么是链表 之前不懂链表的数据结构&#xff0c;一看到链表的题目就看不明白 链表是通过next指针来将每个节点连接起来的&#xff0c;题目中给的链表是单向链表&#xff0c;有两个值&#xff0c;一个val表示值&#xff0c;一个next&#xff1a;表示连接的下一个…

在21世纪的我用C语言探寻世界本质——字符函数和字符串函数(2)

人无完人&#xff0c;持之以恒&#xff0c;方能见真我&#xff01;&#xff01;&#xff01; 共同进步&#xff01;&#xff01; 文章目录 一、strncpy函数的使用二、strncat函数的使用三、strncmp函数的使用四、strstr的使用和模拟实现五、strtok函数的使用六、strerror和pe…

stack_queue的底层,模拟实现,deque和priority_queue详解

文章目录 适配器Stack的模拟实现Queue的模拟实现vector和list的对比dequedeque的框架deque的底层 priority_queuepriority_queue的使用priority_queue的底层仿函数的使用仿函数的作用priority_queue模拟实现 适配器 适配器是一种模式&#xff0c;这种模式将类的接口转化为用户希…

LLM - 大模型 ScallingLaws 的 CLM 和 MLM 中不同系数(PLM) 教程(2)

欢迎关注我的CSDN&#xff1a;https://spike.blog.csdn.net/ 本文地址&#xff1a;https://spike.blog.csdn.net/article/details/145188660 免责声明&#xff1a;本文来源于个人知识与公开资料&#xff0c;仅用于学术交流&#xff0c;欢迎讨论&#xff0c;不支持转载。 Scalin…

杰盛微 IRS2336STRPBF 700V带使能和故障报告的三相反逻辑驱动芯片 SOP28封装

IRS2336STRPBF 700V带使能和故障报告的三相反逻辑驱动芯片 IRS2336是 N型高压、高速功率 MOSFET/IGBT高低侧三相栅极驱动芯片&#xff0c;包含三路独立的半桥驱动电路。内部集成了欠压保护和过流保护功能&#xff0c;出现异常时立即关断六通道输出。提供外部使能控制可同时关断…

深入理解第三范式(3NF):数据库设计中的重要性与实践

title: 深入理解第三范式(3NF):数据库设计中的重要性与实践 date: 2025/1/17 updated: 2025/1/17 author: cmdragon excerpt: 在数据库设计中,规范化是确保数据完整性、减少冗余和提高查询效率的关键过程。第三范式(3NF)作为关系数据库设计的高级规范,建立在前两范式…

mongoose 支持https踩坑纪实

简述 mongoose是C编写的嵌入式web服务&#xff0c;它能够支持https协议&#xff0c;可以简单的部署&#xff0c;但要做到完美部署&#xff0c;不是那么容易。 部署方法 本人使用的是最新的7.16版&#xff0c;以前版本似乎是要通过修改 头文件中的 MG_ENABLE_SSL 宏定义&…

RK3576 Android14 状态栏和导航栏增加显示控制功能

问题背景&#xff1a; 因为RK3576 Android14用户需要手动控制状态栏和导航栏显示隐藏控制&#xff0c;包括对锁屏后下拉状态栏的屏蔽&#xff0c;在设置功能里增加此功能的控制&#xff0c;故参考一些博客完成此功能&#xff0c;以下是具体代码路径的修改内容。 解决方案&…

C#高级:通过 Assembly 类加载 DLL 和直接引用DLL的方法大全

一、主项目不添加引用 &#xff08;主项目不添加引用&#xff0c;而是通过路径获取指定dll&#xff09; 1.打印类的属性名称 namespace ReflectionDemo {class Program{static void Main(string[] args){// 指定【编译输出】的项目类库dll&#xff08;启动项目编译输出目录下…

【k8s面试题2025】1、练气期

主要通过呼吸吐纳等方法&#xff0c;将外界的天地灵气吸入体内&#xff0c;初步改造身体&#xff0c;使身体素质远超常人。 文章目录 docker 和虚拟机的不同Kubernetes 和 docker 的关系Kube-proxy IPVS 和 iptables 的异同蓝绿发布Kubernetes中常见的数据持久化方式关于 Docke…

音视频入门基础:RTP专题(4)——FFmpeg源码中,判断某文件是否为SDP文件的实现

一、引言 执行《音视频入门基础&#xff1a;RTP专题&#xff08;2&#xff09;——使用FFmpeg命令生成RTP流》中的“媒体文件转推RTP的FFmpeg命令”会生成一个SDP文件&#xff0c;该文件内容如下&#xff1a; v0 o- 0 0 IN IP4 127.0.0.1 sNo Name t0 0 atool:libavformat 61…

【大数据2025】Hadoop 万字讲解

文章目录 一、大数据通识大数据诞生背景与基本概念大数据技术定义与特征大数据生态架构概述数据存储数据计算与易用性框架分布式协调服务和任务调度组件数仓架构流处理架构 二、HDFSHDFS 原理总结一、系统架构二、存储机制三、数据写入流程四、心跳机制与集群管理 安全模式&…

电脑换固态硬盘

参考&#xff1a; https://baijiahao.baidu.com/s?id1724377623311611247 一、根据尺寸和缺口可以分为以下几种&#xff1a; 1、M.2 NVME协议的固态 大部分笔记本是22x42MM和22x80MM nvme固态。 在京东直接搜&#xff1a; M.2 2242 M.2 2280 2、msata接口固态 3、NGFF M.…

回顾2024年在CSDN的成长

文章目录 我与CSDN的初次邂逅初学阶段的阅读CSDN&#xff1a;编程新手的避风港初学者的福音&#xff1a;细致入微的知识讲解考试复习神器&#xff1a;技术总结的“救命指南”曾经的自己&#xff1a;为何迟迟不迈出写博客的第一步兴趣萌芽&#xff1a;从“读”到“想写”的初体验…

抖音ip属地不准是什么原因?可以改吗

在数字化时代&#xff0c;社交媒体平台如抖音已成为人们日常生活的重要组成部分。随着各大平台对用户隐私和数据安全的日益重视&#xff0c;IP属地的显示功能应运而生。然而&#xff0c;不少抖音用户在使用过程中发现&#xff0c;显示的IP属地与实际位置存在偏差&#xff0c;这…

Win11 安装与配置 Java环境 JDK(以JDK11为例)

0&#xff0c;下载JDK 访问JDK官网&#xff1a;Java Downloads | Oracle 选择对应版本进行下载&#xff0c;目前21和23都是可以直接下载的 但是如果需要下载旧版本&#xff0c;往下拉找到要下载的版本&#xff0c;不过这时候下载就需要登录账号了&#xff0c;注册一个就成 2&…

LabVIEW串口通信调试与数据接收问题

在使用LabVIEW进行串口通信时&#xff0c;常常会遇到无法接收数据的情况。这可能与串口设置、连接、设备响应等多方面因素相关。本文将详细讨论如何使用LabVIEW进行串口通信&#xff0c;并提供常见问题的排查与解决方法&#xff0c;帮助用户更高效地进行数据接收调试。通过调整…

概率扩散去噪模型DDPM

文章目录 摘要abstract高斯噪声扩散模型正向过程逆向过程 论文阅读论文创新点解决的问题 总结参考文献 摘要 本周主要学习了高斯噪声在扩散模型中的应用及相关算法实现。扩散模型受到自然现象的启发&#xff0c;通过在图像中引入高斯噪声&#xff0c;模拟出扩散效果&#xff0…

Python操作Excel——openpyxl使用笔记(3)

3 单元格基本操作 3.1 访问单元格和读写其内容 在前面的例子中&#xff0c;已经简单演示过了向单元格中写入和读取数据。这里进一步提供访问单元格的一些方法。和前面一样&#xff0c;使用工作表的索引方式&#xff0c;可以快速定位一个单元格&#xff1a; import openpyxl w…