STM32通过I2C软件读写MPU6050

文章目录​​​​​​​

1. MPU6050

1.1 运动学概念

1.2 工作原理

2. 参数

2.1 量程选择

2.2 I2C从机地址配置

3. 硬件电路

4. 框架图

5. 软件和硬件波形对比

6. 软件I2C读写MPU6050

6.1 程序整体构架

6.2 一些需要注意的点:

6.3 MPU6050初始化配置

6.4 传感器模型

7. 代码实现

​​​​​​​7.1  MyI2C.c

​​​​​​​7.2  MyI2C.h

7.3 MPU6050.C

7.4 MPU6050.H

7.5 MPU6050_Reg.h

7.6 main.c


1. MPU6050

对于I2C通信和MPU6050的详细解析可以看下面这篇文章

STM32单片机I2C通信详解-CSDN博客

对于STM32通过I2C硬件读写MPU6050的代码,可以看下面这篇文章

STM32通过I2C硬件读写MPU6050-CSDN博客

MPU6050是一个6轴姿态传感器,可以测量芯片自身X、Y、Z轴的加速度、角速度参数,通过数据融合,可进一步得到姿态角,常应用于平衡车、飞行器等需要检测自身姿态的场景

3轴加速度计(Accelerometer):测量X、Y、Z轴的加速度

3轴陀螺仪传感器(Gyroscope):测量X、Y、Z轴的角速度

1.1 运动学概念

  • 欧拉角
    • 欧拉角是用来描述三维空间中刚体旋转的三个角度:俯仰角(Pitch)、滚转角(Roll)和偏航角(Yaw)。
    • 俯仰角(Pitch):飞机机头上下倾斜的角度。
    • 滚转角(Roll):飞机左右倾斜的角度。
    • 偏航角(Yaw):飞机左右转向的角度。

1.2 工作原理

  1. 加速度计
    • 在X、Y、Z轴方向上测量加速度。通过检测重力加速度,可以推断出设备的倾斜角度。例如,在一个静止状态下,测量到的重力加速度可以用来计算设备相对于水平面的倾斜角度。
  2. 陀螺仪
    • 测量设备在X、Y、Z轴上的角速度。陀螺仪可以用于检测设备的旋转运动,比如快速转动或者缓慢旋转等。
    • 陀螺仪具有动态稳定性,但不具备静态稳定性。

2. 参数

ADC和数据存储

  • 16位ADC:MPU6050集成了16位的ADC,用于将模拟信号转换为数字信号,量化范围为-32768到32767。
  • 量化过程:ADC将模拟信号转换为数字信号,并以两个字节进行存储。

加速度计满量程选择:±2、±4、±8、±16(g)

陀螺仪满量程选择: ±250、±500、±1000、±2000(°/sec)​​​​​​​

可配置的数字低通滤波器

  • 允许用户根据应用需求配置滤波器,滤除噪声和干扰。

可配置的时钟源

  • 支持多种时钟源,包括内部振荡器、外部参考时钟等。

采样率

  • 采样率可以通过配置寄存器设置,决定数据采集的频率。

I2C地址配置

  • I2C从机地址
    • 当AD0引脚接低电平(AD0=0):地址为0x68。
    • 当AD0引脚接高电平(AD0=1):地址为0x69。
    • 具体地址配置决定了在I2C总线上的唯一性,避免地址冲突。

2.1 量程选择

满量程选择

  • 剧烈运动:选择较大的满量程,确保测量范围足够大。
  • 平缓运动:选择较小的满量程,提升测量分辨率。

加速度计满量程示例

  • ±16g
    • 读取的ADC值为最大值32768时,对应实际加速度为16g。
    • ADC值为32768的一半(16384)时,对应加速度为8g。
  • ±2g
    • 读取的ADC值为最大值32768时,对应实际加速度为2g。
    • ADC值为32768的一半(16384)时,对应加速度为1g。

测量分辨率

  • 满量程越小,测量分辨率越高,测量越精细。
  • 满量程越大,测量范围越广。
  • ADC值与加速度值呈线性关系,可以通过乘一个系数从ADC值计算出实际加速度。

2.2 I2C从机地址配置

