sparkfun / SparkFun_ICM-20948_ArduinoLibrary

Arduino support for ICM_20948 w/ portable C backbone
Other
160 stars 69 forks source link

How to get FSYNC delay? #124

Closed joshgalvan closed 8 months ago

joshgalvan commented 1 year ago

Hello!

I can't seem to find the ability to get the FSYNC delay from a gyro event (I'm pretty sure gyro is the only sensor that can output an FSYNC delay timestamp), and I also don't see anywhere to enable FSYNC.

Would you mind pointing me in the right direction within the library to get this working?

Thank you!

PaulZC commented 1 year ago

Hi Josh (@joshgalvan ),

I think you might be the first person to ask about FSYNC...

All I can say is:

So, it may be a question of wading through the datasheet:

The FSYNC_CONFIG register and bits are defined. Likewise for DELAY_TIME and its bits.

I hope this helps. Please send us a Pull Request if you get this going!

Best wishes, Paul

joshgalvan commented 1 year ago

Thank you for the insight!

I've implemented everything and have assured that I am correctly setting the FSYNC_CONFIG register with 0b1000_0000, but am unable to gather the delay time from DELAY_TIMEH and DELAY_TIMEL.

In my main loop I raise my FSYNC pin for 1ms, then lower it, then once there is data (via myICM.dataReady()), I try to print it with:

ICM_20948_Status_e ICM_20948::readFSYNCDelay(uint16_t *read) {
  status = ICM_20948_get_delay(&_device, read);
  return status;
}

ICM_20948_Status_e ICM_20948_get_delay(ICM_20948_Device_t *pdev, uint16_t *delay) {
  ICM_20948_Status_e retval = ICM_20948_Stat_Ok;
  uint8_t delay_h;
  uint8_t delay_l;
  if (delay == NULL) {
    return ICM_20948_Stat_ParamErr;
  }
  ICM_20948_set_bank(pdev, 0);
  retval = ICM_20948_execute_r(pdev, AGB0_REG_DELAY_TIMEH, &delay_h, 1);
  if (retval != ICM_20948_Stat_Ok) {
    return retval;
  }
  retval = ICM_20948_execute_r(pdev, AGB0_REG_DELAY_TIMEL, &delay_l, 1);
  if (retval != ICM_20948_Stat_Ok) {
    return retval;
  }
  pdev->_delay_h = delay_h;
  pdev->_delay_l = delay_l;
  *delay = (uint16_t)(delay_h << 8) | delay_l;
  return retval;
}

I'm not trying to use FSYNC as an interrupt and I can't seem to find anything else to configure to collect the delay properly.

This is my setup() and loop():

void setup()
{

  SERIAL_PORT.begin(115200);
  while (!SERIAL_PORT)
  {
  };

  WIRE_PORT.begin();
  WIRE_PORT.setClock(400000);

  pinMode(FSYNC, INPUT);

  //myICM.enableDebugging(); // Uncomment this line to enable helpful debug messages on Serial

  bool initialized = false;
  while (!initialized)
  {
    myICM.begin(WIRE_PORT, AD0_VAL);
    myICM.enableFSYNCDetection();
    SERIAL_PORT.print(F("Initialization of the sensor returned: "));
    SERIAL_PORT.println(myICM.statusString());
    if (myICM.status != ICM_20948_Stat_Ok)
    {
      SERIAL_PORT.println("Trying again...");
      delay(500);
    }
    else
    {
      initialized = true;
    }
  }
  // uint8_t fsync_config;
  // myICM.readFSYNCConfig(&fsync_config);
  // SERIAL_PORT.print("fsync_config: ");
  // SERIAL_PORT.println(fsync_config, BIN); // prints 10000000
  // while (1) {
  //   delay(10);
  // }
}

void loop()
{
  digitalWrite(FSYNC, HIGH);
  delay(1);
  digitalWrite(FSYNC, LOW);

  if (myICM.dataReady())
  {
    myICM.readFSYNCDelay(&fs_delay);
    myICM.getAGMT();         // The values are only updated when you call 'getAGMT'
                             //    printRawAGMT( myICM.agmt );     // Uncomment this to see the raw values, taken directly from the agmt structure
    printScaledAGMT(&myICM); // This function takes into account the scale settings from when the measurement was made to calculate the values with units
    Serial.println(fs_delay);
    Serial.println(myICM._device._delay_l); // Yes I made _device public to print it easier
    Serial.println(myICM._device._delay_h);
    delay(30);
  }
  else
  {
    SERIAL_PORT.println("Waiting for data");
    delay(500);
  }
}

The GPIO I'm using to assert the FSYNC pin on the ICM is going through a level shifter, so I'm going to check that everything is working there next, but just wanted some insight on if you think this is good.

joshgalvan commented 1 year ago

It was an issue with the level shifter, everything works. I don't exactly have time to do a full PR for the FSYNC code but this is what I wrote:

ICM_20948_Status_e ICM_20948::enableFSYNCDetection(bool enable) {
  ICM_20948_FSYNC_CONFIG_t reg;
  status = ICM_20948_fsync_config(&_device, NULL, &reg);
  if (status != ICM_20948_Stat_Ok) {
    return status;
  }
  reg.DELAY_TIME_EN = enable;
  status = ICM_20948_fsync_config(&_device, &reg, NULL);
  if (status != ICM_20948_Stat_Ok) {
    return status;
  }

  ICM_20948_INT_PIN_CFG_t int_reg;
  status = ICM_20948_int_pin_cfg(&_device, NULL, &int_reg); // read phase
  if (status != ICM_20948_Stat_Ok)
  {
    return status;
  }
  int_reg.ACTL_FSYNC = 0;
  int_reg.FSYNC_INT_MODE_EN = 1;
  status = ICM_20948_int_pin_cfg(&_device, &int_reg, NULL); // write phase
  if (status != ICM_20948_Stat_Ok)
  {
    return status;
  }

  return status;
}
ICM_20948_Status_e ICM_20948::readFSYNCDelay(uint16_t *computed_time) {
  uint8_t delay_h;
  uint8_t delay_l;
  status = ICM_20948_get_delay(&_device, &delay_h, &delay_l);
  if (status != ICM_20948_Stat_Ok) {
    return status;
  }

  // From datasheet @ 8.17: Delay time in us = (DELAY_TIMEH * 256 + DELAY_TIMEL) * 0.9645

  *computed_time = (delay_h * 256 + delay_l) * 0.9645;

  return status;
}

And then in C backbone:

ICM_20948_Status_e ICM_20948_fsync_config(ICM_20948_Device_t *pdev, ICM_20948_FSYNC_CONFIG_t *write, ICM_20948_FSYNC_CONFIG_t *read) {
  ICM_20948_Status_e retval = ICM_20948_Stat_Ok;
  retval = ICM_20948_set_bank(pdev, 2); // Must be in the right bank
  if (write != NULL) {
    retval = ICM_20948_execute_w(pdev, AGB2_REG_FSYNC_CONFIG, (uint8_t *)write, sizeof(ICM_20948_FSYNC_CONFIG_t));
    if (retval != ICM_20948_Stat_Ok) {
      return retval;
    }
  }
  if (read != NULL) {
    retval = ICM_20948_execute_r(pdev, AGB2_REG_FSYNC_CONFIG, (uint8_t *)read, sizeof(ICM_20948_FSYNC_CONFIG_t));
    if (retval != ICM_20948_Stat_Ok) {
      return retval;
    }
  }
  return retval;
}
ICM_20948_Status_e ICM_20948_get_delay(ICM_20948_Device_t *pdev, uint8_t *delay_h, uint8_t *delay_l) {
  ICM_20948_Status_e retval = ICM_20948_Stat_Ok;
  if (delay_h == NULL || delay_l == NULL) {
    return ICM_20948_Stat_ParamErr;
  }

  retval = ICM_20948_set_bank(pdev, 0);
  if (retval != ICM_20948_Stat_Ok) {
    return retval;
  }

  // Read high byte
  retval = ICM_20948_execute_r(pdev, AGB0_REG_DELAY_TIMEH, delay_h, 1);
  if (retval != ICM_20948_Stat_Ok) {
    return retval;
  }
  // Read low byte
  retval = ICM_20948_execute_r(pdev, AGB0_REG_DELAY_TIMEL, delay_l, 1);
  if (retval != ICM_20948_Stat_Ok) {
    return retval;
  }

  return retval;
}

I've also written functions to modify and read GYRO_CONFIG_1 and GYRO_SMPLRT_DIV because the FSYNC delay is the time between your pulse to the FSYNC pin and the next gyro ODR, so reading and configuring the gyro is helpful.