陀螺仪mpu6050的调试与dmp库的移植经历

通过三天的痛苦挣扎终于调通了mpu以及移植好了dmp库

一开始动手使用mpu6050的时候移植了一个例程想试下效果

不曾想调用dmp的时候就出错了,然后尝试了各种方法试图定位

错误,一开始怀疑模拟IIC有问题,然后仔细核对了一下IIC的时序

例程程序:

//MPU IIC 延时
void MPU_IIC_Delay(void)
{
	delay_us(2);
}
//产生II起始信号
void MPU_IIC_Start(void)
{
	MPU_SDA_OUT();     
	MPU_IIC_SDA=1;	  	  
	MPU_IIC_SCL=1;
	MPU_IIC_Delay();
 	MPU_IIC_SDA=0;
	MPU_IIC_Delay();
	MPU_IIC_SCL=0;
}	  

可以发现延时有点不对,可以对照时序图

然后以为是时序问题,就索性自己写的模拟IIC程序代替了,结果还是不行,排除时序问题,实际上例程这么写是为了加快读取数据的速度,

然后自己便尝试了硬件iic读取mpu6050的数据,看代码

#include "delay.h"

#include "IICMPU.h"

//初始化IIC
void MPU_IIC_INIT(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
	I2C_InitTypeDef I2C_InitStructure; 
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1,ENABLE);  

	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7; //
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; 
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD; 
	GPIO_Init(GPIOB, &GPIO_InitStructure); 
  I2C_InitStructure.I2C_Mode = I2C_Mode_I2C ; 
	I2C_InitStructure.I2C_Ack = I2C_Ack_Enable; 
	I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit; 
	I2C_InitStructure.I2C_ClockSpeed = 50000; 

	I2C_Init(I2C1, &I2C_InitStructure);	   	
	I2C_Cmd  (I2C1,ENABLE); 
	I2C_AcknowledgeConfig(I2C1, ENABLE); 


}
/*********写一个字节到寄存器
					REG_Address 几首数据的iic设备寄存器的地址
					REG_data 待写入的数据******************************/
void I2C_ByteWrite(uint8_t REG_Address,uint8_t REG_data)
{

I2C_GenerateSTART(I2C1,ENABLE);
while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_MODE_SELECT));
I2C_Send7bitAddress(I2C1,SlaveAddress,I2C_Direction_Transmitter);
while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));
I2C_SendData(I2C1,REG_Address);
while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_TRANSMITTED));
I2C_SendData(I2C1,REG_data);
while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_TRANSMITTED));
I2C_GenerateSTOP(I2C1,ENABLE);
}
/*************?MPU6050读一个字节**********************************/
uint8_t I2C_ByteRead(uint8_t REG_Address)
{
uint8_t REG_data;

while(I2C_GetFlagStatus(I2C1,I2C_FLAG_BUSY));
I2C_GenerateSTART(I2C1,ENABLE);//起始信号
while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_MODE_SELECT));
I2C_Send7bitAddress(I2C1,SlaveAddress,I2C_Direction_Transmitter);//发送设备地址+写信号
while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));//
I2C_Cmd(I2C1,ENABLE);
I2C_SendData(I2C1,REG_Address);//发送储存单元,从0开始
while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_TRANSMITTED));
I2C_GenerateSTART(I2C1,ENABLE);//起始信号
while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_MODE_SELECT));
I2C_Send7bitAddress(I2C1,SlaveAddress,I2C_Direction_Receiver);//发送设备地址+读信号
while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED));
I2C_AcknowledgeConfig(I2C1,DISABLE);
I2C_GenerateSTOP(I2C1,ENABLE);
while(!(I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_RECEIVED)));
REG_data=I2C_ReceiveData(I2C1);//读出数据寄存器

