Problems connecting LSM6DS3 to Xiao Ble on SPI

Hi everyone,

I need to connect an imu (lsm6ds3trc) to a Xiao Ble (not sense), using the SPI bus.

The main reason is becuase I already have another sensor connected in SPI and I would like to share the same bus.

I tried various LSM6DS3 libraries, the Seed , the Sparkfun, and the Adrafruit.

The only one that works is the Adafruit, but I found the library not very flexible, mainly because I would like to write directly to the imu registers, to optimize the imu performances and power consumption.

The seed library (which works well in i2c) simply couldn´t find the imu in spi, and the sparkfun library is stuck in myimu.begin().

This is the code:

#include <SPI.h>
#include <ADS1220_WE.h>
//#include <SparkFunLSM6DS3.h>
//#include <Adafruit_LSM6DS3.h>
#include <LSM6DS3.h>


#define SAMPLE_RATE       90
#define IMU_CS    D6
#define ADS_CS    D7
#define ADS_DRDY  D3

bool adcok = true;
bool imuok = true;


LSM6DS3 MyImu(SPI_MODE, IMU_CS);
ADS1220_WE ads(ADS_CS, ADS_DRDY);


void setup() {
  Serial.begin(115200);
  delay(1000);


  Serial.println("Init Sensors");

  ads.setSPIClockSpeed(1000000);
  if (!ads.init()) {
    Serial.println("ADS1220 is not connected!");
    adcok = false;
  } else {
    Serial.println("ADS1220 found");
    ads.setGain(ADS1220_GAIN_128);
    ads.setOperatingMode(ADS1220_DUTY_CYCLE_MODE);
    //ads.setDataRate(ADS1220_DR_LVL_2);  // 90 SPS normal mode
    ads.setDataRate(ADS1220_DR_LVL_4);  // 82.5 SPS duty cycle mode
    ads.setConversionMode(ADS1220_CONTINUOUS);
    ads.setCompareChannels(ADS1220_MUX_1_2);
    ads.setVRefSource(ADS1220_VREF_REFP1_REFN1);
    ads.setLowSidePowerSwitch(ADS1220_SWITCH); 
  }

delay(500);

  if( MyImu.begin() != 0)
  {
	  Serial.println("IMU not found!");
    imuok = false;
  } else {
    Serial.println("IMU found");
  }

} 

void loop() {
  int cnt = SAMPLE_RATE;
  int32_t reading = 0;
  float GyroZ;

//  int gxRaw, gyRaw, gzRaw;         // BMI160 raw gyro values

  while (cnt > 0){
    
      if (adcok) {
        reading = ads.getRawData(); // get raw data
      } else delay(15);

      if(imuok) GyroZ = MyImu.readFloatGyroZ();
      cnt--;
  }

  Serial.print("Bridge Reading: ");
  Serial.println(reading);
  Serial.print("Gyroscope: Z1 = ");
  Serial.println(GyroZ, 4);
  
}

Thank you

Luca

Hi there,

SO, My first guess would be shared SPI bus handling.

A few reasons:

  • the IMU works over I2C, so the sensor is probably fine
  • the Adafruit library works in SPI, so the hardware path is at least basically correct
  • the failures only show up with the other SPI libraries while sharing the bus with the ADS1220

That points more toward chip-select handling, SPI mode/transaction conflicts, or library assumptions than what I first suspected to missing pull-ups.

The LSM6DS3 supports both I2C and SPI, with CS selecting SPI mode, and the SparkFun-style constructor for SPI is the expected pattern.

What I would try first:

  1. Set both CS pins as outputs at startup and drive them HIGH before calling either library:
pinMode(IMU_CS, OUTPUT);
pinMode(ADS_CS, OUTPUT);
digitalWrite(IMU_CS, HIGH);
digitalWrite(ADS_CS, HIGH);
    • Test the IMU alone with the SparkFun/Seeed library, with the ADS1220 code completely removed.
    • Then test the ADS1220 alone.
    • When sharing the bus, make sure the other device’s CS is HIGH before every transaction.
    • Compare the Adafruit library behavior, since that one already works. It may simply be managing SPI transactions or chip select more carefully.

