MPU-6050:从FIFO寄存器正确读取数据

MPU-6050: Correctly reading data from the FIFO register

简介

MPU-6050 是一款流行的模块,包含温度传感器、加速度计和陀螺仪。用户可以通过 I2C 或 SPI 读取传感器信息。有两个文档可公开获取,用于从 IC 寄存器中读取数据。它们是:

  1. The MPU-6000 and MPU-6050 Register Map and Descriptions Document

  2. The MPU-6000 and MPU-6050 Product Specification


上下文

由于总线通信延迟,通过 I2C 读取 IMU 的各个寄存器会导致样本随时间偏移。因此,传感器的 X、Y 和 Z 轴寄存器的顺序读取不同步。为了解决这个问题,该设备提供了一个 1024 字节的内部 FIFO 队列。配置为推送到队列的数据以采样率一起推送。因此读取 FIFO 会产生同步数据。

参见(2),第 7.17 节

The MPU-60X0 contains a 1024-byte FIFO register that is accessible via the Serial Interface. The FIFO configuration register determines which data is written into the FIFO. Possible choices include gyro data, accelerometer data, temperature readings, auxiliary sensor readings, and FSYNC input. A FIFO counter keeps track of how many bytes of valid data are contained in the FIFO. The FIFO register supports burst reads. The interrupt function may be used to determine when new data is available


问题

数据sheets 指定为了从 FIFO 中读取,您必须执行以下操作:

  1. 启用 FIFO(第 6 位,寄存器 0x6A,文档 (1),第 4.29 节)
  2. 使用要推送的传感器信息配置 FIFO(寄存器 0x23,文档 (1),第 4.7 节)。我通过分别设置位 6、5、4 和 3 来启用 XG_FIFO_ENYG_FIFO_ENZG_FIFO_ENACCEL_FIFO_EN

如果您执行了这些步骤,则它声称(文档 (1),第 4.33 节):

Data is written to the FIFO in order of register number (from lowest to highest). If all the FIFO enable flags (see below) are enabled and all External Sensor Data registers (Registers 73 to 96) are associated with a Slave device, the contents of registers 59 through 96 will be written in order at the Sample Rate. The contents of the sensor data registers (Registers 59 to 96) are written into the FIFO buffer when their corresponding FIFO enable flags are set to 1 in FIFO_EN (Register 35).

然而,我发现这并不成立。鉴于我在配置寄存器中启用的标志,我希望以下序列来自 FIFO:

 * ----------------------------------------------------------- *
 *     BYTE #    |         VALUE          |    Register (dec)  *
 * ----------------------------------------------------------- *
 *       0       |     ACCEL_XOUT[15:8]   |         59         *
 *       1       |     ACCEL_XOUT[7:0]    |         60         *
 * ----------------------------------------------------------- *
 *       2       |     ACCEL_YOUT[15:8]   |         61         *
 *       3       |     ACCEL_YOUT[7:0]    |         62         *
 * ----------------------------------------------------------- *
 *       4       |     ACCEL_ZOUT[15:8]   |         63         *
 *       5       |     ACCEL_ZOUT[7:0]    |         64         *
 * ----------------------------------------------------------- *
 *       6       |     GYRO_XOUT[15:8]    |         67         *
 *       7       |     GYRO_XOUT[7:0]     |         68         *
 * ----------------------------------------------------------- *
 *       8       |     GYRO_YOUT[15:8]    |         69         *
 *       9       |     GYRO_YOUT[7:0]     |         70         *
 * ----------------------------------------------------------- *
 *      10       |     GYRO_ZOUT[15:8]    |         71         *
 *      11       |     GYRO_ZOUT[7:0]     |         72         *
 * ----------------------------------------------------------- *

然而,从 FIFO 中读取 12 个字节与读取各个寄存器时的相同数据不对应。当我加速 IMU 或旋转它时,它似乎也没有多大意义。因此,我不确定如何准确读取 FIFO。这是我面临的问题


问答

  1. Are you sure you are correctly write to registers?: 是的,我可以设置各种配置,例如采样率、中断等。我有信心能够正确读取 FIFO
  2. Are you sure to have anything in the FIFO to read?: 是的,我启用了FIFO溢出中断。我目前在等待中断,然后从FIFO寄存器读取。
  3. 读取前是否检查FIFO长度寄存器?是的,当FIFO溢出中断发生时,它包含1024字节(最大容量)。
  4. 其他人没有做过吗?: 没有人具体解释如何读取FIFO(例如:this similar question on another forum that gets an RTFM)。大多数与读取 FIFO 相关的可搜索问题 (a) 未回答,(b) 被告知使用通用 XYZ Arduino 库(我不能使用它),(c) 被告知读取数据 sheet(我有) .

好的,我已经找到问题了。问题是我在读取 FIFO 之前未能 重置 - 否则一切都或多或少没问题。我将向您展示我现在是如何设置 IMU 的。


源文件

我创建了一个源文件来读取 MPU-6050 寄存器。我已将它们附在此处以供参考,以供参考:


设置

为了设置 IMU,我在 FreeRTOS 任务中执行了以下步骤(在主循环之前)。

// Performs the I2C configuration for the MPU-6050 IMU. Saves handle
static mpu6050_err_t init_imu (mpu6050_i2c_cfg_t **handle) {
    mpu6050_err_t err = MPU6050_ERR_OK;
    uint8_t flags;

    // Configure the MPU-6050 I2C data structure
    static mpu6050_i2c_cfg_t i2c_cfg = (mpu6050_i2c_cfg_t) {
        .sda_pin        = I2C_SDA_PIN,
        .scl_pin        = I2C_SCL_PIN,
        .slave_addr     = I2C_IMU_SLAVE_ADDR,
        .i2c_port       = I2C_IMU_PORT_NUM,
        .clk_speed      = I2C_APB_CLK_FREQ / 200,    // Requires 400kHz
        .sda_pullup_en  = IMU_ENABLE_INTERNAL_PULLUPS,
        .scl_pullup_en  = IMU_ENABLE_INTERNAL_PULLUPS
    };

    // Initialize I2C
    if ((err = mpu6050_init(&i2c_cfg)) != MPU6050_ERR_OK) {
        return err;
    }

    // Configure Power Management 1 to wake the IMU (don't reset)
    flags = 0x0;
    if ((err = mpu6050_configure_power(&i2c_cfg, flags)) != MPU6050_ERR_OK) {
        return err;
    }

    // Configure accelerometer sensitivity
    flags = A_CFG_8G;
    if ((err = mpu6050_configure_accelerometer(&i2c_cfg, flags)) 
        != MPU6050_ERR_OK) {
        return err;
    }

    // Configure gyro sensitivity
    flags = G_CFG_500;
    if ((err = mpu6050_configure_gyroscope(&i2c_cfg, flags)) 
        != MPU6050_ERR_OK) {
        return err;
    }

    // Configure the Digital-Low-Pass-Filter
    flags = DLFP_CFG_FILTER_2;
    if ((err = mpu6050_configure_dlfp(&i2c_cfg, flags)) 
        != MPU6050_ERR_OK) {
        return err;
    }

    // Set the sampling rate to ~50Hz
    flags = 19;
    if ((err = mpu6050_set_sample_rate_divider(&i2c_cfg, flags)) 
        != MPU6050_ERR_OK) {
        return err;
    }

    // Configure interrupt behavior
    flags = 0x0;
    if ((err = mpu6050_configure_interrupt(&i2c_cfg, flags)) 
        != MPU6050_ERR_OK) {
        return err;
    }

    // Enable interrupts after every sensor refresh
    flags = INTR_EN_DATA_RDY;
    if ((err = mpu6050_enable_interrupt(&i2c_cfg, flags)) 
        != MPU6050_ERR_OK) {
        return err;
    }

    // Enable + Reset the FIFO
    flags = USER_CTRL_FIFO_EN | USER_CTRL_FIFO_RST;
    if ((err = mpu6050_enable_fifo(&i2c_cfg, flags)) 
        != MPU6050_ERR_OK) {
        return err;
    }

    // Configure the data pushed to the FIFO
    flags = FIFO_CFG_GX | FIFO_CFG_GY | FIFO_CFG_GZ | FIFO_CFG_AXYZ;
    if ((err = mpu6050_configure_fifo(&i2c_cfg, flags)) != MPU6050_ERR_OK) {
        return err;
    }

    // Save the configuration
    *handle = &i2c_cfg;

    return err;
}

如果您按照我的描述进行配置,那么它应该 可以工作。当然,您可能正在为设备使用不同的库或包装器,但您可以启用的功能应该同样可以访问。完成所有这些后,我就可以在每次中断时读取 FIFO,如下所示:

// Read the FIFO length
if (mpu6050_get_fifo_length(i2c_cfg_p, &len) != MPU6050_ERR_OK) {
    ERR("FIFO length fetch error!");
    break;
} 

// Check if enough samples are ready - else continue (check later)
if (len < FIFO_BURST_LEN) {
    continue;
}

// Fetch data from FIFO
if (mpu6050_receive_fifo(i2c_cfg_p, &data) != MPU6050_ERR_OK) {
    ERR("FIFO data fetch error!");
    break;
}