return REG_data;
}
//mpu初始化
void InitMPU6050(void)
{
	I2C_ByteWrite(PWR_MGMT_1,0x00);//解除休眠状态
	I2C_ByteWrite(SMPLRT_DIV,0x07);//采样频率125hz
	I2C_ByteWrite(CONFIG,0x06);//
	I2C_ByteWrite(GYRO_CONFIG,0x18);//陀螺仪量程设置+-2000deg/s,灵敏度16.4LSB/led/s
	I2C_ByteWrite(ACCEL_CONFIG,0x01);//加速度传感器量程设置+-4g,灵敏度8190LSB/g
}


/*******************获取数据*******************************/
unsigned int GetData(unsigned char REG_Address)
{
	char H,L;                                         
	H=I2C_ByteRead(REG_Address);
	L=I2C_ByteRead(REG_Address+1);
	return (H<<8)+L;   //合成数据
}
然后读取数据正常,但是这样移植起DMP库就麻烦多了,具体能不能实现移植还没有去尝试,可以参考http://bbs.eeworld.com.cn/thread-444810-1-1.html
就这样排除了IIC的问题,直接把问题指向mpu初始化配置

通过分析终于找到了问题所在,直接看图



看注释的那条语句,注释掉就可以完美运行,直接使用默认的时钟,但是这样势必加大了最后的出欧拉角的误差。

至于为什么在注释这条语句就会到至错误我自己也想不大明白,如果有读者发现了问题,希望交流交流,

通常情况下DMP库的自检也会导致一些错误,看图


可以尝试注释掉自检;对于自检函数,我们打开来看看里面到底是什么

u8 run_self_test(void)
{
	int result;
	//char test_packet[4] = {0};
	long gyro[3], accel[3]; 
	result = mpu_run_self_test(gyro, accel);
	if (result == 0x3) 
	{
		/* 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);
		return 0;
	}else return 1;
}

下面的文字应用某贴:

细心的人如果将它打开后会发现这里面实际也是一个校零程序,矫正三个方向的陀螺仪的零点漂移,第二个就是纠正重力方向,也就是开机时刻的加速度方向(通俗一点就是我们认为的水平方向)
result = mpu_run_self_test(gyro, accel);  用于获得当前各轴的角速度和加速度,并且反馈芯片的状态。
                                 mpu_get_gyro_sens(&sens);                  这个就不用说了,用于获得角速度的计算比例;好戏在下头。
                                dmp_set_gyro_bias(gyro);                      这个把开机当前的角速度计算完成后送到了DMP库

我们知道,对于运动,我们都要为其设立一个参考系,因为运动都是相对的,陀螺仪输出了值,这个值是相对与谁呢??我们在地球上,相对参考系当然是地面。假如我们在动车上做实验,陀螺仪输出的值是相对与谁呢??当然是动车!!!!   那为什么是动车呢?那我就告诉你,因为开机陀螺仪初始化的时候陀螺仪是相对火车静止的,故开机对谁静止,陀螺仪输出的值就是相对于谁的运动速率。
         回到主题,DMP自检时刻保存了开机时的陀螺仪速率,其实就是让陀螺仪检测到的速率去除开机时刻的速率,自然得到了陀螺仪与开机时刻参考系的相对速率,也就是我们需要的有效值。这也就是抑制温漂的原理。
         再来看看加速度重心的校准程序,与陀螺仪一样,均是修正相对参考系的速率和位置。但是加速度传感器不一样,只要传感器不动,其重力加速度方向是不变的,默认的是-Y是重力加速的的正方向。如果在模块非水平情况下进行了重力校准,那么-Y就不是重力方向了,为了得到无论模块处于什么状态是都需要—y是重力的正方向,我们就有这样的一个高大上的办法。那就是“不要对重力进行校准”
        改掉源代码就是在使用accel_sens前加一行:  accel_sens=0;使其重力校准失效!!!即可!!!

经修改后,模块的初始方位不再收开机时刻模块位置的影响,使用DMP一样需要8S左右的时间稳定,起码不用再担心开机需要去将小车扶正吧!(如果是其他用途,需要开机修正方位的程序,这样就不行了哟!目测比较少


要注意的就是修改以上的定义,如上图修改即可


  • 30
    点赞
  • 230
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值