MPU6050 DMP 代码完全解析 —— mpu_init

846 阅读12分钟

本文已参与 [新人创作礼] 活动,一起开启掘金创作之路。​

inv_mpu.c中,int mpu_init(void)中:

//配置陀螺仪满量程范围,FSR: ±2000°/s,LSB: 16.4 LSB/°/s
if (mpu_set_gyro_fsr(2000))
	return -1;

inv_mpu.c中:

/**
 *  @brief      Set the gyro full-scale range.
 *  @param[in]  fsr Desired full-scale range.
 *  @return     0 if successful.
 */
int mpu_set_gyro_fsr(unsigned short fsr)
{
	unsigned char data;

	if (!(st.chip_cfg.sensors))
		return -1;

	switch (fsr) {
		case 250:
			data = INV_FSR_250DPS << 3; //enum gyro_fsr_e INV_FSR_250DPS=0
			break;
		case 500:
			data = INV_FSR_500DPS << 3; //enum gyro_fsr_e INV_FSR_500DPS=1
			break;
		case 1000:
			data = INV_FSR_1000DPS << 3; //enum gyro_fsr_e INV_FSR_1000DPS=2
			break;
		case 2000:
			data = INV_FSR_2000DPS << 3; //enum gyro_fsr_e INV_FSR_2000DPS=3
			break;
		default:
			return -1;
	}

	if (st.chip_cfg.gyro_fsr == (data >> 3))
		return 0;

	if (i2c_write(st.hw->addr, st.reg->gyro_cfg, 1, &data)) //.gyro_cfg       = 0x1B,
		return -1;

	st.chip_cfg.gyro_fsr = data >> 3;

	return 0;
}

对应手册中的寄存器为:

inv_mpu.c中,int mpu_init(void)中:

//配置加速度计满量程范围,FSR: ±2g,LSB: 16384 LSB/g
if (mpu_set_accel_fsr(2))
    return -1;

inv_mpu.c中:

/**
 *  @brief      Set the accel full-scale range.
 *  @param[in]  fsr Desired full-scale range.
 *  @return     0 if successful.
 */
int mpu_set_accel_fsr(unsigned char fsr)
{
	unsigned char data;

	if (!(st.chip_cfg.sensors))
		return -1;

	switch (fsr) {
		case 2:
			data = INV_FSR_2G << 3; //enum accel_fsr_e INV_FSR_2G=0
			break;
		case 4:
			data = INV_FSR_4G << 3; //enum accel_fsr_e INV_FSR_4G=1
			break;
		case 8:
			data = INV_FSR_8G << 3; //enum accel_fsr_e INV_FSR_8G=2
			break;
		case 16:
			data = INV_FSR_16G << 3; //enum accel_fsr_e INV_FSR_16G=3
			break;
		default:
			return -1;
	}

	if (st.chip_cfg.accel_fsr == (data >> 3))
		return 0;

	if (i2c_write(st.hw->addr, st.reg->accel_cfg, 1, &data)) //.accel_cfg      = 0x1C,
		return -1;

	st.chip_cfg.accel_fsr = data >> 3;

	return 0;
}

对应手册中的寄存器为:

inv_mpu.c中,int mpu_init(void)中:

//配置数字低通滤波器,42Hz
if (mpu_set_lpf(42))
    return -1;

inv_mpu.c中:

/**
 *  @brief      Set digital low pass filter.
 *  The following LPF settings are supported: 188, 98, 42, 20, 10, 5.
 *  @param[in]  lpf Desired LPF setting.
 *  @return     0 if successful.
 */
int mpu_set_lpf(unsigned short lpf)
{
	unsigned char data;

	if (!(st.chip_cfg.sensors))
		return -1;

	if (lpf >= 188)
		data = INV_FILTER_188HZ; //enum lpf_e INV_FILTER_188HZ=1
	else if (lpf >= 98)
		data = INV_FILTER_98HZ; //enum lpf_e INV_FILTER_98HZ=2
	else if (lpf >= 42)
		data = INV_FILTER_42HZ; //enum lpf_e INV_FILTER_42HZ=3
	else if (lpf >= 20)
		data = INV_FILTER_20HZ; //enum lpf_e INV_FILTER_20HZ=4
	else if (lpf >= 10)
		data = INV_FILTER_10HZ; //enum lpf_e INV_FILTER_10HZ=5
	else
		data = INV_FILTER_5HZ; //enum lpf_e INV_FILTER_5HZ=6

	if (st.chip_cfg.lpf == data)
		return 0;

	if (i2c_write(st.hw->addr, st.reg->lpf, 1, &data)) //.lpf            = 0x1A,
		return -1;

	st.chip_cfg.lpf = data;

	return 0;
}

