ICM 20649 data aquisition is slow

Hey there, I am currently working on a project, where I want to measure the vibration of an object using the ICM-20649 board from Adafruit. The problem I am currently running into is that I can only get up to roughly 230 Hz or 4.2 ms per dataset when transmitting even though the sensor should go higher (datasheet page 13).
Here is the code I am currently using to calibrate the sensor and print the data:

#include <Adafruit_ICM20649.h>
#include <Adafruit_ICM20X.h>
#include <Adafruit_Sensor.h>
#include <Wire.h>

// Adafruit_ICM20X icm;
Adafruit_ICM20649 icm;
Adafruit_Sensor *icm_temp, *icm_accel, *icm_gyro;

// For SPI mode, we need a CS pin
#define ICM_CS 10
// For software-SPI mode we need SCK/MOSI/MISO pins
#define ICM_SCK 13
#define ICM_MISO 12
#define ICM_MOSI 11

sensors_event_t accel;
sensors_event_t gyro;
sensors_event_t temp;

//for calibration
float a_mid_x, a_mid_y, a_mid_z;
float g_mid_x, g_mid_y, g_mid_z;

void setup(void) {
  Serial.begin(230400);
  while (!Serial)
    delay(10); // will pause Zero, Leonardo, etc until serial console opens

  Serial.println("Adafruit ICM20649 test!");

  // Try to initialize!
  if (!icm.begin_I2C()) {
    // if (!icm.begin_SPI(ICM_CS)) {
    // if (!icm.begin_SPI(ICM_CS, ICM_SCK, ICM_MISO, ICM_MOSI)) {

    Serial.println("Failed to find ICM20649 chip");
    while (1) {
      delay(10);
    }
  }
  Serial.println("ICM20649 Found!");
  icm.setAccelRange(ICM20649_ACCEL_RANGE_4_G); // Set Accelerometer range: +-4G, +-8G, +-16G, +-32G
  Serial.print("Accelerometer range set to: ");
  switch (icm.getAccelRange()) {
  case ICM20649_ACCEL_RANGE_4_G:
    Serial.println("+-4G");
    break;
  case ICM20649_ACCEL_RANGE_8_G:
    Serial.println("+-8G");
    break;
  case ICM20649_ACCEL_RANGE_16_G:
    Serial.println("+-16G");
    break;
  case ICM20649_ACCEL_RANGE_30_G:
    Serial.println("+-30G");
    break;
  }

  icm.setGyroRange(ICM20649_GYRO_RANGE_500_DPS);
  Serial.print("Gyro range set to: ");
  switch (icm.getGyroRange()) {
  case ICM20649_GYRO_RANGE_500_DPS:
    Serial.println("500 degrees/s");
    break;
  case ICM20649_GYRO_RANGE_1000_DPS:
    Serial.println("1000 degrees/s");
    break;
  case ICM20649_GYRO_RANGE_2000_DPS:
    Serial.println("2000 degrees/s");
    break;
  case ICM20649_GYRO_RANGE_4000_DPS:
    Serial.println("4000 degrees/s");
    break;
  }

  // icm.setAccelRateDivisor(4095);
  icm.setAccelRateDivisor(0);
  uint16_t accel_divisor = icm.getAccelRateDivisor();
  float accel_rate = 1125 / (1.0 + accel_divisor);

  Serial.print("Accelerometer data rate divisor set to: ");
  Serial.println(accel_divisor);
  Serial.print("Accelerometer data rate (Hz) is approximately: ");
  Serial.println(accel_rate);

  icm.setGyroRateDivisor(0);
  uint8_t gyro_divisor = icm.getGyroRateDivisor();
  float gyro_rate = 1100 / (1.0 + gyro_divisor);

  Serial.print("Gyro data rate divisor set to: ");
  Serial.println(gyro_divisor);
  Serial.print("Gyro data rate (Hz) is approximately: ");
  Serial.println(gyro_rate);
  Serial.println();

  icm_temp = icm.getTemperatureSensor();
  icm_temp->printSensorDetails();

  icm_accel = icm.getAccelerometerSensor();
  icm_accel->printSensorDetails();

  icm_gyro = icm.getGyroSensor();
  icm_gyro->printSensorDetails();

  
  // calibration
  Serial.println("Calibrating...");

  // weird data after startup
  delay(1000);

  for (int i = 1; i <= 1000; i++){
    icm.getEvent(&accel, &gyro, &temp);

    a_mid_x = (a_mid_x * (i - 1) + accel.acceleration.x ) / i;
    a_mid_y = (a_mid_y * (i - 1) + accel.acceleration.y ) / i;
    a_mid_z = (a_mid_z * (i - 1) + accel.acceleration.z ) / i;

    g_mid_x = (g_mid_x * (i - 1) + gyro.gyro.x ) / i;
    g_mid_y = (g_mid_y * (i - 1) + gyro.gyro.y ) / i;
    g_mid_z = (g_mid_z * (i - 1) + gyro.gyro.z ) / i;

  }

}

void loop() {
  /* Get a new normalized sensor event */
  //  icm.getEvent(&accel, &gyro, &temp);
   icm_accel->getEvent(&accel);

  //  delay(5);
   Serial.print(millis());Serial.print(",");

  //  Serial.print(temp.temperature); Serial.print(",");
  
   Serial.print(accel.acceleration.x - a_mid_x);
   Serial.print(",");
   Serial.print(accel.acceleration.y - a_mid_y);
   Serial.print(",");
   Serial.print(accel.acceleration.z - a_mid_z - 9.81); // Gravity
  
  //  Serial.print(",");
  //  Serial.print(gyro.gyro.x - g_mid_x);
  //  Serial.print(","); Serial.print(gyro.gyro.y - g_mid_y);
  //  Serial.print(","); Serial.print(gyro.gyro.z - g_mid_z);

   Serial.println();
}

I do not know whether this is a code or a sensor problem. Any help would be appreciated.
I have figured out, that there is a function in the Adafruit Library to change the register however i am unsure about how to use it and if I should use it.

There also seems to be a low-noise mode for the accelerometer, if anyone knows how t turn that on that would be helpful as well.

Same Problem, the native Accel rate from the Adafruit seems barely 1Hz, data sheet should give min >1KHz low noize and 9KHz fast mode. Its going to be a, make my own driver day. I'll let you know if I get over 230Hz.

So I bare metaled it and now getting different accel readings ~ every two prints at about 1KHz, well a 1ms delay + runtime (72us) The accel sample rate divider was set to 0x20 in the high byte reg , I set High byte to 0 and low to 1. The code below is just a hack, reading speed can be halved by doing sequential spi reads and not sending 0's, but its 500x faster than it was. strange the default was so slow. Running on Teensy 4.0 .

#include <SPI.h>
const int slaveSelectPin = 10;
#define BANK_SELECT 0x7F
#define PWR_MGMT_1 0x06
#define  LP_CONFIG 0x05
#define PWR_MGMT_2 0x07
#define ACCEL_SMPL_RATE_DIV_1 16
#define ACCEL_SMPL_RATE_DIV_2 17
#define BANK_0 0x0
#define BANK_1 0x10
#define BANK_2 0x20
#define BANK_3 0x30

void setup() {
  Serial.begin(115200);
  pinMode (slaveSelectPin, OUTPUT);
  SPI.begin();
}

void loop() {
  regW(BANK_SELECT, BANK_0);
  regW(PWR_MGMT_1, 1); // power on, clock auto,no temp sensor
  regW(PWR_MGMT_2, 0); // all accel ,allgyro 0,0,Ax,Ay,Az,Gx,Gy,Gz
  regW(LP_CONFIG, 0); // no duty cycling

  regW(BANK_SELECT, BANK_2);
  regW(ACCEL_SMPL_RATE_DIV_1, 0); // accel sample rate divider1
  regW(ACCEL_SMPL_RATE_DIV_2, 1); // accel sample rate divider2

  regW(BANK_SELECT, BANK_0);

  while (1)
  {
    read_all();
    delayMicroseconds(1000);
  }
}

void read_all()
{
  for (uint8_t n = 45; n < 56; n = n + 2)
  {
    uint16_t high_byte = regR(n);
    uint16_t low_byte = regR(n + 1);
    uint16_t data16 = ((high_byte << 8 ) | (low_byte));
    Serial.printf("%d , ", data16);
  }
  Serial.printf("\n");
}

uint32_t regR(uint16_t addr)
{
  uint16_t readVal = 0;
  digitalWrite(slaveSelectPin, LOW);
  SPI.transfer(addr | 0x80);
  readVal =  SPI.transfer(0);
  digitalWrite(slaveSelectPin, HIGH);
  return (readVal);
}


uint32_t regW(uint16_t addr, uint16_t val)
{
  digitalWrite(slaveSelectPin, LOW);
  SPI.transfer(addr & 0x7F);
  SPI.transfer(val);
  digitalWrite(slaveSelectPin, HIGH);
}

Hi,

I tried out your code. There was an error because of the write function being uint32_t instead of void (it doesn't return anything). Also, I ran it on esp8266 Feather Huzza (I only changed the CS pin number to 14, the rest was pretty much the same), but I only keep receiving 65535 over and over. Are you sure this code runs fine on your device? it all seems logical to me, except that I don't quite understand the "SPI.transfer(addr & 0x7F);" and "SPI.transfer(addr | 0x80);". I am not really sure what the ANDing and ORing are supposed to do here. Thanks in advance!

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.