STM32移植MPU6050 DMP超详细教程_mpu6050移植dmp
STM32移植MPU6050 DMP库完整指南(细节增强版)
一、环境搭建与文件准备
1. 获取官方DMP库
█ 关键步骤:
-
访问TDK InvenSense开发者中心
-
下载\"Embedded MotionDriver\"最新版本(推荐V6.12)
-
详细步骤:CSDN DMP库下载教程
2. 关键文件清单
文件路径:motion_driver_6.12\\msp430\\eMD-6.0\\core\\driver\\eMPL
二、工程配置流程
1、在工程目录下创建名为\"MPU6050\"的文件夹,将包含官方驱动文件的6个文件以及你自主编写的MPU6050.c/MPU6050.h统一存放。如下图所示:
2、紧接着打开工程,进行工程内文件配置。如下图所示:
注意事项:
- 添加完文件点close退出当前界,不要点×关闭当前界面!!!close后点OK退出当前界面,不要点×退出当前界面!!!
3、在左边文件操作栏下打开inv_mpu.h,inv_mpu.c,inv_mpu_dmp_motion_driver.h,inv_mpu_dmp_motion_driver.c文件
4.在inv_mpu.c文件做出以下修改
在inv_mpu.c文件中需通过#include \"MPU6050.h\"
和#include \"Delay.h\"
引入自定义头文件,目的是在官方驱动中调用您实现的底层函数:
- 传感器通信:
MPU6050_Write
/MPU6050_Read
(对应I2C读写协议,在你的MPU6050.c中实现) - 延时控制:
delay_ms
(在Delay.c中实现的毫秒级延时函数)
若你的函数命名与示例不同,需同步修改inv_mpu.c中对这三个函数的调用名称,确保与您实际编写的函数名完全一致即可完成驱动适配。
MPU6050_Read函数如下
int MPU6050_Read(uint8_t addr, uint8_t reg, uint8_t len, uint8_t *buf)// 向指定从机指定地址读len个字节,用于dmp{ MyI2C_Start(); MyI2C_SendByte(addr << 1); MyI2C_ReceiveAck(); MyI2C_SendByte(reg); MyI2C_ReceiveAck(); MyI2C_Start(); MyI2C_SendByte((addr << 1)+1); MyI2C_ReceiveAck(); while (len) { if (len == 1){ *buf = MyI2C_ReceiveByte();MyI2C_SendAck(1);} else{ *buf = MyI2C_ReceiveByte();MyI2C_SendAck(0);} buf++; len--; }MyI2C_Stop();return 0;}
MPU6050_Write函数如下
int MPU6050_Write(uint8_t addr, uint8_t reg, uint8_t len, uint8_t *data) //向指定地址写len个字节,用于dmp{int i; MyI2C_Start(); MyI2C_SendByte(addr << 1); MyI2C_ReceiveAck(); MyI2C_SendByte(reg); MyI2C_ReceiveAck();for (i = 0; i < len; i++) { MyI2C_SendByte(data[i]); MyI2C_ReceiveAck(); } MyI2C_Stop();return 0;}
移植时需特别注意以下关键细节:
- 通信函数适配:
MPU6050_Read/Write
必须严格保持int
返回值(成功时返回0)及形参格式(uint8_t addr, uint8_t reg, uint8_t len, uint8_t *buf
),仅需将MyI2C_Start()
等底层I2C函数替换为实际硬件驱动接口。 - 时间函数声明:在实现毫秒计时函数
void mget_ms(unsigned long *time)
后,需在inv_mpu.h
中添加void mget_ms(unsigned long *timestamp);
声明,确保inv_mpu_dmp_motion_driver.c
文件能正确调用,且函数形参类型必须严格匹配unsigned long*
指针类型,否则可能引发DMP初始化不成功等问题。
5.在inv_mpu_dmp_motion_driver.c文件做出以下修改
完成上述移植步骤后即可尝试编译工程,此时编译器会报出若干错误及警告。接下来我将针对这些编译问题逐一解析并提供解决方案,请根据实际报错信息定位代码中的接口适配或声明缺失等问题。
6、编译异常处理
1)
- 双击警告会弹到警告的位置
2)
- 双击警告会弹到警告的位置
注释掉箭头所示的两行代码
3)
- 双击该错误定位到错误位置,将下图这几行代码注释掉
7、为DMP初始化做铺垫
修改inv_mpu.h,inv_mpu.c文件中的mpu_init函数,CTRL+F呼出下图界面
找到mpu_init函数后,修改如下图
将mpu_init的形参改为void,后面DMP初始化需要用到。
8、初始化DMP及获取姿态角(在MPU6050.c中初始化)
代码只是看着长,很多都是直接移植就行。初始化DMP前要先初始化MPU6050,文章只是介绍如何初始化DMP,省略了MPU6050驱动函数,请读者自行完善。
#include \"inv_mpu_dmp_motion_driver.h\"#include \"inv_mpu.h\"#define q30 1073741824.0f //用于归一化四元数#define DEFAULT_MPU_HZ (200) //DMP采样频率200Hzshort gyro[3], accel[3], sensors; //用于读取DMP_FIFOstatic signed char gyro_orientation[9] = { 1, 0, 0, //方向矩阵 0, 1, 0, 0, 0, 1};static unsigned short inv_row_2_scale(const signed char *row) //用于初始化DMP{ unsigned short b; if (row[0] > 0) b = 0; else if (row[0] < 0) b = 4; else if (row[1] > 0) b = 1; else if (row[1] < 0) b = 5; else if (row[2] > 0) b = 2; else if (row[2] < 0) b = 6; else b = 7; // error return b;}static unsigned short inv_orientation_matrix_to_scalar( const signed char *mtx) //用于初始化DMP{ unsigned short scalar; scalar = inv_row_2_scale(mtx); scalar |= inv_row_2_scale(mtx + 3) << 3; scalar |= inv_row_2_scale(mtx + 6) << 6; return scalar;}static void run_self_test(void) //用于初始化DMP{ int result; long gyro[3], accel[3]; result = mpu_run_self_test(gyro, accel); if (result == 0x7) { /* Test passed. We can trust the gyro data here, so let\'s push it down * to the DMP. */ float sens; unsigned short accel_sens; mpu_get_gyro_sens(&sens); gyro[0] = (long)(gyro[0] * sens); gyro[1] = (long)(gyro[1] * sens); gyro[2] = (long)(gyro[2] * sens); dmp_set_gyro_bias(gyro); mpu_get_accel_sens(&accel_sens); accel[0] *= accel_sens; accel[1] *= accel_sens; accel[2] *= accel_sens; dmp_set_accel_bias(accel); Usart_Printf(\"setting bias succesfully ......\\r\\n\"); }}// DMP(数字运动处理器)初始化函数void DMP_Init(void){ u8 temp[1]={0}; temp[0] = MPU6050_GetID(); // 读取MPU6050设备ID if(temp[0]!=0x68) NVIC_SystemReset(); // 校验设备ID是否为0x68(正常值),错误则系统复位 // 主初始化流程 if(!mpu_init()) // 成功返回0,初始化MPU6050底层,与步骤7相呼应 { // 配置传感器使能状态 if(!mpu_set_sensors(INV_XYZ_GYRO | INV_XYZ_ACCEL)) {} // 启用陀螺仪和加速度计的三轴数据 // FIFO配置 if(!mpu_configure_fifo(INV_XYZ_GYRO | INV_XYZ_ACCEL)) {} // 配置FIFO存储陀螺仪和加速度计数据 // 采样率设置 if(!mpu_set_sample_rate(DEFAULT_MPU_HZ)) {} // 设置采样率为默认值(典型值如100Hz) // DMP固件加载 if(!dmp_load_motion_driver_firmware()) {} // 加载DMP运动驱动固件 // 方向矩阵配置 if(!dmp_set_orientation(inv_orientation_matrix_to_scalar(gyro_orientation))) {} // 设置传感器方向校准矩阵 // 启用DMP特性 if(!dmp_enable_feature(DMP_FEATURE_6X_LP_QUAT | DMP_FEATURE_TAP | // 启用6轴低功耗四元数 | 敲击检测 DMP_FEATURE_ANDROID_ORIENT | DMP_FEATURE_SEND_RAW_ACCEL | // Android方向识别 | 原始加速度数据 DMP_FEATURE_SEND_CAL_GYRO | DMP_FEATURE_GYRO_CAL)) {} // 校准后的陀螺仪数据 | 陀螺仪校准 // FIFO速率设置 if(!dmp_set_fifo_rate(DEFAULT_MPU_HZ)) {} // 设置DMP输出速率与采样率同步 run_self_test(); // 运行自检程序(校准传感器) // 启用DMP if(!mpu_set_dmp_state(1)) {} // 激活DMP功能(1=启用,0=关闭) }}
这几段代码几乎不用修改,只有DMP_Init
中的 MPU6050_GetID()
和Usart_Printf()
是需要替换的,替换成你自己的驱动函数。
u8 MPU6050_DMP_Get_Data(float *pitch, float *roll, float *yaw)
直接调用就行无需修改
u8 MPU6050_DMP_Get_Data(float *pitch, float *roll, float *yaw){ unsigned long sensor_timestamp; unsigned char more; long quat[4]; // 从DMP FIFO读取四元数数据 if (dmp_read_fifo(gyro, accel, quat, &sensor_timestamp, &sensors, &more)) return 1; // 四元数数据解析(q30格式转浮点) if (sensors & INV_WXYZ_QUAT) { q0 = quat[0] / 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 * (q1 * q2 + q0 * q3), q0 * q0 + q1 * q1 - q2 * q2 - q3 * q3) * 57.3; // 偏航角 } else { return 2; } return 0;}``