正在准备今年的国赛,打算做一个PID控制题目,于是就选了一个相对比较简单的风力摆,在CSDN上面搜了很多资料,但是都大同小异,没有看明白,源码也很多,但是无从下手(心酸),偶然之间看到正点原子资料里面有介绍MPU6050模块。
抱着试一试的心理看完了,才明白这个道理——正点原子才是yyds。视频思路非常详细,虽然一些地方需要做一些修改,但是整体还是很清晰的,借着刚学完MPU6050的劲头,写篇博客记录一下学习过程。
陀螺仪是用高速回转体的动量矩敏感壳体相对惯性空间绕正交于自转轴的一个或二个轴的角运动检测装置。利用其他原理制成的角运动检测装置起同样功能的也称陀螺仪。
从力学的观点近似的分析陀螺的运动时,可以把它看成是一个刚体,刚体上有一个万向支点,而陀螺可以绕着这个支点作三个自由度的转动,所以陀螺的运动是属于刚体绕一个定点的转动运动。
更确切地说,一个绕对称铀高速旋转的飞轮转子叫陀螺。将陀螺安装在框架装置上,使陀螺的自转轴有角转动的自由度,这种装置的总体叫做陀螺仪。
陀螺仪的原理就是,一个旋转物体的旋转轴所指的方向在不受外力影响时,是不会改变的。人们根据这个道理,用它来保持方向,制造出来的东西就叫陀螺仪。
我们骑自行车其实也是利用了这个原理。轮子转得越快越不容易倒,因为车轴有一股保持水平的力量。陀螺仪在工作时要给它一个力,使它快速旋转起来,一般能达到每分钟几十万转,可以工作很长时间。然后用多种方法读取轴所指示的方向,并自动将数据信号传给控制系统。
简介:
MPU6050内部整合了三轴MEMS陀螺仪、三轴MEMS加速度计以及一个可扩展的数字运动处理器DMP(Digital Motion Processor),而且还可以连接一个第三方数字传感器(如磁力计),这样的话,就可以通过IIC接口输出一个9轴信号(链接第三方数字传感器才可以输出九轴信号,否则只有六轴信号)。
更加方便的是,有了DMP,可以结合InvenSense公司提供的运动处理资料库,实现姿态解算。
通过自带的DMP,可以通过IIC接口输出9轴融合演算的数据,大大降低了运动处理运算对操作系统的负荷,同时也降低了开发难度。_其实,简单一句话说,陀螺仪就是测角速度的,加速度传感器就是测角加速度的,二者数据通过算法就可以得到PITCH、YAW、ROLL角了。
特点:
MPU6050三轴角(姿态角):
绕向即为正方向,可根据右手螺旋定则确定方向。
MPU6050框图:
可以很清晰地观察到,MPU6050芯片中内置了三轴加速度传感器、三轴陀螺仪和一个温度传感器。右侧INT为中断输出脚,TCS为片选脚、AD0为设置地址脚、SCL和SDA为主IIC接口、AUX_CL和AUX_DA为从IIC接口,主要用到的是AD0、SCL、SDA。
相关寄存器(想深入了解的朋友可以看一下)结合后面的代码更容易理解一些
DEVICE_RESE=1,复位MPU6050,复位完成后,自动清零。SLEEP=1,进入睡眠模式;SLEEP=0,正常工作模式。TEMP_DIS,用于设置是否使能温度传感器,设置为0,则使能CLKSEL[2:0],用于选择系统时钟源,如下所示:
CLKSEL[2:0] | 时钟源 |
---|---|
001 | 内部8M RC晶振 |
010 | PLL,使用X轴陀螺作为参考 |
011 | PLL,使用Y轴陀螺作为参考 |
100 | PLL,使用Z轴陀螺作为参考 |
101 | PLL,使用外部32.768Khz作为参考 |
110 | PLL,使用外部19.2Mhz作为参考 |
11 | 保留 |
001 | 关闭时钟,保持时序产生电路复位状态 |
总之,电源管理寄存器就是复位MPU6050
该寄存器我们只关心FS_SEL[1:0]这两个位,用于设置陀螺仪的满量程范围:0,±250°/s;
1,±500°/s;
2,±1000°/s;
3,±2000°/s;
我们一般设置为3,即±2000°/S,因为陀螺仪的ADC为16位分辨率,所以得到灵敏度为:65536/4000=16.4LSB/(°/S)。
总之,陀螺仪配置寄存器就是配置陀螺仪满量程范围,设置最大
该寄存器我们只关心AFS_SEL[1:0]这两个位,用于设置加速度传感器的满量程范围:0,±2g;
1,±4g;
2,±8g;
3,±16g;
我们一般设置为0,即±2g,因为加速度传感器的ADC也是16位,所以得到灵敏度为:65536/4=16384LSB/g。
总之,加速度传感器配置寄存器就是配置加速度传感器满量程范围,不宜过大
该寄存器用于控制FIFO使能,在简单读取传感器数据的时候,可以不用FIFO,设置对应位为:0,即可禁止FIFO,设置为1,则使能FIFO。加速度传感器的三个轴,全由一个位(ACCEL_FIFO_EN)控制,只要该位为1,则加速度传感器三个通道都开启FIFO;但是陀螺仪传感器的三个轴需要一个一个设置,即XYZ轴分别配置。
总之,FIFO使能寄存器用于控制使能FIFO(First Input First Output)
该寄存器用于设置MPU6050的陀螺仪采样频率,计算公式为:**采样频率 = 陀螺仪输出频率 / (1+SMPLRT_DIV)**。这里陀螺仪的输出频率,是1Khz或者8Khz,与数字低通滤波器(DLPF)的设置有关,当DLPF_CFG=0或7的时候,频率为8Khz,其他情况是1Khz。而且DLPF滤波频率一般设置为采样率的一半。
采样率,我们假定设置为50Hz,那么:SMPLRT_DIV=1000/50-1=19。
总之,陀螺仪采样率分频寄存器就是用于设置陀螺仪的采样频率,如果采样频率为50Hz,那么采样周期就为1/50=20ms,即20ms采集一次陀螺仪的数据。
总之,配置寄存器就是设置数字低通滤波器的DLPF_CFG位来结合陀螺仪采样分频寄存器来共同设置采样周期。
该寄存器的LP_WAKE_CTRL用于控制低功耗时的唤醒频率,用不到。剩下的6位,分别控制加速度和陀螺仪的x/y/z轴是否进入待机模式,这里我们全部都不进入待机模式,所以全部设置为:0 ,即可。
总之,电源管理寄存器2就是用于设置加速度传感器和陀螺仪的X/Y/Z轴是进入休眠还是正常工作。
加速度传感器数据输出寄存器总共由6个寄存器组成,输出X/Y/Z三个轴的加速度传感器值,高字节在前,低字节在后。
总之,加速度传感器数据输出寄存器就是把加速度传感器测量到的数据输出出来。
陀螺仪数据输出寄存器总共由6个寄存器组成,输出X/Y/Z三个轴的陀螺仪传感器数据,高字节在前,低字节在后。
总之,陀螺仪数据输出寄存器就是把陀螺仪测量到的数据输出出来。
通过读取0X41(高8位)和0X42(低8位)寄存器得到,温度换算公式为:
Temperature = 36.53 + regval/340。其中,Temperature为计算得到的温度值,单位为℃,regval为从0X41和0X42读到的温度传感器值。
总之,温度传感器数据输出寄存器就是把温度寄存器测量到的数据处处出来。
VCC:接5V电源
GND:接地
SCL:主IIC时钟线 (我接的PB10)
SDA:主IIC数据线 (我接的PB11)
AD0:地址线,接3V地址为0x68,接地地址为0x69(我接的PA15,高电平,地址为0x68)
MPU6050处理寄存器的相关数据时需要移植几个官方库,以便将数据处理为所需要的欧拉角。正点原子共提供了五个源码,如下图:
这些是需要在写MPU6050代码之前移植过来的,具体代码内容大家可以下载下方源码查看。
MPU6050.h
主要宏定义一些MPU6050寄存器的地址,方便IIC发送给寄存器数据初始化MPU6050。
#ifndef __MPU6050_H
#define __MPU6050_H
#include "mpuiic.h"
#define MPU_AD0_CTRL PAout(15)
#define MPU_SELF_TESTX_REG 0X0D
#define MPU_SELF_TESTY_REG 0X0E
#define MPU_SELF_TESTZ_REG 0X0F
#define MPU_SELF_TESTA_REG 0X10
#define MPU_SAMPLE_RATE_REG 0X19
#define MPU_CFG_REG 0X1A
#define MPU_GYRO_CFG_REG 0X1B
#define MPU_ACCEL_CFG_REG 0X1C
#define MPU_MOTION_DET_REG 0X1F
#define MPU_FIFO_EN_REG 0X23
#define MPU_I2CMST_CTRL_REG 0X24
#define MPU_I2CSLV0_ADDR_REG 0X25
#define MPU_I2CSLV0_REG 0X26
#define MPU_I2CSLV0_CTRL_REG 0X27
#define MPU_I2CSLV1_ADDR_REG 0X28
#define MPU_I2CSLV1_REG 0X29
#define MPU_I2CSLV1_CTRL_REG 0X2A
#define MPU_I2CSLV2_ADDR_REG 0X2B
#define MPU_I2CSLV2_REG 0X2C
#define MPU_I2CSLV2_CTRL_REG 0X2D
#define MPU_I2CSLV3_ADDR_REG 0X2E
#define MPU_I2CSLV3_REG 0X2F
#define MPU_I2CSLV3_CTRL_REG 0X30
#define MPU_I2CSLV4_ADDR_REG 0X31
#define MPU_I2CSLV4_REG 0X32
#define MPU_I2CSLV4_DO_REG 0X33
#define MPU_I2CSLV4_CTRL_REG 0X34
#define MPU_I2CSLV4_DI_REG 0X35
#define MPU_I2CMST_STA_REG 0X36
#define MPU_INTBP_CFG_REG 0X37
#define MPU_INT_EN_REG 0X38
#define MPU_INT_STA_REG 0X3A
#define MPU_ACCEL_XOUTH_REG 0X3B
#define MPU_ACCEL_XOUTL_REG 0X3C
#define MPU_ACCEL_YOUTH_REG 0X3D
#define MPU_ACCEL_YOUTL_REG 0X3E
#define MPU_ACCEL_ZOUTH_REG 0X3F
#define MPU_ACCEL_ZOUTL_REG 0X40
#define MPU_TEMP_OUTH_REG 0X41
#define MPU_TEMP_OUTL_REG 0X42
#define MPU_GYRO_XOUTH_REG 0X43
#define MPU_GYRO_XOUTL_REG 0X44
#define MPU_GYRO_YOUTH_REG 0X45
#define MPU_GYRO_YOUTL_REG 0X46
#define MPU_GYRO_ZOUTH_REG 0X47
#define MPU_GYRO_ZOUTL_REG 0X48
#define MPU_I2CSLV0_DO_REG 0X63
#define MPU_I2CSLV1_DO_REG 0X64
#define MPU_I2CSLV2_DO_REG 0X65
#define MPU_I2CSLV3_DO_REG 0X66
#define MPU_I2CMST_DELAY_REG 0X67
#define MPU_SIGPATH_RST_REG 0X68
#define MPU_MDETECT_CTRL_REG 0X69
#define MPU_USER_CTRL_REG 0X6A
#define MPU_PWR_MGMT1_REG 0X6B
#define MPU_PWR_MGMT2_REG 0X6C
#define MPU_FIFO_CNTH_REG 0X72
#define MPU_FIFO_CNTL_REG 0X73
#define MPU_FIFO_RW_REG 0X74
#define MPU_DEVICE_ID_REG 0X75
#define MPU_ADDR 0X68
u8 MPU_Init(void);
u8 MPU_Write_Len(u8 addr,u8 reg,u8 len,u8 *buf);
u8 MPU_Read_Len(u8 addr,u8 reg,u8 len,u8 *buf);
u8 MPU_Write_Byte(u8 reg,u8 data);
u8 MPU_Read_Byte(u8 reg);
u8 MPU_Set_Gyro_Fsr(u8 fsr);
u8 MPU_Set_Accel_Fsr(u8 fsr);
u8 MPU_Set_LPF(u16 lpf);
u8 MPU_Set_Rate(u16 rate);
u8 MPU_Set_Fifo(u8 sens);
short MPU_Get_Temperature(void);
u8 MPU_Get_Gyroscope(short *gx,short *gy,short *gz);
u8 MPU_Get_Accelerometer(short *ax,short *ay,short *az);
#endif
MPU6050.c
主要是单片机通过IIC协议向MPU6050写数据读数据的函数以及MPU6050初始化函数
#include "mpu6050.h"
#include "sys.h"
#include "delay.h"
#include "usart.h"
u8 MPU_Init(void)
{
u8 res;
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_15;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable,ENABLE);
MPU_AD0_CTRL=0;
MPU_IIC_Init();
MPU_Write_Byte(MPU_PWR_MGMT1_REG,0X80);
delay_ms(100);
MPU_Write_Byte(MPU_PWR_MGMT1_REG,0X00);
MPU_Set_Gyro_Fsr(3);
MPU_Set_Accel_Fsr(0);
MPU_Set_Rate(50);
MPU_Write_Byte(MPU_INT_EN_REG,0X00);
MPU_Write_Byte(MPU_USER_CTRL_REG,0X00);
MPU_Write_Byte(MPU_FIFO_EN_REG,0X00);
MPU_Write_Byte(MPU_INTBP_CFG_REG,0X80);
res=MPU_Read_Byte(MPU_DEVICE_ID_REG);
if(res==MPU_ADDR)
{
MPU_Write_Byte(MPU_PWR_MGMT1_REG,0X01);
MPU_Write_Byte(MPU_PWR_MGMT2_REG,0X00);
MPU_Set_Rate(50);
}else return 1;
return 0;
}
u8 MPU_Set_Gyro_Fsr(u8 fsr)
{
return MPU_Write_Byte(MPU_GYRO_CFG_REG,fsr<<3);
}
u8 MPU_Set_Accel_Fsr(u8 fsr)
{
return MPU_Write_Byte(MPU_ACCEL_CFG_REG,fsr<<3);
}
u8 MPU_Set_LPF(u16 lpf)
{
u8 data=0;
if(lpf>=188)data=1;
else if(lpf>=98)data=2;
else if(lpf>=42)data=3;
else if(lpf>=20)data=4;
else if(lpf>=10)data=5;
else data=6;
return MPU_Write_Byte(MPU_CFG_REG,data);
}
u8 MPU_Set_Rate(u16 rate)
{
u8 data;
if(rate>1000)rate=1000;
if(rate<4)rate=4;
data=1000/rate-1;
data=MPU_Write_Byte(MPU_SAMPLE_RATE_REG,data);
return MPU_Set_LPF(rate/2);
}
short MPU_Get_Temperature(void)
{
u8 buf[2];
short raw;
float temp;
MPU_Read_Len(MPU_ADDR,MPU_TEMP_OUTH_REG,2,buf);
raw=((u16)buf[0]<<8)|buf[1];
temp=36.53+((double)raw)/340;
return temp*100;
}
u8 MPU_Get_Gyroscope(short *gx,short *gy,short *gz)
{
u8 buf[6],res;
res=MPU_Read_Len(MPU_ADDR,MPU_GYRO_XOUTH_REG,6,buf);
if(res==0)
{
*gx=((u16)buf[0]<<8)|buf[1];
*gy=((u16)buf[2]<<8)|buf[3];
*gz=((u16)buf[4]<<8)|buf[5];
}
return res;
}
u8 MPU_Get_Accelerometer(short *ax,short *ay,short *az)
{
u8 buf[6],res;
res=MPU_Read_Len(MPU_ADDR,MPU_ACCEL_XOUTH_REG,6,buf);
if(res==0)
{
*ax=((u16)buf[0]<<8)|buf[1];
*ay=((u16)buf[2]<<8)|buf[3];
*az=((u16)buf[4]<<8)|buf[5];
}
return res;
}
u8 MPU_Write_Len(u8 addr,u8 reg,u8 len,u8 *buf)
{
u8 i;
MPU_IIC_Start();
MPU_IIC_Send_Byte((addr<<1)|0);
if(MPU_IIC_Wait_Ack())
{
MPU_IIC_Stop();
return 1;
}
MPU_IIC_Send_Byte(reg);
MPU_IIC_Wait_Ack();
for(i=0;i {
MPU_IIC_Send_Byte(buf[i]);
if(MPU_IIC_Wait_Ack())
{
MPU_IIC_Stop();
return 1;
}
}
MPU_IIC_Stop();
return 0;
}
u8 MPU_Read_Len(u8 addr,u8 reg,u8 len,u8 *buf)
{
MPU_IIC_Start();
MPU_IIC_Send_Byte((addr<<1)|0);
if(MPU_IIC_Wait_Ack())
{
MPU_IIC_Stop();
return 1;
}
MPU_IIC_Send_Byte(reg);
MPU_IIC_Wait_Ack();
MPU_IIC_Start();
MPU_IIC_Send_Byte((addr<<1)|1);
MPU_IIC_Wait_Ack();
while(len)
{
if(len==1) *buf=MPU_IIC_Read_Byte(0);
else *buf=MPU_IIC_Read_Byte(1);
len--;
buf++;
}
MPU_IIC_Stop();
return 0;
}
u8 MPU_Write_Byte(u8 reg,u8 data)
{
MPU_IIC_Start();
MPU_IIC_Send_Byte((MPU_ADDR<<1)|0);
if(MPU_IIC_Wait_Ack())
{
MPU_IIC_Stop();
return 1;
}
MPU_IIC_Send_Byte(reg);
MPU_IIC_Wait_Ack();
MPU_IIC_Send_Byte(data);
if(MPU_IIC_Wait_Ack())
{
MPU_IIC_Stop();
return 1;
}
MPU_IIC_Stop();
return 0;
}
u8 MPU_Read_Byte(u8 reg)
{
u8 res;
MPU_IIC_Start();
MPU_IIC_Send_Byte((MPU_ADDR<<1)|0);
MPU_IIC_Wait_Ack();
MPU_IIC_Send_Byte(reg);
MPU_IIC_Wait_Ack();
MPU_IIC_Start();
MPU_IIC_Send_Byte((MPU_ADDR<<1)|1);
MPU_IIC_Wait_Ack();
res=MPU_IIC_Read_Byte(0);
MPU_IIC_Stop();
return res;
}
mpuiic.h
MPU的IIC协议函数头文件,PB11为SDA,PB10为SCL。
#ifndef __MPUIIC_H
#define __MPUIIC_H
#include "sys.h"
#define MPU_SDA_IN() {GPIOB->CRH &= 0XFFFF0FFF;GPIOB->CRH |= 8<<12;}
#define MPU_SDA_OUT() {GPIOB->CRH &= 0XFFFF0FFF;GPIOB->CRH |= 3<<12;}
#define MPU_IIC_SCL PBout(10)
#define MPU_IIC_SDA PBout(11)
#define MPU_READ_SDA PBin(11)
void MPU_IIC_Delay(void);
void MPU_IIC_Init(void);
void MPU_IIC_Start(void);
void MPU_IIC_Stop(void);
void MPU_IIC_Send_Byte(u8 txd);
u8 MPU_IIC_Read_Byte(unsigned char ack);
u8 MPU_IIC_Wait_Ack(void);
void MPU_IIC_Ack(void);
void MPU_IIC_NAck(void);
void IMPU_IC_Write_One_Byte(u8 daddr,u8 addr,u8 data);
u8 MPU_IIC_Read_One_Byte(u8 daddr,u8 addr);
#endif
mpuiic.c
MPU的IIC协议函数编写
#include "mpuiic.h"
#include "delay.h"
void MPU_IIC_Delay(void)
{
delay_us(2);
}
void MPU_IIC_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10|GPIO_Pin_11;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_SetBits(GPIOB,GPIO_Pin_10|GPIO_Pin_11);
}
void MPU_IIC_Start(void)
{
MPU_SDA_OUT();
MPU_IIC_SDA=1;
MPU_IIC_SCL=1;
MPU_IIC_Delay();
MPU_IIC_SDA=0;
MPU_IIC_Delay();
MPU_IIC_SCL=0;
}
void MPU_IIC_Stop(void)
{
MPU_SDA_OUT();
MPU_IIC_SCL=0;
MPU_IIC_SDA=0;
MPU_IIC_Delay();
MPU_IIC_SCL=1;
MPU_IIC_SDA=1;
MPU_IIC_Delay();
}
u8 MPU_IIC_Wait_Ack(void)
{
u8 ucErrTime=0;
MPU_SDA_IN();
MPU_IIC_SDA=1;MPU_IIC_Delay();
MPU_IIC_SCL=1;MPU_IIC_Delay();
while(MPU_READ_SDA)
{
ucErrTime++;
if(ucErrTime>250)
{
MPU_IIC_Stop();
return 1;
}
}
MPU_IIC_SCL=0;
return 0;
}
void MPU_IIC_Ack(void)
{
MPU_IIC_SCL=0;
MPU_SDA_OUT();
MPU_IIC_SDA=0;
MPU_IIC_Delay();
MPU_IIC_SCL=1;
MPU_IIC_Delay();
MPU_IIC_SCL=0;
}
void MPU_IIC_NAck(void)
{
MPU_IIC_SCL=0;
MPU_SDA_OUT();
MPU_IIC_SDA=1;
MPU_IIC_Delay();
MPU_IIC_SCL=1;
MPU_IIC_Delay();
MPU_IIC_SCL=0;
}
void MPU_IIC_Send_Byte(u8 txd)
{
u8 t;
MPU_SDA_OUT();
MPU_IIC_SCL=0;
for(t=0;t<8;t++)
{
MPU_IIC_SDA=(txd&0x80)>>7;
txd<<=1;
MPU_IIC_SCL=1;
MPU_IIC_Delay();
MPU_IIC_SCL=0;
MPU_IIC_Delay();
}
}
u8 MPU_IIC_Read_Byte(unsigned char ack)
{
unsigned char i,receive=0;
MPU_SDA_IN();
for(i=0;i<8;i++)
{
MPU_IIC_SCL=0;
MPU_IIC_Delay();
MPU_IIC_SCL=1;
receive<<=1;
if(MPU_READ_SDA)receive++;
MPU_IIC_Delay();
}
if (!ack)
MPU_IIC_NAck();
else
MPU_IIC_Ack();
return receive;
}
还有一些函数就不做过多赘述了,上面的代码备注已经做好了,认真吃肯定是会吃透的。多下点功夫肯定可以看懂。
补充1:FIFO
FIFO( First Input First Output)简单说就是指先进先出。由于微电子技术的飞速发展,新一代FIFO芯片容量越来越大,体积越来越小,价格越来越便宜。作为一种新型大规模集成电路,FIFO芯片以其灵活、方便、高效的特性,逐渐在高速数据采集、高速数据处理、高速数据传输以及多机处理系统中得到越来越广泛的应用。
在系统设计中,以增加数据传输率、处理大量数据流、匹配具有不同传输率的系统为目的而广泛使用FIFO存储器,从而提高了系统性能。FIFO存储器是一个先入先出的双口缓冲器,即第一个进入其内的数据第一个被移出,其中一个是存储器的输入口,另一个口是存储器的输出口。
对于单片FIFO来说,主要有两种结构:触发导向结构和零导向传输结构。触发导向传输结构的FIFO是由寄存器阵列构成的,零导向传输结构的FIFO是由具有读和写地址指针的双口RAM构成。详见这篇博客:FIFO工作原理
补充2:关于PA15使用问题
在使用PA15作为普通IO口的时候,需要禁用JTAG才可以,代码如下:
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable,ENABLE);
这是个大坑
补充3:关于MPU6050参考点
当代码烧入后,MPU的参考点是什么呢?参考点其实就是MPU6050初始化之后一开始的位置,没有一个强制的规定哪一个方向就是基准点,初始化之后的初始位置就是(0,0,0)点。
PITCH(俯仰角)、ROLL(翻滚角)、YAW(偏航角),单位均为 度。TEMP为当前温度,单位为摄氏度。
MPU6050模块通过OLED显示姿态角源码 https://download.csdn.net/download/lihaotian111/19131254?spm=1001.2014.3001.5501
往期博客:OpenMv与STM32通信讲解 https://blog.csdn.net/lihaotian111/article/details/116384913?spm=1001.2014.3001.5501
最新博客:HC-SR04超声波测距模块 https://blog.csdn.net/lihaotian111/article/details/118975220?spm=1001.2014.3001.5501
推荐阅读
01 |加入嵌入式交流群 |
02 |嵌入式资源获取 |
03 |STM32中断优先级详解 |
04 |STM32下载程序新思路--使用串口下载程序 |