二进制地址转换为十六进制

  • 以从机地址1101000为例。
  • 把7位二进制数1101000转换为十六进制,即分割低4位和高3位:0110 1000,转换后为0x68

I2C时序中的地址格式

  • 在I2C通信时,需要发送7位从机地址1101000加上1位读写位。
  • 认为0x68是从机地址,需要将0x68左移1位,再加上读写位(0或1)。
  • 转换步骤:
    • 0x68左移1位:1101 0000(即0xD0)。
    • 再与读写位(0或1)进行或操作:(0x68 << 1) | 0(0x68 << 1) | 1

实际应用

  • 写操作时,从机地址为0xD0
  • 读操作时,从机地址为0xD1

3. 硬件电路

引脚功能

VCC、GND

电源3.3v

SCL、SDA

I2C通信引脚

XCL、XDA

主机I2C通信引脚

AD0

从机地址最低位

INT

中断信号输出

4. 框架图

5. 软件和硬件波形对比

软件I2C实现

  1. 波形特点:软件I2C的波形较为不规整,每个时钟周期和空闲时间都不一致。
  2. 操作特点:软件I2C时的引脚操作会有一定的延时,因此各个时钟周期的间隔和占空比都不均匀。

硬件I2C实现

  1. 波形特点:硬件I2C的波形更加规整,时钟周期和占空比非常一致。
  2. 操作特点:每个时钟周期后都有严格的延时,保证每个周期的时间相同。

6. 软件I2C读写MPU6050

6.1 程序整体构架

首先建立I2C通信层的.c和.h模块,再建立MPU6050.c, 最后是main.c

  • Main.c
    • 调用MPU6050初始化函数。
    • 循环读取数据并进行显示。
  • MPU6050.c
    • 基于I2C通信协议,设定设备地址,发送读写指令。
    • 配置寄存器,读取传感器数据。
  • I2C.c
    • 初始化GPIO。
    • 编写基本的I2C操作函数,包括起始条件、终止条件、发送/接收一个字节、发送/接收应答等。

6.2 一些需要注意的点:

起始和终止条件

  • 起始条件:在SCL高电平期间,SDA由高电平变为低电平,产生起始条件(Start Condition)。这表示一次I2C通信的开始。

  • 终止条件:在SCL高电平期间,SDA由低电平变为高电平,产生终止条件(Stop Condition)。这表示一次I2C通信的结束。

数据传输过程

  • 数据位传输:在SCL低电平期间,主机可以改变SDA的电平,即在SCL的每一个低电平周期内,主机将要传输的数据位放在SDA线上。

  • 数据位读取:在SCL高电平期间,从机读取SDA线上的数据。此时,SDA线上的电平表示当前传输的数据位。

应答位的发送和接收

  • 发送应答:在主机发送完一个字节数据后,从机会在下一个SCL高电平期间将SDA线拉低,表示已接收到数据(ACK)。

  • 接收应答:主机在发送完数据后,将SDA线释放为高电平,然后在SCL高电平期间读取SDA的电平,判断是否收到从机的应答。

I2C引脚配置

  • 开漏输出+弱上拉:I2C引脚配置为开漏输出,并启用弱上拉电阻。这意味着,当引脚输出高电平时,实际上是释放引脚,由上拉电阻将其拉高。当引脚输出低电平时,引脚被拉低。

接收Byte程序

  • 循环读取SDA:在接收数据时,主机会不断循环读取SDA引脚的电平。在SCL时钟的驱动下,从机会在SDA线上放置数据。
  • 数据有效性:由于从机会在SCL高电平期间改变SDA的电平,因此主机在读取SDA时,可以获得从机发送的数据。

6.3 MPU6050初始化配置

配置电源管理寄存器1:0000 0001

设备复位:值:0  含义:设备不复位。

睡眠模式:值:0  含义:解除睡眠。

循环模式:值:0  含义:不需要循环。

无关位:值:0

温度传感器失能:值:0   含义:温度传感器不失能。

时钟源选择:值:001   含义:选择X轴的陀螺仪时钟。

配置电源管理寄存器2:0x00

循环唤醒模式:值:00  含义:不需要循环唤醒模式。

待机位:值:每个轴的待机位全部给0。

电源管理寄存器1和2主要用于控制MPU6050的电源状态和工作模式。通过设置这些参数,可以确保MPU6050在最佳状态下运行,并且根据需要调整其功耗表现。

