源代码的问题,本文就不啰嗦了,大家参考正点原子的MPU6050例程即可。MPU9150和MPU6050用一模一样的代码就可以。
本文只罗列在DMP使用过程中遇到的坑和解法。
问题1:MPU6050的AD0引脚接了3.3V,器件地址在程序中设置了是0X69,但是初始化不成功?
首先,一般的初始化代码大致如下:
//初始化MPU6050
//返回值:0,成功
// 其他,错误代码
u8 MPU_Init(void)
{
u8 res;
IIC_Init();//初始化IIC总线
MPU_Write_Byte(MPU_PWR_MGMT1_REG,0X80); //复位MPU6050
delay_ms(100);
MPU_Write_Byte(MPU_PWR_MGMT1_REG,0X00); //唤醒MPU6050
MPU_Set_Gyro_Fsr(3); //陀螺仪传感器,±2000dps
MPU_Set_Accel_Fsr(0); //加速度传感器,±2g
MPU_Set_Rate(50); //设置采样率50Hz
MPU_Write_Byte(MPU_INT_EN_REG,0X00); //关闭所有中断
MPU_Write_Byte(MPU_USER_CTRL_REG,0X00); //I2C主模式关闭
MPU_Write_Byte(MPU_FIFO_EN_REG,0X00); //关闭FIFO
MPU_Write_Byte(MPU_INTBP_CFG_REG,0X80); //INT引脚低电平有效
res=MPU_Read_Byte(MPU_DEVICE_ID_REG);
if(res==MPU_ADDR) //器件ID正确 // 注意这里MPU_ADDR=0X69
{
MPU_Write_Byte(MPU_PWR_MGMT1_REG,0X01); //设置CLKSEL,PLL X轴为参考
MPU_Write_Byte(MPU_PWR_MGMT2_REG,0X00); //加速度与陀螺仪都工作
MPU_Set_Rate(50); //设置采样率为50Hz
}else return 1; // 返回1,代表初始化失败
return 0; // 返回0,代表初始化成功
}
大家可以在这段初始化代码里设置断点,看看问题发生在哪个位置!
解法1:
- 若是在”if(res==MPU_ADDR) “这句话以前就发生问题了,那么应该是I2C的设置有问题,连线出错了,或者是电源电压,总之硬件和软件I2C的配置应该有问题。
- 若是问题发生在”if(res==MPU_ADDR) “,那么大家可以打印出”res”这个变量,这个变量的值应该是0X68。因此,肯定和我们设置好的”MPU_ADDR=0X69″不符了,自然初始化就运行不下去了。这时候解决方法很简单:
if(res==MPU_ADDR) 》》改成》》 if(res==0x68)
至于为什么AD0接了3.3V,器件返回的地址还是0X68?
这个其实是我们自己弄混了。器件地址就是0X68,这是内置的。至于我们通过AD0接了3.3V,改变的只是器件的操作地址,我们必须通过0X69来操作器件了。而器件自己认为自己的地址还是0X68。
问题2:AD0引脚接了3.3V,MPU6050初始化成功,但是DMP的初始化(mpu_dmp_init)失败,导致欧拉角数据全为0?
mpu_dmp_init函数的代码如下:
//mpu6050,dmp初始化
//返回值:0,正常
// 其他,失败
u8 mpu_dmp_init(void)
{
u8 res=0;
IIC_Init(); //初始化IIC总线
if(mpu_init()==0) //初始化MPU6050
{
res=mpu_set_sensors(INV_XYZ_GYRO|INV_XYZ_ACCEL);//设置所需要的传感器
if(res)return 1;
res=mpu_configure_fifo(INV_XYZ_GYRO | INV_XYZ_ACCEL);//设置FIFO
if(res)return 2;
res=mpu_set_sample_rate(DEFAULT_MPU_HZ); //设置采样率
if(res)return 3;
res=dmp_load_motion_driver_firmware(); //加载dmp固件
if(res)return 4;
res=dmp_set_orientation(inv_orientation_matrix_to_scalar(gyro_orientation));//设置陀螺仪方向
if(res)return 5;
res=dmp_enable_feature(DMP_FEATURE_6X_LP_QUAT|DMP_FEATURE_TAP| //设置dmp功能
DMP_FEATURE_ANDROID_ORIENT|DMP_FEATURE_SEND_RAW_ACCEL|DMP_FEATURE_SEND_CAL_GYRO|
DMP_FEATURE_GYRO_CAL);
if(res)return 6;
res=dmp_set_fifo_rate(DEFAULT_MPU_HZ); //设置DMP输出速率(最大不超过200Hz)
if(res)return 7;
res=run_self_test(); //自检
if(res)return 8;
res=mpu_set_dmp_state(1); //使能DMP
if(res)return 9;
}
return 0;
}
老实说,这段代码写得很瞎。没想到这还是官方提供的。我们先分析一下这段代码:
if(mpu_init()==0)
{
***
}
这句话说明,mpu_init()返回值是0时,才执行if语句内部的DMP初始化命令,也就是说mpu_init()返回值必须是0啊,要是不是0,连DMP初始化命令都不执行,那DMP初始化肯定失败呀。
但是,往下看你会发现,要是mpu_init()返回值不是0的话,不执行if语句,那么就直接跳到return 0这句话上了。而返回值为0,代表的是初始化正常。。。
看到这,我真是无语了。。。我之前一直认为DMP初始化成功了,只是数据读不出来。结果发现这里有问题,我也是费了好大一番劲呢。
解法2:
我遇到的问题是mpu_init()返回值不是0,然后直接没有运行DMP的初始化命令。
首先,MPU初始化成功了,另外原始数据也可以读到,可以说明I2C应该没有问题。应该问题就是出在DMP初始化上了。
一步步的追溯发现了问题的根源:
mpu_dmp_init()
|-- mpu_init()
|-- if (i2c_write(st.hw->addr, st.reg->pwr_mgmt_1, 1, data)) return -1; // 这句话中出错了,返回了-1
我发现在上面这句if语句中出错了,返回了-1。于是,我进一步查看了st.hw->addr这个变量的数值,发现这个变量被设置成了0X68。。。
我的AD0引脚接了3.3V了啊,这里必须得是0X69呀,于是我改成了0X69。一切都恢复正常了,可以读到欧拉角数据了。
这里我不得不再吐槽吐槽,这个地方的地址要改,怎么从来没有人说过呢???
改的位置,我告诉大家:
// 这可以在inv_mpu.c文件的开头部分找到
const struct hw_s hw={
0x69, //addr // 就是这里
1024, //max_fifo
118, //num_reg
340, //temp_sens
-521, //temp_offset
256 //bank_size
};
问题3:MPU6050初始化成功,DMP的初始化成功,但读出来的欧拉角数据全为0?
解法3:
这个问题很常见,就是FIFO溢出导致的。大家可以缩短读取数据的间隔时间,避免数据溢出。
while(mpu_dmp_get_data(&pitch,&roll,&yaw)!=0){}
试试看这句话,如果是因为FIFO溢出的话,写上这句话,肯定就能读出来了。
总结
MPU6050/MPU9150的数据读取阶段可以分成:
- I2C配置
- MPU6050/MPU9150的初始化
- DMP功能初始化
- 读取原始数据和欧拉角
问题可能出现的位置:
- MPU6050/MPU9150的初始化就不成功,那么需要检查I2C的问题了,或者是地址的配置问题,硬件层面的问题可能性很大。
- 读取原始数据可以,但欧拉角不行。那说明 MPU6050/MPU9150的初始化成功了,I2C应该也没什么问题,那就是DMP配置的问题了,这就是软件层面的问题了。
- 如果DMP配置也没有问题,那么就是FIFO读取时候的问题了。这个容易解决。
最后给大家一点小贴士,一定要用仿真器单步调试程序,定位出问题的位置,这样才能最快的解决问题。一味在网上搜索类似的问题,很可能现象相同,但是根本的原因又完全不同!