0

我正在尝试为 MPU-6050 编写驱动程序,但我对如何继续读取原始加速度计/陀螺仪/温度读数感到困惑。例如,MPU-6050 的加速度计 X 读数位于 2 个寄存器中:地址 0x3B 的 ACCEL_XOUT[15:8] 和地址 0x3C 的 ACCEL_XOUT[7:0]。当然,要读取原始值,我需要读取两个寄存器并将它们放在一起。

在寄存器的描述中(在寄存器映射和描述表中,https://invensense.tdk.com/wp-content/uploads/2015/02/MPU-6000-Register-Map1.pdf)它说保证来自同一采样时刻的读数 一旦检测到空闲 I2C 总线,我必须使用突发读取 b/c,传感器寄存器会用新采样时刻的新数据刷新。数据表片段显示了简单的 I2C 突发读取:

在此处输入图像描述

但是,如果支持自动增量,这种方法(据我所知)仅适用于从同一采样时刻读取 ACCEL_X 寄存器(这样上述序列中的第一个 DATA 将来自 ACCEL_XOUT[15:8] @ 地址 0x3B 和第二个数据将来自 ACCEL_XOUT[7:0] @ 地址 0x3C)。但是数据表(https://invensense.tdk.com/wp-content/uploads/2015/02/MPU-6000-Datasheet1.pdf)只提到 I2C 突发写入支持自动增量功能。如果 I2C 读取端没有自动增量,我将如何在保持相同采样时刻的同时读取两个不同的寄存器?

我也认识到我可以使用传感器的 FIFO 功能或中断来完成我所追求的,但是(出于我自己的好奇心)我想要一个不依赖于任何一个的解决方案。

4

2 回答 2

0

正如卢卡所说,突发读取语义似乎因读取操作开始的寄存器而异。

读取一致的样本

要读取一组一致的原始数据值,您可以使用I2C.readRegister(int, ByteBuffer, int)寄存器号为 59 (ACCEL_XOUTR[15:8])、长度为 14 的方法,一次操作读取所有传感器数据 ACCEL、TEMP 和 GYRO,并获得一致数据。

突发读取 FIFO 数据

但是,如果您使用芯片的 FIFO 缓冲区,您可以在寄存器 116 (FIFO_R_W) 上使用相同的方法签名启动突发读取,以从芯片内部的 fifo 缓冲区中读取给定数量的数据。这样做你必须记住,在一次突发操作中可以读取的字节数是有限制的。如果我正在解释https://github.com/joan2937/pigpio/blob/c33738a320a3e28824af7807edafda440952c05d/pigpio.c#L3914正确,则在单个突发操作中最多可以读取 31 个字节。

于 2022-01-06T16:19:19.050 回答
0

我也有同样的问题,看起来这个主题的文档不完整。

读取单个样本

我觉得你可以爆读了ACCEL_*OUT_*TEMP_OUT_*GYRO_*OUT_*。事实上,我尝试一次读取一个寄存器的数据,但我经常遇到数据损坏。
然后,只是为了尝试,我请求了 6 个字节ACCEL_XOUT_H,6 个字节GYRO_XOUT_H和 2 个字节,TEMP_OUT_H并且......它成功了!不再有数据损坏!

我认为他们只是忘记在注册地图中提及这一点。

如何

下面是一些可以在 Arduino 环境中运行的示例代码。

这些是我使用的功能,它们不是很安全,但它适用于我的项目:

////////////////////////////////////////////////////////////////
inline void requestBytes(byte SUB, byte nVals)
{
    Wire.beginTransmission(SAD);
    Wire.write(SUB);
    Wire.endTransmission(false);
    Wire.requestFrom(SAD, nVals);
    while (Wire.available() == 0);
}

////////////////////////////////////////////////////////////////
inline byte getByte(void)
{
    return Wire.read();
}

////////////////////////////////////////////////////////////////
inline void stopRead(void)
{
    Wire.endTransmission(true);
}

////////////////////////////////////////////////////////////////
byte readByte(byte SUB)
{
    requestBytes(SUB, 1);

    byte result = getByte();

    stopRead();

    return result;
}

////////////////////////////////////////////////////////////////
void readBytes(byte SUB, byte* buff, byte count)
{
    requestBytes(SUB, count);

    for (int i = 0; i < count; i++)
        buff[i] = getByte();

    stopRead();
}

此时,您可以通过这种方式简单地读取值:

// ACCEL_XOUT_H

// burst read the registers using auto-increment:
byte data[6];
readBytes(ACCEL_XOUT_H, data, 6);

// convert the data:
acc_x = (data[0] << 8) | data[1];
// ...

警告!

看起来这不能用于其他寄存器。例如,要读取 FIFO_COUNT_* 我必须这样做(否则我会得到不正确的结果):

uint16_t FIFO_size(void)
{
    byte bytes[2];

    // this does not work
    //readBytes(FIFO_COUNT_H, bytes, 2);

    bytes[1] = readByte(FIFO_COUNT_H);
    bytes[2] = readByte(FIFO_COUNT_L);

    return unisci_bytes(bytes[1], bytes[2]);
}

读取 FIFO

看起来 FIFO 的工作方式不同:您可以通过简单地从FIFO_R_W寄存器请求多个字节来进行突发读取,而 MPU6050 将在不增加寄存器的情况下为您提供 FIFO 中的字节。

我发现了这个例子,他们用来I2Cdev::readByte(SAD, FIFO_R_W, buffer)从 FIFO 中读取给定数量的字节,如果你看一下I2Cdev::readByte()这里)它只是从 FIFO 寄存器中请求 N 个字节:


// ... send FIFO_R_W and request N bytes ...

for(...; ...; count++)
    data[count] = Wire.receive();
// ...

如何

这很简单,因为 FIFO_R_W 不会自动递增:

byte data[12];

void loop() {
// ...
readBytes(FIFO_R_W, data, 12); // <- replace 12 with your burst size
// ...
}

警告!

  • 使用FIFO_size()速度很慢!
  • 另外我的建议是使用 400kHz I2C 频率,这是 MPU6050 的最大速度

希望能帮助到你 ;)

于 2020-12-17T08:47:02.417 回答