采样率分频:值:0x09   含义:10分频,8位决定了数据输出的快慢,值越小越快。

配置寄存器:寄存器值:0x06

  • 外部同步:全部给0,不需要。

  • 数字低通滤波器:110,最平滑的滤波。

陀螺仪配置寄存器:0x18

  1. 自测使能:值:0  含义:不自测使能。

  2. 满量程选择:值:11  含义:选择最大量程。

  3. 无关位:后三位无关。

加速度计配置寄存器:0x18

  1. 自测使能:值:000  含义:不自测使能。
  2. 满量程选择:值:11  含义:选择最大量程。
  3. 高通滤波器:不使用。

6.4 传感器模型

​​​​​​​这里借用一张图片

  • 陀螺仪旋转检测

    • 陀螺仪绕Z轴旋转,陀螺仪Z轴会输出对应的角速度。
    • 图示中,三维空间的坐标轴X、Y、Z对应陀螺仪的三个方向。
    • 通过陀螺仪的测量,可以获得绕某一轴的旋转角速度信息,帮助理解物体的旋转状态。
  • 加速度计检测

    • 在正方体中放置一个小球,小球压在哪个面上就产生对应轴的输出。
    • 当前芯片水平放置,对应正方体的X轴、Y轴数据基本为0。
    • 小球在底面上,产生1个g的重力加速度,这里显示的数据是1943。
    • 1943代表Z轴方向的支持力,所以Z轴加速度为正。
  • 数据计算

    • 根据测量值1943和满量程32768(16位ADC),计算得出加速度的实际值。
    • 根据测量值1943和满量程32768(16位ADC),计算得出加速度的实际值。
    • 公式: 1943/32768 = Z/16g
    • 所以Z轴的加速度为0.95g。
  • 测量值比例公式
    • 读到的ADC值与满量程值之间的比例关系。
    • 公式: 读到的数据/32768 = X/满量程 (其中,满量程在16位系统中为-32768到32767)

7. 代码实现

软件I2C读写MPU6050

​​​​​​​7.1  MyI2C.c

