XIAO IMU I2C High Frequency Sampling


I’m currently trying to sample IMU readings from the XIAO nRF52840 Sense at 1600Hz. I notice from the schematics that the internal IMU (LSM6DS3TR-C) is internally connected to I2C protocol. The IMU documentation provided by ST (Documentation) mentions that the IMU is capable of 1.66KHz ODR for the Gyroscope and the Accelerometer.

Could someone please point in the direction of sampling the IMU values at the said frequency? I’m assuming I should be modifying the LSM6DS3TR-C registers to enable high-performance mode and setting the sampling rate for the task.

Thanks a ton!

Hi aedatha,
You can manipulate the registers directly as follows.

  myIMU.writeRegister(LSM6DS3_ACC_GYRO_CTRL2_G, 0x8C);     //1.66kHz 2000dps
  myIMU.writeRegister(LSM6DS3_ACC_GYRO_CTRL1_XL, 0x8A);    //1.66kHz 4G

That would work perfectly. Thanks a ton @msfujino. Would you also know of any test cases to verify that LSM6DS3TR-C is working at the specified specs and is not being bottlenecked by the XIAO?

I measured the execution time as a test. Interrupts and FIFOs are not actively considered.

waiting for read enable 200 to 600uS
read data 1400uS
print 3500uS

//BSP : Seeed nRF52 mbed-enabled Borads 2.9.1
//Board : Seeed nRF52 mbed-enabled Borads / Seeed XIAO BLE Sense - nRF52840

#include <Wire.h>
#include <LSM6DS3.h>

LSM6DS3 myIMU(I2C_MODE, 0x6A);

float ax, ay, az;
float gx, gy, gz;
uint8_t readData = 0; // for IMU status read

void setup() 
  while (!Serial);

  pinMode(LED_RED, OUTPUT);
  pinMode(LED_GREEN, OUTPUT);   
  digitalWrite(LED_RED, HIGH);
  digitalWrite(LED_GREEN, HIGH);

  // See LSM6D3.cpp:351
  myIMU.settings.gyroRange = 2000;
  myIMU.settings.accelRange = 4;
  if (myIMU.begin() != 0) 
    Serial.println("Device error");
  Wire1.setClock(400000UL);  // I2C1 SCL=400kHz
  myIMU.writeRegister(LSM6DS3_ACC_GYRO_CTRL2_G, 0x8C);     //1.66kHz 2000dps
  myIMU.writeRegister(LSM6DS3_ACC_GYRO_CTRL1_XL, 0x8A);    //1.66kHz 4G
  // Set gyroscope power mode to high performance and bandwidth to 16 mHz
  myIMU.writeRegister(LSM6DS3_ACC_GYRO_CTRL7_G, 0x00);
  // Set the ODR config register to ODR/4
  myIMU.writeRegister(LSM6DS3_ACC_GYRO_CTRL8_XL, 0x09);

void loop()
  digitalWrite(LED_RED, LOW);
  unsigned int timestamp = micros();
  do {  
    myIMU.readRegister(&readData, LSM6DS3_ACC_GYRO_STATUS_REG); // 0,0,0,0,0,TDA,GDA,XLDA 
  } while ((readData & 0x07) != 0x07);
  Serial.println(micros() - timestamp);  // wait for read enable 200~600uS
  digitalWrite(LED_RED, HIGH);

  digitalWrite(LED_GREEN, LOW);
  timestamp = micros();
  ax = myIMU.readFloatAccelX();
  ay = myIMU.readFloatAccelY();
  az = myIMU.readFloatAccelZ();
  gx = myIMU.readFloatGyroX();
  gy = myIMU.readFloatGyroY();
  gz = myIMU.readFloatGyroZ();
  Serial.println(micros() - timestamp); // read 6 float data 1400uS

  timestamp = micros();
  Serial.print(ax); Serial.print(", ");
  Serial.print(ay); Serial.print(", ");
  Serial.print(az); Serial.print(", ");
  Serial.print(gx); Serial.print(", ");
  Serial.print(gy); Serial.print(", ");
  Serial.print(gz); Serial.println();  
  Serial.println(micros() - timestamp); // print 6 data 3500uS
  digitalWrite(LED_GREEN, HIGH);

Once again, thanks a million @msfujino. This is really helpful. Just to confirm the interpretation of your results, the XIAO/Arduino IDE does seem to be a bottleneck. 1 sample set (of 6 values) is ~ 2000uS (including max read enable time). Hence the ODR of 1600 sample sets would be 1600*2000 = 3.2sec. Do you have any tips on reducing this?

The following functions can be used to save time by reading the data consecutively.
readRegisterRegion(uint8_t* outputPointer, uint8_t offset, uint8_t length)

  int8_t dataBuff[12];
  myIMU.readRegisterRegion(dataBuff, LSM6DS3_ACC_GYRO_OUTX_L_G, 12);
  gx = myIMU.calcGyro((int16_t)dataBuff[0] | int16_t(dataBuff[1] << 8));
  gy = myIMU.calcGyro((int16_t)dataBuff[2] | int16_t(dataBuff[3] << 8));
  gz = myIMU.calcGyro((int16_t)dataBuff[4] | int16_t(dataBuff[5] << 8));
  ax = myIMU.calcAccel((int16_t)dataBuff[6] | int16_t(dataBuff[7] << 8));
  ay = myIMU.calcAccel((int16_t)dataBuff[8] | int16_t(dataBuff[9] << 8));
  az = myIMU.calcAccel((int16_t)dataBuff[10] | int16_t(dataBuff[11] << 8));
1 Like