MPU6050 usage experience (briefly share)

Preface

MPU6050 is selected for tilt detection function.

Preliminary preparation

Development board: Zhengdian Atom STM32F103 Elite Edition (STM32F103ZET6)
Insert image description here

Module: GY-521 MPU6050
Insert image description here

Others: several DuPont lines, programming lines, FlyMcu, Keil5, and supporting kits for the Zhengdian Atomic Development Board (TFTLCD)

Routines and data downloads

From Taobao seller: https://pan.baidu.com/share/init?surl=dNDqcp76L9QdM7iSZYfz_A Password: 4eum

GY-521 MPU6050模块 三维角度传感器6DOF三轴加速度计电子陀螺仪\MPU6050六轴角度加速度传感器\11,ATK-MPU6050六轴传感器模块\2,程序源码\(库函数版本,适合精英STM32开发板)实验30 MPU6050六轴传感器实验.rar
Our routine just happened to adapt to our development version of the punctual atomic routine. Although this program is for ATK-MPU6050, there is actually not much difference between the two modules. The routine can be used universally, which is very convenient.

Schematic diagram

We can look at the schematic diagrams of the two models and find that the ATK-MPU6050 only has a few pins that are not connected. If not used, there is actually no difference.

GY-521 MPU6050

Insert image description here

ATK-MPU6050

The AUX_CL and AUX_DA here are not connected.
Insert image description here

Pin description

Source: attitude sensor——MPU6050

SCL、SDA:是连接MCU的IIC接口,MCU通过这个IIC接口来控制MPU6050,此时MPU6050作为一个IIC从机设备,接单片机的I2C_SCL。
XCL、XDA:辅助IIC用来连接其他器件,可用来连接外部从设备,比如磁传感器,这样就可以组成一个九轴传感器,不需要连接单片机。
AD0:地址管脚,可以不接单片机。当MPU6050作为一个IIC从机设备的时候,有8位地址,高7位的地址是固定的,就是WHOAMI寄存器的默认——0x68,最低的一位是由AD0的连线决定的。
AD0接GND时,高8位的最后一位是0,所以iic从机地址是0x68;
AD0接VCC时,高8位的最后一位是1,所以iic从机地址是0x69。
INT:数据输出的中断引脚,可以不接单片机,准备好数据之后,通过中断告诉STM32,从而获取数据。
VCC:接3.3V或5V电源
GND:接地

Therefore, it doesn’t matter if the XCL and XDA of ATK-MPU6050 are not connected, it will not affect it.

wiring

You can refer to the reference materials ATK-MPU6050六轴传感器模块用户手册_V1.0.pdf, or directly look at the source code of the routine.

VCC -》 3.3V
GND -》 GND
SCL -》 PB10
SDA -》 PB11
AD0 -》 PA15

Analysis of key parts of source code

main.c

The core part of concern is while(mpu_dmp_init())DMP initialization. Pay attention to the parts that may cause problems.

inv_mpu.c

//mpu6050,dmp初始化
//返回值:0,正常
//    其他,失败
u8 mpu_dmp_init(void)
{
	u8 res=0;
	MPU_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;     
	}else return 10;
	return 0;
}

The run_self_test()key is that the self-test operation will adjust each angle of the module to "zero degree". The so-called zero degree is not true zero degree. I will add it later.
If you fail the self-test, the LCD will keep beating MPU6050 Error. Of course, you may be lucky and pass it directly. You may not know hh. Then
follow the normal operation. Under the official demo, you should place the module horizontally on the ground and place the chip still. Upward (actually downwards can also be used, but there are pitfalls!!!), and then reset the program (you can also do it without resetting, it will run in an endless loop). If it goes well, you can see the values ​​of various angles. Of course, be careful You will find some clues and the display Yaw航向角will jump. At this time, you can read the explanation of this article: Analysis and processing of common problems of MPU6050. It is very well explained and hits the pain points directly. In fact, it is drift caused by hardware problems. It needs Additional magnetometer added. However, the function I need to implement is only tilt detection, so the Yaw heading angle does not need to be used, and it will not affect it.
Insert image description here

So at this step, even if you disable the module and flatten it, you still cannot complete the self-test, so let's continue in depth. Previous suggestion: You can try changing a module (because after in-depth research, I finally found out that it was a module problem)

Come in run_self_test(void)and find out

