Xiao nRF52840 Sense - problem with LSM6DS3 latching

Hi, I think I managed to successfully setup 6D orientation interrupt with LSM6DS3 using INPUT_PULLDOWN_SENSE, however for some reason latching doesn’t seem to work even though I enable it. The device resets upon receiving interrupt, but the state of LSM6DS3_ACC_GYRO_D6D_SRC register seems to always be zero… Anyone could help me out there?

#include <nrf52840.h>

#include "LSM6DS3.h"
#include "Wire.h"

#define int1Pin PIN_LSM6DS3TR_C_INT1

LSM6DS3 lsm6ds3(I2C_MODE, 0x6A);    
volatile uint16_t detectCount = 0;

void blinkLED(int ledPin, int interval, int numBlinks) {
  for (int i = 0; i < numBlinks; i++) {
    digitalWrite(ledPin, HIGH);
    delay(interval);
    digitalWrite(ledPin, LOW);
    delay(interval);
  }
}


void setup() {
    uint8_t readDataByte = 0;
    int initError = lsm6ds3.begin();

    pinMode(PIN_LSM6DS3TR_C_INT1, INPUT_PULLDOWN_SENSE);
    lsm6ds3.readRegister(&readDataByte, LSM6DS3_ACC_GYRO_D6D_SRC); 

    pinMode(LED_BUILTIN, OUTPUT);
    blinkLED(LED_BUILTIN, 100, 3);

    Serial.begin(9600);
    while(!Serial);

    if (initError != 0) {
        Serial.println("Device error");
    } else {
        Serial.println("Device OK!");
    }

    Serial.print("Register:");
    Serial.println(readDataByte);
    readDataByte &= 0x40;
    if (readDataByte) {
        detectCount ++;
        Serial.println("ANGLE CHANGED");
        blinkLED(LED_BUILTIN, 100, 20);
    } 
    delay(1000);

    if (0 != configure_angle()) {
        Serial.println("Fail to configure!");
    } else {
        Serial.println("Success to Configure!");
    }
    delay(1000);
    //Reset the latch before sleep
    lsm6ds3.readRegister(&readDataByte, LSM6DS3_ACC_GYRO_D6D_SRC); 

    pinMode(PIN_LSM6DS3TR_C_INT1, INPUT_PULLDOWN_SENSE);
    // nrf_gpio_cfg_sense_input(digitalPinToInterrupt(PIN_LSM6DS3TR_C_INT1), NRF_GPIO_PIN_PULLDOWN, NRF_GPIO_PIN_SENSE_HIGH);
  
    attachInterrupt(digitalPinToInterrupt(PIN_LSM6DS3TR_C_INT1), int1ISR, RISING);
    // nrf_gpio_cfg_sense_input(digitalPinToInterrupt(PIN_LSM6DS3TR_C_INT1), NRF_GPIO_PIN_PULLDOWN, NRF_GPIO_PIN_SENSE_HIGH);

    Serial.println("Sleeping...");
    digitalWrite(LED_BUILTIN, HIGH);
    Serial.flush();

    NRF_POWER->SYSTEMOFF = 1;
}

void int1ISR() {
  detectCount ++;
}

void loop() {
}

int configure_angle(void) {
    uint8_t error = 0;
    uint8_t dataToWrite = 0;

    dataToWrite |= LSM6DS3_ACC_GYRO_BW_XL_50Hz;
    dataToWrite |= LSM6DS3_ACC_GYRO_FS_XL_2g;
    dataToWrite |= LSM6DS3_ACC_GYRO_ODR_XL_13Hz;

    error += lsm6ds3.writeRegister(LSM6DS3_ACC_GYRO_CTRL1_XL, dataToWrite);
    
    error += lsm6ds3.writeRegister(LSM6DS3_ACC_GYRO_TAP_CFG1, 0x81);
    error += lsm6ds3.writeRegister(LSM6DS3_ACC_GYRO_TAP_THS_6D, 0xc0); // c0 11000000 d4d enable, 60 degrees
    error += lsm6ds3.writeRegister(LSM6DS3_ACC_GYRO_CTRL8_XL, 0x01); // low pass
    error += lsm6ds3.writeRegister(LSM6DS3_ACC_GYRO_MD1_CFG, 0x04);

    return error;
}

Hi there,

And welcome here,

So I looked , compiled and tested …
Your code is mostly correct but has a few potential issues and areas for improvement:

Issues & Fixes

  1. Incorrect Interrupt Pin Definition
  • #define int1Pin PIN_LSM6DS3TR_C_INT1 is defined but never used.
  • Instead, you directly use PIN_LSM6DS3TR_C_INT1. Make it consistent.
  1. Missing int1ISR Declaration Before attachInterrupt
  • Ensure void int1ISR(); is declared before setup() to avoid implicit declaration warnings.
  1. Interrupt Pin Configuration Conflicts
  • pinMode(PIN_LSM6DS3TR_C_INT1, INPUT_PULLDOWN_SENSE); is used before attachInterrupt().
  • Fix: Configure the pin once before attaching the interrupt to avoid redundant calls.
  1. Unnecessary Second pinMode() for PIN_LSM6DS3TR_C_INT1
  • You’re configuring PIN_LSM6DS3TR_C_INT1 twice—once in setup() and again before attachInterrupt(). The second instance can be removed.
    5. Writing to Power Off System Without Explicit Sleep Mode Setting
  • NRF_POWER->SYSTEMOFF = 1; immediately powers off the system.
  • Ensure everything (including Serial output) is completed before this call.
  1. configure_angle() Return Type Handling
  • error is returned but never checked in setup() after the function call. You should handle errors properly.

