基于战舰V3的MPU6050六轴陀螺仪实验
陀螺仪的分类
3轴传感器指的是3轴的加速度,根据这个加速度我们解算出XY两轴的角度。
6轴传感器指的是3轴的加速度和3轴角速度,根据这两个数据我们解算出XYZ三轴的角度(Z轴是角速度积分解算,所以存在累计误差)。
9轴传感器指的是3轴的加速度、3轴角速度和3轴磁场,根据这三个数据我们解算出XYZ三轴的角度(Z轴是磁场解算,相当于电子罗盘,但受磁场干扰的影响),九轴传感器彻底解决了Z轴角度的漂移,使用前需磁场校准,使用时原理磁场干扰区域。
10轴传感器指的是3轴的加速度、3轴角速度、和3轴磁场气压,功能比9轴传感器多了气压和高度,十轴传感器彻底解决了Z轴角度的漂移,使用前需磁场校准,使用时原理磁场干扰区域。
FIFO存储器简介
在系统设计中,以增加数据传输率、处理大量数据流、匹配具有不同传输率的系统(就是输入输出速度可以不相同)为目的而广泛使用FIFO存储器,从而提高了系统性能。FIFO存储器是一个先入先出的双口缓冲器,即第一个进入其内的数据第一个被移出,其中一个是存储器的输入口,另一个口是存储器的输出口。
你可以将FIFO看作是一个队列缓冲区,外部传感器不断采集数据发送至MCU,MCU被动接收并且处理数据,这就导致MCU的数据接收处理能力要慢于外部传感器的数据采集能力。但是如果MCU一次处理多个数据那么MCU处理输出的效率就上去了,这样,MCU与外部传感器就会达到一个平衡:MCU处理输出的速度=传感器向MCU发送数据的速度。这样做也有利于降低功耗,一个一个的处理会导致MCU频繁处于运行状态,但是如果每隔一段时间处理一次数据,那么这一段时间内数据存储器消耗的功率肯定要小于MCU工作所消耗的功率,总体来说是降低了系统功耗。
MPU6050引脚简介
表格 1
INT | 中断输出引脚(可在多种情况下输出中断信号) |
VCC | 3.3V或者5V |
GND | 电源地 |
IIC_SDA | 时钟输出引脚 |
IIC_SCL | 时钟输入引脚 |
MPU6050与MCU的IIC通信
当我们实现I2C的通信函数之后就可以与带有I2C通信接口的芯片进行通信,那么怎样通信呢?其实很简单——你可以把每个芯片比做为一个巨大的储物柜,储物柜里每个抽屉里存着相应的东西,你想让佣人帮你去拿个东西,只要告诉佣人对应的抽屉号就行了。这里I2C总线相当于这个佣人,每个抽屉相当于芯片中的寄存器,抽屉号相当于寄存器地址。当你想设置芯片的某些属性时是向对应的寄存器内写数据,当想从芯片内获取相关数据时,就要通过I2C向对应的地址写数据然后接收芯片返回的数据。这里的8~31行为MPU-6050芯片内几个常用的寄存器地址,前四个常用来作为设置芯片工作属性,15~28共14个寄存器地址用来获取传感器的3轴加速度、3轴角速度和温度的数据(这里每一种信息都包括H和L两位,是由于8位表示不完该数据,于是分高低两部分)
系统架构
系统中的辅助IIC的功能如下:
从上图可以看到辅助IIC用于MPU6050作为主机同最多4个IIC从机进行通信。
当MPU6050作为IIC从机与MCU主机通信时,AD0的高低电平影响MPU6050的IIC的从机地址:
C语言中有符号数的表示
有符号数的表示方法是由硬件决定(从寄存器中读取16位有符号数的时候会发现),而不是由C决定的。有三种表示方法:
计算机中的负数是以其补码形式存在的补码=原码取反+1,一个字节有8位可以表示的数值范围在-128到+127(unsigned char类型所表示的数值范围),用二进制表示也就是 10000000 - 01111111(注意:最高位表示符号)最高位是1的都是负数 最高位是0的都是正数。
例如:-7 原码是 10000111 然后取反(注意:最高位是符号位,数值位仅为[7:0]位,只有数值位才取反)得11111000,那+1得11111001,那么-7的二进制数就是 11111001。
相反,我们由负数的反码得到十进制的负数,也可以逆操作:取反+1,例如:11111001的数据类型为有符号数据类型,对数值位[7:0]取反后为10000110,再+1得到10000111,最终化为十进制有符号数值为-7。(符号位为1,数值为负数,否则为正数)。
故计算机中有符号数据的补码为:
1. 正数的补码:正数本身
2. 负数的补码:符号位不变,数值位取反再加一。
MPU6050寄存器简介
0X0D~0X10-自检寄存器
这里无需知道如何自检,只需知道自检的原理是什么即可。
自检测功能是允许用户自己去测试MPU6050的陀螺仪和加速度计的机械电气部分是否正常的一个功能,其中STR是自检测功能开启后设备自己测试的到的一个值,FT是厂家测试的到的一个值,如果6050的响应值相对于厂家测试值的百分比不再范围内(%=(STR-FT)/FT),则说明芯片测的数据不正常
开始自检之后Gyroscope就会测量地转偏向,Gyroscope Sensor 就会测量这个偏向;Accelerometer就会用电子运动产生激励,Accelerometer Sensor就会测量这个加速度。两个测量值与原值相减得到一个差值,差值再和预设值比较,范围内则通过自检,范围外则不通过。
注意:自检是要静止的,自检可以通过6050的输出数据判断芯片正不正常。
陀螺仪的STR值得测量原理如下所示:
就是相当于一个校准过程,我们先自己测量一个准确值,然后对比器件输出得值,看看偏差是否在合理范围之内。以Xg为例,Xg的[4:0]位存在于在0X0D寄存器中,我们将测量好的值放入0X0D寄存器中,然后器件会自动计算出结果和自身的测量值比较,看看是否在容许误差范围之内。
加速度计的STR值得测量原理如下所示:
就是相当于一个校准过程,我们先自己测量一个准确值,然后对比器件输出得值,看看偏差是否在合理范围之内。那Xa举例,Xa的[4:2]和[1:0]分开放置在两个寄存器中,我们要将我们测量得到的由5位二进制数组成的Xa的[4:2]和[1:0]位分别写入0X10和0X0D寄存器中。然后器件会自动计算出结果和自身的测量值比较,看看是否在容许误差范围之内。
0X19-采样频率分频寄存器
该寄存器的输入参数为8位无符号二进制数,该寄存器设定了采样频率与输出频率的关系:
器件的输出频率=采样频率/(SMPLRT_DIV+1)
我们要知道陀螺仪和加速度计的输出频率是不一样的,因此分频之后采样频率也不一样:
1. Gyroscope Output Rate = 8kHz when the DLPF is disabled (DLPF_CFG = 0 or 7), and 1kHz when the DLPF is enabled;
2. The accelerometer output rate is 1kHz。
这里陀螺仪的输出频率,是1Khz或者8Khz,与数字低通滤波器(DLPF)的设置有关,当DLPF_CFG=0/7的时候,频率为8Khz,其他情况是1Khz。而且DLPF滤波频率一般设置为采样率的一半。采样率,我们假定设置为50Hz,那么:SMPLRT_DIV=1000/50-1=19。
既然陀螺仪和加速度不可以同步采样,那器件是如何轮询陀螺仪和加速度计从而读取数据的呢?
说明书中有这样一句话:
这句话的含义如下:
假设加速度计,陀螺仪,FIFO存储器访问速度(=输出频率)分别为1KHz,2KHz,2KHz,那么访问顺序如下:
第一次,先访问加速度计,陀螺仪,FIFO存储器;
第二次,只访问陀螺仪,FIFO存储器,因为这两者的访问速度比加速度计快一倍;
第三次,在访问加速度计,陀螺仪,FIFO存储器,相当于总体访问周期是1/2KHz。
0X1A-配置寄存器
这个寄存器的作用就是:
This register configures the external Frame Synchronization (FSYNC) pin sampling and the Digital Low Pass Filter (DLPF) setting for both the gyroscopes and accelerometers.(为陀螺仪和加速度计配置外部帧同步采样引脚FSYNC和DLPF低通滤波器设置)。
我们不禁要问:为什么我们要有低通滤波器,加速度和旋转角度不是测得多少就是多少吗?
由于我们的动作速度都是低频的,但是测量过程中会夹杂着高频杂波,低通的目的只是过滤掉高频振动但是对正常的转动还是会如实反应的。传感器是把轴上的振动滤掉了,电机倒置底座产生一个100hz,均值为0的振动,低通滤波就把振动滤掉了。但是如果电机转动的时候,机体进行10度/秒的转动,这个10度任然会被测量到。
我们这里只关注DLPF_CFG[2:0]位:
DLPF_CFG[2:0]位为3位无符号整数位,用于设置低通滤波器的带宽,也就是说我们可以认为的配置过滤杂波的范围来去除我们不想要的频带。我们学过信号与系统的同学都知道“香农采样定理”,我们的采样频率一定要大于低通滤波器截止频率的2倍。
0X18-陀螺仪配置寄存器
在前面我们提到过自检寄存器,但是自检寄存器中的参数是让我们输入的(我们拿软件测量得到的当前参数),并没有使能器件本身测量的功能,不将器件自身测量值与我们人工测量的至做对比,怎能知道器件是否合格呢?这个寄存器中包含了“使能器件测量角度和加速度并且将自身测量值和我们人工测量的值进行对比”的位:
自检有关的位我们可能通常用不到,但是FS_SEL[1:0]位我们需要了解并使用:
这个位用于设置陀螺仪可测量的范围,正如厂家手册中规定的参数含义:
0X1C-加速度计配置寄存器
与陀螺仪配置寄存器大同小异,也包含了“使能器件测量角度和加速度并且将自身测量值和我们人工测量的值进行对比”的位和“用于设置测量范围”的位,AFS_SEL[1:0]测量范围的位:
2^16的分辨率和测量范围代表了测量的灵敏度:
该寄存器我们只关心AFS_SEL[1:0]这两个位,用于设置加速度传感器的满量程范围:0,±2g;1,±4g;2,±8g;3,±16g;我们一般设置为0,即±2g,因为加速度传感器的ADC也是16位,所以得到灵敏度为:65536/4=16384LSB/g。
0X23-FIFO使能寄存器
我们前面介绍过,FIFO存储器是用来存储数据的,等到存储到一定程度就成段的向MCU发送数据。这个寄存器就是表明有哪些数据可以存储进FIFO存储器:
我们这里只关注:ACCEL_FIFO_EN(代表加速度计的数据),ZG_FIFO_EN,XG_FIFO_EN,YG_FIFO_EN,TEMPG_FIFO_EN这几个位,位被置一代表采样得到的相应数据可以先存入FIFO存储器,然后在成段的发动给MCU。
0X6B-电源管理寄存器1
CLKSEL[2:0]代表了时钟来源:
这里我们选择内部8MHz作为器件工作的时钟源,但是我们知道:内部时钟源易受温漂影响,不准确,尤其是测量角度时稍有偏差就会对飞机运行姿态产生重大影响,我们推荐使用外部时钟源来克服温漂问题。
DEVICE_RESET 设置为1时,该位将所有内部寄存器复位为默认值。一旦复位完成,该位自动清零。每个寄存器的默认值可以在第3节找到。
SLEEP:当该位置1时,该位将MPU-60X0置于睡眠模式。
CYCLE:当该位设置为1且SLEEP被禁止时,MPU-60X0将循环。在睡眠模式和唤醒之间以LP_WAKE_CTRL(电源管理寄存器2)确定的速率从活动传感器获取单个样本数据。低功耗的原因就在于“MPU6050此时不是一种处于工作状态”,而是“按一定频率一会睡眠一会工作”,这样间接性的降低了功耗。
TEMP_DIS:设置为1时,该位禁用温度传感器。
0X6C-电源管理寄存器2
唤醒频率设置位LP_WAKE_CTRL[1:0]:
其余各个位含义如下:
该寄存器的LP_WAKE_CTRL用于控制低功耗时的唤醒频率,剩下的6位,分别控制加速度和陀螺仪的x/y/z轴是否进入待机模式,这里我们全部都不进入待机模式,所以全部设置为0 即可。
如果我们想要MPU6050工作在低功耗模式下,我们做如下配置:
0X72~0X73-FIFO存储器计数寄存器
这个寄存器用于表明存储在FIFO缓冲区中的字节数。这个数字又是可以从FIFO缓冲区读取的字节数。
0X74-FIFO读写寄存器
参数介绍如下:
0X75-“我是谁”寄存器
用于表明从IIC地址(不包含最低位,最低位取决于AD0引脚的电平)的。
0X3B~0X40-加速度计测量值保存寄存器
这6个寄存器分别为3组:0X3B和0X3C,0X3D和0X3E,0X3F和0X40,分别表征X,Y,Z轴的加速度测量值,但是注意这些值的输出形式:
当我们将低8位和高8位数据组合成16位数据时,由于加速度有正负之分,因此这16位数据为有符号的补码形式,我们要输出一个十进制数值,就必须了解C语言的数据编码格式:C语言再学习 -- 负数_不积跬步,无以至千里-CSDN博客_c语言负数
0X41~0X42-温度测量值保存寄存器
这个温度输出值也不是实际的温度输出值,需要进行换算,这个输出的温度值为有符号的16位整形数据:
其与实际温度的关系如下:
0X43~0X48-陀螺仪角度测量值保存寄存器
经过将“高八位和低八位组合”之后可得到16位数据,这16位数据的格式如下:
如此看来,这16位数据也为补码的形式。
0X68-信号复位寄存器
该寄存器用于重置陀螺仪、加速度计和温度传感器的模拟和数字信号路径。复位将使信号路径模数转换器和滤波器恢复到其上电前的配置。其参数如下:
这里要注意:这个寄存器只复位角度测量,加速度测量,温度测量的信号传输通道,并没有清空陀螺仪,温度,加速度计相关寄存器配置。
0X6A-用户控制寄存器
该寄存器相较于信号复位寄存器的特点就在于:这个寄存器可以清空陀螺仪,温度,加速度计相关寄存器配置。各个位含义如下:
我们这里只关心陀螺仪,加速度计,温度传感器的复位位(SIG_COND_RESET)和IIC主模式使能位(I2C_MST_EN)。
1. 陀螺仪,加速度计,温度传感器的复位位(SIG_COND_RESET)一旦被置一,陀螺仪,加速度计,温度传感器的信号传输通道和相关寄存器全部被复位,恢复至上电前的状态;
2. 我们想要MPU6050工作在IIC从模式下,作为MCU的从设备,因此要将IIC主模式使能位(I2C_MST_EN)置0。
0X37-中断配置寄存器
各个位的含义如下:
这里要注意:INT_RD_CLEAR位,该位置1表明:读中断标志位可以清0该位;如果该位置0则表明:任何读操作都会清楚所有中断标志位。INT_LEVEL位表示:INT引脚的有效电平是高电平还是低电平。LATCH_INT_EN位表示:置0——如果中断事件发生,则中断引脚出现一个宽度为50us的脉冲;置1——当中断事件来临时,INT引脚一直保持有效电平。
0X38-中断使能寄存器
参数含义如下:
0X3A-中断状态寄存器
我们这里重点介绍:FIFO_OFLOW_INT位,DATA_RDY_INT位,MOT_INT位,这三个位分别表示着“FIFO存储器溢出中断标志位”,“数据就绪中断标志位(表明MPU6050中所有寄存器中的数据准备就绪,MCU可以进行读操作)”,“测量值超过阈值中断”。
MPU6050代码简介
MCU的读/写操作
开始标志(S)发出后,主设备会传送一个 7 位的 Slave 地址,并且后面跟着一个第 8
位,称为 Read/Write 位。R/W 位表示主设备是在接受从设备的数据还是在向其写数据。然
后,主设备释放 SDA 线,等待从设备的应答信号(ACK)。每个字节的传输都要跟随有一
个应答位。应答产生时,从设备将 SDA 线拉低并且在 SCL 为高电平时保持低。数据传输总是以停止标志(P)结束,然后释放通信线路。
然而,主设备也可以产生重复的开始信号去操作另一台从设备,而不发出结束标志,此时无应答信号NACK就相当于STOP停止信号了。综上可知,所有的 SDA 信号变化都要在 SCL 时钟为低电平时进行,除了开始和结束标志。
信号标识的含义:
写时序如下所示:
对于写操作来说,第一个DATA其实是寄存器地址,我们在写操作中将写入的寄存器地址也算入数据DATA的范畴。
如果要写 MPU-60X0 寄存器,主设备除了发出开始标志(S)和地址位,还要加一个R/W 位,0 为写,1 为读。在第 9 个时钟周期(高电平时),MPU-60X0 产生应答信号。然后主设备开始传送寄存器地址(RA),接到应答后,开始传送寄存器数据,然后仍然要有应
答信号,依次类推。
单字节写:
//IIC写一个字节
//reg:寄存器地址
//data:数据
//返回值: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()) //等待ACK
{
MPU_IIC_Stop();
return 1;
}
MPU_IIC_Stop();
return 0;
}
连续写:
//IIC连续写
//addr:器件地址
//reg:寄存器地址
//len:写入长度
//buf:数据区
//返回值:0,正常
// 其他,错误代码
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<len; i++)
{
MPU_IIC_Send_Byte(buf[i]); //发送数据
if(MPU_IIC_Wait_Ack()) //等待ACK
{
MPU_IIC_Stop();
return 1;
}
}
MPU_IIC_Stop();
return 0;
}
读时序中信号标识的含义如下:
如果要读取 MPU-60X0 寄存器的值,首先由主设备产生开始信号(S),然后发送从设
备地址位和一个写数据位,然后发送寄存器地址,才能开始读寄存器。紧接着,收到应答信
号后,主设备再发一个开始信号,然后发送从设备地址位和一个读数据位。然后,作为从设
备的 MPU-60X0 产生应答信号并开始发送寄存器数据。通信以主设备产生的拒绝应答信号
(NACK)和结束标志(P)结束。拒绝应答信号(NACK)产生定义为 SDA 数据在第 9 个
时钟周期一直为高。
单字节读时序:
//IIC读一个字节
//reg:寄存器地址
//返回值:读到的数据
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);//读取数据,发送nACK
MPU_IIC_Stop(); //产生一个停止条件
return res;
}
连续读时序
//IIC连续读
//addr:器件地址
//reg:要读取的寄存器地址
//len:要读取的长度
//buf:读取到的数据存储区
//返回值: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);//读数据,发送nACK
else *buf=MPU_IIC_Read_Byte(1); //读数据,发送ACK
len--;
buf++;
}
MPU_IIC_Stop(); //产生一个停止条件
return 0;
}
读取加速度计的测量结果
从0X3B~0X40寄存器读取加速度计结果,寄存器中数据的存储结构如下:
我们需要将8位数据按照高低位顺序组合成16位数据,但是这个数据按照二进制补码的格式显示,我们需按照前面提及的补码
//得到加速度值(原始值)
//gx,gy,gz:陀螺仪x,y,z轴的原始读数(带符号)
//返回值:0,成功
// 其他,错误代码
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];
*ax=(*ax&0x8000)?(~*ax+1):*ax;
*ay=((u16)buf[2]<<8)|buf[3];
*ay=(*ay&0x8000)?(~*ay+1):*ay;
*az=((u16)buf[4]<<8)|buf[5];
*ax=(*az&0x8000)?(~*az+1):*az;
}
return res;;
}
读取旋转角度的测量结果
从0X43~0X48寄存器读取加速度计结果,寄存器中数据的存储结构如下:
我们需要将8位数据按照高低位顺序组合成16位数据,但是这个数据按照二进制补码的格式显示,我们需按照前面提及的补码
//得到陀螺仪值(原始值)
//gx,gy,gz:陀螺仪x,y,z轴的原始读数(带符号)
//返回值:0,成功
// 其他,错误代码
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];
*gx=(*gx&0x8000)?(~*gx+1):*gx;
*gy=((u16)buf[2]<<8)|buf[3];
*gy=(*gy&0x8000)?(~*gy+1):*gy;
*gz=((u16)buf[4]<<8)|buf[5];
*gz=(*gz&0x8000)?(~*gz+1):*gz;
}
return res;;
}
读取温度的测量结果
从0X43~0X48寄存器读取加速度计结果,寄存器中数据的存储结构如下:
我们需要将8位数据按照高低位顺序组合成16位数据,但是这个数据并不是真实的温度数据,而需通过如下方程式计算得到真实的温度值:
//得到温度值
//返回值:温度值(扩大了100倍)
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;;
}
我们这里扩大100倍,是为了将浮点数据类型转化为short短整型数据类型,然后再除以100转化为浮点型,这样就可以实现保留两位有效数字了。
设置输出频率和采样频率的关系(设置分频值)
配置0X19寄存器中的SMPLRT_DIV[7:0]位可以由采样频率分频得到输出频率。我们这里假定MPU6050的采样频率为1KHz,那么输出频率设定值不可以大于1KHz,我们使用采样频率分频与输出频率之间的关系式可以得到我们想要设定的分频系数:
SMPLRT_DIV=采样频率/器件的输出频率+1
程序如下所示:
//设置MPU6050的采样率(假定Fs=1KHz)
//rate:4~1000(Hz)
//返回值:0,设置成功
// 其他,设置失败
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); //自动设置LPF为采样率的一半
}
设置数字低通滤波器DLPF
操作0X1A配置寄存器中的DLPF_CFG位可以实现滤波器带宽的设置:
设定数字低通滤波器带宽的原则为:(香农采样定律)采样频率大于2倍的滤波器的截止频率。
//设置MPU6050的数字低通滤波器
//lpf:数字低通滤波频率(Hz)
//返回值:0,设置成功
// 其他,设置失败
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);//设置数字低通滤波器
}
设置角度/加速度的满量程测量范围
配置0X18寄存器的FS_SEL[1:0]位,可以实现角度测量上下限的设定:
具体代码如下:
//设置MPU6050陀螺仪传感器满量程范围
//fsr:0,±250dps;1,±500dps;2,±1000dps;3,±2000dps
//返回值:0,设置成功
// 其他,设置失败
u8 MPU_Set_Gyro_Fsr(u8 fsr)
{
return MPU_Write_Byte(MPU_GYRO_CFG_REG,fsr<<3);//设置陀螺仪满量程范围
}
配置0X1C寄存器的AFS_SEL[1:0]位,可以实现角度测量上下限的设定:
具体代码如下:
//设置MPU6050加速度传感器满量程范围
//fsr:0,±2g;1,±4g;2,±8g;3,±16g
//返回值:0,设置成功
// 其他,设置失败
u8 MPU_Set_Accel_Fsr(u8 fsr)
{
return MPU_Write_Byte(MPU_ACCEL_CFG_REG,fsr<<3);//设置加速度传感器满量程范围
}
MPU6050的初始化
这部分代码可分为两部分:与MPU6050相连的MCU引脚的初始化和MPU6050相关配置。
1. 与MPU6050相连的MCU引脚的初始化
u8 res;
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);//使能AFIO时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//先使能外设IO PORTA时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_15; // 端口配置
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //IO口速度为50MHz
GPIO_Init(GPIOA, &GPIO_InitStructure); //根据设定参数初始化GPIOA
GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable,ENABLE);//禁止JTAG,从而PA15可以做普通IO使用,否则PA15不能做普通IO!!!
这里一定要注意最后一句代码,STM32固件库相关代码注释中给出的含义如下:
@arg GPIO_Remap_SWJ_JTAGDisable : JTAG-DP Disabled and SW-DP Enabled
这句话表明:我们关闭了JTAGD打开了SWD,使用SWD模式调试我们的芯片。
2. MPU6050相关配置
MPU_AD0_CTRL=0; //控制MPU6050的AD0脚为低电平,从机地址为:0X68
MPU_IIC_Init();//初始化IIC总线
MPU_Write_Byte(MPU_PWR_MGMT1_REG,0X80); //复位MPU6050
delay_ms(100);
MPU_Write_Byte(MPU_PWR_MGMT1_REG,0X00); //唤醒MPU6050
MPU_Set_Gyro_Fsr(3); //陀螺仪传感器,±2000dps
MPU_Set_Accel_Fsr(0); //加速度传感器,±2g
MPU_Set_Rate(50); //设置采样率50Hz
MPU_Write_Byte(MPU_INT_EN_REG,0X00); //关闭所有中断
MPU_Write_Byte(MPU_USER_CTRL_REG,0X00); //I2C主模式关闭
MPU_Write_Byte(MPU_FIFO_EN_REG,0X00); //关闭FIFO
MPU_Write_Byte(MPU_INTBP_CFG_REG,0X80); //INT引脚低电平有效
res=MPU_Read_Byte(MPU_DEVICE_ID_REG);
if(res==MPU_ADDR)//器件ID正确
{
MPU_Write_Byte(MPU_PWR_MGMT1_REG,0X01); //设置CLKSEL,PLL X轴为参考
MPU_Write_Byte(MPU_PWR_MGMT2_REG,0X00); //加速度与陀螺仪都工作
MPU_Set_Rate(50); //设置采样率为50Hz
} else return 1;
1. 我们前面提到过:我们将低通滤波截止频率设置为21Hz,这里我们设置采样频率为50KHz,50Hz大于2倍的21Hz,满足香农采样定律:
2. 由于陀螺仪测量要求的时钟精度很高,因此我们将陀螺仪X轴的时钟选为MPU6050的时钟源。
一个频率为8 mhz的内部振荡器,基于陀螺仪的时钟或外部信息源都可以被选为MPU-60X0的时钟源。
3. 由于我们这里的采样速率不快,因此我们无需FIFO存储器;
4. MPU6050在上电后默认处于睡眠状态,我们需要在上电后清楚电源管理寄存器1中的SLEEP位来唤醒MPU6050,并且设置陀螺仪,加速度计,温度传感器全部处于工作状态;
5. 设置测量参数的可测量上下限。
大体上配置顺序如下:
以上是我解释的正点原子的程序代码,推荐看一看: