您现在的位置是:首页 > 科技前沿

阿波罗 STM32F767 开发板资料连载第四十章 MPU9250 九轴传感器

智慧创新站 2025-04-30【科技前沿】77人已围观

简介1)实验平台:alientek阿波罗STM32F767开发板第四十章MPU9250九轴传感器实验本章,我们介绍一款主流的九轴(三轴加速度+三轴角速度(陀螺仪)+三轴磁力计)传感器:MPU9250,该传感器广泛用于四轴、平衡车和空中鼠标等设计,具有非常广泛的应用范围。ALIENTEK阿波罗STM32F...

1)实验平台:alientek阿波罗STM32F767开发板


第四十章MPU9250九轴传感器实验

本章,我们介绍一款主流的九轴(三轴加速度+三轴角速度(陀螺仪)+三轴磁力计)传感器:

MPU9250,该传感器广泛用于四轴、平衡车和空中鼠标等设计,具有非常广泛的应用范围。

ALIENTEK阿波罗STM32F767开发板自带了MPU9250传感器。本章我们将使用STM32F767

来驱动MPU9250,读取其原始数据,并利用其自带的DMP结合MPL库实现姿态解算,结合

匿名四轴上位机软件和LCD显示,教大家如何使用这款功能强大的九轴传感器。本章分为如下

几个部分:

40.1MPU9250简介

40.2硬件设计

40.3软件设计

40.4下载验证

40.1MPU9250简介

本节,我们将分2个部分介绍:1,MPU9250基础介绍。2,DMP使用简介。另外,所有

MPU9250的相关资料,都在光盘:A盘7,硬件资料MPU9250资料文件夹里面。

40.1.1MPU9250基础介绍

MPU9250是InvenSense公司推出的全球首款整合性9轴运动处理组件,相较于多组件方

案,免除了组合陀螺仪与加速器时之轴间差的问题,减少了体积和功耗。

MPU9250内部集成有3轴陀螺仪、

3轴加速度计和3轴磁力计,输出都是16位的数字量;可

以通过集成电路总线(IIC)接口和单片机进行数据交互,传输速率可达400kHz/s。陀螺仪的角

速度测量范围最高达±2000(°/s),具有良好的动态响应特性。加速度计的测量范围最大为±

16g(g为重力加速度),静态测量精度高。磁力计采用高灵度霍尔型传感器进行数据采集,磁感

应强度测量范围为±4800μT,可用于对偏航角的辅助测量。

MPU9250自带的数字运动处理器(DMP:DigitalMotionProcessor)硬件加速引擎,可以整

合九轴传感器数据,向应用端输出完整的9轴融合演算数据。有了DMP,我们可以使用

InvenSense公司提供的运动处理库(MPL:MotionProcessLibrary),非常方便的实现姿态解算,

降低了运动处理运算对操作系统的负荷,同时大大降低了开发难度。

MPU9250的特点包括:

①以数字形式输出9轴旋转矩阵、四元数(quaternion)、欧拉角格式(EulerAngleforma)的

融合演算数据(需DMP支持)

②集成16位分辨率,量程为:±250、±500、±1000°与±2000°/sec的3轴角速度

传感器(陀螺仪)

③集成16位分辨率,量程为:±2g、±4g、±8g和±16g的3轴加速度传感器

④集成16位分辨率,量程为:±4800uT的磁场传感器(磁力计)

⑤自带数字运动处理(DMP:DigitalMotionProcessing)引擎可减少MCU复杂的融合演算

数据、感测器同步化、姿势感应等的负荷

⑥自带一个数字温度传感器

⑦可编程数字滤波器

⑧支持SPI接口,通信速度高达20Mhz

⑨自带512字节FIFO缓冲区

⑩高达400Khz的IIC通信接口

⑪超小封装尺寸:3x3x1mm(QFN)

MPU9250传感器的检测轴如图40.1.1.1所示:

图40.1.1.1MPU9250检测轴及其方向

MPU9250的内部框图如图40.1.1.2所示:

图40.1.1.2MPU9250框图

其中,SCL和SDA可以连接MCU的IIC接口,MCU通过这个IIC接口来控制MPU9250,

另外还有一个IIC接口:AUX_CL和AUX_DA,这个接口可用来连接外部从设备,比气压传感

器。VDDIO是IO口电压,该引脚最低可以到1.8V,我们一般直接接VDD即可。AD0是从IIC

接口(接MCU)的地址控制引脚,该引脚控制IIC地址的最低位。如果接GND,则MPU9250

的IIC地址是:0X68,如果接VDD,则是0X69,注意:这里的地址是不包含数据传输的最低

位的(最低位用来表示读写)!!注意:当使用SPI接口的时候,使用:SCLK、SDO、SDI和

nCS脚来传输数据。

这里需要和大家说明一下的是:MPU9250,实际上是内部集成了一个MPU6500六轴传感

器和一个AK8963三轴磁力计,他们共用一个IIC接口,这样组合成一个九轴传感器。前面说

了我们开发板上MPU9250的IIC地址是0X68,实际上是指MPU6500的地址是0X68,而AK8963

磁力计的IIC地址,则是:0X0C(不包含最低位)。

在阿波罗STM32开发板上,AD0是接GND的,所以MPU9250的IIC地址是0X68(不含

最低位),IIC通信的时序我们在之前已经介绍过(第二十九章,IIC实验),这里就不再细说了。

接下来,我们介绍一下利用STM32F767读取MPU9250的加速度和角度传感器数据(非

中断方式),需要哪些初始化步骤:

1)初始化IIC接口

MPU9250采用IIC与STM32F767通信,所以我们需要先初始化与MPU9250连接的SDA

和SCL数据线。这个在前面的IIC实验章节已经介绍过了,这里MPU9250与24C02共用一个

IIC,所以初始化IIC完全一模一样。

2)复位MPU9250

这一步让MPU9250内部所有寄存器恢复默认值,通过对电源管理寄存器1(0X6B)的bit7

写1实现。复位后,电源管理寄存器1恢复默认值(0X40),然后必须设置该寄存器为0X00,

以唤醒MPU9250,进入正常工作状态。

3)设置角速度传感器(陀螺仪)和加速度传感器的满量程范围

这一步,我们设置两个传感器的满量程范围(FSR),分别通过陀螺仪配置寄存器(0X1B)

和加速度传感器配置寄存器(0X1C)设置。我们一般设置陀螺仪的满量程范围为±2000dps,

加速度传感器的满量程范围为±2g。

4)设置其他参数

这里,我们还需要配置的参数有:关闭中断、关闭AUXIIC接口、禁止FIFO、设置陀螺

仪采样率和设置数字低通滤波器(DLPF)等。本章我们不用中断方式读取数据,所以关闭中断,

然后也没用到AUXIIC接口外接其他传感器,所以也关闭这个接口。分别通过中断使能寄存器

(0X38)和用户控制寄存器(0X6A)控制。MPU9250可以使用FIFO存储传感器数据,不过

本章我们没有用到,所以关闭所有FIFO通道,这个通过FIFO使能寄存器(0X23)控制,默

认都是0(即禁止FIFO),所以用默认值就可以了。陀螺仪采样率通过采样率分频寄存器(0X19)

控制,这个采样率我们一般设置为50即可。数字低通滤波器(DLPF)则通过配置寄存器(0X1A)

设置,一般设置DLPF为带宽的1/2即可。

5)配置系统时钟源并使能角速度传感器和加速度传感器

系统时钟源同样是通过电源管理寄存器1(0X6B)来设置,该寄存器的最低三位用于设置

系统时钟源选择,默认值是0(内部8MRC震荡),不过我们一般设置为1,选择x轴陀螺PLL

作为时钟源,以获得更高精度的时钟。同时,使能角速度传感器和加速度传感器,这两个操作

通过电源管理寄存器2(0X6C)来设置,设置对应位为0即可开启。

6)配置AK8963磁场传感器(磁力计)

经过前面5步配置,我们完成了对MPU6500的配置,此步需要对AK8963进行配置。首

先设置控制寄存器2(0X0B)的最低位为1,对AK8963进行软复位。随后设置控制寄存器1

(0X0A)为0X11,选择16位输出,单次测量模式。随后就可以读取磁力计数据了。

至此,MPU9250的初始化就完成了,可以正常工作了(其他未设置的寄存器全部采用默认值即可),接下来,我们就可以读取相关寄存器,得到加速度传感器、角速度传感器和温度传感

器的数据了。不过,我们先简单介绍几个重要的寄存器。

首先,我们介绍电源管理寄存器1,该寄存器地址为0X6B,各位描述如表40.1.1.1所示:

图40.1.1.1电源管理寄存器1各位描述

其中,H_RESET位用来控制复位,设置为1,复位MPU9250,复位结束后,MPU硬件自

动清零该位。SLEEEP位用于控制MPU9250的工作模式,复位后,该位为1,即进入了睡眠模

式(低功耗),所以我们要清零该位,以进入正常工作模式。最后CLKSEL[2:0]用于选择系统时

钟源,选择关系如表40.1.1.2所示:

图40.1.1.2CLKSEL选择列表

默认是使用内部20MRC晶振的,精度不高,我们一般设置其自动选择最有效的时钟源,

一般设置CLKSEL=001即可。

接着,我们看陀螺仪配置寄存器,该寄存器地址为:0X1B,各位描述如表40.1.3所示:

表40.1.1.3陀螺仪配置寄存器各位描述

该寄存器我们只关心GYRO_FS_SEL[1:0]和FCHOICE[1:0]这四个位,GYRO_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)。FCHOICE[1:0]用于控制DLPF旁路,我们一般设置为3,不旁路

DLPF。

接下来,我们看加速度传感器配置寄存器,寄存器地址为:0X1C,各位描述如表40.1.1.4

所示:

表40.1.1.4加速度传感器配置寄存器各位描述

该寄存器我们只关心ACCEL_FS_SEL[1:0]这两个位,用于设置加速度传感器的满量程范围:

0,±2g;1,±4g;2,±8g;3,±16g;我们一般设置为0,即±2g,因为加速度传感器的

ADC也是16位,所以得到灵敏度为:65536/4=16384LSB/g。

接下来,我看看FIFO使能寄存器,寄存器地址为:0X23,各位描述如表40.1.1.5所示:

表40.1.1.5FIFO使能寄存器各位描述

该寄存器用于控制FIFO使能,在简单读取传感器数据的时候,可以不用FIFO,设置对应

位为0即可禁止FIFO,设置为1,则使能FIFO。注意加速度传感器的3个轴,全由1个位(ACCEL)

控制,只要该位置1,则加速度传感器的三个通道都开启FIFO了。