对应手册中的寄存器为:

inv_mpu.c中,int mpu_init(void)中:

//配置采样频率分频寄存器,50Hz
if (mpu_set_sample_rate(50))
	return -1;

inv_mpu.c中:

/**
 *  @brief      Set sampling rate.
 *  Sampling rate must be between 4Hz and 1kHz.
 *  @param[in]  rate    Desired sampling rate (Hz).
 *  @return     0 if successful.
 */
int mpu_set_sample_rate(unsigned short rate)
{
	unsigned char data;

	if (!(st.chip_cfg.sensors))
		return -1;

	if (st.chip_cfg.dmp_on)
		return -1;
	else {
		if (st.chip_cfg.lp_accel_mode) {
			if (rate && (rate <= 40)) {
				/* Just stay in low-power accel mode. */
				mpu_lp_accel_mode(rate);
				return 0;
			}
			/* Requested rate exceeds the allowed frequencies in LP accel mode,
			 * switch back to full-power mode.
			 */
			mpu_lp_accel_mode(0);
		}
		if (rate < 4)
			rate = 4;
		else if (rate > 1000)
			rate = 1000;

		data = 1000 / rate - 1;
		if (i2c_write(st.hw->addr, st.reg->rate_div, 1, &data)) //.rate_div       = 0x19,
			return -1;

		st.chip_cfg.sample_rate = 1000 / (1 + data);

#ifdef AK89xx_SECONDARY
		mpu_set_compass_sample_rate(min(st.chip_cfg.compass_sample_rate, MAX_COMPASS_SAMPLE_RATE));
#endif

		/* Automatically set LPF to 1/2 sampling rate. */
		mpu_set_lpf(st.chip_cfg.sample_rate >> 1);

		return 0;
	}
}

对应手册中的寄存器为:

mpu_set_sample_rate函数中调用了mpu_lp_accel_mode函数。该函数也在inv_mpu.c中,源码如下:

/**
 *  @brief      Enter low-power accel-only mode.
 *  In low-power accel mode, the chip goes to sleep and only wakes up to sample
 *  the accelerometer at one of the following frequencies:
 *  \n MPU6050: 1.25Hz, 5Hz, 20Hz, 40Hz
 *  \n MPU6500: 1.25Hz, 2.5Hz, 5Hz, 10Hz, 20Hz, 40Hz, 80Hz, 160Hz, 320Hz, 640Hz
 *  \n If the requested rate is not one listed above, the device will be set to
 *  the next highest rate. Requesting a rate above the maximum supported
 *  frequency will result in an error.
 *  \n To select a fractional wake-up frequency, round down the value passed to
 *  @e rate.
 *  @param[in]  rate        Minimum sampling rate, or zero to disable LP
 *                          accel mode.
 *  @return     0 if successful.
 */
int mpu_lp_accel_mode(unsigned char rate)
{
	unsigned char tmp[2];

	if (rate > 40)
		return -1;

	if (!rate) {
		mpu_set_int_latched(0);
		tmp[0] = 0;
		tmp[1] = BIT_STBY_XYZG; //#define BIT_STBY_XYZG       (BIT_STBY_XG | BIT_STBY_YG | BIT_STBY_ZG)
		if (i2c_write(st.hw->addr, st.reg->pwr_mgmt_1, 2, tmp)) //.pwr_mgmt_1     = 0x6B,
			return -1;
		st.chip_cfg.lp_accel_mode = 0;

		return 0;
	}

	/* For LP accel, we automatically configure the hardware to produce latched
	 * interrupts. In LP accel mode, the hardware cycles into sleep mode before
	 * it gets a chance to deassert the interrupt pin; therefore, we shift this
	 * responsibility over to the MCU.
	 *
	 * Any register read will clear the interrupt.
	 */
	mpu_set_int_latched(1);
#if defined MPU6050
	tmp[0] = BIT_LPA_CYCLE; //#define BIT_LPA_CYCLE       (0x20)
	if (rate == 1) {
		tmp[1] = INV_LPA_1_25HZ; //enum lp_accel_rate_e INV_LPA_1_25HZ=0
		mpu_set_lpf(5);
	} else if (rate <= 5) {
		tmp[1] = INV_LPA_5HZ; //enum lp_accel_rate_e INV_LPA_5HZ=1
		mpu_set_lpf(5);
	} else if (rate <= 20) {
		tmp[1] = INV_LPA_20HZ; //enum lp_accel_rate_e INV_LPA_20HZ=2
		mpu_set_lpf(10);
	} else {
		tmp[1] = INV_LPA_40HZ; //enum lp_accel_rate_e INV_LPA_40HZ=3
		mpu_set_lpf(20);
	}
	tmp[1] = (tmp[1] << 6) | BIT_STBY_XYZG;
	if (i2c_write(st.hw->addr, st.reg->pwr_mgmt_1, 2, tmp)) //.pwr_mgmt_1     = 0x6B,
		return -1;
#elif defined MPU6500
	/* Set wake frequency. */
	if (rate == 1)
		tmp[0] = INV_LPA_1_25HZ;
	else if (rate == 2)
		tmp[0] = INV_LPA_2_5HZ;
	else if (rate <= 5)
		tmp[0] = INV_LPA_5HZ;
	else if (rate <= 10)
		tmp[0] = INV_LPA_10HZ;
	else if (rate <= 20)
		tmp[0] = INV_LPA_20HZ;
	else if (rate <= 40)
		tmp[0] = INV_LPA_40HZ;
	else if (rate <= 80)
		tmp[0] = INV_LPA_80HZ;
	else if (rate <= 160)
		tmp[0] = INV_LPA_160HZ;
	else if (rate <= 320)
		tmp[0] = INV_LPA_320HZ;
	else
		tmp[0] = INV_LPA_640HZ;
	if (i2c_write(st.hw->addr, st.reg->lp_accel_odr, 1, tmp))
		return -1;
	tmp[0] = BIT_LPA_CYCLE;
	if (i2c_write(st.hw->addr, st.reg->pwr_mgmt_1, 1, tmp))
		return -1;
#endif

	st.chip_cfg.sensors = INV_XYZ_ACCEL;
	st.chip_cfg.clk_src = 0;
	st.chip_cfg.lp_accel_mode = 1;
	mpu_configure_fifo(0);

	return 0;
}

对应手册中的寄存器为:

mpu_lp_accel_mode函数中又调用了mpu_set_int_latched函数,该函数同样在inv_mpu.c中,源码如下:

/**
 *  @brief      Enable latched interrupts.
 *  Any MPU register will clear the interrupt.
 *  @param[in]  enable  1 to enable, 0 to disable.
 *  @return     0 if successful.
 */
int mpu_set_int_latched(unsigned char enable)
{
	unsigned char tmp;

	if (st.chip_cfg.latched_int == enable)
		return 0;

	if (enable)
		tmp = BIT_LATCH_EN | BIT_ANY_RD_CLR; //#define BIT_LATCH_EN        (0x20), #define BIT_ANY_RD_CLR      (0x10)
	else
		tmp = 0;

	if (st.chip_cfg.bypass_mode)
		tmp |= BIT_BYPASS_EN; //#define BIT_BYPASS_EN       (0x02)

	if (st.chip_cfg.active_low_int)
		tmp |= BIT_ACTL; //#define BIT_ACTL            (0x80)

	if (i2c_write(st.hw->addr, st.reg->int_pin_cfg, 1, &tmp)) //.int_pin_cfg    = 0x37,
		return -1;

	st.chip_cfg.latched_int = enable;

	return 0;
}

对应手册中的寄存器为:

inv_mpu.c中,int mpu_init(void)中:

//选择FIFO
if (mpu_configure_fifo(0))
	return -1;

inv_mpu.c中:

/**
 *  @brief      Select which sensors are pushed to FIFO.
 *  @e sensors can contain a combination of the following flags:
 *  \n INV_X_GYRO, INV_Y_GYRO, INV_Z_GYRO
 *  \n INV_XYZ_GYRO
 *  \n INV_XYZ_ACCEL
 *  @param[in]  sensors Mask of sensors to push to FIFO.
 *  @return     0 if successful.
 */