#include "stm32f10x.h"                  // Device header
#include "Delay.h"/*引脚配置层*//*** 函    数:I2C写SCL引脚电平* 参    数:BitValue 协议层传入的当前需要写入SCL的电平,范围0~1* 返 回 值:无* 注意事项:此函数需要用户实现内容,当BitValue为0时,需要置SCL为低电平,当BitValue为1时,需要置SCL为高电平*/
void MyI2C_W_SCL(uint8_t BitValue)
{GPIO_WriteBit(GPIOB, GPIO_Pin_10, (BitAction)BitValue);		//根据BitValue,设置SCL引脚的电平Delay_us(10);												//延时10us,防止时序频率超过要求
}/*** 函    数:I2C写SDA引脚电平* 参    数:BitValue 协议层传入的当前需要写入SDA的电平,范围0~0xFF* 返 回 值:无* 注意事项:此函数需要用户实现内容,当BitValue为0时,需要置SDA为低电平,当BitValue非0时,需要置SDA为高电平*/
void MyI2C_W_SDA(uint8_t BitValue)
{GPIO_WriteBit(GPIOB, GPIO_Pin_11, (BitAction)BitValue);		//根据BitValue,设置SDA引脚的电平,BitValue要实现非0即1的特性Delay_us(10);												//延时10us,防止时序频率超过要求
}/*** 函    数:I2C读SDA引脚电平* 参    数:无* 返 回 值:协议层需要得到的当前SDA的电平,范围0~1* 注意事项:此函数需要用户实现内容,当前SDA为低电平时,返回0,当前SDA为高电平时,返回1*/
uint8_t MyI2C_R_SDA(void)
{uint8_t BitValue;BitValue = GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11);		//读取SDA电平Delay_us(10);												//延时10us,防止时序频率超过要求return BitValue;											//返回SDA电平
}/*** 函    数:I2C初始化* 参    数:无* 返 回 值:无* 注意事项:此函数需要用户实现内容,实现SCL和SDA引脚的初始化*/
void MyI2C_Init(void)
{/*开启时钟*/RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);	//开启GPIOB的时钟/*GPIO初始化*/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;GPIO_Init(GPIOB, &GPIO_InitStructure);					//将PB10和PB11引脚初始化为开漏输出/*设置默认电平*/GPIO_SetBits(GPIOB, GPIO_Pin_10 | GPIO_Pin_11);			//设置PB10和PB11引脚初始化后默认为高电平(释放总线状态)
}/*协议层*//*** 函    数:I2C起始* 参    数:无* 返 回 值:无*/
void MyI2C_Start(void)
{MyI2C_W_SDA(1);							//释放SDA,确保SDA为高电平MyI2C_W_SCL(1);							//释放SCL,确保SCL为高电平MyI2C_W_SDA(0);							//在SCL高电平期间,拉低SDA,产生起始信号MyI2C_W_SCL(0);							//起始后把SCL也拉低,即为了占用总线,也为了方便总线时序的拼接
}/*** 函    数:I2C终止* 参    数:无* 返 回 值:无*/
void MyI2C_Stop(void)
{MyI2C_W_SDA(0);							//拉低SDA,确保SDA为低电平MyI2C_W_SCL(1);							//释放SCL,使SCL呈现高电平MyI2C_W_SDA(1);							//在SCL高电平期间,释放SDA,产生终止信号
}/*** 函    数:I2C发送一个字节* 参    数:Byte 要发送的一个字节数据,范围:0x00~0xFF* 返 回 值:无*/
void MyI2C_SendByte(uint8_t Byte)
{uint8_t i;for (i = 0; i < 8; i ++)				//循环8次,主机依次发送数据的每一位{MyI2C_W_SDA(Byte & (0x80 >> i));	//使用掩码的方式取出Byte的指定一位数据并写入到SDA线MyI2C_W_SCL(1);						//释放SCL,从机在SCL高电平期间读取SDAMyI2C_W_SCL(0);						//拉低SCL,主机开始发送下一位数据}
}/*** 函    数:I2C接收一个字节* 参    数:无* 返 回 值:接收到的一个字节数据,范围:0x00~0xFF*/
uint8_t MyI2C_ReceiveByte(void)
{uint8_t i, Byte = 0x00;					//定义接收的数据,并赋初值0x00,此处必须赋初值0x00,后面会用到MyI2C_W_SDA(1);							//接收前,主机先确保释放SDA,避免干扰从机的数据发送for (i = 0; i < 8; i ++)				//循环8次,主机依次接收数据的每一位{MyI2C_W_SCL(1);						//释放SCL,主机机在SCL高电平期间读取SDAif (MyI2C_R_SDA() == 1){Byte |= (0x80 >> i);}	//读取SDA数据,并存储到Byte变量//当SDA为1时,置变量指定位为1,当SDA为0时,不做处理,指定位为默认的初值0MyI2C_W_SCL(0);						//拉低SCL,从机在SCL低电平期间写入SDA}return Byte;							//返回接收到的一个字节数据
}/*** 函    数:I2C发送应答位* 参    数:Byte 要发送的应答位,范围:0~1,0表示应答,1表示非应答* 返 回 值:无*/
void MyI2C_SendAck(uint8_t AckBit)
{MyI2C_W_SDA(AckBit);					//主机把应答位数据放到SDA线MyI2C_W_SCL(1);							//释放SCL,从机在SCL高电平期间,读取应答位MyI2C_W_SCL(0);							//拉低SCL,开始下一个时序模块
}/*** 函    数:I2C接收应答位* 参    数:无* 返 回 值:接收到的应答位,范围:0~1,0表示应答,1表示非应答*/
uint8_t MyI2C_ReceiveAck(void)
{uint8_t AckBit;							//定义应答位变量MyI2C_W_SDA(1);							//接收前,主机先确保释放SDA,避免干扰从机的数据发送MyI2C_W_SCL(1);							//释放SCL,主机机在SCL高电平期间读取SDAAckBit = MyI2C_R_SDA();					//将应答位存储到变量里MyI2C_W_SCL(0);							//拉低SCL,开始下一个时序模块return AckBit;							//返回定义应答位变量
}

​​​​​​​7.2  MyI2C.h

#ifndef __MYI2C_H
#define __MYI2C_Hvoid MyI2C_Init(void);
void MyI2C_Start(void);
void MyI2C_Stop(void);
void MyI2C_SendByte(uint8_t Byte);
uint8_t MyI2C_ReceiveByte(void);
void MyI2C_SendAck(uint8_t AckBit);
uint8_t MyI2C_ReceiveAck(void);#endif

7.3 MPU6050.C

#include "stm32f10x.h"                  // Device header
#include "MyI2C.h"
#include "MPU6050_Reg.h"#define MPU6050_ADDRESS		0xD0		//MPU6050的I2C从机地址/*** 函    数:MPU6050写寄存器* 参    数:RegAddress 寄存器地址,范围:参考MPU6050手册的寄存器描述* 参    数:Data 要写入寄存器的数据,范围:0x00~0xFF*/
void MPU6050_WriteReg(uint8_t RegAddress, uint8_t Data)
{MyI2C_Start();						//I2C起始MyI2C_SendByte(MPU6050_ADDRESS);	//发送从机地址,读写位为0,表示即将写入MyI2C_ReceiveAck();					//接收应答MyI2C_SendByte(RegAddress);			//发送寄存器地址MyI2C_ReceiveAck();					//接收应答MyI2C_SendByte(Data);				//发送要写入寄存器的数据MyI2C_ReceiveAck();					//接收应答MyI2C_Stop();						//I2C终止
}/*** 函    数:MPU6050读寄存器* 参    数:RegAddress 寄存器地址,范围:参考MPU6050手册的寄存器描述* 返 回 值:读取寄存器的数据,范围:0x00~0xFF*/
uint8_t MPU6050_ReadReg(uint8_t RegAddress)
{uint8_t Data;MyI2C_Start();						//I2C起始MyI2C_SendByte(MPU6050_ADDRESS);	//发送从机地址,读写位为0,表示即将写入MyI2C_ReceiveAck();					//接收应答MyI2C_SendByte(RegAddress);			//发送寄存器地址MyI2C_ReceiveAck();					//接收应答MyI2C_Start();						//I2C重复起始MyI2C_SendByte(MPU6050_ADDRESS | 0x01);	//发送从机地址,读写位为1,表示即将读取MyI2C_ReceiveAck();					//接收应答Data = MyI2C_ReceiveByte();			//接收指定寄存器的数据MyI2C_SendAck(1);					//发送应答,给从机非应答,终止从机的数据输出MyI2C_Stop();						//I2C终止return Data;
}/*** 函    数:MPU6050初始化* 参    数:无* 返 回 值:无*/
void MPU6050_Init(void)
{MyI2C_Init();									//先初始化底层的I2C/*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;						//数据拼接,通过输出参数返回
}

7.4 MPU6050.H

#ifndef __MPU6050_H
#define __MPU6050_Hvoid MPU6050_WriteReg(uint8_t RegAddress, uint8_t Data);
uint8_t MPU6050_ReadReg(uint8_t RegAddress);void MPU6050_Init(void);
uint8_t MPU6050_GetID(void);
void MPU6050_GetData(int16_t *AccX, int16_t *AccY, int16_t *AccZ, int16_t *GyroX, int16_t *GyroY, int16_t *GyroZ);#endif

7.5 MPU6050_Reg.h

#ifndef __MPU6050_REG_H
#define __MPU6050_REG_H#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