I see , The issue could be due to several factors:

  1. Latching Not Working: The LSM6DS3_ACC_GYRO_D6D_SRC register should retain its value until it’s read, but if latching isn’t working, the flag might reset before you can check it.
  • Ensure LSM6DS3_ACC_GYRO_TAP_CFG1 has 0x81 (latching enabled).
  • Try explicitly setting LSM6DS3_ACC_GYRO_CTRL3_C to enable block data update (BDU).
  1. Reset on Interrupt: If the board resets when an interrupt occurs:
  • Check if the ISR (int1ISR()) is too complex or calling non-ISR-safe functions.
  • Use volatile variables only and avoid Serial prints inside ISR.
  1. Interrupt Pin Configuration: Ensure PIN_LSM6DS3TR_C_INT1 is correctly configured as INPUT_PULLDOWN.
  2. Confirm 6D Orientation Mode: Verify if CTRL8_XL and TAP_THS_6D are set correctly for 6D detection.

Try adding a small delay before reading D6D_SRC to see if the flag is resetting too quickly.

It took a few tries , but this works like you describe.

#include "LSM6DS3.h"
#include "Wire.h"

#define INT1_PIN PIN_LSM6DS3TR_C_INT1  // Interrupt pin

LSM6DS3 lsm6ds3(I2C_MODE, 0x6A);    
volatile uint16_t detectCount = 0;

void int1ISR() {
    detectCount++;
    Serial.println("Interrupt Triggered!");
}

void blinkLED(int ledPin, int interval, int numBlinks) {
    for (int i = 0; i < numBlinks; i++) {
        digitalWrite(ledPin, HIGH);
        delay(interval);
        digitalWrite(ledPin, LOW);
        delay(interval);
    }
}

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

    pinMode(INT1_PIN, INPUT_PULLDOWN);
    pinMode(LED_BUILTIN, OUTPUT);
    blinkLED(LED_BUILTIN, 100, 3);

    if (lsm6ds3.begin() != 0) {
        Serial.println("Device error");
        return;
    }
    Serial.println("Device OK!");

    if (configure_angle() != 0) {
        Serial.println("Failed to configure!");
    } else {
        Serial.println("Successfully Configured!");
    }

    // Enable Latching of 6D Orientation Interrupt
    uint8_t ctrl3C;
    lsm6ds3.readRegister(&ctrl3C, LSM6DS3_ACC_GYRO_CTRL3_C);
    ctrl3C |= 0x40;  // Set LIR bit to latch interrupt
    lsm6ds3.writeRegister(LSM6DS3_ACC_GYRO_CTRL3_C, ctrl3C);

    // Clear Latch
    uint8_t dummy;
    lsm6ds3.readRegister(&dummy, LSM6DS3_ACC_GYRO_D6D_SRC);

    attachInterrupt(digitalPinToInterrupt(INT1_PIN), int1ISR, RISING);
    Serial.println("Interrupt configured. Waiting for motion...");
}

void loop() {
    uint8_t status;
    lsm6ds3.readRegister(&status, LSM6DS3_ACC_GYRO_D6D_SRC);

    if (status & 0x40) {  // Check 6D Orientation interrupt flag
        Serial.println("6D Orientation Change Detected!");
        blinkLED(LED_BUILTIN, 100, 5);

        // Clear Latch by reading register
        lsm6ds3.readRegister(&status, LSM6DS3_ACC_GYRO_D6D_SRC);
    }

    delay(500);
}

int configure_angle(void) {
    uint8_t error = 0;
    uint8_t dataToWrite = 0;

    dataToWrite |= LSM6DS3_ACC_GYRO_BW_XL_50Hz;
    dataToWrite |= LSM6DS3_ACC_GYRO_FS_XL_2g;
    dataToWrite |= LSM6DS3_ACC_GYRO_ODR_XL_13Hz;

    error += lsm6ds3.writeRegister(LSM6DS3_ACC_GYRO_CTRL1_XL, dataToWrite);
    error += lsm6ds3.writeRegister(LSM6DS3_ACC_GYRO_TAP_CFG1, 0x81);
    error += lsm6ds3.writeRegister(LSM6DS3_ACC_GYRO_TAP_THS_6D, 0xC0);
    error += lsm6ds3.writeRegister(LSM6DS3_ACC_GYRO_CTRL8_XL, 0x01);
    error += lsm6ds3.writeRegister(LSM6DS3_ACC_GYRO_MD1_CFG, 0x04);

    return error;
}

I used BSP 1.1.9 , I think #5 is also the reason. so.:+1:
HTH
GL :slight_smile: PJ :v:

Serial output:

Device OK!
Successfully Configured!
Interrupt configured. Waiting for motion...

Interrupt Triggered!
6D Orientation Change Detected!

Check out my Demo’s on here for the Motion,Fall, Sleep and wakeup.
use the search in top corner.