int mpu_configure_fifo(unsigned char sensors)
{
	unsigned char prev;
	int result = 0;

	/* Compass data isn't going into the FIFO. Stop trying. */
	sensors &= ~INV_XYZ_COMPASS; //#define INV_XYZ_COMPASS (0x01)

	if (st.chip_cfg.dmp_on)
		return 0;
	else {
		if (!(st.chip_cfg.sensors))
			return -1;
		prev = st.chip_cfg.fifo_enable;
		st.chip_cfg.fifo_enable = sensors & st.chip_cfg.sensors;
		if (st.chip_cfg.fifo_enable != sensors)
			/* You're not getting what you asked for. Some sensors are
			 * asleep.
			 */
			result = -1;
		else
			result = 0;

		if (sensors || st.chip_cfg.lp_accel_mode)
			set_int_enable(1);
		else
			set_int_enable(0);

		if (sensors) {
			if (mpu_reset_fifo()) {
				st.chip_cfg.fifo_enable = prev;
				return -1;
			}
		}
	}

	return result;
}

mpu_configure_fifo函数中调用了set_int_enable函数。该函数也在inv_mpu.c中,源码如下:

/**
 *  @brief      Enable/disable data ready interrupt.
 *  If the DMP is on, the DMP interrupt is enabled. Otherwise, the data ready
 *  interrupt is used.
 *  @param[in]  enable      1 to enable interrupt.
 *  @return     0 if successful.
 */
static int set_int_enable(unsigned char enable)
{
	unsigned char tmp;

	if (st.chip_cfg.dmp_on) {
		if (enable)
			tmp = BIT_DMP_INT_EN; //#define BIT_DMP_INT_EN      (0x02)
		else
			tmp = 0x00;
		if (i2c_write(st.hw->addr, st.reg->int_enable, 1, &tmp)) //.int_enable     = 0x38,
			return -1;
		st.chip_cfg.int_enable = tmp;
	} else {
		if (!st.chip_cfg.sensors)
			return -1;
		if (enable && st.chip_cfg.int_enable)
			return 0;
		if (enable)
			tmp = BIT_DATA_RDY_EN; //#define BIT_DATA_RDY_EN     (0x01)
		else
			tmp = 0x00;
		if (i2c_write(st.hw->addr, st.reg->int_enable, 1, &tmp)) //.int_enable     = 0x38,
			return -1;
		st.chip_cfg.int_enable = tmp;
	}

	return 0;
}

对应手册中的寄存器为:

mpu_configure_fifo函数中还调用了mpu_reset_fifo函数。该函数也在inv_mpu.c中,源码如下:

/**
 *  @brief  Reset FIFO read/write pointers.
 *  @return 0 if successful.
 */
int mpu_reset_fifo(void)
{
	unsigned char data;

	if (!(st.chip_cfg.sensors))
		return -1;

	data = 0;
	if (i2c_write(st.hw->addr, st.reg->int_enable, 1, &data)) //.int_enable     = 0x38,
		return -1;
	if (i2c_write(st.hw->addr, st.reg->fifo_en, 1, &data)) //.fifo_en        = 0x23,
		return -1;
	if (i2c_write(st.hw->addr, st.reg->user_ctrl, 1, &data)) //.user_ctrl      = 0x6A,
		return -1;

	if (st.chip_cfg.dmp_on) {
		data = BIT_FIFO_RST | BIT_DMP_RST; //#define BIT_FIFO_RST        (0x04), #define BIT_DMP_RST         (0x08)
		if (i2c_write(st.hw->addr, st.reg->user_ctrl, 1, &data)) //.user_ctrl      = 0x6A,
			return -1;
		delay_ms(50);

		data = BIT_DMP_EN | BIT_FIFO_EN; //#define BIT_DMP_EN          (0x80), #define BIT_FIFO_EN         (0x40)
		if (st.chip_cfg.sensors & INV_XYZ_COMPASS) //#define INV_XYZ_COMPASS (0x01)
			data |= BIT_AUX_IF_EN; //#define BIT_AUX_IF_EN       (0x20)
		if (i2c_write(st.hw->addr, st.reg->user_ctrl, 1, &data)) //.user_ctrl      = 0x6A,
			return -1;

		if (st.chip_cfg.int_enable)
			data = BIT_DMP_INT_EN; //#define BIT_DMP_INT_EN      (0x02)
		else
			data = 0;
		if (i2c_write(st.hw->addr, st.reg->int_enable, 1, &data)) //.int_enable     = 0x38,
			return -1;

		data = 0;
		if (i2c_write(st.hw->addr, st.reg->fifo_en, 1, &data)) //.fifo_en        = 0x23,
			return -1;
	} else {
		data = BIT_FIFO_RST; //#define BIT_FIFO_RST        (0x04)
		if (i2c_write(st.hw->addr, st.reg->user_ctrl, 1, &data)) //.user_ctrl      = 0x6A,
			return -1;

		if (st.chip_cfg.bypass_mode || !(st.chip_cfg.sensors & INV_XYZ_COMPASS)) //#define INV_XYZ_COMPASS (0x01)
			data = BIT_FIFO_EN; //#define BIT_FIFO_EN         (0x40)
		else
			data = BIT_FIFO_EN | BIT_AUX_IF_EN; //#define BIT_FIFO_EN         (0x40), #define BIT_AUX_IF_EN       (0x20)
		if (i2c_write(st.hw->addr, st.reg->user_ctrl, 1, &data)) //.user_ctrl      = 0x6A,
			return -1;
		delay_ms(50);

		if (st.chip_cfg.int_enable)
			data = BIT_DATA_RDY_EN; //#define BIT_DATA_RDY_EN     (0x01)
		else
			data = 0;
		if (i2c_write(st.hw->addr, st.reg->int_enable, 1, &data)) //.int_enable     = 0x38,
			return -1;

		if (i2c_write(st.hw->addr, st.reg->fifo_en, 1, &st.chip_cfg.fifo_enable)) //.fifo_en        = 0x23,
			return -1;
	}

	return 0;
}

mpu_reset_fifo这个函数很长,一段一段来分析。

data = 0;
if (i2c_write(st.hw->addr, st.reg->int_enable, 1, &data)) //.int_enable     = 0x38,
    return -1;
if (i2c_write(st.hw->addr, st.reg->fifo_en, 1, &data)) //.fifo_en        = 0x23,
    return -1;
if (i2c_write(st.hw->addr, st.reg->user_ctrl, 1, &data)) //.user_ctrl      = 0x6A,
    return -1;

INT_ENABLE(0x38)这个寄存器上边已经介绍过了,此处写0代表不使能MOT_EN、FIFO_OFLOW_EN、I2C_MST_INT_EN、DATA_RDY_EN。

FIFO_ENABLE(0x23)寄存器如下图所示:

USER_CTRL(0x6A)寄存器如下图所示:

​编辑

mpu_reset_fifo函数接下来的代码分为了两种情况:使用DMP和不使用DMP。

使用DMP时,

data = BIT_FIFO_RST | BIT_DMP_RST; //#define BIT_FIFO_RST        (0x04), #define BIT_DMP_RST         (0x08)
if (i2c_write(st.hw->addr, st.reg->user_ctrl, 1, &data)) //.user_ctrl      = 0x6A,
		return -1;

不使用DMP时,

data = BIT_FIFO_RST; //#define BIT_FIFO_RST        (0x04)
if (i2c_write(st.hw->addr, st.reg->user_ctrl, 1, &data)) //.user_ctrl      = 0x6A,
	return -1;

可以看到无论是否使用DMP,BIT_FIFO_RST(0x04)位(Bit2)都是要设置的,参考上面的USER_CTRL(0x6A)寄存器说明,是将FIFO_RESET位置1,也就是说复位FIFO缓冲区,复位后该位自动清零。当然,此时由于还没有设置FIFO_EN位,因此暂时还不能起作用,只是先设置上而已。

如果使用了DMP功能,则需要多设置一个BIT_DMP_RST(0x08)。同样参考上边的USER_CTRL(0x6A)寄存器说明,Bit3并没有意义,因此这里应该是手册中的寄存器说明不够详细,实际上是有意义的。

继续往下看,

使用DMP时,

