StrongerHangover丶萌
笔者之前也接触过MPU6050模块,但是并没有真正的去了解内部的通讯方式与内部的寄存器操作,况且之前接触的程序是基于51单片机的,笔者只是使用者并未自己书写。虽然说,不管是基于51单片机还是基于STM32单片机,它内部的通讯方式和寄存器配置依然相同,主要区别于操作方式有所区别。当然STM32单片机又分为基于寄存器版本和基于库函数版本的,笔者是基于固件库书写的MPU6050测试程序。笔者觉得MPU6050的程序主要分为I2C协议和MPU6050寄存器两部分(基于STM32有一定基础),我主要分为以下:
一、I2C通讯协议
二、MPU6050寄存器解析
三、MPU6050程序调试及Bug
一、 I2C通讯协议
简介:I2C是两线式串行总线。它是由数据线SDA和时钟SCL构成的串行总线,可发送和接收数据,一般这两条线是开漏和双向。在CPU与被控IC之间、IC与IC之间进行双向传送,高速IIC总线一般可达400kbps以上。IIC是半双工通信方式。
笔者将以下几点讲解I2C协议:
1)空闲状态:I2C总线的SDA和SCL两条信号线同时处于高电平时,规定为总线的空闲状态。此时各个器件的输出级场效应管均处在截止状态,即释放总线,由两条信号线各自的上拉电阻把电平拉高。 也就是说在没有数据传输时,SDA=1,和SCL=1。可以看下图1空闲状态下两条线都是高电平。
Photos 1
2)启动信号START:如上图,当SCL为高期间,SDA由高到低的跳变;启动信号是一种电平跳变时序信号,而不是一个电平信号。简单的说就是SCL=1,SDA=1->SDA=0,意思就是在时钟到来之前把要传输的数据准备好,保证数据有效传输。就相当于,要做某一件事,那是不是要做一定的准备,以保证把这件事做好。一个好的开始嘛!
3)停止信号STOP:如上图,当SCL为高期间,SDA由低到高的跳变;停止信号也是一种电平跳变时序信号,而不是一个电平信号。简单的说就是SCL=1,SDA=0->SDA=1,意思就是时钟线拉高,数据线才会拉高,如果时钟还在允许数据传输的状态下,数据线已经拉低,这样就可能会导致数据丢失,时钟线线拉高是保证数据有效传输。
4)应答信号ACK:发送器每发送一个字节,就在时钟脉冲9期间释放数据线,由接收器反馈一个应答信号。 应答信号为低电平时,规定为有效应答位(ACK简称应答位),表示接收器已经成功地接收了该字节;应答信号为高电平时,规定为非应答位(NACK),一般表示接收器接收该字节没有成功。 对于反馈有效应答位ACK的要求是,接收器在第9个时钟脉冲之前的低电平期间将SDA线拉低,并且确保在该时钟的高电平期间为稳定的低电平。 如果接收器是主控器,则在它收到最后一个字节后,发送一个NACK信号,以通知被控发送器结束数据发送,并释放SDA线,以便主控接收器发送一个停止信号。简单的说就是你叫别人的名字,别人给你一个回应,就说明他听到你的呼应了,这就是有效应答,否则就是得不到回应,不知道别人没有听见你的呼应,这是就是无效应答。
Photos 2
5)数据有效性:I2C总线进行数据传送时,时钟信号为高电平期间,数据线上的数据必须保持稳定,只有在时钟线上的信号为低电平期间,数据线上的高电平或低电平状态才允许变化。即:数据在SCL的上升沿到来之前就需准备好。并在在下降沿到来之前必须稳定。
Photos 3
6)数据传输:在I2C总线上传送的每一位数据都有一个时钟脉冲相对应(或同步控制),即在SCL串行时钟的配合下,在SDA上逐位地串行传送每一位数据。数据位的传输是边沿触发。简单的说就是第一个时钟脉冲来到时,取高位(MSB)传输,当高位被取走之后,数据就要左移,这样次高位的数据就会移到高位,等到下一个时钟脉冲进行数据传输,依次进行传输八位。
I2C总线的信号:
1.起始信号:当SCL保持高电平期间,SDA有高变低跳变,称为起始信号。
2.结束信号:当SCL保持高电平期间,SDA有低变高跳变,称为结束信号。
3.数据位: 当SCL保持高电平期间,SDA保持电平稳定有效性,称为有效数据位。
4.应答信号:当SCL保持高电平期间,当发送一个字节的数据后,必须要有对方的应答信号。
5.停止信号:当SCL保持低电平期间,SDA由低变高的跳变,称为停止信号。
I2C总线的写时序和读时序:在MPU6050中体现更好,
1.Write Time Series:START信号,哪一个设备地址,写地址,紧跟连续两个字节的数据:要写的地址,对方收到8bit地址后回应ACK,再8bit数据发给从设备,对方收到8bit数据后回应ACK,处理器写完后发送停止信号。
2.Read Time Series:读的过程:start信号,从设备地址,写待读取存储地址,再一个start信号,从设备地址,读8个时钟,从设备就把对应的数据反馈给处理器。
注意:读写时序要有相应的ACK信号。读写设备地址和数据时不一样,设备地址(小的)是七位地址,最后一位如果为0表示写,如果为1表示读。而数据寄存器的话,读写可能是先写一个读写的指令如0xA0等,然后直接写入8位的数据地址。
笔者觉得I2C通讯协议是非常有必要学习的,因为这一种通讯存在广泛应用,许多模块都是应用该通讯方式,就例如我们的MPU6050就是用I2C通讯协议,需要熟练的掌握该方式。学会看懂I2C的时序图,这是最基础的。在保证时序正确的情况,才能保证你使用该通讯方式获取正确的数据。
二、 MPU6050寄存器解析
简介:MPU6050是InvenSense公司推出的全球首款整合性6轴运动处理组件,内带3轴陀螺仪和3轴加速度传感器,并且含有一个第二IIC接口,可用于连接外部磁力传感器,利用自带数字运动处理器(DMP: Digital Motion Processor)硬件加速引擎,通过主IIC接口,可以向应用端输出完整的9轴姿态融合演算数据。有了DMP,我们可以使用InvenSense公司提供的运动处理资料库,非常方便的实现姿态解算,降低了运动处理运算对操作系统的负荷,同时大大降低了开发难度 。
MPU6050简介-MPU6050的特点:
1)自带数字运动处理( DMP: Digital Motion Processing ),可以输出6轴或9轴(需外接磁传感器)姿态解算数据。
2)集成可程序控制,测量范围为±250、±500、±1000与±2000°/sec 的3轴角速度感测器(陀螺仪)
3)集成可程序控制,范围为±2g、±4g、±8g和±16g的3轴加速度传感器
4)自带数字温度传感器
5)可输出中断(interrupt),支持姿势识别、摇摄、画面放大缩小、滚动、快速下降中断、high-G中断、零动作感应、触击感应、摇动感应功能
6)自带1024字节FIFO,有助于降低系统功耗
7)高达400Khz的IIC通信接口
8)超小封装尺寸:4x4x0.9mm(QFN)
Photos 4
MPU6050简介-MPU6050初始化:
1)初始化IIC接口:MPU6050采用IIC与STM32F1通信,需要先初始化与MPU6050 连接的SDA和SCL数据线。
2)复位MPU6050,由电源管理寄存器1(0X6B)控制。MPU6050内部所有寄存器恢复默认值,通过对电源管理寄存器1(0X6B)的bit7写1实现,复位后,电源管理寄存器1恢复默认值(0X40),然后必须设置该寄存器为 0X00,以唤醒 MPU6050,进入正常工作状态。
3)设置角速度传感器和加速度传感器的满量程范围。两个传感器的满量程范围(FSR),分别通过陀螺仪配置寄存器(0X1B)和加速度传感器配置寄存器(0X1C)设置。我们一般设置陀螺仪的满量程范围为±2000dps,加速度传感器的满量程范围为±2g。
4)设置其他参数。配置中断,由中断使能寄存器(0X38)控制;设置AUX IIC接口,由用户控制寄存器(0X6A)控制;设置FIFO,由FIFO使能寄存器(0X23)控制;陀螺仪采样率 ,由采样率分频寄存器(0X19)控制;设置数字低通滤波器,由配置寄存器(0X1A)控制。
5)设置系统时钟。由电源管理寄存器1(0X6B)控制。一般选择x轴陀螺PLL作为时钟源,以获得更高精度的时钟。
6)使能角速度传感器(陀螺仪)和加速度传感器。由电源管理寄存器2(0X6C)控制来设置,设置对应位为 0 即可开启。
初始化完成,即可读取陀螺仪、加速度传感器和温度传感器的数据了!!(原始数据)
MPU6050简介-寄存器介绍:
1) 电源管理寄存器1(0X6B):
Photos 5
该寄存器主要设置如下:1)
DEVICE_RESE=1,复位MPU6050,复位完成后,自动清零。
SLEEP=1,进入睡眠模式;SLEEP=0,正常工作模式。
TEMP_DIS,用于设置是否使能温度传感器,设置为0,则使能。
CLKSEL[2:0],用于选择系统时钟源,如右表所示:
CLKSEL[2:0] 时钟源
000 内部8M RC晶振
001 PLL,使用X轴陀螺作为参考(一般选X轴)
010 PLL,使用Y轴陀螺作为参考
011 PLL,使用Z轴陀螺作为参考
100 PLL,使用外部32.768Khz作为参考
101 PLL,使用外部19.2Mhz作为参考
110 保留
111 关闭时钟,保持时序产生电路复位状态
Photos 6
该寄存器主要设置如下:
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)。
Photos 7
该寄存器主要设置如下:
AFS_SEL[1:0]这两个位,用于设置加速度传感器的满量程范围:
0,±2g;
1,±4g;
2,±8g;
3,±16g;
一般设置为0,即±2g,因为加速度传感器的ADC也是16位,灵敏度为:65536/4=16384LSB/g。
Photos 8
该寄存器主要设置如下:
该寄存器用于控制FIFO使能,在简单读取传感器数据的时候,可以不用FIFO,设置对应位为:0,即可禁止FIFO,设置为1,则使能FIFO。注意:加速度传感器的3个轴,全由1个位(ACCEL_FIFO_EN)控制,只要该位置1,则加速度传感器的三个通道都开启FIFO了。
Photos 9
该寄存器主要设置如下:
设置 MPU6050 的陀螺仪采样频率,计算公式为:
采样频率 = 陀螺仪输出频率 / (1+SMPLRT_DIV)
陀螺仪的输出频率,是1Khz或者8Khz,与数字低通滤波器(DLPF)的设置有关,DLPF_CFG=0/7(说明一下:数字低通滤波器的DLPF_CFG有三个位控制,DLPF_CFG=0/7,是指000和111这两种配置下,陀螺仪的输出频率都是8Khz,其他配置下都是1Khz)的时候,频率为8Khz,其他情况是 1Khz。而且 DLPF滤波频率一般设置为采样率的一半。采样率,我们假定设置为50Hz,那么SMPLRT_DIV=1000/50-1=19。
Photos 10
该寄存器主要设置如下:
数字低通滤波器(DLPF)的设置位,即:DLPF_CFG[2:0],加速度计和陀螺仪,都是根据这三个位的配置进行过滤的,如下表:
DLPF_CFG[2:0] 加速度传感器Fs=1Khz 角速度传感器(陀螺仪)
带宽(Hz) 延迟(ms) 带宽(Hz) 延迟(ms) Fs(Khz)
000 260 0 256 0.98 8
001 184 2.0 188 1.9 1
010 94 3.0 98 2.8 1
011 44 4.9 42 4.8 1
100 21 8.5 20 8.3 1
101 10 13.8 10 13.4 1
110 5 19.0 5 18.6 1
111 保留 保留 8
解析上表:Fs是指陀螺仪的输出频率,例如上面设置的采样频率为50Hz,那么贷款就是25Hz,DLPF_CFG =100符合要求,50=1000/(1+DIV),DIV=19,这也就是陀螺仪采样率分频寄存器写入的值。在陀螺仪采样率分频寄存器讲到DLPF_CFG值的设置,该表就是很好的证明,0000和111对应的陀螺仪输出频率都是8Khz,其他设置都是1Khz.
注意:带宽=1/2采样率。
7)电源管理寄存器2(0X6C)
Photos 11
该寄存器主要设置如下:
寄存器的LP_WAKE_CTRL用于控制低功耗时的唤醒频率,剩下的6位,分别控制加速度和陀螺仪的x/y/z轴是否进入待机模式,全部都不进入待机模式,全部设置为:0 。
Photos 12
该寄存器主要设置如下:
加速度传感器数据输出寄存器总共由6个寄存器组成,输出X/Y/Z三个轴的加速度传感器值,高字节在前,低字节在后。
Photos 13
该寄存器主要设置如下:
陀螺仪数据输出寄存器总共由6个寄存器组成,输出X/Y/Z三个轴的陀螺仪传感器数据,高字节在前,低字节在后。
10)温度传感器数据输出寄存器(0X41~0X42):
Photos 14
该寄存器主要设置如下:
通过读取0X41(高8位)和0X42(低8位)寄存器得到,温度换算公式为:
Temperature = 36.53 + regval/340
其中,Temperature为计算得到的温度值,单位为℃,regval为从0X41和0X42读到的温度传感器值。
MPU6050的寄存器我们主要掌握着十个寄存器就差不多了,笔者在前面也讲过了初始化了,再针对每个寄存器都讲解了一遍该如何配置,这就根据自己需要来配置相应参数,以便获取可靠的数据。
注意:加速度传感器数据输出寄存器和陀螺仪数据输出寄存器获取的X/Y/Z轴的加速度和X/Y/Z轴的角速度是原始数据,并没进行姿态融合解算(四元数AHRS姿态解算),原始数据对于玩四轴之类的人来说并没有太大的意义。而对于玩四轴的笔者,更期望得到姿态数据,也就是欧拉角:航向角(yaw)、横滚角(roll)和俯仰角(pitch)。
要得到欧拉角数据,就得利用我们的原始数据,进行姿态融合解算,而MPU6050自带了数字运动处理器,即DMP,并且,InvenSense提供了一个MPU6050的嵌入式运动驱动库,结合MPU6050的DMP,可以将我们的原始数据,直接转换成四元数输出,而得到四元数之后,就可以很方便的计算出欧拉角,从而得到yaw、roll和pitch。
使用内置的DMP,可以大大简化代码设计,MCU不用进行姿态解算过程,大大降低了MCU的负担,从而有更多的时间去处理其他事件,提高系统实时性。
MPU6050 DMP输出的是姿态解算后的四元数,采用q30格式,也就是放大了2的30次方,我们要得到欧拉角,就得做一个转换,代码如下:
q0=quat[0] / q30; //q30格式转换为浮点数
q1=quat[1] / q30;
q2=quat[2] / q30;
q3=quat[3] / q30;
//计算得到俯仰角/横滚角/航向角
Pitch=asin(-2 * q1 * q3 + 2 * q0* q2)* 57.3; //俯仰角
Roll=atan2(2 * q2 * q3 + 2 * q0 * q1, -2 * q1 * q1 - 2 * q2* q2 + 1)* 57.3;//横滚角
Yaw=atan2(2*(q1q2 + q0q3),q0q0+q1q1-q2q2-q3q3) * 57.3;//航向角
笔者对四元数AHRS姿态解算、、、、、、、、、、、、、、、、、、、、
三、 MPU6050程序调试及Bug
笔者主要是对MPU6050程序进行讲解,对I2C程序进行部分解析。笔者在对寄存器解析时已经大概讲了一下MPU6050初始化,想要获取MPU6050的数据就必须保证I2C通讯成功,这是前提条件;然后就是MPU6050的正确配置,这样获取数据就稳妥妥了(程序已正确)。
从I2C下手,配置相应的IO,主要有两条线时钟线SCL和数据线SDA,想必时钟线就不必详解了(时钟脉冲),将其配置为输出就可以了(笔者在程序中配置为通用推挽输出)。数据线SDA(笔者也将其配置为通用推挽输出,此部分只是在IO初始化时配置的)既有输入又有输出,那我们又该如何解决?这时我们就该对寄存器进行操作了,如图:
Photos 15
笔者基于 STM32_FLY 四旋翼无人机开发平台 Mini V1.0 进行学习,配置的IO口也是根据该开发平台定义的。笔者在宏定义中给数据线SDA配置了两种模式(SDA输入和SDA输出两种),也就是该IO既允许输入也允许输出,此处笔者直接对寄存器进行操作, 既然允许了输入输出,笔者则给其配置两种模式(输入和输出模式),直接对端口配置低寄存器进行操作,不管是使用什么模块,在使用之前应将其之前内容清空,然后在对应位置赋相应的值,笔者配置输入模式赋予的值是8(1000),输出模式配置的值是3(0011),此处笔者只配置低寄存器,并未配置高寄存器。如:
#define MPU_IIC_SDA PBout(0) //数据线输出
#define MPU_READ_SDA PBin(0) //数据线输入
#define MPU_SDA_IN() {GPIOB->CRL&=0xfffffff0;GPIOB->CRL|=8<<0;}
#define MPU_SDA_OUT() {GPIOB->CRL&=0xfffffff0;GPIOB->CRL|=3<<0;}
Photos 16
其实只要会看时序图,I2C的程序并不难写,重点在灵活应用于实际。笔者主要对两个函数进行解析,如图:
一个是发送函数,一个是接收函数。I2C每次发送的字节不受限制,此处笔者写的是发送一个字节函数和接收一个字节函数。笔者主要是说明i2c有定义字节格式,发送到SDA 线上的每个字节必须为8 位,每次传输可以发送的字节数量不受限制。每个字节后必须跟一个响应位。首先传输的是数据的最高位(MSB),如果从机要完成一些其他功能后(例如一个内部中断服务程序)才能接收或发送下一个完整的数据字节,可以使时钟线SCL保持低电平,迫使主机进入等待状态,当从机准备好接收下一个数据字节并释放时钟线SCL 后数据传输继续。意思是如果你发送0x01那么前面很多个0一定是第一个发送出去的(MSB),1是最后发送出去的(LSB)。如图:
Photos 17
进到此函数就配置为输出模式,byte&0x80 的意思就是取该字节的高位,后面的 >>7 就是右移七位,byte<<=1也就是数据一位,学过C语言的应该对这个不陌生,况且都上STM32了,那应该玩过51单片机了吧。如果不懂这个,笔者建议学好C语言和玩好51单片机。
有发送就有接收,如图:
Photos 18
进到此函数就配置为输入模式,既然写一个字节,那么就读一个字节。此处的形参并不是数据,而是答应信号的标志,读1个字节,ACK=1时,发送ACK,ACK=0,发送nACK。
笔者觉得这两个函数在I2C程序中是主要的,重点了解一下的,当然其他函数也很重要,毕竟他们组合起来是一个通讯框架。I2C就先如此如此这般这般吧、、、、、、、、、、、、、、、、、、、、、、
进入正题–MPU6050,既然它的通讯方式I2C通讯,那么就以I2C来对其寄存器进行读写和数据传输。I2C通讯有主从之分,那么我们的主控是主机,MPU6050就是作为一个从机。既然要通讯,就要获取该从机的地址。寄存器117的地址0x75,其是一个八位寄存器,但是bit7保留,也就是说我们的MPU6050的地址是由七位控制的,而bit6bit1是MPU6050的6位I2C地址,上电复位之后该6位的值为110100。Bit0则是由MPU6050上的引脚AD0控制的,该引脚逻辑低电平时,bit6bit0的值为1101000;该引脚逻辑高电平时,bit6~bit0的值为1101001。
笔者使用的开发平台中的MPU6050的AD0引脚是默认接GND,也就是低电平。根据117寄存器说明,该平台的MPU6050的地址为1101000。笔者直接在宏定义中书写AD0引脚电平为低,MPU6050的地址为0x68,如下图:
Photos 19
初始化如图所示:
Photos 20
初始化中有一个判断语句,这个判断的用意在于是否把地址写入成功,如果成功的话,而写入成功之后就会把该值返回(I2C通讯没毛病的情况下返回才正确)。如果DataReg不等于MPU_ADDR(MPU6050 ID)的话,判断语句if将不会进入所包含的初始化,也就是说初始化并未成功,串口将会打印MPU6050 Errors!出现错误的几种可能:
1.硬件问题:MPU6050没焊好或者杜邦线没接好,可能会导致I2C通讯不成功。
2.I2C程序时序有误,I2C的读取函数有问题
3. MPU6050_Read_Reg_Byte(I2C读一个字节)有问题。
4.MPU6050的I2C地址错误。
此处笔者用printf函数打印只是为了方便在串口上观察是什么状况,以便解决。
串口是个好东西,在调试中起了很大作用。
Photos 21
图21,在程序中会看到((MPU_ADDR<<1)|0)和((MPU_ADDR<<1)|1)两种写法,相信会跟我有一样的疑惑。当笔者看到如此神的操作时也是一头雾水,后来突然看到I2C读写寄存器的时候,才恍然大悟。笔者再讲I2C时有这么一段话:“读写设备地址和数据时不一样,设备地址(小的)是七位地址,最后一位如果为0表示写,如果为1表示读。而数据寄存器的话,读写可能是先写一个读写的指令如0xA0等,然后直接写入8位的数据地址。” MPU_ADDR是MPU6050的地址,是否还记得bit7之前是保留的,MPU_ADDR<<1将MPU60550的地址左移一位,一个字节的地址,左移七位之后,就相当于bit0保留。(MPU_ADDR<<1)|0此刻或上一个0,就在此处。将其或上之后有什么用?还有此处是按位或,也将相当于把0写到了bit0,那么bit0位成为了一个读写标志位,bit0=0时,是写命令;bit0=1,是读命令。((MPU_ADDR<<1)|0)和((MPU_ADDR<<1)|1),解读出来就是(器件地址+写命令)和(器件地址+读命令)。笔者的疑惑就此解开。
Photos 22
MPU6050在程序有三个函数(MPU6050_Get_Temperature(void)、MPU6050_Get_Gyroscope和MPU6050_Get_Accelerometer)的内容有这种写法,只对一个地方说明,因为都是一样的。Buff是笔者定义的一个数组,Buff[0]~ Buff[5]留个位,一个位是一个字节。而从陀螺仪数据输出寄存器获取的数据存在Buff数组中,GYRO_X、GYRO_Y和GYRO_Z都是十六位的数据,而它们的高八位和第八位都分别存在不同的地址,那么读取出来的Buff[0]和Buff[1]分别是X轴角加速度的高八位和第八位,Buff[2]和Buff[3]分别是Y轴角加速度的高八位和第八位,Buff[4]和Buff[5]分别是Z轴角加速度的高八位和第八位。将其整合起来如图,强制将Buff[0]、Buff[2]和Buff[4]这三个位为两个字节的,把高位数据左移八位,将其低位的数据放到该位的low八位,这样就将数据整合了。以此类推,其他数据整合亦是如此。
笔者在前面也说过,直接从MPU6050的陀螺仪数据输出寄存器和加速度传感器数据输出寄存器输出的数据是原始数据,并不是我们要的欧拉角。笔者在介绍MPU6050的特点时有讲到自带数字运动处理器(DMP),可以输出六轴或者九轴(需外接磁力计)姿态结算数据。
初始化成功后,就可以读取MPU6050的加速度传感器、陀螺仪和温度传感器的原始数据,需得经过数据处理才能的得到姿态数据,即欧拉角:航向角(Yaw)、横滚角(Roll)和俯仰角(Picth)。需要利用我们读取的原始数据进行姿态融合解算,MPU6050自带了数字运动处理器,即DMP,InvenSense提供了一个MPU6050的嵌入式运动驱动库,结合MPU6050的DMP,可以将我们的原始数据,直接转换成四元数输出,而得到四元数之后,就可以很方便的计算出欧拉角,从而得到Yaw、Roll和Pitch。
笔者对于DMP并未深入研究,而只是移植了 正点原子 已经移植好的程序。该程序基于MSP430单片机的,该程序人家是已经移植成功的,那么敲好I2C和MPU6050程序之后,再将DMP程序移植过来。首先,将其根目录下文件全部复制过来,虽然 dmKeyp.h和dmpmap.h用不到,如果不移植过来就会出现头文件报错,因为在inv_mpu.c和inv_mpu_dmp_motion_driver.c用到了,所以最好将其一起移植过来。
Photos 23
将两个 .c 文件添加到过程中,同时也要记得路径,不然头文件报错。 i2c_write、i2c_read、delay_ms和get_ms这四个函数前两个是关键。先打开inv_mpu.c文件,如图:
Photos 24
i2c_write和i2c_read的宏定义很关键,赋予其的是函数名,此处需修改成自己写的MPU6050寄存器函数和MPU6050读寄存器函数,警记!.c
Photos 25
找到mpu_dmp_init函数,再将函数里面的I2C总线初始化函数换成自己书写的I2C总线初始化函数。
改完这几处应该就差不多了,因为笔者是在测试成功之后的几天才会的总结,所以难免会忘记一些东西,所以笔者建议在做某一件事时,碰到问题做一下笔记,修改的地方做一下笔记,解决方法做一下笔记,养成良好的习惯,以便下次碰见快速解决。
以下就可以开始测试了,在主函数中可以先检测以下MPU6050_init和mpu_dmp_init两个初始化函数,看看是否初始化成功,至于检测结果怎么判断成不成功,就得看看你自己在函数中怎么书写程序。笔者则是通过返回值和串口打印标志位来判断的,这样就更好方便查找问题所在,省时省事。在mpu_dmp_init初始化时,有可能不通过,而且在串口上打印“Product ID read as 0 indicates device is either ""incompatible or an MPU3050.”这一串英语,就说明从设备的ID错误(Address Error),该问题应该是自检通过了,但是地址出错;解决了前一个问题,有可能会出现“Unsupported software product rev”一串英文,这个应该是不支持此类产品,也就是自检没有通过。笔者不太记得,出现以上问题是怎么解决的了,但是笔者想想觉得应该是I2C通讯的问题,既然是地址出错和产品检测错误,那就是I2C通讯时序出现问题了,这时应该查找一下I2C程序的时序是否书写正确。
下图是笔者测试数据,有温度数据、原始数据和姿态融合数据(DMP),即得到了欧拉角:Yaw、Roll和Pitch
Photos 26
以下是要点:
1.与运算:要其他位不变,其他位赋值1。如:GPIOB->CRL&=0xfffffff0。
2.或运算:要其他位不变,其他位赋值0。如:GPIOB->CRL&=0x0000000f。
笔者建议学会使用串口调试,通过串口可以观察出很多情况。当程序不知道卡死在哪的时候,可以选择从尾入手,一步一步回倒检测,也许有点繁琐,但是很实用。如果有经验的话,就会很快查出问题所在。
第一篇博客,海涵!
时间 :2020.4.29
工欲善其事,必先利其器。