7.6 main.c

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "MPU6050.h"uint8_t ID;								//定义用于存放ID号的变量
int16_t AX, AY, AZ, GX, GY, GZ;			//定义用于存放各个数据的变量int main(void)
{/*模块初始化*/OLED_Init();		//OLED初始化MPU6050_Init();		//MPU6050初始化/*显示ID号*/OLED_ShowString(1, 1, "ID:");		//显示静态字符串ID = MPU6050_GetID();				//获取MPU6050的ID号OLED_ShowHexNum(1, 4, ID, 2);		//OLED显示ID号while (1){MPU6050_GetData(&AX, &AY, &AZ, &GX, &GY, &GZ);		//获取MPU6050的数据OLED_ShowSignedNum(2, 1, AX, 5);					//OLED显示数据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.mzph.cn/pingmian/30617.shtml

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

相关文章

支持 MKV、MP4、AVI、MPG 等格式视频转码器

一、简介 1、一款开源的视频转码器&#xff0c;适用于 Linux、Mac 和 Windows。它是一个免费的工具&#xff0c;由志愿者们开发&#xff0c;可以将几乎所有格式的视频转换为现代、广泛支持的编码格式。你可以在官网上下载该应用或源代码。该软件支持 MKV、MP4、AVI、MPG 等格式…

如何计算文档会消耗的Token数量?

在AI的世界里&#xff0c;"token"就像是把我们说的话或写的文字拆分成的小块块&#xff0c;每块可以是一个词、一个短语、一个标点&#xff0c;甚至一个字母。不同的AI系统可能有不同的拆分方法。 阿里云的灵积平台有个工具&#xff0c;叫做Token计算器。这个工具就…

经常混淆的ADC输入类型!

大家好,这里是大话硬件。 这篇文章我们来聊聊ADC的输入类型。 ADC的输入类型根据ADI的官网,分为了3种类型,单端,差分,伪差分。如下图快速选型的界面所示。 同时,TI的官网对ADC的输入类型划分也是同样的3种类型。 可见,两个器件厂家对ADC的输入类型都是这样定义的。 …

springMVC的bug

写SpringMVC时&#xff0c;配置视图解析器路径中少写了个“/”导致url拼接错误&#xff0c;无法返回视图

支持向量机 (SVM) 算法详解

支持向量机 (SVM) 算法详解 支持向量机&#xff08;Support Vector Machine, SVM&#xff09;是一种监督学习模型&#xff0c;广泛应用于分类和回归分析。SVM 特别适合高维数据&#xff0c;并且在处理复杂非线性数据时表现出色。本文将详细讲解 SVM 的原理、数学公式、应用场景…

项目中选择Entity Framework Core还是Dapper?

我是将 Dapper 还是 Entity framework core 用于下一个 .NET 项目&#xff1f;当你必须做出这个决定时&#xff0c;总是令人困惑&#xff0c;为了项目的成功&#xff0c;你需要做出正确的决定。让我来帮你... 介绍 使用 .NET 开发的应用程序可以根据其使用的对象关系映射器 &…

Java | Leetcode Java题解之第168题Excel表列名称

题目&#xff1a; 题解&#xff1a; class Solution {public String convertToTitle(int columnNumber) {StringBuffer sb new StringBuffer();while (columnNumber ! 0) {columnNumber--;sb.append((char)(columnNumber % 26 A));columnNumber / 26;}return sb.reverse().t…

【APP移动端性能测试】第一节.APP应用架构、环境和敏捷开发模型介绍

文章目录 前言一、APP应用架构二、APP项目环境 2.1 后端项目环境 2.2 前端项目环境三、Scrum敏捷开发模型 3.1 Scrum敏捷模型基础介绍 3.2 Scrum敏捷开发开发流程总结 前言 一、APP应用架构 &#xff08;1&#xff09;APP应用架构 &#xff08;2&#xff0…

springboot应用cpu飙升的原因排除

1、通过top或者jps命令查到是那个java进程&#xff0c; top可以看全局那个进程耗cpu&#xff0c;而jps则默认是java最耗cpu的&#xff0c;比如找到进程是196 1.1 top (推荐)或者jps命令均可 2、根据第一步获取的进程号&#xff0c;查询进程里那个线程最占用cpu&#xff0c;发…

js 用正则表达式 匹配自定义字符之间的字符串数据,如:( )、[ ]、{ }、< >、【】等括号之间的字符串数据

要使用正则表达式匹配尖括号()之间的数据&#xff0c;可以使用以下代码示例&#xff1a; 在JavaScript中&#xff0c;你可以使用正则表达式来匹配括号()之间的数据。以下是一个简单的例子&#xff0c;它展示了如何使用正则表达式来获取两对括号之间的文本。 // 示例字符串 con…

LENOVO联想 小新 16 IAH8 2023款(83BG)笔记本原厂Windows11系统,恢复出厂开箱状态预装OEM系统镜像安装包下载

适用型号&#xff1a;小新 16 IAH8【83BG】 链接&#xff1a;https://pan.baidu.com/s/18VbGbBXtQEW5P8wLIyJtAQ?pwddv1s 提取码&#xff1a;dv1s 联想原装Win11系统自带所有驱动、出厂主题壁纸、系统属性联机支持标志、系统属性专属LOGO标志、Office办公软件、联想电脑管家…

董宇辉的人生关键词:年轻人的成长指南

在当今这个信息爆炸、竞争激烈的社会中&#xff0c;年轻人面临着前所未有的挑战与机遇。如何从纷繁复杂的世界中找到属于自己的道路&#xff1f;近日&#xff0c;知名人生导师董宇辉为年轻人提出了几个人生关键词&#xff0c;这些词汇不仅凝聚了他多年的人生感悟&#xff0c;更…

【STM32-ST-Link】

STM32-ST-Link ■ ST-Link简介■ ST-Link驱动的安装。■ ST-Link编程软件(MDK)配置。■ ST-Link固件升级方法 ■ ST-Link简介 由于德产 J-LINK 价格非常昂贵&#xff0c; 而国产 J-LINK 因为版权问题将在万能的淘宝销声匿迹。 所以我们有必要给大家介绍 JTAG/SWD 调试工具中另…

如何做好技术管理与技术规划?

一、背景 做好技术管理不仅要求紧跟行业前沿动态&#xff0c;同时也需把握好产品开发的阶段性分期&#xff0c;确保技术成果转化和产品落地的顺畅进行。技术管理的成功与否&#xff0c;在很大程度上取决于能否精准捕捉市场需求&#xff0c;据此调整任务优先级。面对瞬息万变的…

Day10—Spark SQL基础

Spark SQL介绍 ​ Spark SQL是一个用于结构化数据处理的Spark组件。所谓结构化数据&#xff0c;是指具有Schema信息的数据&#xff0c;例如JSON、Parquet、Avro、CSV格式的数据。与基础的Spark RDD API不同&#xff0c;Spark SQL提供了对结构化数据的查询和计算接口。 Spark …

IDEA上MySQL的jar包导入教程

jar包下载网址——》https://mvnrepository.com/ 1.进入界面&#xff0c;点击搜索框&#xff0c;搜索mysql&#xff1a; 外国网站&#xff0c;可能有点慢,耐心等待即可。 2.点击查询结果&#xff1a; 进入界面&#xff0c;点击前两个结果的其中一个&#xff0c;两个都可以 …

计算机毕业设计Python+Vue.js+Flask+Scrapy电影大数据分析 电影推荐系统 电影爬虫可视化 电影数据分析 大数据毕业设计 协同过滤算法

开发技术 协同过滤算法、机器学习、vue.js、echarts、Flask、Python、MySQL 创新点 协同过滤推荐算法、爬虫、数据可视化 补充说明 两种Python协同过滤推荐算法集成 (ItemCF推荐算法 和 UserCF 推荐算法) 2.专业美工整体设计的细腻的酷黑主题&#xff0c;前后端分离一体化系统&…

leetCode-hot100-链表专题

leetCode-hot100-链表专题 链表简介单链表单链表的使用例题206.反转链表19.删除链表的倒数第N个结点24.两两交换链表中的节点25.K个一组翻转链表 双向链表双向链表的使用 循环链表61.旋转链表141.环形链表142.环形链表Ⅱ LinkedListLinkedList的使用 链表简介 参考博客&#x…

文章解读与仿真程序复现思路——电力系统自动化EI\CSCD\北大核心《协同考虑空气质量与热舒适度的空调系统双层优化控制策略》

本专栏栏目提供文章与程序复现思路&#xff0c;具体已有的论文与论文源程序可翻阅本博主免费的专栏栏目《论文与完整程序》 论文与完整源程序_电网论文源程序的博客-CSDN博客https://blog.csdn.net/liang674027206/category_12531414.html 电网论文源程序-CSDN博客电网论文源…

数据资产安全保卫战:构建多层次、全方位的数据安全防护体系,守护企业核心数据资产安全

一、引言 在信息化时代&#xff0c;数据资产已成为企业运营的核心&#xff0c;其安全性直接关系到企业的生存与发展。然而&#xff0c;随着网络技术的飞速发展&#xff0c;数据泄露、黑客攻击等安全威胁日益增多&#xff0c;给企业的数据资产安全带来了严峻挑战。因此&#xf…