data = BIT_DMP_EN | BIT_FIFO_EN; //#define BIT_DMP_EN          (0x80), #define BIT_FIFO_EN         (0x40)
if (st.chip_cfg.sensors & INV_XYZ_COMPASS) //#define INV_XYZ_COMPASS (0x01)
	data |= BIT_AUX_IF_EN; //#define BIT_AUX_IF_EN       (0x20)
if (i2c_write(st.hw->addr, st.reg->user_ctrl, 1, &data)) //.user_ctrl      = 0x6A,
	return -1;

不使用DMP时,

if (st.chip_cfg.bypass_mode || !(st.chip_cfg.sensors & INV_XYZ_COMPASS)) //#define INV_XYZ_COMPASS (0x01)
    data = BIT_FIFO_EN; //#define BIT_FIFO_EN         (0x40)
else
	data = BIT_FIFO_EN | BIT_AUX_IF_EN; //#define BIT_FIFO_EN         (0x40), #define BIT_AUX_IF_EN       (0x20)
if (i2c_write(st.hw->addr, st.reg->user_ctrl, 1, &data)) //.user_ctrl      = 0x6A,
	return -1;

不论是否使用DMP,BIT_FIFO_EN(0x40)位(Bit6)都要设置。这一位就是上边提到过的FIFO_EN位,这位连同上边的FIFO_RESET位同时置1,就复位了FIFO缓冲区,复位后该位自动清零。

使用DMP功能的时候还需要设置BIT_DMP_EN(0x80),对应USER_CTRL(0x6A)寄存器中的Bit7,这位同样没有给出意义,应该也是这个手册不够详细所致。但是我们可以猜测出大概的意思,BIT_DMP_EN连同BIT_DMP_RST一起设置,作用是复位DMP模式下的FIFO(只是猜测而已)。

BIT_AUX_IF_EN(0x20)位(Bit5)代表USER_CTRL(0x6A)寄存器中的I2C_MST_EN,该位置1,使能I2C主机模式;该位清0,辅助I2C总线逻辑上由主I2C总线驱动。

其实这段代码很好理解,只要是外接地磁传感器的场景,就需要使能I2C主机模式,否则就禁止该模式。

继续往下看,

使用DMP时,

if (st.chip_cfg.int_enable)
    data = BIT_DMP_INT_EN; //#define BIT_DMP_INT_EN      (0x02)
else
	data = 0;
if (i2c_write(st.hw->addr, st.reg->int_enable, 1, &data)) //.int_enable     = 0x38,
	return -1;

不使用DMP时,

if (st.chip_cfg.int_enable)
    data = BIT_DATA_RDY_EN; //#define BIT_DATA_RDY_EN     (0x01)
else
    data = 0;
if (i2c_write(st.hw->addr, st.reg->int_enable, 1, &data)) //.int_enable     = 0x38,
	return -1;

这段代码差别最为明显,使用DMP功能时用的是BIT_DMP_INT_EN(0x02)位(Bit1),不使用DMP功能时用的是BIT_DATA_RDY_EN(0x01)位(Bit0),参见上边INT_ENABLE(0x38)寄存器的内容,DATA_RDY_EN位置1,使能数据就绪中断,所有的传感器寄存器写操作完成时都会产生。Bit1同样没有列出具体意义,猜测应该是使能DMP模式下的数据就绪中断。当然,这一切的前提是配置了中断使能,如果不需要使能中断,则data直接置为0。

继续往下看,来到mpu_reset_fifo函数的最后一部分了。

使用DMP时,

data = 0;
if (i2c_write(st.hw->addr, st.reg->fifo_en, 1, &data)) //.fifo_en        = 0x23,
	return -1;

不使用DMP时,

if (i2c_write(st.hw->addr, st.reg->fifo_en, 1, &st.chip_cfg.fifo_enable)) //.fifo_en        = 0x23,
    return -1;

这段差别也很明显,使用DMP功能时,完全不需要开启FIFO ENABLE(0x23)寄存器中的任何位,也就是说加速度、陀螺仪、地磁和温度的FIFO都不啊用开启;而不使用DMP功能时,则需要根据之前的设定对FIFO ENABLE(0x23)寄存器进行设置。

到这里,mpu_reset_fifo函数就全部分析完了,而这也意味着mpu_configure_fifo函数分析完了。

inv_mpu.c中,int mpu_init(void)中:

/* Already disabled by setup_compass. */
if (mpu_set_bypass(0))
	return -1;

inv_mpu.c中:

/**
 *  @brief      Set device to bypass mode.
 *  @param[in]  bypass_on   1 to enable bypass mode.
 *  @return     0 if successful.
 */
int mpu_set_bypass(unsigned char bypass_on)
{
	unsigned char tmp;

	if (st.chip_cfg.bypass_mode == bypass_on)
		return 0;

	if (bypass_on) {
		if (i2c_read(st.hw->addr, st.reg->user_ctrl, 1, &tmp)) //.user_ctrl      = 0x6A,
			return -1;
		tmp &= ~BIT_AUX_IF_EN; //#define BIT_AUX_IF_EN       (0x20)
		if (i2c_write(st.hw->addr, st.reg->user_ctrl, 1, &tmp))
			return -1;
		delay_ms(3);
		tmp = BIT_BYPASS_EN; //#define BIT_BYPASS_EN       (0x02)
		if (st.chip_cfg.active_low_int)
			tmp |= BIT_ACTL; //#define BIT_ACTL            (0x80)
		if (st.chip_cfg.latched_int)
			tmp |= BIT_LATCH_EN | BIT_ANY_RD_CLR; //#define BIT_LATCH_EN        (0x20), #define BIT_ANY_RD_CLR      (0x10)
		if (i2c_write(st.hw->addr, st.reg->int_pin_cfg, 1, &tmp)) //.int_pin_cfg    = 0x37,
			return -1;
	} else {
		/* Enable I2C master mode if compass is being used. */
		if (i2c_read(st.hw->addr, st.reg->user_ctrl, 1, &tmp)) // //.user_ctrl      = 0x6A,
			return -1;
		if (st.chip_cfg.sensors & INV_XYZ_COMPASS) //#define INV_XYZ_COMPASS (0x01)
			tmp |= BIT_AUX_IF_EN; //#define BIT_AUX_IF_EN       (0x20)
		else
			tmp &= ~BIT_AUX_IF_EN;
		if (i2c_write(st.hw->addr, st.reg->user_ctrl, 1, &tmp))
			return -1;
		delay_ms(3);
		if (st.chip_cfg.active_low_int)
			tmp = BIT_ACTL; //#define BIT_ACTL            (0x80)
		else
			tmp = 0;
		if (st.chip_cfg.latched_int)
			tmp |= BIT_LATCH_EN | BIT_ANY_RD_CLR; //#define BIT_LATCH_EN        (0x20), #define BIT_ANY_RD_CLR      (0x10)
		if (i2c_write(st.hw->addr, st.reg->int_pin_cfg, 1, &tmp)) //.int_pin_cfg    = 0x37,
			return -1;
	}
	st.chip_cfg.bypass_mode = bypass_on;

	return 0;
}

mpu_set_bypass函数中分为两种情况:配置为旁路和配置为非旁路。

先来看旁路的情况。首先读取USER_CTRL(0x6A)寄存器,重点关注BIT_AUX_IF_EN(0x20)位(Bit5)。上边已经介绍过,这位实际上对应的是I2C_MST_EN。代码中将该位清零,之后写回USER_CTRL(0x6A)寄存器。实际上是辅助I2C总线(AUX_DA和AUX_CL)逻辑上由主I2C总线(SDA和SCL)驱动。之后延时3毫秒。然后对于INT引脚/旁路有效使能配置(0x37)寄存器进行赋值,I2C_BYPASS_EN位置1,根据配置依次对于INT_LEVEL、LATCH_INT_EN、INT_RD_CLR进行赋值。

再来看非旁路的情况。同样是首先读取USER_CTRL(0x6A)寄存器的内容,然后根据是否连接了地磁芯片进行配置,连接了地磁传感器,BIT_AUX_IF_EN(0x20)位(Bit5)置1,即使能 I2C 主机模式;未连接地磁传感器,该位清零,即辅助I2C总线(AUX_DA和AUX_CL)逻辑上由主I2C总线(SDA和SCL)驱动。之后也同样是延时3毫秒。然后也是对于INT引脚/旁路有效使能配置(0x37)寄存器进行赋值,根据配置依次对于INT_LEVEL、LATCH_INT_EN、INT_RD_CLR进行赋值。注意,I2C_BYPASS_EN位此时是置0的。

