XIAO nRF52840 does not sleep after writing to flash

I am using an nRF52840 with SX1262 as a remote sensor. It achieves approximately 20 μA in normal use. I added a boot counter using the Adafruit LittleFS and InternalFileSystem libraries and can read and write files to the flash without problem. However, if I write to or delete a file, I can no longer put the nRF52840 to sleep: its current consumption rises to 660 μA and the red LED turns on. I assume something to do with flash is generating interrupts following a write.

Any ideas on how to debug this and shutdown the interrupts?

Hi there,

SO , looks like you’re close,
Red LED state: The red LED turning on is a massive clue. Check your board definition or pin mappings (LED_BUILTIN or the red user LED on the XIAO nRF52840). Is the filesystem library or boot counter inadvertently toggling or holding a GPIO pin high, or is a peripheral error state triggering an error handler that drives the LED and blocks sleep?

Recommended Next Steps for Debugging:

  1. Isolate the write: Write a minimal test sketch that boots, mounts LittleFS, performs one write, closes the file, and immediately enters a deep sleep state without running any BLE or sensor code. Check if the current still spikes to 660 µA. If it does, it confirms the LittleFS/InternalFS driver on the nRF52840 is leaving a clock or peripheral enabled.

HTH
GL :slight_smile: PJ :v:

Here is some code that demonstrates the problem. On a bare board with no external peripherals or SX1262 connected, I measure 7.5 μA when sleeping, but jumps to 650 μA following writing a byte to flash.

#include <Arduino.h>
#include <Adafruit_SPIFlash.h> 
#include <RadioLib.h>
#include <InternalFileSystem.h>
#include <Adafruit_LittleFS.h>

const unsigned long kSerialSpeed = 115200;

/// other pin connections
const u_int32_t kPinBatEnable = 14;     // Send LOW to enable voltage divider for battery monitor
const u_int32_t kPinBatADC = PIN_VBAT;  // ADC pin connected to XIAO on-board battery voltage divider

const char kPathTest[] = "/testfile";       // LittleFS path to store sensor id

using namespace Adafruit_LittleFS_Namespace;

Adafruit_FlashTransport_QSPI flashTransport;

// task handle for direct to task notifications
// - faster than a semamphore
static TaskHandle_t xTaskToNotify = NULL;

bool ReadWriteFlash() {

  if (!InternalFS.begin()) {
    // unable to initialise InternalFS
    return false;
  }

  uint8_t test_data[]{9};

  Adafruit_LittleFS_Namespace::File file(InternalFS);

  // open file for reading
  file.open(kPathTest, FILE_O_READ);
  // if file exists, read 1st byte and close
  if (file) { // file exists
    file.read((uint8_t*) &test_data, 1);
    file.close();
  }
  
  /*
  // write byte to file
  file.open(kPathTest, FILE_O_WRITE);
  if (file) {
    file.seek(0);
    file.write((uint8_t*) &test_data, 1);
    file.close();
  }
  */
  
  InternalFS.end();
  
  return true;
}

/**
 * Power down the flash memory.
 * (Saves approximately 10 µA.)
 */
void PowerDownFlash(void) {
  flashTransport.begin();
  flashTransport.runCommand(0xB9); 
  delayMicroseconds(5);
  flashTransport.end();  
}

/**
 * Activate battery voltage measurement by connecting one end of the voltage divider to ground.
 * NB although this increases total current consumption by several µA, this is necessary to
 * avoid over-voltage of the ADC by the connected battery.
 */
void EnableVoltageDivider(void) {
  pinMode(kPinBatEnable, OUTPUT);
  digitalWrite(kPinBatEnable, LOW);
}

void setup() {
  // necessary to prevent over-voltage of ADC pin
  // - increases quiescent current by Vbat / 1.5E6
  // - for 4.5 V supply : 3 microamps
  EnableVoltageDivider();

  // test read/write to flash storage
  ReadWriteFlash();

  // Use DC-DC converter to save power - best improvement is on high duty cycle
  NRF_POWER->DCDCEN = 1;   

  // saves a few microamps
  PowerDownFlash();

  xTaskToNotify = xTaskGetCurrentTaskHandle();

  while(true) {
    // istop here and go to sleep until woken by interrupt
    ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
  }
}

void loop() {
  // never gets here
}

I’m getting closer. Doesn’t look like interrupts, might just be a GPIO pin left high. Will report back when I have solved it.