//MPU6050自测试
//返回值:0,正常
//    其他,失败
u8 run_self_test(void)
{
	int result;
	//char test_packet[4] = {0};
	long gyro[3], accel[3];
	
	
	//float sens;
	//unsigned short accel_sens;
	
	
	result = mpu_run_self_test(gyro, accel);
	
	// 强制自检成功,万不得已的情况下使用
	//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;
	*/
	
	// 如果加速度和陀螺仪自检通过,就把当前读取到的各个xyz轴数据设置,作为基准值
	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;
}

After seeing the source code, it is actually obvious. If the self-test fails, it means that mpu_run_self_testthe result returned by the function is not 0x3. Then we will continue to delve into mpu_run_self_testthe function.

/* 
这段注释解释了一个触发传感器自检的函数。自检过程会返回一个掩码,用于指示哪些传感器通过了自检,哪些未通过。
其中,第 0 位表示陀螺仪,第 1 位表示加速度计,第 2 位表示磁罗盘。
注释中还提到目前 MPU6500 不支持硬件自检,但仍然可以通过此函数获取加速度计和陀螺仪的偏移。
另外,为了保证自检的准确性,调用此函数时设备应该处于面朝上或面朝下的状态,即 z 轴与重力平行。
函数会返回一个结果掩码,用于表示哪些传感器未通过自检,哪些通过了。
*/
/**
 *  @brief      Trigger gyro/accel/compass self-test.
 *  On success/error, the self-test returns a mask representing the sensor(s)
 *  that failed. For each bit, a one (1) represents a "pass" case; conversely,
 *  a zero (0) indicates a failure.
 *
 *  \n The mask is defined as follows:
 *  \n Bit 0:   Gyro.
 *  \n Bit 1:   Accel.
 *  \n Bit 2:   Compass.
 *
 *  \n Currently, the hardware self-test is unsupported for MPU6500. However,
 *  this function can still be used to obtain the accel and gyro biases.
 *
 *  \n This function must be called with the device either face-up or face-down
 *  (z-axis is parallel to gravity). 
目前,MPU6500 不支持硬件自检。然而,该函数仍可用于获取加速度计和陀螺仪的偏移。
调用此函数时,设备必须处于面朝上或面朝下的状态(z 轴与重力平行)。
 *  @param[out] gyro        Gyro biases in q16 format.
 *  @param[out] accel       Accel biases (if applicable) in q16 format.
 *  @return     Result mask (see above).
 */
 // 此时的gyro,accel是前一个函数定义的局部变量,没有初始值
int mpu_run_self_test(long *gyro, long *accel)
{
#ifdef MPU6050
	  // 原来这个参数为2,堆栈中为0x02,即使改为0x03,在仿真堆栈中任然为0x02
    const unsigned char tries = 2;
    long gyro_st[3], accel_st[3];
    unsigned char accel_result, gyro_result;
#ifdef AK89xx_SECONDARY
    unsigned char compass_result;
#endif
    int ii;
#endif
    int result;
    unsigned char accel_fsr, fifo_sensors, sensors_on;
    unsigned short gyro_fsr, sample_rate, lpf;
    unsigned char dmp_was_on;

    if (st.chip_cfg.dmp_on) {
        mpu_set_dmp_state(0);
        dmp_was_on = 1;
    } else
        dmp_was_on = 0;

    /* Get initial settings. */
		// 0x07D0
    mpu_get_gyro_fsr(&gyro_fsr);
		// 0x02
    mpu_get_accel_fsr(&accel_fsr);
		// 0x002A
    mpu_get_lpf(&lpf);
		// 0x0064
    mpu_get_sample_rate(&sample_rate);
		// 0x78 'x'
    sensors_on = st.chip_cfg.sensors;
		// 0x78 'x'
    mpu_get_fifo_config(&fifo_sensors);

    /* For older chips, the self-test will be different. */
#if defined MPU6050
		
    for (ii = 0; ii < tries; ii++)
        if (!get_st_biases(gyro, accel, 0))
            break;
    if (ii == tries) {
        /* If we reach this point, we most likely encountered an I2C error.
         * We'll just report an error for all three sensors.
         */
        result = 0;
        goto restore;
    }
    for (ii = 0; ii < tries; ii++)
        if (!get_st_biases(gyro_st, accel_st, 1))
            break;
    if (ii == tries) {
        /* Again, probably an I2C error. */
        result = 0;
        goto restore;
    }
		
	printf("accel=%ld, %ld, %ld\r\n", accel[0], accel[1], accel[2]);
	printf("accel_st=%ld, %ld, %ld\r\n", accel_st[0], accel_st[1], accel_st[2]);
	printf("gyro=%ld, %ld, %ld\r\n", gyro[0], gyro[1], gyro[2]);
	printf("gyro_st=%ld, %ld, %ld\r\n", gyro_st[0], gyro_st[1], gyro_st[2]);
		
	// 加速度传感器自检
    accel_result = accel_self_test(accel, accel_st);
	// 陀螺仪传感器的自检 
    gyro_result = gyro_self_test(gyro, gyro_st);
		
	printf("accel_result=%d, gyro_result=%d\r\n", accel_result, gyro_result);

    result = 0;
    if (!gyro_result)
        result |= 0x01;
    if (!accel_result)
        result |= 0x02;

#ifdef AK89xx_SECONDARY
    compass_result = compass_self_test();
    if (!compass_result)
        result |= 0x04;
#endif
restore:
#elif defined MPU6500
    /* For now, this function will return a "pass" result for all three sensors
     * for compatibility with current test applications.
     */
    get_st_biases(gyro, accel, 0);
    result = 0x7;
#endif
    /* Set to invalid values to ensure no I2C writes are skipped. */
    st.chip_cfg.gyro_fsr = 0xFF;
    st.chip_cfg.accel_fsr = 0xFF;
    st.chip_cfg.lpf = 0xFF;
    st.chip_cfg.sample_rate = 0xFFFF;
    st.chip_cfg.sensors = 0xFF;
    st.chip_cfg.fifo_enable = 0xFF;
    st.chip_cfg.clk_src = INV_CLK_PLL;
    mpu_set_gyro_fsr(gyro_fsr);
    mpu_set_accel_fsr(accel_fsr);
    mpu_set_lpf(lpf);
    mpu_set_sample_rate(sample_rate);
    mpu_set_sensors(sensors_on);
    mpu_configure_fifo(fifo_sensors);

    if (dmp_was_on)
        mpu_set_dmp_state(1);

    return result;
}

After coming in, you can actually find that the two self-test functions that affect the result value are actually these two self-test functions. As long as one of the two sensors fails, it will be GG. You can also determine which error is caused by the return value.

// 加速度传感器自检
accel_result = accel_self_test(accel, accel_st);
// 陀螺仪传感器的自检 
gyro_result = gyro_self_test(gyro, gyro_st);

My mistake at that time was in the acceleration area accel_self_test. In fact, the implementation of these two areas is similar. Let's continue to delve deeper into accel_self_testthe function.

/*
用于加速度传感器自检的函数,主要是通过比较两组偏移值来检查传感器是否正常工作。
函数的目标是检测传感器在不同轴向上的偏移值是否在允许的范围内,如果都在范围内,则返回0,表示自检通过。
*/
// bias_regular[0] = 0xFFFF14F6, bias_st[0] = 0xFFFFA5DD
static int accel_self_test(long *bias_regular, long *bias_st)
{
    int jj, result = 0;
    float st_shift[3], st_shift_cust, st_shift_var;

	// 计算加速度传感器的标定偏移
	// st_shift[3] = {0.561419249, 0.47499004, 0.60024482}
    get_accel_prod_shift(st_shift);
    for(jj = 0; jj < 3; jj++) {
		// 强制跳过z轴检测
		if(2 == jj) break;
			
	    // 计算当前轴向的自定义偏移,即标定偏移和测试偏移之间的差值除以一个常数
		// 第三组数据时,st_shift_cust = 0,0xFFF10000
        st_shift_cust = labs(bias_regular[jj] - bias_st[jj]) / 65536.f;
		// 如果标定偏移不为零(即不等于零),则进入这个条件判断
        if (st_shift[jj]) {
			// 计算偏移变化,即自定义偏移相对于标定偏移的比例减去1
			// test.max_accel_var设置于全局变量,为0.14
			// 如果偏移变化的绝对值大于某个阈值 test.max_accel_var,则表示偏移变化超过了允许范围,将当前轴向的位设置到 result 变量中
            if (fabs(st_shift_var) > test.max_accel_var)
                result |= 1 << jj;
				printf("accel_self_test fabs(st_shift_var)[%d]=%f\r\n", jj, fabs(st_shift_var));
				// min_g=0.3, max_g=0.95
        } else if ((st_shift_cust < test.min_g) ||
            (st_shift_cust > test.max_g)) {
            result |= 1 << jj;
					printf("st_shift_cust[%d]=%f\r\n", jj, st_shift_cust);
			}
    }

    return result;
}

// 陀螺仪传感器的自检,通过计算偏移值和变化来检测陀螺仪是否在正常工作范围内。如果所有轴向的偏移值都在允许范围内,函数返回0,表示自检通过
// bias_regular[0] = 0xFFFF7447, bias_st[0] = 0x0031289B
static int gyro_self_test(long *bias_regular, long *bias_st)
{
	 // 定义了整型变量 jj 和 result,用于迭代循环和存储自检结果
    int jj, result = 0;
	 // 定义了一个长度为 3 的无符号字符数组 tmp,用于存储临时数据
    unsigned char tmp[3];
	 // 定义了三个浮点数变量 st_shift,st_shift_cust 和 st_shift_var,分别用于存储陀螺仪的标定偏移、自定义偏移和偏移变化
    float st_shift, st_shift_cust, st_shift_var;

	 // 从 I2C 设备中读取数据,并将其存储在 tmp 数组中。如果读取失败,返回错误代码 0x07
    if (i2c_read(st.hw->addr, 0x0D, 3, tmp))
        return 0x07;

	// 通过按位与操作,将读取的数据中的高三位清零,以获得有效的偏移码
    tmp[0] &= 0x1F;
    tmp[1] &= 0x1F;
    tmp[2] &= 0x1F;

		
    for (jj = 0; jj < 3; jj++) {
		// 强制跳过z轴检测
		if(2 == jj) break;
			
			
		// 计算了自定义偏移和偏移变化,并进行了自检。如果偏移变化超过了阈值,或者自定义偏移值超出了允许的范围,则将当前轴向的位设置到 result 变量中
        st_shift_cust = labs(bias_regular[jj] - bias_st[jj]) / 65536.f;
        if (tmp[jj]) {
            st_shift = 3275.f / test.gyro_sens;
			// 将 st_shift 初始化为一个计算值。然后根据偏移码逐次进行迭代,每次将 st_shift 乘以 1.046,以计算标定偏移值
            while (--tmp[jj])
                st_shift *= 1.046f;
            st_shift_var = st_shift_cust / st_shift - 1.f;
            if (fabs(st_shift_var) > test.max_gyro_var) {
                result |= 1 << jj;
					printf("gyro_self_test fabs(st_shift_var)[%d]=%f\r\n", jj, fabs(st_shift_var));
			}
        } else if ((st_shift_cust < test.min_dps) ||
            (st_shift_cust > test.max_dps)) {
            result |= 1 << jj;
					printf("st_shift_cust[%d]=%f\r\n", jj, st_shift_cust);
			}
    }
    return result;
}

You can find that I added a z-axis skip part, which you can ignore. So let's take a look at what they are. In fact, I have also added comments. Simply put, it is to calculate two offset values, and then determine whether they are within the limited normal range. If not, the result will be modified, which can be used to determine whether If there is an error in that part, then there will be a total of 3 sets of data verification, xyz axis. In fact, at this point, you can already locate the specific part that does not meet the inspection standards. Then you can lower the inspection standards by modifying the inspection interval. Of course, it is possible. If the modification still doesn't work, then you have to take another step.

Where does our self-test data come from? One level up, from get_st_biasesfunctions.

// 此时传入的gyro accel
static int get_st_biases(long *gyro, long *accel, unsigned char hw_test)
{
    unsigned char data[MAX_PACKET_LENGTH];
    unsigned char packet_count, ii;
    unsigned short fifo_count;

    data[0] = 0x01;
    data[1] = 0;
    if (i2c_write(st.hw->addr, st.reg->pwr_mgmt_1, 2, data))
        return -1;
    delay_ms(200);
    data[0] = 0;
    if (i2c_write(st.hw->addr, st.reg->int_enable, 1, data))
        return -1;
    if (i2c_write(st.hw->addr, st.reg->fifo_en, 1, data))
        return -1;
    if (i2c_write(st.hw->addr, st.reg->pwr_mgmt_1, 1, data))
        return -1;
    if (i2c_write(st.hw->addr, st.reg->i2c_mst, 1, data))
        return -1;
    if (i2c_write(st.hw->addr, st.reg->user_ctrl, 1, data))
        return -1;
		// 0x0c
    data[0] = BIT_FIFO_RST | BIT_DMP_RST;
    if (i2c_write(st.hw->addr, st.reg->user_ctrl, 1, data))
        return -1;
    delay_ms(15);
		// 0x01
    data[0] = st.test->reg_lpf;
    if (i2c_write(st.hw->addr, st.reg->lpf, 1, data))
        return -1;
		// 0x00
    data[0] = st.test->reg_rate_div;
    if (i2c_write(st.hw->addr, st.reg->rate_div, 1, data))
        return -1;
    // 此处在计算2个比较值的地方有所出入
		if (hw_test)
			  // 0xE0
        data[0] = st.test->reg_gyro_fsr | 0xE0;
    else
        data[0] = st.test->reg_gyro_fsr;
    if (i2c_write(st.hw->addr, st.reg->gyro_cfg, 1, data))
        return -1;

		// 此处在计算2个比较值的地方有所出入
    if (hw_test)
			  // 0XF8
        data[0] = st.test->reg_accel_fsr | 0xE0;
    else
        data[0] = test.reg_accel_fsr;
    if (i2c_write(st.hw->addr, st.reg->accel_cfg, 1, data))
        return -1;
		// 此处在计算2个比较值的地方有所出入
    if (hw_test)
        delay_ms(200);

    /* Fill FIFO for test.wait_ms milliseconds. */
		// 0x40
    data[0] = BIT_FIFO_EN;
    if (i2c_write(st.hw->addr, st.reg->user_ctrl, 1, data))
        return -1;
		
		// 0x78
    data[0] = INV_XYZ_GYRO | INV_XYZ_ACCEL;
    if (i2c_write(st.hw->addr, st.reg->fifo_en, 1, data))
        return -1;
    delay_ms(test.wait_ms);
    data[0] = 0;
    if (i2c_write(st.hw->addr, st.reg->fifo_en, 1, data))
        return -1;

		// data[0]=0x04
    if (i2c_read(st.hw->addr, st.reg->fifo_count_h, 2, data))
        return -1;

		// 0x0400
    fifo_count = (data[0] << 8) | data[1];
		// 0x55
    packet_count = fifo_count / MAX_PACKET_LENGTH;
    gyro[0] = gyro[1] = gyro[2] = 0;
    accel[0] = accel[1] = accel[2] = 0;
		
		printf("packet_count=%d\r\n", packet_count);

    for (ii = 0; ii < packet_count; ii++) {
        short accel_cur[3], gyro_cur[3];
	    // 读取寄存器地址st.reg->fifo_r_w数据到data
        if (i2c_read(st.hw->addr, st.reg->fifo_r_w, MAX_PACKET_LENGTH, data))
            return -1;
		// printf("data[2, 3]=%d, %d\r\n", data[2], data[3]);
		// printf("data[4, 5]=%d, %d\r\n", data[4], data[5]);
        accel_cur[0] = ((short)data[0] << 8) | data[1];
        accel_cur[1] = ((short)data[2] << 8) | data[3];
        accel_cur[2] = ((short)data[4] << 8) | data[5];
        accel[0] += (long)accel_cur[0];
        accel[1] += (long)accel_cur[1];
        accel[2] += (long)accel_cur[2];
        gyro_cur[0] = (((short)data[6] << 8) | data[7]);
        gyro_cur[1] = (((short)data[8] << 8) | data[9]);
        gyro_cur[2] = (((short)data[10] << 8) | data[11]);
        gyro[0] += (long)gyro_cur[0];
        gyro[1] += (long)gyro_cur[1];
        gyro[2] += (long)gyro_cur[2];
    }
#ifdef EMPL_NO_64BIT
    gyro[0] = (long)(((float)gyro[0]*65536.f) / test.gyro_sens / packet_count);
    gyro[1] = (long)(((float)gyro[1]*65536.f) / test.gyro_sens / packet_count);
    gyro[2] = (long)(((float)gyro[2]*65536.f) / test.gyro_sens / packet_count);
    if (has_accel) {
        accel[0] = (long)(((float)accel[0]*65536.f) / test.accel_sens /
            packet_count);
        accel[1] = (long)(((float)accel[1]*65536.f) / test.accel_sens /
            packet_count);
        accel[2] = (long)(((float)accel[2]*65536.f) / test.accel_sens /
            packet_count);
        /* Don't remove gravity! */
        accel[2] -= 65536L;
    }
#else
		
	/*
	printf("******* before\r\n");
	printf("accel=%ld, %ld, %ld\r\n", accel[0], accel[1], accel[2]);
	printf("gyro=%ld, %ld, %ld\r\n", gyro[0], gyro[1], gyro[2]);
	printf("*******\r\n");
	*/
		
    gyro[0] = (long)(((long long)gyro[0]<<16) / test.gyro_sens / packet_count);
    gyro[1] = (long)(((long long)gyro[1]<<16) / test.gyro_sens / packet_count);
    gyro[2] = (long)(((long long)gyro[2]<<16) / test.gyro_sens / packet_count);
    accel[0] = (long)(((long long)accel[0]<<16) / test.accel_sens /
        packet_count);
    accel[1] = (long)(((long long)accel[1]<<16) / test.accel_sens /
        packet_count);
    accel[2] = (long)(((long long)accel[2]<<16) / test.accel_sens /
        packet_count);
    /* Don't remove gravity! */
		printf("******* after\r\n");
		printf("accel=%ld, %ld, %ld\r\n", accel[0], accel[1], accel[2]);
		printf("gyro=%ld, %ld, %ld\r\n", gyro[0], gyro[1], gyro[2]);
		printf("*******\r\n");
		
    if (accel[2] > 0L)
        accel[2] -= 65536L;
    else
        accel[2] += 65536L;
#endif

    return 0;
}

Seeing this, the data source is actually reading data from the module register through i2c. Then we will reach the end of our journey, what is the content of the corresponding register. Reference: MPU-6000 & MPU-6050 寄存器表及其描述(中文版).pdf
Then push up layer by layer, and find 4.18 S REGISTERS 9 59 TO 4 64 – – ACCELEROMETER MEASUREMENTS
Insert image description here
Insert image description here
that at this time, through the print I added earlier, and the value output by my module at that time, the third set of data of the acceleration, that is, the value of the Z axis will not change, no matter what I How to place it, how to shake it. After it passes the calculation, the result is 0, which then causes the result to be different from 0, ultimately causing the self-test to never pass. However, after forcibly skipping the z-axis verification, the roll angle and pitch angle are barely usable (barely). .

So at this point, my judgment is that the problem is caused by module damage. After replacing the module, it can be easily solved in seconds. . .

Now that the module has passed the test, let’s go deeper and look at the tilt detection. In fact, the module needs to be horizontally stationary for self-test. In fact, it can pass the self-test even if it is not horizontal. Basically, it only needs to be stationary. Certain angles may The self-test will fail, and the main problem is still the acceleration of the z-axis. But the problem is not big, so this is a foreshadowing at the end. After obtaining the 3 angles in the main, a simple interval limit can be used to determine whether the limited tilt threshold has been exceeded. However, during use, I found that Check the center, Rollroll angle, there will be problems!

When the normal line of your chip is pointing upward toward the horizontal line (toward the ceiling), the Roll starts from 0 degrees after self-check.
When the normal line of your chip is pointing downward toward the horizontal line (towards the ground), the Roll self-test starts from -179.9 degrees.
A pit appeared, and after an offset of about 0 degrees, it was -1,1. After the offset of -179.9 degrees, it is -179,179. In addition, the Roll calculation will not be normal when facing the ground! ! !
Then the interval judgment angle we initially limited is no longer suitable under the two self-test situations.
So when we cannot limit the general orientation of the module, this problem has a great impact.
Then the plan I gave is to determine the orientation of the chip by judging whether the initial Roll is close to 0 or -180 after self-test, and then adapt two algorithms for implementation, but only for completely parallel ground . If it is facing downward or diagonally downward, the Roll value will not be read properly, and the overall angle ratio will be out of balance! ! !

ps: The chip has just finished its self-test, and the data will still fluctuate for a while. You need to wait for a while before it can be used normally after it stabilizes. (Especially when the chip faces the ground)

Problem points that need attention:
yaw angle (yaw) zero drift: analysis and processing of common problems of MPU6050
Gimbal lock: understanding of Euler angle and universal joint deadlock

Guess you like

Origin blog.csdn.net/Ikaros_521/article/details/132420748