Here’s a cleaned-up test version that does the main things I would want on a shared SPI bus:

  • sets both CS pins HIGH before any device init
  • initializes SPI first
  • forces the other device deselected before talking to one device
  • adds a little structure so the bus is not left in a bad state
  • reads the IMU only once per loop pass

This does not fix a broken library internally, but it gives YOU a much better shot at making the shared bus behave.

/*
  XIAO nRF52840 / XIAO BLE
  Shared SPI test: ADS1220 + LSM6DS3TR-C

  Purpose:
  - Force proper chip-select handling on a shared SPI bus
  - Help diagnose why LSM6DS3 SPI init fails with some libraries

  Notes:
  - Both CS pins are driven HIGH before init
  - Only one device is selected at a time
  - IMU is tested with SparkFun/Seeed style LSM6DS3 library constructor
*/

#include <SPI.h>
#include <ADS1220_WE.h>
#include <LSM6DS3.h>

#define SAMPLE_RATE   90

#define IMU_CS        D6
#define ADS_CS        D7
#define ADS_DRDY      D3

bool adcok = false;
bool imuok = false;

LSM6DS3 MyImu(SPI_MODE, IMU_CS);
ADS1220_WE ads(ADS_CS, ADS_DRDY);

// -----------------------------
// Helper functions
// -----------------------------
void deselectAllSPI() {
  digitalWrite(IMU_CS, HIGH);
  digitalWrite(ADS_CS, HIGH);
}

void selectIMU() {
  digitalWrite(ADS_CS, HIGH);
  digitalWrite(IMU_CS, LOW);
}

void deselectIMU() {
  digitalWrite(IMU_CS, HIGH);
}

void selectADS() {
  digitalWrite(IMU_CS, HIGH);
  digitalWrite(ADS_CS, LOW);
}

void deselectADS() {
  digitalWrite(ADS_CS, HIGH);
}

// -----------------------------
// Setup
// -----------------------------
void setup() {
  Serial.begin(115200);
  delay(1000);

  Serial.println();
  Serial.println("=================================");
  Serial.println("Shared SPI Test: ADS1220 + LSM6DS3");
  Serial.println("=================================");

  // IMPORTANT: set CS lines first
  pinMode(IMU_CS, OUTPUT);
  pinMode(ADS_CS, OUTPUT);

  deselectAllSPI();
  delay(10);

  // Start SPI bus
  SPI.begin();
  delay(10);

  Serial.println("Init Sensors...");

  // -----------------------------
  // Init ADS1220
  // -----------------------------
  deselectAllSPI();
  delay(5);

  ads.setSPIClockSpeed(1000000);

  // Make extra sure IMU is not selected during ADS init
  digitalWrite(IMU_CS, HIGH);
  if (!ads.init()) {
    Serial.println("ADS1220 is not connected!");
    adcok = false;
  } else {
    Serial.println("ADS1220 found");
    adcok = true;

    ads.setGain(ADS1220_GAIN_128);
    ads.setOperatingMode(ADS1220_DUTY_CYCLE_MODE);
    ads.setDataRate(ADS1220_DR_LVL_4);  // 82.5 SPS duty cycle mode
    ads.setConversionMode(ADS1220_CONTINUOUS);
    ads.setCompareChannels(ADS1220_MUX_1_2);
    ads.setVRefSource(ADS1220_VREF_REFP1_REFN1);
    ads.setLowSidePowerSwitch(ADS1220_SWITCH);
  }

  delay(200);

  // -----------------------------
  // Init LSM6DS3
  // -----------------------------
  deselectAllSPI();
  delay(5);

  // Make extra sure ADS is not selected during IMU init
  digitalWrite(ADS_CS, HIGH);

  int imuStatus = MyImu.begin();
  if (imuStatus != 0) {
    Serial.print("IMU not found! begin() returned: ");
    Serial.println(imuStatus);
    imuok = false;
  } else {
    Serial.println("IMU found");
    imuok = true;
  }

  deselectAllSPI();
  delay(100);

  Serial.println("Setup complete.");
  Serial.println();
}