inv_mpu.c中,int mpu_init(void)中:

mpu_set_sensors(0);

inv_mpu.c中:

/**
 *  @brief      Turn specific sensors on/off.
 *  @e sensors can contain a combination of the following flags:
 *  \n INV_X_GYRO, INV_Y_GYRO, INV_Z_GYRO
 *  \n INV_XYZ_GYRO
 *  \n INV_XYZ_ACCEL
 *  \n INV_XYZ_COMPASS
 *  @param[in]  sensors    Mask of sensors to wake.
 *  @return     0 if successful.
 */
int mpu_set_sensors(unsigned char sensors)
{
	unsigned char data;
#ifdef AK89xx_SECONDARY
	unsigned char user_ctrl;
#endif

	if (sensors & INV_XYZ_GYRO) //#define INV_XYZ_GYRO    (INV_X_GYRO | INV_Y_GYRO | INV_Z_GYRO)
		data = INV_CLK_PLL; //enum clock_sel_e INV_CLK_PLL=1
	else if (sensors)
		data = 0;
	else
		data = BIT_SLEEP; //#define BIT_SLEEP           (0x40)
	if (i2c_write(st.hw->addr, st.reg->pwr_mgmt_1, 1, &data)) { //.pwr_mgmt_1     = 0x6B,
		st.chip_cfg.sensors = 0;
		return -1;
	}
	st.chip_cfg.clk_src = data & ~BIT_SLEEP;

	data = 0;
	if (!(sensors & INV_X_GYRO)) //#define INV_X_GYRO      (0x40)
		data |= BIT_STBY_XG; //#define BIT_STBY_XG         (0x04)
	if (!(sensors & INV_Y_GYRO)) //#define INV_Y_GYRO      (0x20)
		data |= BIT_STBY_YG; //#define BIT_STBY_YG         (0x02)
	if (!(sensors & INV_Z_GYRO)) //#define INV_Z_GYRO      (0x10)
		data |= BIT_STBY_ZG; //#define BIT_STBY_ZG         (0x01)
	if (!(sensors & INV_XYZ_ACCEL)) //#define INV_XYZ_ACCEL   (0x08)
		data |= BIT_STBY_XYZA; //#define BIT_STBY_XYZA       (BIT_STBY_XA | BIT_STBY_YA | BIT_STBY_ZA)
	if (i2c_write(st.hw->addr, st.reg->pwr_mgmt_2, 1, &data)) { //.pwr_mgmt_2     = 0x6C,
		st.chip_cfg.sensors = 0;
		return -1;
	}

	if (sensors && (sensors != INV_XYZ_ACCEL))
		/* Latched interrupts only used in LP accel mode. */
		mpu_set_int_latched(0);

#ifdef AK89xx_SECONDARY
#ifdef AK89xx_BYPASS
	if (sensors & INV_XYZ_COMPASS)
		mpu_set_bypass(1);
	else
		mpu_set_bypass(0);
#else
	if (i2c_read(st.hw->addr, st.reg->user_ctrl, 1, &user_ctrl))
		return -1;
	/* Handle AKM power management. */
	if (sensors & INV_XYZ_COMPASS) {
		data = AKM_SINGLE_MEASUREMENT;
		user_ctrl |= BIT_AUX_IF_EN;
	} else {
		data = AKM_POWER_DOWN;
		user_ctrl &= ~BIT_AUX_IF_EN;
    }
	if (st.chip_cfg.dmp_on)
		user_ctrl |= BIT_DMP_EN;
	else
		user_ctrl &= ~BIT_DMP_EN;
	if (i2c_write(st.hw->addr, st.reg->s1_do, 1, &data))
		return -1;
	/* Enable/disable I2C master mode. */
	if (i2c_write(st.hw->addr, st.reg->user_ctrl, 1, &user_ctrl))
		return -1;
#endif
#endif

	st.chip_cfg.sensors = sensors;
	st.chip_cfg.lp_accel_mode = 0;
	delay_ms(50);

	return 0;
}

至此,int mpu_init(void)就全部解析完毕了。