motion_driver_6.12(官方DMP库)的移植和MPU6050驱动的实现
大约 8 分钟
motion_driver_6.12(官方DMP库)的移植和MPU6050驱动的实现
目录
motion driver_6.12
- motion driver_6.12 是从invensense网站上下载的最新的DMP库。
- Motion Driver是传感器驱动程序层的嵌入式软件堆栈,可轻松配置和利用InvenSense运动跟踪解决方案的许多功能。
- 支持的运动设备为MPU6050 / MPU6500 / MPU9150 / MPU9250。
- 硬件和板载数字运动处理器(DMP)的许多功能都封装在可以使用和引用的模块化API中。
- Motion Driver设计为一种可轻松移植到大多数MCU的解决方案。
- Motion Driver 6.0包括一个用于ARM MCU和TI-MSP430的9轴解决方案。
- 仅6轴解决方案应继续参考运动驱动程序5.1.2,以便更容易理解软件。
motion_driver_6.12的三个主要目录
driver/
- 包含了与 MPU 传感器通信和控制相关的底层驱动程序和库文件。
- 实现了对MPU寄存器读写的函数(inv_mpu.c)
- 基于对MPU寄存器读写的函数(inv_mpu.c),实现了:写入DMP固件、操作DMP的一些函数(inv_mpu_dmp_motion_driver.c)。
只需要对这部分代码做修改就能实现移植,主要为:
- 修改宏定义,提供通信接口
- 修改宏定义,提供获取时间的接口
- 修改宏定义,提供延时函数接口
mllite/
- 应该就是轻量级版本的Motion Processing Library (MPL)
- 简化版的数据处理和传感器融合功能
mpl/
:- 完整的 Motion Processing Library
- 提供了更高级的数据处理和传感器融合功能,包括姿态估计、传感器数据融合等。
- 但只包含一些头文件,和一个
libmpllib.a
的静态库文件,没有源代码。
motion_driver_6.12目录结构
motion_driver_6.12/
- driver/
- eMPL/
inv_mpu.c
实现了与MPU交互的基本功能,如读写寄存器inv_mpu_dmp_motion_driver.c
: 实现了与DMP交互的功能,包括:写入DMP固件到MPU、初始化、数据处理。dmpmap.h
DMP寄存器映射的定义dmpKey.h
DMP 寄存器映射和键值定义inv_mpu.h
头文件inv_mpu_dmp_motion_driver.h
: 头文件
- include/
log.h
: 日志记录功能的头文件。mlinclude.h
: 包含了其他头文件的汇总文件。mlmath.h
: 数学函数库的头文件。mlos.h
: 操作系统相关功能的头文件。mltypes.h
: 数据类型定义的头文件。stdint_invensense.h
: 固定宽度整数类型的定义文件。
- eMPL/
- mllite/
data_builder.c
: 构建数据结构的实现文件。data_builder.h
: 构建数据结构的头文件。hal_outputs.c
: 实现了硬件抽象层的输出功能。hal_outputs.h
: 定义了硬件抽象层的输出接口。invensense.h
: InvenSense 库的主头文件。message_layer.c
: 实现了消息层的功能。message_layer.h
: 定义了消息层的接口。mlmath.c
: 数学函数库的实现文件。ml_math_func.c
: 数学函数的具体实现。ml_math_func.h
: 数学函数的声明。mpl.c
: 实现了 Motion Processing Library 的核心功能。mpl.h
: 提供了 Motion Processing Library 的函数声明。results_holder.c
: 保存结果数据的实现文件。results_holder.h
: 定义了保存结果数据的接口。start_manager.c
: 管理启动过程的实现文件。start_manager.h
: 定义了管理启动过程的接口。storage_manager.c
: 管理数据存储的实现文件。storage_manager.h
: 定义了数据存储的接口。
- mpl/
accel_auto_cal.h
: 加速度计自动校准的头文件。compass_vec_cal.h
: 罗盘向量校准的头文件。fast_no_motion.h
: 快速静止检测的头文件。fusion_9axis.h
: 九轴传感器融合的头文件。gyro_tc.h
: 陀螺仪温度补偿的头文件。heading_from_gyro.h
: 从陀螺仪计算航向的头文件。invensense_adv.h
: InvenSense 高级功能的头文件。inv_math.h
: InvenSense 数学函数库的头文件。libmpllib.a
: Motion Processing Library 的静态库文件。mag_disturb.h
: 磁场扰动检测的头文件。motion_no_motion.h
: 运动和静止检测的头文件。no_gyro_fusion.h
: 无陀螺仪传感器融合的头文件。quaternion_supervisor.h
: 四元数监督的头文件。
DMP
简单来说,
- DMP 是 MPU-6050 内部的一个硬件加速器,
- 可以执行复杂的运动处理任务,如传感器数据融合、姿态估计等,无需主处理器的干预。
- 可以直接输出四元数等姿态信息。
- DMP是MPU设备中的快速,低功耗,可编程,嵌入式轻量级处理器。
- 设计是从MCU卸载功能,如传感器融合和手势识别,以节省系统中的总体功率。
- DMP具有许多功能,可以在运行时动态关闭和打开。
- 也可以禁用个别功能,同时让其他功能运行。
- 除计步器外,所有DMP数据都输出到FIFO。
- DMP还可以编程为通过手势或数据就绪生成中断。
- 功能
- ①3轴低功率四元数 --- 仅陀螺四元数。启用此功能后,将以200Hz的频率整合陀螺仪数据,同时以用户请求的速率将传感器融合数据输出到FIFO。200Hz集成将允许更准确的传感器融合数据。在MD6中,如果启用此功能,驱动程序会将3轴四元数推入MPL库,MPL将处理加速度和罗盘集成。
- ②6轴低功率四元数 --- 陀螺仪和加速四元数。与3轴LPQ类似,以200Hz采样速率集成加速度和陀螺仪将以用户请求的速率输出到FIFO。3轴LPQ和6轴LPQ是互斥的,不应同时运行。如果启用,则可以将6轴四元数推入MPL库,MPL将处理9轴的罗盘集成。
- ③方向手势识别 ---使用传感器数据检测设备方向是否有纵向,横向,反向纵向和反向方向的变化。非常依赖于方向矩阵。
- ④点击手势识别 --- 设备上的多方向点击检测。此功能将让用户知道检测到哪个轴位置或负值。它可以检测多达4个多抽头.PSP可用于配置此功能的阈值,死区时间和分接头数。
- ⑤计步器手势识别 --- 简单的计步器提供步数和时间戳。此功能会自动启用,但在检测到连续5秒的步骤后才会触发。5秒钟后,计数和时间戳将开始,数据可以从DMP存储器中读出。
- ⑥DMP中断 --- 可以将中断配置为在传感器数据就绪(FIFO输出速率)或检测到抽头或方向手势时生成。
MPL算法
- Motion Driver 6.12包含一个二进制库,其中包含用于传感器融合和动态校准的InvenSense专有算法。
- MD 6.12驱动程序将传感器数据推入MPL,MPL将处理包括罗盘集成在内的9轴传感器融合。
- 在启用MPL库之前配置MPL功能。它们可以通过API调用动态关闭和打开到MPL中。
MPL 算法 | 描述 |
---|---|
陀螺仪校准(快速无动作) | 运行时间校准例程。一旦检测到无运动状态,陀螺校准将被触发。在无运动状态检测5秒内完成校准。 |
陀螺仪温度补偿 | 在每个陀螺仪校准之后,MPL将记录内部温度。在多个数据点之后,MPL将能够为陀螺仪建立多点温度斜率,并将其应用于校准偏差。这将补偿陀螺仪由于温度造成的漂移。 |
指南针校准 | 用于MPU9150和MPU9250的运行时间硬铁罗盘校准。MPL读取并建立设备周围的磁场环境。一旦存在足够的数据,可以应用罗盘偏移,并且可以生成9轴四元数。如果你处于一个磁场不稳定的环境,指南针就不会被校准。如果罗盘没有校准,四元数只使用6轴。图8在装置上的运动将加快校准。 |
Mag干扰拒绝 | 校准后,MPL库将跟踪磁场,如果有异常检测,MPL库将拒绝指南针数据,并切换回6轴融合。在检测到磁干扰之后,MPL库将继续每隔5秒检查指南针数据。在每次检查中,如果干扰不再存在,它将切换回9轴融合,否则它将继续拒绝数据。 |
3轴融合 | 陀螺角四元数 |
6轴融合 | 陀螺与加速度传感器的四元数 |
9轴融合 | 陀螺仪、AccEL和罗盘四元数 |
motion_driver_6.12的移植
只需要修改
inv_mpu.c
和inv_mpu_dmp_motion_driver.c
文件,添加一些宏定义,即可完成移植
inv_mpu.c
// start ################# 添加的代码 #################
#if defined EMPL_TARGET_STM32F1
#include "Soft_I2C.h"
#include "Delay.h"
#include "RTC_Time.h"
#include "OLED_Printf.h"
// 提供通信接口 注意这里要求函数当执行成功时返回值0
#define i2c_write(slave_addr,reg_addr,length,p_data) (!Soft_I2C_Write_Device_Register_Datas(slave_addr,reg_addr,p_data,length))
#define i2c_read(slave_addr,reg_addr,length,p_data) (!Soft_I2C_Read_Device_Register_Datas(slave_addr,reg_addr,p_data,length))
// 提供延时接口
#define delay_ms Delay_ms
// 提供获取时间的接口
#define get_ms RTC_Time_GetTime_MS
// 提供打印输出的接口
#define log_i OLED_Printf
#define log_e OLED_Printf
#define min(a,b) ((a<b)?a:b)
// end ###############################################
#elif defined EMPL_TARGET_STM32F4
#include "i2c.h"
#include "main.h"
#include "log.h"
#include "board-st_discovery.h"
#define i2c_write Sensors_I2C_WriteRegister
#define i2c_read Sensors_I2C_ReadRegister
#define delay_ms mdelay
#define get_ms get_tick_count
#define log_i MPL_LOGI
#define log_e MPL_LOGE
#define min(a,b) ((a<b)?a:b)
inv_mpu_dmp_motion_driver.c
// start ################# 添加的代码 #################
#if defined EMPL_TARGET_STM32F1
#include "Soft_I2C.h"
#include "RTC_Time.h"
// 提供通信接口
#define i2c_write(slave_addr,reg_addr,length,p_data) Soft_I2C_Write_Device_Register_Datas(slave_addr,reg_addr,p_data,length)
#define i2c_read(slave_addr,reg_addr,length,p_data) Soft_I2C_Read_Device_Register_Datas(slave_addr,reg_addr,p_data,length)
// 提供获取时间的接口
#define get_ms RTC_Time_GetTime_MS
// end ###############################################
#elif defined EMPL_TARGET_STM32F4
#include "i2c.h"
#include "main.h"
#include "board-st_discovery.h"
#define i2c_write Sensors_I2C_WriteRegister
#define i2c_read Sensors_I2C_ReadRegister
#define get_ms get_tick_count
基于motion_driver_6.12实现MPU6050的驱动
/Drivers/mpu6050.h
#ifndef _MPU_6050_H_
#define _MPU_6050_H_
#include "motion_driver_6.12/driver/eMPL/inv_mpu.h" // 操作MPU
#include "motion_driver_6.12/driver/eMPL/inv_mpu_dmp_motion_driver.h" // 操作DMP
#include "motion_driver_6.12/mllite/ml_math_func.h" // 数学计算
#include "OLED_Printf.h"
#include "math.h"
#define log OLED_Printf
#ifdef __cplusplus
extern "C"
{
#endif
int MPU6050_DMP_Init();
int MPU6050_DMP_Get_Data(float *pitch, float *roll, float *yaw);
#ifdef __cplusplus
}
#endif
#endif
/Drivers/mpu6050.c
#include "mpu6050.h"
#define COMPASS_ENABLED 0
/* Data read from MPL. */
#define PRINT_ACCEL (0x01)
#define PRINT_GYRO (0x02)
#define PRINT_QUAT (0x04)
#define PRINT_COMPASS (0x08)
#define PRINT_EULER (0x10)
#define PRINT_ROT_MAT (0x20)
#define PRINT_HEADING (0x40)
#define PRINT_PEDO (0x80)
#define PRINT_LINEAR_ACCEL (0x100)
#define PRINT_GRAVITY_VECTOR (0x200)
#define ACCEL_ON (0x01)
#define GYRO_ON (0x02)
#define COMPASS_ON (0x04)
#define MOTION (0)
#define NO_MOTION (1)
/* Starting sampling rate. */
#define DEFAULT_MPU_HZ (20)
#define FLASH_SIZE (512)
#define FLASH_MEM_START ((void *)0x1800)
#define PEDO_READ_MS (1000)
#define TEMP_READ_MS (500)
#define COMPASS_READ_MS (100)
/* Every time new gyro data is available, this function is called in an
* ISR context.
*/
static void gyro_data_ready_cb(void)
{
}
/* Platform-specific information. Kinda like a boardfile. */
struct platform_data_s
{
signed char orientation[9];
};
/* The sensors can be mounted onto the board in any orientation. The mounting
* matrix seen below tells the MPL how to rotate the raw data from the
* driver(s).
* TODO: The following matrices refer to the configuration on internal test
* boards at Invensense. If needed, please modify the matrices to match the
* chip-to-body matrix for your particular set up.
*/
static int8_t orientation[8] = {1, 0, 0,
0, 1, 0,
0, 0, 1};
int run_self_test();
int MPU6050_DMP_Init()
{
int result;
unsigned char accel_fsr, new_temp = 0;
unsigned short gyro_rate, gyro_fsr;
unsigned long timestamp;
struct int_param_s int_param;
int_param.cb = gyro_data_ready_cb;
#if COMPASS_ENABLED
unsigned char new_compass = 0;
unsigned short compass_fsr;
#endif
if (mpu_init(&int_param)) // mpu6050初始化
return -1;
/* Get/set hardware configuration. Start gyro. */
/* Wake up all sensors. */
#if COMPASS_ENABLED
if (mpu_set_sensors(INV_XYZ_GYRO | INV_XYZ_ACCEL | INV_XYZ_COMPASS)) // 设置所有传感器
return -2;
#else
if (mpu_set_sensors(INV_XYZ_GYRO | INV_XYZ_ACCEL)) // 设置传感器
return -3;
#endif
/* Push both gyro and accel data into the FIFO. */
if (mpu_configure_fifo(INV_XYZ_GYRO | INV_XYZ_ACCEL)) // 配置fifo
return -4;
if (mpu_set_sample_rate(DEFAULT_MPU_HZ)) // 设置采样率
return -5;
#if COMPASS_ENABLED
/* The compass sampling rate can be less than the gyro/accel sampling rate.
* Use this function for proper power management.
*/
if (mpu_set_compass_sample_rate(1000 / COMPASS_READ_MS)) // 设置罗盘采样率
return -6;
#endif
/* Read back configuration in case it was set improperly. */
if (mpu_get_sample_rate(&gyro_rate)) // 获取采样率
return -7;
if (mpu_get_gyro_fsr(&gyro_fsr)) // 获取满量程范围
return -8;
if (mpu_get_accel_fsr(&accel_fsr)) // 获取满量程范围
return -9;
#if COMPASS_ENABLED
if (mpu_get_compass_fsr(&compass_fsr)) // 获取满量程范围
return -10;
#endif
if (dmp_load_motion_driver_firmware()) // 加载DMP固件
{
log("Could not download DMP.\n");
return -11;
}
if (dmp_set_orientation(inv_orientation_matrix_to_scalar(orientation))) // 设置陀螺仪方向
return -12;
/*
* Known Bug -
* DMP when enabled will sample sensor data at 200Hz and output to FIFO at the rate
* specified in the dmp_set_fifo_rate API. The DMP will then sent an interrupt once
* a sample has been put into the FIFO. Therefore if the dmp_set_fifo_rate is at 25Hz
* there will be a 25Hz interrupt from the MPU device.
*
* There is a known issue in which if you do not enable DMP_FEATURE_TAP
* then the interrupts will be at 200Hz even if fifo rate
* is set at a different rate. To avoid this issue include the DMP_FEATURE_TAP
*
* DMP sensor fusion works only with gyro at +-2000dps and accel +-2G
*/
if (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功能
return -13;
if (dmp_set_fifo_rate(DEFAULT_MPU_HZ)) // 设置输出速率
return -14;
if (run_self_test()) // 自检
return -15;
if (mpu_set_dmp_state(1)) // 使能DMP
return -16;
}
// 2^30
#define Q30 (1073741824.0f)
/**
* @brief run_self_test
* @return 0 if successful.
*/
int MPU6050_DMP_Get_Data(float *pitch, float *roll, float *yaw)
{
short gyro[3]; // 重力加速度
short accel[3]; // 角加速度
long quat[4]; // Q30格式四元数
float q[4]; // float格式四元数
unsigned long timestamp;
short sensors;
unsigned char more;
if (dmp_read_fifo(gyro, accel, quat, ×tamp, &sensors, &more))
return -1;
if (sensors & INV_WXYZ_QUAT)
{
q[0] = quat[0] / Q30;
q[1] = quat[1] / Q30;
q[2] = quat[2] / Q30;
q[3] = quat[3] / Q30;
// 四元数转欧拉角
// 1rad = 57.3 degree
*roll = atan2(2 * (q[0] * q[1] + q[2] * q[3]), 1 - 2 * (q[1] * q[1] + q[2] * q[2])) * 57.3;
*pitch = asin(2 * q[0] * q[2] - 2 * q[1] * q[3]) * 57.3;
*yaw = atan2(2 * (q[1] * q[2] + q[0] * q[3]), 1 - 2 * (q[2] * q[2] + q[3] * q[3])) * 57.3;
}
return 0;
}
/**
* @brief run_self_test
* @return 0 if successful.
*/
int run_self_test()
{
int result;
long gyro[3], accel[3];
#if defined(MPU6500) || defined(MPU9250)
result = mpu_run_6500_self_test(gyro, accel, 0);
#elif defined(MPU6050) || defined(MPU9150)
result = mpu_run_self_test(gyro, accel);
#endif
if (result == 0x07)
{
log("Passed!\n");
log("accel: %7.4f %7.4f %7.4f\n",
accel[0] / 65536.f,
accel[1] / 65536.f,
accel[2] / 65536.f);
log("gyro: %7.4f %7.4f %7.4f\n",
gyro[0] / 65536.f,
gyro[1] / 65536.f,
gyro[2] / 65536.f);
/* Test passed. We can trust the gyro data here, so now we need to update calibrated data*/
#ifdef USE_CAL_HW_REGISTERS
/*
* This portion of the code uses the HW offset registers that are in the MPUxxxx devices
* instead of pushing the cal data to the MPL software library
*/
unsigned char i = 0;
for (i = 0; i < 3; i++)
{
gyro[i] = (long)(gyro[i] * 32.8f); // convert to +-1000dps
accel[i] *= 2048.f; // convert to +-16G (bug fix from +-8G)
accel[i] = accel[i] >> 16;
gyro[i] = (long)(gyro[i] >> 16);
}
mpu_set_gyro_bias_reg(gyro);
#if defined(MPU6500) || defined(MPU9250)
mpu_set_accel_bias_6500_reg(accel);
#elif defined(MPU6050) || defined(MPU9150)
mpu_set_accel_bias_6050_reg(accel);
#endif
#else
/* Push the calibrated data to the MPL library.
*
* MPL expects biases in hardware units << 16, but self test returns
* biases in g's << 16.
*/
unsigned short accel_sens;
float gyro_sens;
mpu_get_accel_sens(&accel_sens);
accel[0] *= accel_sens;
accel[1] *= accel_sens;
accel[2] *= accel_sens;
inv_set_accel_bias(accel, 3);
mpu_get_gyro_sens(&gyro_sens);
gyro[0] = (long)(gyro[0] * gyro_sens);
gyro[1] = (long)(gyro[1] * gyro_sens);
gyro[2] = (long)(gyro[2] * gyro_sens);
inv_set_gyro_bias(gyro, 3);
#endif
}
else
{
if (!(result & 0x1))
log("Gyro failed.\n");
if (!(result & 0x2))
log("Accel failed.\n");
if (!(result & 0x4))
log("Compass failed.\n");
return -1;
}
return 0;
}