// -----------------------------
// Main loop
// -----------------------------
void loop() {
  int cnt = SAMPLE_RATE;
  int32_t reading = 0;
  float gyroZ = 0.0f;

  while (cnt > 0) {
    // ----- Read ADS1220 -----
    if (adcok) {
      // Make sure IMU is deselected first
      deselectAllSPI();
      delayMicroseconds(5);

      reading = ads.getRawData();

      deselectADS();
    } else {
      delay(15);
    }

    // ----- Read IMU -----
    if (imuok) {
      // Make sure ADS is deselected first
      deselectAllSPI();
      delayMicroseconds(5);

      gyroZ = MyImu.readFloatGyroZ();

      deselectIMU();
    }

    cnt--;
  }

  Serial.print("Bridge Reading: ");
  Serial.println(reading);

  Serial.print("Gyroscope Z = ");
  Serial.println(gyroZ, 4);

  Serial.println("-----------------------------");
  delay(500);
} 

First, test IMU only:

  • comment out all ADS1220 code
  • see whether MyImu.begin() works reliably

Then test ADS only.

If both work alone but fail together, that is strong proof of a shared-bus conflict, not a dead sensor.

If the IMU still fails with this version, the next things I would look at are:

  • SPI mode mismatch between the IMU library and ADS library
  • breakout board wiring for the LSM6DS3 SPI pins
  • whether the IMU breakout is really set up for 4-wire SPI
  • whether the library is doing odd things internally during begin()

Try that and report back , please.

HTH
GL :slight_smile: PJ :v:

Hi PJ , thank you for the suggestions.

I already did some of the tests that you suggested, only didn’t set the two CS to high before calling the library, this will be my next test.

The ads1220 alone works well with every library, the imu only works with the adafruit library.

The two work together with the adafruit library.

Meanwhile, I did another thing, I modified the adafruit library and create the two missing functions , readRegister and writeRegister.

This probably will solve my problem , that is the need to access all the configuration options available in the chip.

I am currently testing the modified library , it seems to work… I will confirm as soon as I can

1 Like

Hi there,

So Thanks for the update :+1: that actually tells us quite a lot.

Since:

  • the IMU works with the Adafruit library
  • the ADS1220 + IMU work together with the Adafruit library
  • and your modified Adafruit version now appears to give you the register access you needed

then the hardware side is probably fine, and this is looking more like a library behavior difference on SPI rather than a wiring issue.

So yes, your modified Adafruit library may solve the immediate problem.

That said, I would still treat it as a workaround, not the final explanation.
the Adafruit library working proves the hardware and shared bus concept are basically sound.
So the better long-term answer is to figure out why the other library fails on this setup, not just patch around it.
Why I’d be cautious about editing the library:

  • future library updates become painful
  • local patches are easy to lose
  • the real bug stays hidden
  • if the failure is actually SPI transaction handling / CS timing / bus state, that problem may come back later in a different form

What I suspect is really happening:

  1. Adafruit’s driver is simply handling SPI more robustly
    likely better transaction wrapping, CS timing, or register access sequencing.
  2. The other library may not like this shared-bus environment
    even though it claims SPI support. SparkFun’s library does support SPI in concept, but that does not guarantee it behaves well with another active SPI device and another library on the same bus

So I think your current path is reasonable:

  • continue testing the modified Adafruit library
  • confirm it is stable over time
  • but keep in mind that the deeper issue is probably still a library SPI handling difference

If your modified Adafruit version proves stable, that may be the most practical solution. I just would not conclude from this that the other libraries are “impossible” on SPI, more likely they are just less happy with this particular shared-bus setup.

Great stuff, keep going.

HTH
GL :slight_smile: PJ :v: