IMU generated interrupt servicing on XIAO BLE sense nrf52840 with mbed-enabled

I’m really struggling with triggering on IMU generated interrupts.

It seems that I am setting up the interrupt on the IMU correctly since the interrupt pin on the nrf is going up and down when I read it in the loop instead of in the ISR. However, the ISR appears to never be triggered or it’s otherwise stuck.

I initially started with sending data out using BLE and thought that I somehow encountered this bug: Only one pin interrupt supported · Issue #7 · arduino/ArduinoCore-nRF528x-mbedos · GitHub, so I went back to basing the ISR on the HighLevelExample and eventually even to the LowLevelExample to isolate the issue, attached below. I couldn’t even light up an LED in the ISR. Any ideas what’s wrong?

#include "LSM6DS3.h"
#include "Wire.h"
//#include "SPI.h"

uint16_t errorsAndWarnings = 0;
int16_t gyroX, gyroY, gyroZ, accX, accY, accZ;

//Create instance of LSM6DS3Core
LSM6DS3Core myIMU(I2C_MODE, 0x6A);    //I2C device address 0x6A
#define int1Pin PIN_LSM6DS3TR_C_INT1

void setup() {
    //Init Serial port
    Serial.begin(9600);
    while (!Serial);

    //Call .beginCore() to configure the IMU
    if (myIMU.beginCore() != 0) {
        Serial.print("\nDevice Error.\n");
    } else {
        Serial.print("\nDevice OK.\n");
    }

    uint8_t dataToWrite = 0;  //Temporary variable

    //Setup the accelerometer******************************
    dataToWrite = 0; //Start Fresh!
//    dataToWrite |= LSM6DS3_ACC_GYRO_BW_XL_50Hz;
//    dataToWrite |= LSM6DS3_ACC_GYRO_FS_XL_8g;
    dataToWrite |= LSM6DS3_ACC_GYRO_ODR_XL_13Hz;

    //Now, write the patched together data
    errorsAndWarnings += myIMU.writeRegister(LSM6DS3_ACC_GYRO_CTRL1_XL, dataToWrite);

    //Set the ODR bit
    //errorsAndWarnings += myIMU.readRegister(&dataToWrite, LSM6DS3_ACC_GYRO_CTRL4_C);
    //dataToWrite &= ~((uint8_t)LSM6DS3_ACC_GYRO_BW_SCAL_ODR_ENABLED);
//    myIMU.writeRegister(LSM6DS3_ACC_GYRO_INT1_CTRL, LSM6DS3_ACC_GYRO_INT1_DRDY_XL_ENABLED|LSM6DS3_ACC_GYRO_INT1_DRDY_G_ENABLED);
    errorsAndWarnings += myIMU.writeRegister(LSM6DS3_ACC_GYRO_INT1_CTRL, LSM6DS3_ACC_GYRO_INT1_DRDY_XL_ENABLED);

    Serial.println("Attaching IMU interrupt ...");
    pinMode(int1Pin, INPUT);
    attachInterrupt(digitalPinToInterrupt(int1Pin), interruptService, RISING);
 //   interrupts();
}

void loop() {
    //Get all parameters
    Serial.print("\nAccelerometer Counts:\n");
    Serial.println(digitalRead(int1Pin));
/*    //Acelerometer axis X
    if (myIMU.readRegisterInt16(&gyroX, LSM6DS3_ACC_GYRO_OUTX_L_XL) != 0) {
        errorsAndWarnings++;
    }
    */
    Serial.print(" X = ");
    Serial.println(gyroX);

    //Acelerometer axis Y
    Serial.print(" Y = ");
    Serial.println(gyroY);

    //Acelerometer axis Z
    Serial.print(" Z = ");
    Serial.println(gyroZ);

    Serial.println();
    Serial.print("Total reported Errors and Warnings: ");
    Serial.println(errorsAndWarnings);

    delay(1000);
//    while(1);
}

