mpl库是Invensense公司针对mpu6050/9250系列MEMS传感器开发的,基于DMP硬件解算的商业闭源库。可以大大简化惯性导航姿态解算部分和传感器融合的运算。
姿态解算是很复杂的,有多种算法需要利用四元数等等,不过因为有了mpl库,我们可以暂时免去这方面的学习。
传感器融合也很复杂。因为陀螺仪有漂移,短时精确,长时不精确,加速度计和磁力计有数据抖动,静止情况下短时不精确,长时精确。为了互补,且考虑动态和静态下的区别,有复杂的算法,也需要卡尔曼滤波等滤波方式。不过有了mpl库我们都不用自己写。
然而,mpl库还是有点问题的,首先,官方下载渠道太麻烦了!其次,他的没有底层驱动,而且例程也只有msp430的……移植到stm32上要改的地方太多了……
所以,我们要感谢正点原子二次封装,帮我们写了底层驱动并开源了。
不过,还是不完美,正点原子的代码要移植还是复杂……
所以,我们要感谢CSDN大佬鸡翅中大大的三次封装,大大简化了移植的难度。XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
但是还是有个问题……这位大佬用的不是stm32,导致他封装的移植到stm32还是要经过一些麻烦的过程……
所以我进行了四次封装,移植简单且适用于stm32。对于mpl库,我之前用c8t6,为了减小flash把其中用串口调试的代码部分注释掉了(这些部分也本来没有使用),不过不影响。我现在把它发出来,然后楼下解释下代码。对了,这个工程完后我会进一步模块化,便于大家的五次开发,不过现在重点是标定校准惯性器件,并实验算法,所以先这么用了。
打开那个uvision文件即可。
关于程序的烧录,江协科技的视频里有。这里我建议买双串口开发板,然后串口烧录。
串口烧录不方便debug,但是好处是在后续校准的时候方便,再也不用用杜邦线外接一个容易掉的串口模块了,插个microUSB口的数据线就行。(typec版本很贵,不知道为啥typec版本那么贵……)
串口烧录要用flymcu
这个在江协科技那里能下载到,我直接放过来了,不用安装,解压即可使用exe
使用方式也可以在江协科技那里学。实际上除了烧录慢一点都非常方便,配置一个DTR和RTS后都不用手动拔插跳线帽。详见江协科技的教程。
(审核大人非常抱歉,因为一些原因那个程序有之前没发现的问题,改了好多次,现在应该没问题了,麻烦了)
好的,首先,我们看看硬件连接。
先简介以下mpu9250模块,它本质上是mpu6500六轴惯性传感器和ak8963磁力计的集成。
网上买的mpu9250共有10个引脚,我们一个个讲(从易到难,并不按照模块上的顺序)。
以下“<->”左侧表示mpu9250模块上的引脚,右侧表示stm32f103rct6开发板上的引脚
首先:
VCC <-> VCC
GND <->GND
不用多说,给模块供电
其次:
FSYNC <->GND
配合其他传感器同步传输数据,这里不用,接地即可。(以后用其他传感器也不需要同步)
再次:
AD0 <-> GND
这个引脚是选择模块在I2C通信中的从机地址的。I2C通信中主机要先发一个地址(相当于从机的“名字”),来确定与哪个从机通信。mpu9250的地址就是mpu6500的地址,ak8963有另外的地址。mpu9250的七位二进制地址(或者是mpu6500的七位二进制地址)是0b10100x
末位x是AD0的数字输入,即,它接入VCC即1,整体换成十六进制是0x69,接入GND即0,整体是0x68。ak8963的地址则是0x0C,不可更改。
mpl里面用到的mpu9250地址是0x68,所以接地就行。读者如果想自己改也没问题,不过有点麻烦,要在inv_mpu.c里面改,建议不要改。
接下来:
NCS 悬空
这是用SPI通信时才有用的,这个模块只能I2C通信,我也不知道为啥要把这个引脚引出来
EDA 悬空
ECL 悬空
这两个引脚是用于扩展其他传感器的,这个时候mpu9250作为主机,不过我们不用。
然后:
INT <-> PB0
这个是中断引脚,可以配置输出类型,默认是DMP一次运算完毕输出后会拉低,单片机那边用外部中断检测,来判断什么时候读取姿态数据。由于他输出姿态频率不高,所以这个引脚是非常有用的,避免了程序每次循环都读取数据,能留更多时间给运算。选择的引脚也是任意的,这里比较方便,程序里就用这个了,到时候如果有必要修改很方便的。
最后:
SCL <-> PB1
SDA <-> PB10
这个是mpu9250与单片机进行I2C通信的两个引脚,分别是时间线和数据线。I2C具体的通信时序就不提了,可以去其他地方找,也可以看江协科技,不过不二次开发的话用不到的。
之所以选择这两个引脚,是因为它们在这个开发板上挨得近,如果换其他开发板,或者自己打板子的话可以改,也非常方便,后面会告诉大家怎么改。
对了顺便说一句,这个开发板上的GND刚好够用,不用另外引出线来,只需要7根母母头杜邦线即可连接。
接下来,看看软件部分。
如图
打开后应该长这样,如果不是的话可能是文件没打开,右侧点user旁边的加号,然后单击main.c即可,其他文件也一样。还有其他的原因可能是keil的配置问题,应该不会出现,若出现,可以看看江协科技的教程的一二讲,还有个无脑的办法,所有弹窗都点确定,不过我不能保证它不出问题。
Start组和Library组是单片机相关的库文件,一般不动它们。其他的文件夹接下来介绍。说一下,如果有不会怎么添加组和文件的可以去查,不过以后要添加我会把整个工程压缩后发出来,所以不二次开发没必要。
首先,我们说下System组,里面只有两个文件Delay.c和Delay.h,用于提供延时函数,不用动,以后直接用就行。这是江协科技编写的,大家也能看出来,这个工程的分组风格之类的都很接近江协科技的。
还有件事,这里按个人喜好把.h文件也放进去了,可能看着有点臃肿。
其次,是Hardware组,只有Serial.c和Serial.h,用于提供串口通信相关函数,只有串口发,没有串口接收。串口通信是一种点对点的通信,比较简单,一般用于单片机和电脑通信。这个是从江协科技的例程改编的,这个代码风格是我学csdn上看到的某个大佬,如图这种宏定义,方便后期更改引脚啊参数啊什么。![image.png]()
这里的头文件给出了它的可调用的函数,这里重定义了printf,就是Serial_Printf,好处是方便,坏处是整个工程只能有一个函数重定义printf,不过这个工程没问题。用法和printf完全一样,唯一的区别是它是单片机通过串口向电脑打印的。
接下来是User组,里面有几个是配置中断的,和本项目有关且我们需要重要关注的只有两个,一个是main.c,不用说了,主程序,另一个是Calibration.h以后用于存放校准用的宏定义,比如某个量x,应该是kx+b才准确,所以这里会存放k和b,但是现在不需要用。
然后是MPU_ForUser组,这是鸡翅中大大的封装,这里是移植时用于配置的文件。MPU_ForUser.c和MPU_ForUser.h是封装,用于调用mpl库的函数。他定义了一些结构体,这让程序的移植与修改更加方便。我稍微改动了一下,删掉了点正点原子二次封装时就留下来的一些没必要的部分,比如mpu初始化没必要手动初始化一遍等等。其实这个文件里面也有些没必要的函数,不过也许以后有用,先留着。这部分比较重要,所以相关函数我会放到后面讲。
接下来是MPU9250_Config.h这是鸡翅中大大搞的移植时用的配置文件,其中为了适应stm32和标准库,我改了一些地方。大家可以打开这个文件看看,开头的那些倒腾来倒腾去的宏定义是为了适配不同文件。嵌入式开发中,为了标明位数,常常用typedef重定义,比如char就写位uint8_t等等。正点原子用的是u8,鸡翅中大大用的是uint8,我受江协科技影响比较大,习惯用uint8_t,非常杂,所以就这么搞了一个定义。24行和29引入自带的延时函数和GPIO函数(用于模拟I2C时序),GPIO那边我用了我写的MPU9250_GPIO.h,后面详解。39行选择DMP还是MPL,看注释即可,我们选择MPL。后面还有些函数定义,这些都是为了移植的库常见的写法,注释写得挺好了,主要还是GPIO和延时那块。重点是48-56行,配置SCL和SDA的GPIO口,前面说SCL和SDA引脚可以改就是在这里改,I2C_GPIO_Clock_Cmd不用改,因为stm32f103所有GPIO都是用APB2时钟,其他都要更改。(49和50行那种宏定义是我为了让函数形式和鸡翅中大大用的LPC546XX单片机的库的函数一样搞的,实际函数有两个参数,不过这里的形式不用改所以问题不大)
接下来是MPU9250_GPIO.c和MPU9250_GPIO.h,这是我编写的GPIO底层函数,使用软件模拟I2C时序。不用硬件I2C的原因很抽象,就是硬件I2C本来应该更精确快速的,但它反而用不了,甚至过不了初始化,我也不知道为啥,只知道软件I2C能用
然后MPU9250_EXTI.c和MPU9250_EXTI.h这是我编写的外部中断底层函数,只有一个初始化函数,另一个是中断函数,在main.c里。注意,中断初始化后就一定要有中断函数,否则会卡死。MPU9250_EXTI.h里可以改外部中断引脚,如果要改的话。改的地方挺多的……不过有个简单的方式,看某个单词末尾的大写字母ABC(这里用B),还有阿拉伯数字(这里用0),全部改了就行。
最后就是MPU9250-Lock组了,这也是鸡翅中大大为了封装建的,里面除了MPU9250.c和MPU9250.h外,全是mpl库的内容,绝大多数时候别动。虽然移植的时候本来应该要改,很复杂,但是封装这么多遍,只要不改上述两个文件中的函数名等等就不用动了。MPU9250.c和MPU9250.h开始是正点原子写的,我把读取原始数据和初始化部分删掉了,只留下两个函数。这个文件建议不要动,因为这里是MPU9250的通讯时序,而且函数的名字和移植mpl库有关(里面也有个宏定义把里面的某个东西宏定义成这个,在inv_mpu.c里面),知道这个文件干什么的就行。
OK这里整个工程就大致介绍完了。晚上我再更新下main.c和MPU_ForUser.c里面的两个最重要的函数,并解释下这个工程里面是怎么获取各种数据的。
我们看到main.c,25行到52行是初始化内容,有串口啊,外部中断啊等等,重点是这里。
这里是初始化MPU9250及dmp的。
框选后右键MPU_Init_ForUser(),选择Go To Definition Of "MPU_Init_ForUser"跳转,会到MPU_ForUser.c中
这里dmp的初始化包括mpu9250的初始化了,不用另外搞。这里接收返回值用于查找问题。
同样方法打开 mpu_dmp_init(),跳转到inv_mpu.c
这里的mpu_init()就是mpu9250的初始化函数,不用向正点原子那样手动配置寄存器,因为它这个库就会配置寄存器。
这里提一嘴,同样方法打开mpu_init()
779-789行,这里是配置加速度和角速度的量程的,我们要拉到最大,已经搞好了,如果未来用于火箭的话,发现某个量程没必要这么大,可以适当减小,让数据更精确,那个时候才要来这里修改。这里写入寄存器,由于其他地方要用是读取寄存器的,所以只需要在这里一个地方改。
回到mpu_dmp_init(),看第3408行,这是防强磁干扰,检测到磁场过强后会自动关掉磁力计的融合,在探空火箭领域用处不大,而且可能有负面影响,这里直接注释掉。
然后这里有两个宏定义
DEFAULT_MPU_HZ和COMPASS_READ_MS,前者是mpl采样率,后者是磁力计采样间隔时间。(不知道为啥不统一……不过不管了)
这里同样方式跳转到inv_mpu.h,那里的注释可以忽略,实测DEFAULT_MPU_HZ为20最好,太高了反而输出延迟很大(我也不知道为啥
)
磁力计采样换成频率不能超过mpl采样频率,我用50ms,也就是频率相同,实测再大一点也没问题。
注释里提到的DMP_SAMPLE_RATE别动,保持200,是dmp输出频率,我也不知道为啥,实测太小会有问题。
当然这些都是以后要输出姿态才搞的,现在暂时没用,所以了解下,不用管。
剩下的部分大家可以看看注释,不用改动。(当然整个程序都不用改动)
OK以上就是初始化部分,晚上搞获取数据部分,就结束了。
接下来看看58行的READ_FIFO
这个全局变量表示DMP准备完成,可以读取姿态数据。在中断函数MPU_EXTI_Int_Fuction中置1。(这个函数当然也是用了宏定义的,在MPU9250_EXTI.h中)
Refresh_MPUTeam是刷新函数,同样方法打开,跳转到MPU_ForUser.c
我稍微改了下,原来有用自己写的函数读取原始数据的,我改成用mpl库自带的函数了。
这个函数相对简单后面都是读取各种数据。
重点是看看读取的数据存储在哪里。
在MPU_Data这个结构体中,按类似图中的方法引出即可。
在MPU_ForUser.h中有定义。(38-79行)
OK,那这个工程就基本讲完了。
看到main.c,这里就是存在类型为short(十六位有符号,-32768到32767)的数组中,然后通过串口输出。
这个程序本来是校准用的,为了电脑的程序输入方便,末尾用的是分号,先看看可不可以正常输出吧。可以通过解除或加上注释来控制输出。以加速度为例,按理说静止正放应该读到的是0,0,1024(一个重力加速度在+-16g的量程下是测出数值是1024,还有为啥不是0,0,0,这是因为传感器本质上测量的是压力)
当然肯定有偏差,这就需要校准了。
对了,电脑没有读取串口的程序可以用这个,江协科技提供的。我用的是9600的波特率,8位数据,1位停止位,偶校验,记得选择文本模式。
OK,移植这一篇文章就到这里了,下一章是MPU的标定或者说校准。
放一张实验效果图,这里输出的是加速度
确实看着不好看,反正这只是一个输出数据供校准用的,可以不管这个。
对了,这里有个重点要说下!我修改了一部分mpl库内部的代码!我不知道为什么,可能在多次包装的过程中有人误动了!mpl库里面几个文件不管是代码还是注释,对欧拉角的定义域都没有统一!我按照《惯性导航》的标准修改了!这些以后会讲,大家最好下载我发的这个代码。