接下来,我们看陀螺仪采样率分频寄存器,寄存器地址为:0X19,各位描述如表40.1.1.6

所示:

表40.1.1.6陀螺仪采样率分频寄存器各位描述

该寄存器用于设置MPU9250的陀螺仪采样频率,计算公式为:

采样频率=陀螺仪输出频率/(1+SMPLRT_DIV)

这里陀螺仪的输出频率,是1Khz、8Khz或32Khz,与数字低通滤波器(DLPF)的设置有

关,当FCHOICE[1:0]不为11的时候,频率为32Khz,其他情况:当DLPF_CFG=0/7的时候,

频率为8Khz,否则是1Khz。而且DLPF滤波频率一般设置为采样率的一半。采样率,我们假

定设置为50Hz,那么SMPLRT_DIV=1000/50-1=19。

接下来,我们看配置寄存器,寄存器地址为:0X1A,各位描述如表40.1.1.7所示:

表40.1.1.7配置寄存器各位描述

这里,我们主要关心数字低通滤波器(DLPF)的设置位,即:DLPF_CFG[2:0],陀螺仪根

据这三个位的配置进行过滤。DLPF_CFG不同配置对应的过滤情况如表40.1.1.8所示:

表40.1.1.8DLPF_CFG配置表

一般我们设置角速度传感器的带宽为其采样率的一半,如前面所说的,如果设置采样率为

50Hz,那么带宽就应该设置为25Hz,取近似值20Hz,就应该设置DLPF_CFG=100。需要注意:

FCHOICE[1:0](通过0X1B寄存器配置)必须设置为11,否则固定32K频率,且DLPF_CFG

的配置无效!

接下来,我们看电源管理寄存器2,寄存器地址为:0X6C,各位描述如表40.1.1.9所示:

表40.1.1.9电源管理寄存器2各位描述

该寄存器低六位有效,分别控制加速度和陀螺仪的x/y/z轴是否开启,这里我们设置全部都

开启,所以全部设置为0即可。

接下来,我们看看陀螺仪数据输出寄存器,总共由6个寄存器组成,地址为:0X43~0X48,

通过读取这6个寄存器,就可以读到陀螺仪x/y/z轴的值,比如x轴的数据,可以通过读取0X43

(高8位)和0X44(低8位)寄存器得到,其他轴以此类推。

同样,加速度传感器数据输出寄存器,也有6个,地址为:0X3B~0X40,通过读取这6个

寄存器,就可以读到加速度传感器x/y/z轴的值,比如读x轴的数据,可以通过读取0X3B(高

8位)和0X3C(低8位)寄存器得到,其他轴以此类推。

另外,温度传感器的值,可以通过读取0X41(高8位)和0X42(低8位)寄存器得到,

温度换算公式为:

Temperature=21+regval/338.87

其中,Temperature为计算得到的温度值,单位为℃,regval为从0X41和0X42读到的温度

传感器值。

接下来,我们看AK8963的控制寄存器1,寄存器地址为:0X0A,各位描述如表40.1.1.10

所示:

表40.1.1.10AK8963控制寄存器1

其中,BIT位控制AK8963输出位数,0,表示14位;1,表示16位;我们一般设置为1。

MODE[3:0]用于控制AK8963的工作模式:0000,掉电模式;0001,单次测量模式;0010,

连续测量模式1;0110,连续测量模式2;0100,外部触发测量模式;1000,自测试模式;

1111,FuseROM访问模式;我们一般设置MODE[3:0]=0001,即单次测量模式。

接下来,我们看AK8963的控制寄存器2,寄存器地址为:0X0B,各位描述如表40.1.1.11

所示:

表40.1.1.11AK8963控制寄存器2

该寄存器仅最低位有效,用于控制AK8963的软复位,我们在初始化的时候,设置SRST=1

即可让AK8963进行一次软复位,复位结束后,自动设置为0。

最后,我们看看磁力计数据输出寄存器,总共由6个寄存器组成,地址为:0X03~0X08,

通过读取这6个寄存器,就可以读到磁力计x/y/z轴的值,比如x轴的数据,可以通过读取0X03

(低8位)和0X04(高8位)寄存器得到,其他轴以此类推。

关于MPU9250的基础介绍,我们就介绍到这。MPU9250的详细资料和相关寄存器介绍,

请参考光盘:7,硬件资料MPU9250资料和

这两个文档,另外该目录还提供了部分MPU9250的中文资料,供大家参考学习。

40.1.2DMP使用简介

经过40.1.1节的介绍,我们可以读出MPU9250的加速度传感器和角速度传感器的原始数

据。不过这些原始数据,对想搞四轴之类的初学者来说,用处不大,我们期望得到的是姿态数

据,也就是欧拉角:航向角(yaw)、横滚角(roll)和俯仰角(pitch)。有了这三个角,我们就

可以得到当前四轴的姿态,这才是我们想要的结果。

要得到欧拉角数据,就得利用我们的原始数据,进行姿态融合解算,这个比较复杂,知识

点比较多,初学者不易掌握。而MPU9250自带了数字运动处理器,即DMP,并且,InvenSense

提供了一个MPU9250的嵌入式运动处理库(MPL),结合MPU9250的DMP,可以将我们的传

感器原始数据,直接转换成四元数输出,而得到四元数之后,就可以很方便的计算出欧拉角,

从而得到yaw、roll和pitch。

使用内置的DMP,大大简化了四轴的代码设计,且MCU不用进行姿态解算过程,大大降

低了MCU的负担,从而有更多的时间去处理其他事件,提高系统实时性。

InvenSense提供的最新MPL库版本为:6.12版本,它提供了基于STM32F4Discovery板的

参考例程(IAR工程),我们只需要将它移植到我们的开发板上即可。官方原版驱动在光盘:7,

硬件资料MPU9250资料motion_driver_6.12.zip。解压之后,里面有MPL的参考例程

(arm/msp430)、LIB库(mpllibraries)和说明文档(documentation)等资料,大家可以参考

documentation文件夹下的几个PDF教程来学习MPL的使用。

官方MPL库移植起来,还是比较简单的,主要是实现这4个函数:i2c_write,i2c_read,

delay_ms和get_ms,具体细节,我们就不详细介绍了,移植后的驱动代码,我们放在本例程

HARDWAREMPU9250MPL文件夹内,包含4个文件夹,如图40.1.2.1所示:

图40.1.2.1移植后的驱动库代码

为了方便大家使用该驱动库(MPL),我们在inv_里面添加了两个函数:

mpu_dmp_init

和mpu_mpl_get_data这两个函数,这里我们简单介绍下这两个函数。

mpu_dmp_init,是MPU9250DMP初始化函数,该函数代码如下:

//MPU9250,dmp初始化

//返回值:0,正常

//其他,失败

u8mpu_dmp_init(void)

{

u8res=0;

structint_param_sint_param;

unsignedcharaccel_fsr;

unsignedshortgyro_rate,gyro_fsr;

unsignedshortcompass_fsr;

IIC_Init();

//初始化IIC总线

if(mpu_init(int_param)==0)

//初始化MPU9250

{

res=inv_init_mpl();

//初始化MPL

if(res)return1;

inv_enable_quaternion();

inv_enable_9x_sensor_fusion();

inv_enable_fast_nomot();

inv_enable_gyro_tc();

inv_enable_vector_compass_cal();

inv_enable_magnetic_disturbance();

inv_enable_eMPL_outputs();

res=inv_start_mpl();//开启MPL

if(res)return1;

res=mpu_set_sensors(INV_XYZ_GYRO|INV_XYZ_ACCEL|

INV_XYZ_COMPASS);//设置所需要的传感器

if(res)return2;

res=mpu_configure_fifo(INV_XYZ_GYRO|INV_XYZ_ACCEL);//设置FIFO

if(res)return3;

res=mpu_set_sample_rate(DEFAULT_MPU_HZ);

//设置采样率

if(res)return4;

res=mpu_set_compass_sample_rate(1000/COMPASS_READ_MS);//磁力计采样率

if(res)return5;

mpu_get_sample_rate(gyro_rate);

mpu_get_gyro_fsr(gyro_fsr);

mpu_get_accel_fsr(accel_fsr);

mpu_get_compass_fsr(compass_fsr);

inv_set_gyro_sample_rate(1000000L/gyro_rate);

inv_set_accel_sample_rate(1000000L/gyro_rate);

inv_set_compass_sample_rate(COMPASS_READ_MS*1000L);

inv_set_gyro_orientation_and_scale(

inv_orientation_matrix_to_scalar(gyro_orientation),(long)gyro_fsr15);

inv_set_accel_orientation_and_scale(

inv_orientation_matrix_to_scalar(gyro_orientation),(long)accel_fsr15);

inv_set_compass_orientation_and_scale(

inv_orientation_matrix_to_scalar(comp_orientation),(long)compass_fsr15);

res=dmp_load_motion_driver_firmware();//加载dmp固件

if(res)return6;

res=dmp_set_orientation(inv_orientation_matrix_to_scalar(gyro_orientation));

//设置陀螺仪方向

if(res)return7;

res=dmp_enable_feature(DMP_FEATURE_6X_LP_QUAT|DMP_FEATURE_TAP|

DMP_FEATURE_ANDROID_ORIENT|DMP_FEATURE_SEND_RAW_ACCEL|

DMP_FEATURE_SEND_CAL_GYRO|DMP_FEATURE_GYRO_CAL);

//设置dmp功能

if(res)return8;

res=dmp_set_fifo_rate(DEFAULT_MPU_HZ);//设置DMP输出速率(不超过200Hz)

if(res)return9;

res=run_self_test();

//自检

if(res)return10;

res=mpu_set_dmp_state(1);//使能DMP

if(res)return11;

}

return0;

}

此函数首先通过IIC_Init(需外部提供)初始化与MPU9250连接的IIC接口,然后调用

mpu_init函数,初始化MPU9250,之后就是设置DMP所用传感器、FIFO、采样率和加载固件

等一系列操作,在所有操作都正常之后,最后通过mpu_set_dmp_state(1)使能DMP功能,在使

能成功以后,我们便可以通过mpu_mpl_get_data来读取姿态解算后的数据了。

mpu_mpl_get_data函数代码如下:

//得到mpl处理后的数据(注意,本函数需要比较多堆栈,局部变量有点多)

//pitch:俯仰角精度:0.1°范围:-90.0°---+90.0°

//roll:横滚角精度:0.1°范围:-180.0°---+180.0°

//yaw:航向角精度:0.1°范围:-180.0°---+180.0°

//返回值:0,正常

//其他,失败

u8mpu_mpl_get_data(float*pitch,float*roll,float*yaw)

{

unsignedlongsensor_timestamp,timestamp;

shortgyro[3],accel_short[3],compass_short[3],sensors;

unsignedcharmore;

longcompass[3],accel[3],quat[4],temperature;

longdata[9];

int8_taccuracy;

if(dmp_read_fifo(gyro,accel_short,quat,sensor_timestamp,sensors,more))return1;

if(sensorsINV_XYZ_GYRO)

{

inv_build_gyro(gyro,sensor_timestamp);

//把新数据发送给MPL

mpu_get_temperature(temperature,sensor_timestamp);

inv_build_temp(temperature,sensor_timestamp);//发温度值给MPL,仅陀螺仪需要

}

if(sensorsINV_XYZ_ACCEL)

{

accel[0]=(long)accel_short[0];

accel[1]=(long)accel_short[1];

accel[2]=(long)accel_short[2];

inv_build_accel(accel,0,sensor_timestamp);

//把加速度值发给MPL

}

if(!mpu_get_compass_reg(compass_short,sensor_timestamp))

{

compass[0]=(long)compass_short[0];

compass[1]=(long)compass_short[1];

compass[2]=(long)compass_short[2];

inv_build_compass(compass,0,sensor_timestamp);//把磁力计值发给MPL

}

inv_execute_on_data();

inv_get_sensor_type_euler(data,accuracy,×tamp);

*roll=(data[0]/q16);

*pitch=-(data[1]/q16);

*yaw=-data[2]/q16;

return0;

}

此函数用于得到DMP姿态解算后的俯仰角、横滚角和航向角。不过本函数局部变量有点

多,大家在使用的时候,如果死机,那么请设置堆栈大一点(在startup_里面设置,

默认是800)。

利用这两个函数,我们就可以读取到姿态解算后的欧拉角,使用非常方便。DMP部分,我

们就介绍到这。

40.2硬件设计

本实验采用STM32F767的2个普通IO连接MPU9250(IIC),本章实验功能简介:程序先

初始化MPU9250等外设,然后利用MPL库,初始化MPU9250及使能DMP,最后,在死循

环里面不停读取:温度传感器、加速度传感器、陀螺仪、磁力计、MPL姿态解算后的欧拉角等

数据,通过串口上报给上位机(温度不上报),利用上位机软件(ANO_TC匿名科创地面站

),可以实时显示MPU9250的传感器状态曲线,并显示3D姿态,可以通过KEY0按

键开启/关闭数据上传功能。同时,在LCD模块上面显示温度和欧拉角等信息。DS0来指示程

序正在运行。

所要用到的硬件资源如下:

1)指示灯DS0

2)KEY0按键

3)LCD模块

4)串口

5)MPU9250

前4个,在之前的实例已经介绍过了,这里我们仅介绍MPU9250与阿波罗STM32F767开

发板的连接。该接口与MCU的连接原理图如40.2.1所示:

图40.2.1MPU9250与STM32F767的连接电路图

从上图可以看出,MPU9250的SCL和SDA与STM32F767开发板的PH4和PH5连接,与

24C02等共用IIC总线。图中,AD0接的GND,所以MPU9250的器件地址是:0X68。

注意:9D_INT信号,是连接在PCF8574T的P5脚上的,并没有直接连接到MCU,所以,

在需要读取9D_INT的时候,需要先初始化PCF8574T。不过,本例程用不到9D_INT,所以,

可以不初始化PCF8574T,直接通过IIC总线读取数据即可。

40.3软件设计

打开本章实验工程可以看到,我们在HARDWARE分组之下添加了MPU9250驱动源文件

,并且包含了其对应的头文件。同时,将MPL驱动库代码(见光盘例程

源码:实验35MPU9250九轴传感器实验\HARDWARE\MPU9250\MPL目录)添加到新建的

MPL分组之下,工程结构如图39.3.1所示:

图39.3.1MPU9250工程结构图

注意:MPL代码,要求在MDKOptionsforTarget的C/C++选项卡里面,要勾选C99模

式,否则编译出错。

由于篇幅所限,MPL部分的代码,我们就不详细介绍了,请大家参考motion_driver_6.12.zip

里面的相关教程进行学习。我们仅介绍里面的部分函数,首先是:MPU_Init,该函

数代码如下:

//初始化MPU9250

//返回值:0,成功

//其他,错误代码

u8MPU9250_Init(void)

{

u8res=0;

IIC_Init();//初始化IIC总线

MPU_Write_Byte(MPU9250_ADDR,MPU_PWR_MGMT1_REG,0X80);//复位MPU9250

delay_ms(100);//延时100ms

MPU_Write_Byte(MPU9250_ADDR,MPU_PWR_MGMT1_REG,0X00);//唤醒MPU9250

MPU_Set_Gyro_Fsr(3);

//陀螺仪传感器,±2000dps

MPU_Set_Accel_Fsr(0);

//加速度传感器,±2g

MPU_Set_Rate(50);

//设置采样率50Hz

MPU_Write_Byte(MPU9250_ADDR,MPU_INT_EN_REG,0X00);//关闭所有中断

MPU_Write_Byte(MPU9250_ADDR,MPU_USER_CTRL_REG,0X00);//主模式关闭

MPU_Write_Byte(MPU9250_ADDR,MPU_FIFO_EN_REG,0X00);//关闭FIFO

MPU_Write_Byte(MPU9250_ADDR,MPU_INTBP_CFG_REG,0X82);

//INT引脚低电平有效,开启bypass模式,可以直接读取磁力计

res=MPU_Read_Byte(MPU9250_ADDR,MPU_DEVICE_ID_REG);

//读取MPU6500的ID

if(res==MPU6500_ID)//器件ID正确

{

MPU_Write_Byte(MPU9250_ADDR,MPU_PWR_MGMT1_REG,0X01);

//设置CLKSEL,PLLX轴为参考

MPU_Write_Byte(MPU9250_ADDR,MPU_PWR_MGMT2_REG,0X00);

//加速度与陀螺仪都工作

MPU_Set_Rate(50);

//设置采样率为50Hz

}elsereturn1;

res=MPU_Read_Byte(AK8963_ADDR,MAG_WIA);//读取AK8963ID

if(res==AK8963_ID)

{

MPU_Write_Byte(AK8963_ADDR,MAG_CNTL1,0X11);//设置AK8963为单次测量模式

}elsereturn1;

return0;

}

该函数就是按我们在39.1.1节介绍的方法,对MPU9250进行初始化,该函数执行成功后,

便可以读取传感器数据了。

然后,我们再看MPU_Get_Temperature、MPU_Get_Gyroscope、MPU_Get_Accelerometer

和MPU_Get_Magnetometer等四个函数,源码如下:

//得到温度值

//返回值:温度值(扩大了100倍)

shortMPU_Get_Temperature(void)

{

u8buf[2];

shortraw;

floattemp;

MPU_Read_Len(MPU9250_ADDR,MPU_TEMP_OUTH_REG,2,buf);

raw=((u16)buf[0]8)|buf[1];

temp=21+((double)raw)/333.87;

returntemp*100;;

}

//得到陀螺仪值(原始值)

//gx,gy,gz:陀螺仪x,y,z轴的原始读数(带符号)

//返回值:0,成功

//其他,错误代码

u8MPU_Get_Gyroscope(short*gx,short*gy,short*gz)

{

u8buf[6],res;

res=MPU_Read_Len(MPU9250_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];

}

returnres;;

}

//得到加速度值(原始值)

//gx,gy,gz:陀螺仪x,y,z轴的原始读数(带符号)

//返回值:0,成功

//其他,错误代码

u8MPU_Get_Accelerometer(short*ax,short*ay,short*az)

{

u8buf[6],res;

res=MPU_Read_Len(MPU9250_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];

}

returnres;;

}

//得到磁力计值(原始值)

//mx,my,mz:磁力计x,y,z轴的原始读数(带符号)

//返回值:0,成功

//其他,错误代码

u8MPU_Get_Magnetometer(short*mx,short*my,short*mz)

{

u8buf[6],res;

res=MPU_Read_Len(AK8963_ADDR,MAG_XOUT_L,6,buf);

if(res==0)

{

*mx=((u16)buf[1]8)|buf[0];

*my=((u16)buf[3]8)|buf[2];

*mz=((u16)buf[5]8)|buf[4];

}

MPU_Write_Byte(AK8963_ADDR,MAG_CNTL1,0X11);

//AK8963每次读完以后都需要重新设置为单次测量模式

returnres;;

}

其中MPU_Get_Temperature用于获取MPU9250自带温度传感器的温度值,然后

MPU_Get_Gyroscope、MPU_Get_Accelerometer和MPU_Get_Magnetometer分别用于读取

陀螺仪、加速度传感器和磁力计的原始数据。

最后看MPU_Write_Len和MPU_Read_Len这两个函数,代码如下:

//IIC连续写

//addr:器件地址

//reg:寄存器地址

//len:写入长度

//buf:数据区

//返回值:0,正常

//其他,错误代码

u8MPU_Write_Len(u8addr,u8reg,u8len,u8*buf)

{

u8i;

IIC_Start();

IIC_S_Byte((addr1)|0);

//发送器件地址+写命令

if(IIC_Wait_Ack()){IIC_Stop();return1;}

//等待应答

IIC_S_Byte(reg);

//写寄存器地址

IIC_Wait_Ack();

//等待应答

for(i=0;ilen;i++)

{

IIC_S_Byte(buf[i]);

//发送数据

if(IIC_Wait_Ack()){IIC_Stop();return1;}//等待应答

}

IIC_Stop();

return0;

}

//IIC连续读

//addr:器件地址

//reg:要读取的寄存器地址

//len:要读取的长度

//buf:读取到的数据存储区

//返回值:0,正常

//其他,错误代码

u8MPU_Read_Len(u8addr,u8reg,u8len,u8*buf)

{

IIC_Start();

IIC_S_Byte((addr1)|0);

//发送器件地址+写命令

if(IIC_Wait_Ack()){IIC_Stop();return1;}

//等待应答

IIC_S_Byte(reg);

//写寄存器地址

IIC_Wait_Ack();

//等待应答

IIC_Start();

IIC_S_Byte((addr1)|1);

//发送器件地址+读命令

IIC_Wait_Ack();

//等待应答

while(len)

{

if(len==1)*buf=IIC_Read_Byte(0);

//读数据,发送nACK

else*buf=IIC_Read_Byte(1);

//读数据,发送ACK

len--;

buf++;

}

IIC_Stop();

//产生一个停止条件

return0;

}

MPU_Write_Len用于指定器件和地址,连续写数据,可用于实现MPL部分的:i2c_write

函数。而MPU_Read_Len用于指定器件和地址,连续读数据,可用于实现MPL部分的:i2c_read

函数。MPL移植部分的4个函数,这里就实现了2个,剩下的delay_ms就直接采用我们

里面的delay_ms实现,get_ms则直接提供一个空函数即可。

关于我们就介绍到这,的代码,我们这里就不再贴出了,大家看光

盘源码即可。

最后看看内容,代码如下:

//串口1发送1个字符

//c:要发送的字符

voidusart1_s_char(u8c)

{

while(__HAL_UART_GET_FLAG(UART1_Handler,UART_FLAG_TC)==RESET){};

USART1-TDR=c;

}

//传送数据给匿名四轴地面站(V4版本)

//fun:功能字.0X01~0X1C

//data:数据缓存区,最多28字节!!

//len:data区有效数据个数

voidusart1_niming_report(u8fun,u8*data,u8len)

{

u8s_buf[32];

u8i;

if(len28)return;

//最多28字节数据

s_buf[len+3]=0;

//校验数置零

s_buf[0]=0XAA;

//帧头

s_buf[1]=0XAA;

//帧头

s_buf[2]=fun;

//功能字

s_buf[3]=len;

//数据长度

for(i=0;ilen;i++)s_buf[4+i]=data[i];

//复制数据

for(i=0;ilen+4;i++)s_buf[len+4]+=s_buf[i];//计算校验和

for(i=0;ilen+5;i++)usart1_s_char(s_buf[i]);//发送数据到串口1

}

//发送加速度传感器数据+陀螺仪数据(传感器帧)

//aacx,aacy,aacz:x,y,z三个方向上面的加速度值

//gyrox,gyroy,gyroz:x,y,z三个方向上面的陀螺仪值

voidmpu9250_s_data(shortaacx,shortaacy,shortaacz,shortgyrox,shortgyroy,shortgyroz)

{

u8tbuf[18];

tbuf[0]=(aacx8)0XFF;

tbuf[1]=aacx0XFF;

tbuf[2]=(aacy8)0XFF;

tbuf[3]=aacy0XFF;

tbuf[4]=(aacz8)0XFF;

tbuf[5]=aacz0XFF;

tbuf[6]=(gyrox8)0XFF;

tbuf[7]=gyrox0XFF;

tbuf[8]=(gyroy8)0XFF;

tbuf[9]=gyroy0XFF;

tbuf[10]=(gyroz8)0XFF;

tbuf[11]=gyroz0XFF;

tbuf[12]=0;//开启MPL后,无法直接读取磁力计数据,所以这里直接屏蔽掉.用0替代.

tbuf[13]=0;

tbuf[14]=0;

tbuf[15]=0;

tbuf[16]=0;

tbuf[17]=0;

usart1_niming_report(0X02,tbuf,18);//传感器帧,0X02

}

//通过串口1上报结算后的姿态数据给电脑(状态帧)

//roll:横滚角.单位0.01度。-18000-18000对应-180.00-180.00度

//pitch:俯仰角.单位0.01度。-9000-9000对应-90.00-90.00度

//yaw:航向角.单位为0.1度0-3600对应0-360.0度

//csb:超声波高度,单位:cm

//prs:气压计高度,单位:mm

voidusart1_report_imu(shortroll,shortpitch,shortyaw,shortcsb,intprs)

{

u8tbuf[12];

tbuf[0]=(roll8)0XFF;

tbuf[1]=roll0XFF;

tbuf[2]=(pitch8)0XFF;

tbuf[3]=pitch0XFF;

tbuf[4]=(yaw8)0XFF;

tbuf[5]=yaw0XFF;

tbuf[6]=(csb8)0XFF;

tbuf[7]=csb0XFF;

tbuf[8]=(prs24)0XFF;

tbuf[9]=(prs16)0XFF;

tbuf[10]=(prs8)0XFF;

tbuf[11]=prs0XFF;

usart1_niming_report(0X01,tbuf,12);//状态帧,0X01

}

intmain(void)

{

u8t=0,report=1;

//默认开启上报

u8key;

floatpitch,roll,yaw;

//欧拉角

shortaacx,aacy,aacz;

//加速度传感器原始数据

shortgyrox,gyroy,gyroz;

//陀螺仪原始数据

shorttemp;

//温度

Cache_Enable();//打开L1-Cache

MPU_Memory_Protection();//保护相关存储区域

HAL_Init();

//初始化HAL库

Stm32_Clock_Init(432,25,2,9);//设置时钟,216Mhz

delay_init(180);

//初始化延时函数

…//此处省略部分初始化代码

while(mpu_dmp_init())

{

LCD_ShowString(30,130,200,16,16,"MPU9250Error");delay_ms(200);

LCD_Fill(30,130,239,130+16,WHITE);delay_ms(200);

LED0_Toggle;//DS0闪烁

}

LCD_ShowString(30,130,200,16,16,"MPU9250OK");

LCD_ShowString(30,150,200,16,16,"KEY0:UPLOADON/OFF");

POINT_COLOR=BLUE;//设置字体为蓝色

LCD_ShowString(30,170,200,16,16,"UPLOADON");

LCD_ShowString(30,200,200,16,16,"Temp:.C");

LCD_ShowString(30,220,200,16,16,"Pitch:.C");

LCD_ShowString(30,240,200,16,16,"Roll:.C");

LCD_ShowString(30,260,200,16,16,"Yaw:.C");

while(1)

{

key=KEY_Scan(0);

if(key==KEY0_PRES)

{

report=!report;

if(report)LCD_ShowString(30,170,200,16,16,"UPLOADON");

elseLCD_ShowString(30,170,200,16,16,"UPLOADOFF");

}

if(mpu_mpl_get_data(pitch,roll,yaw)==0)

{

temp=MPU_Get_Temperature();//得到温度值

MPU_Get_Accelerometer(aacx,aacy,aacz);//得到加速度传感器数据

MPU_Get_Gyroscope(gyrox,gyroy,gyroz);//得到陀螺仪数据

if(report)mpu9250_s_data(aacx,aacy,aacz,gyrox,gyroy,gyroz);

//发送加速度+陀螺仪原始数据

if(report)usart1_report_imu((int)(roll*100),(int)(pitch*100),(int)(yaw*100),0,0);

if((t%10)==0)

{

if(temp0)

{

LCD_ShowChar(30+48,200,'-',16,0);

//显示负号

temp=-temp;

//转为正数

}elseLCD_ShowChar(30+48,200,'',16,0);

//去掉负号

LCD_ShowNum(30+48+8,200,temp/100,3,16);

//显示整数部分

LCD_ShowNum(30+48+40,200,temp%10,1,16);

//显示小数部分

temp=pitch*10;

if(temp0)

{

LCD_ShowChar(30+48,220,'-',16,0);

//显示负号

temp=-temp;

//转为正数

}elseLCD_ShowChar(30+48,220,'',16,0);

//去掉负号

LCD_ShowNum(30+48+8,220,temp/10,3,16);

//显示整数部分

LCD_ShowNum(30+48+40,220,temp%10,1,16);

//显示小数部分

temp=roll*10;

if(temp0)

{

LCD_ShowChar(30+48,240,'-',16,0);

//显示负号

temp=-temp;

//转为正数

}elseLCD_ShowChar(30+48,240,'',16,0);

//去掉负号

LCD_ShowNum(30+48+8,240,temp/10,3,16);

//显示整数部分

LCD_ShowNum(30+48+40,240,temp%10,1,16);

//显示小数部分

temp=yaw*10;

if(temp0)

{

LCD_ShowChar(30+48,260,'-',16,0);

//显示负号

temp=-temp;

//转为正数

}elseLCD_ShowChar(30+48,260,'',16,0);

//去掉负号

LCD_ShowNum(30+48+8,260,temp/10,3,16);

//显示整数部分

LCD_ShowNum(30+48+40,260,temp%10,1,16);

//显示小数部分

t=0;

LED0_Toggle;//DS0闪烁

}

}

t++;

}

}

此部分代码除了main函数,还有几个函数,用于上报数据给上位机软件,利用上位机软件

显示传感器波形,以及3D姿态显示,有助于更好的调试MPU9250。上位机软件使用:ANO_TC

匿名科创地面站,该软件在:开发板光盘6,软件资料软件匿名地面站文件夹里

面可以找到,该软件的使用方法,见该文件夹下的:飞控通信协议,这里我们

不做介绍。其中,usart1_niming_report函数用于将数据打包、计算校验和,然后上报给匿名地

面站软件。MPU9250_s_data函数用于上报加速度和陀螺仪的原始数据,可用于波形显示传

感器数据,通过传感器帧(02H)发送。而usart1_report_imu函数,则用于上报飞控显示帧,

可以实时3D显示MPU9250的姿态,传感器数据等,通过状态帧(01H)发送。

这里,main函数是比较简单的,大家看代码即可,不过需要注意的是,为了高速上传数据,

这里我们将串口1的波特率设置为500Kbps了,测试的时候要注意下。

至此,我们的软件设计部分就结束了。

40.4下载验证

在代码编译成功之后,我们通过下载代码到ALIENTEK阿波罗STM32开发板上,可以看

到LCD显示如图40.4.1所示的内容:

图40.4.1程序运行时LCD显示内容

屏幕显示了MPU9250的温度、俯仰角(pitch)、横滚角(roll)和航向角(yaw)的数值。

然后,我们可以晃动开发板,看看各角度的变化。

另外,通过按KEY0可以开启或关闭数据上报,开启状态下,我们可以打开:ANO_TC匿

名科创地面站,这个软件,接收STM32F767上传的数据,从而图形化显示传感器数据以

及飞行姿态,如图40.4.2和图40.4.3所示:

图40.4.2传感器数据波形显示

图40.4.3飞控状态显示

图40.4.2就是波形化显示我们通过MPU9250_s_data函数发送的数据,采用传感器帧(02)

发送,总共6条线(ACC_X、ACC_Y、ACC_Z、GYRO_X、GYRO_Y和GYRO_Z)显示波形,

全部来自传感器帧,分别代表:加速度传感器x/y/z和角速度传感器(陀螺仪)x/y/z方向的原

始数据(请注意把选项“程序设置-上位机设置-数据校验”设置为Off,否则可能看不到数

据和飞控状态变化)。

图图40.4.3则3D显示了我们开发板的姿态,通过usart1_report_imu函数发送的数据显示,

采用状态帧(01)上传,同时还显示了加速度陀螺仪等传感器的原始数据。

很赞哦!(60)