void interruptService () {
//    digitalWrite(LED_BUILTIN, HIGH);
//    detachInterrupt(digitalPinToInterrupt(int1Pin));
    //Acelerometer axis X
    if (myIMU.readRegisterInt16(&gyroX, LSM6DS3_ACC_GYRO_OUTX_L_XL) != 0) {
        errorsAndWarnings++;
    }

    //Acelerometer axis Y
    if (myIMU.readRegisterInt16(&gyroY, LSM6DS3_ACC_GYRO_OUTY_L_XL) != 0) {
        errorsAndWarnings++;
    }

    //Acelerometer axis Z
    if (myIMU.readRegisterInt16(&gyroZ, LSM6DS3_ACC_GYRO_OUTZ_L_XL) != 0) {
        errorsAndWarnings++;
    }
}```

I too had some understanding to gain, I use this and it helped,
I’m thinking the interrupt isn’t getting the right configuration or the sensitivity is too LOW.
The built in LED wasn’t defined?

/*****************************************************************************/
// IMU Interrupt Example for XIAO BLE Sense
// This example shows how to configure LMSD6S3TR-C on XIAO BLE SENSE to interrupt
// on INT1 after a "Double Tap" was recognized.
// Additionally, the device goes into System OFF state, after 5 interrupts were
// received. Another "Double Tap" will wake up the device again.
// 
// Original by chuck
//poatched and toasted by pjg
/*******************************************************************************/

#include "LSM6DS3.h"
#include "Wire.h"
#include <U8x8lib.h>
LSM6DS3 myIMU(I2C_MODE, 0x6A); // IMU 
#define int1Pin PIN_LSM6DS3TR_C_INT1

U8X8_SSD1306_128X64_NONAME_HW_I2C u8x8(/* clock=*/ PIN_WIRE_SCL, /* data=*/ PIN_WIRE_SDA, /* reset=*/ U8X8_PIN_NONE);
 // OLEDs without Reset of the Display

const int buttonPin = 1;     // the number of the pushbutton pin
int buttonState = 0;         // variable for reading the pushbutton status
int BuzzerPin = A3;
uint8_t interruptCount = 0; // Amount of received interrupts
uint8_t prevInterruptCount = 0; // Interrupt Counter from last loop


void setup() {
    Serial.begin(9600);
	  delay(1000); //relax...
	  Serial.println("Processor came out of reset.\n");
    u8x8.begin();
    u8x8.setFlipMode(1);   // set number from 1 to 3, the screen word will rotary 180
    
    pinMode(LED_BUILTIN, OUTPUT);// initialize the LED pin as an output:
    pinMode(buttonPin, INPUT_PULLUP);// initialize the pushbutton pin as an input:
    pinMode(BuzzerPin, OUTPUT);
    pinMode(LEDR, OUTPUT);
    pinMode(LEDG, OUTPUT);
    pinMode(LEDB, OUTPUT);
    setLedRGB(false, false, true); // set blue led

    myIMU.settings.gyroEnabled = 0; // Gyro currently not used, disabled to save power 
    if (myIMU.begin() != 0) {
        Serial.println("IMU error");
    } else {
        Serial.println("IMU OK!");
    }
    
    setupDoubleTapInterrupt();
    
    pinMode(int1Pin, INPUT);
    attachInterrupt(digitalPinToInterrupt(int1Pin), int1ISR, RISING);
    u8x8.setFont(u8x8_font_8x13B_1x2_r);
    u8x8.clearDisplay();
    u8x8.setCursor(4, 0);
    u8x8.print("TAP");
    u8x8.setCursor(5, 3);
    u8x8.print("&");
    u8x8.setCursor(1, 6);
    u8x8.print("Activity-");
    u8x8.print("Demo");
    
    
  
}

void loop() {
    setLedRGB(false, false, true); // reset led to blue only
    u8x8.setCursor(0, 2);
    u8x8.print(interruptCount);
    Serial.print("\Iterrupt Counter: ");
    Serial.println(interruptCount);

    // if interrupt was received in this cycle
    if (interruptCount > prevInterruptCount) {
      Serial.println("\Interrupt received!");
      setLedRGB(false, true, false); // set green only
    }
    
    prevInterruptCount = interruptCount;
    
    if (interruptCount >= 5) {
      // Trigger System OFF after 5 interrupts
      goToPowerOff();
    }
    
    delay(500);
}


// -------------------- System ------------------------- //

void goToPowerOff() {
  setLedRGB(false, false, false);
  Serial.println("Going to System OFF");
  u8x8.clearDisplay();
  u8x8.setCursor(0, 3);
  u8x8.print("SLEEP");
  setupDoubleTapInterrupt(); // not needed here, if already applied..
  delay(1000); // delay seems important to apply settings, before going to System OFF
  //Ensure interrupt pin from IMU is set to wake up device
  nrf_gpio_cfg_sense_input(digitalPinToInterrupt(int1Pin), NRF_GPIO_PIN_PULLDOWN, NRF_GPIO_PIN_SENSE_HIGH);
  // Trigger System OFF
  NRF_POWER->SYSTEMOFF = 1;
}

// -------------------- Interrupts ------------------------- //

void setupDoubleTapInterrupt() {
  uint8_t error = 0;
  uint8_t dataToWrite = 0;

  // Double Tap Config
  myIMU.writeRegister(LSM6DS3_ACC_GYRO_CTRL1_XL, 0x60); //* Acc = 416Hz (High-Performance mode)// Turn on the accelerometer
  // ODR_XL = 416 Hz, FS_XL = 2g
  myIMU.writeRegister(LSM6DS3_ACC_GYRO_TAP_CFG1, 0x8E);// INTERRUPTS_ENABLE, SLOPE_FDS// Enable interrupts and tap detection on X, Y, Z-axis
  myIMU.writeRegister(LSM6DS3_ACC_GYRO_TAP_THS_6D, 0x85);// Set tap threshold 8C
  myIMU.writeRegister(LSM6DS3_ACC_GYRO_INT_DUR2, 0x7F);// Set Duration, Quiet and Shock time windows 7F
  myIMU.writeRegister(LSM6DS3_ACC_GYRO_WAKE_UP_THS, 0x80);// Single & double-tap enabled (SINGLE_DOUBLE_TAP = 1)
  myIMU.writeRegister(LSM6DS3_ACC_GYRO_MD1_CFG, 0x08);// Double-tap interrupt driven to INT1 pin
}

void int1ISR()
{
  interruptCount++;
   
}

// -------------------- Utilities ------------------------- //

void setLedRGB(bool red, bool green, bool blue) {
  if (!blue) { digitalWrite(LEDB, HIGH); } else { digitalWrite(LEDB, LOW); }
  if (!green) { digitalWrite(LEDG, HIGH); } else { digitalWrite(LEDG, LOW); }
  if (!red) { digitalWrite(LEDR, HIGH); } else { digitalWrite(LEDR, LOW); }
}

prints the interrupt to the console as well as to display on the dev board. I posted a video of it in action for the sleep feature. using interrupts. workd GREAT!
HTH
GL :slight_smile:

Thanks. I looked at this code before and tried bits of it in one of my solutions. I also ran it more or less wholesale with modifications to remove the display and shutdown procedures. It works.

However, the difference between this use case and my case that in my case the interrupt could fire repeatedly and perhaps rather rapidly whereas in your case it takes a relatively long amount of time between the interrupts.

The module actually appears to be stuck so that the loop executes only once, similarly to what’s described here: I2C Interaction in ISR with 33 BLE and Nano Connect · Issue #301 · arduino/ArduinoCore-mbed · GitHub

Also your code “without” Wire.h might yield different results?
GL
:slight_smile:

Took out the Wire.h which didn’t change anything.

The problem appears to be “myIMU.readRegisterInt16”. Perhaps it isn’t thread safe, etc. or there is a conflict between interrupts and i2c used to access the IMU, eg. interrupts used by i2c may be turned off while in the ISR.

I guess I’ll have to try going medieval, i.e. relatively low level for the ISR. That’s something I was going to do anyway for performance after knocking out a proof of concept at a high level. If I knew a little more about the embed lib used, maybe I’d look under the hood. myIMU doesn’t work for me at all in the non-embed framework. The

The IMU function will perform better when we use the “Seeed nrf52 mbed-enabled Boards Library”

statement in the Wiki is a euphemism.

It’s difficult at best to use i2c in ISRs. No SPI for the IMU on the Xiao, which although not without its own obstacles, would simplify low level access, not to mention make it faster. :anguished:

Oh well, I guess I’ll use a loop for the time being as a POC and then reimplement on a different board with SPI.

Using semaphores may be a viable solution/workaround: i2c with isr · Issue #2894 · espressif/arduino-esp32 · GitHub