Help With SAMD51 Sleep With I2S/DMA

Hey guys,

I’m having a hell of a time trying to get a wav-file playing working with a sleep library. Even declaring the AudioConnection below crashes the sleep library, I assume somehow triggering an interrupt that sets off the _WFI() in the SleepyDog library that won’t let it sleep. How can I get around this? Anyone know how to make sure nothing else will interrupt? Thank you guys!

// Minimal Crashing Example
// Sleep crashes after a certain (seemingly random) number of cycles

#include <Adafruit_SleepyDog.h>
#include <play_fs_wav.h>
#include <Audio.h>

AudioPlayFSWav           playFSWav;
AudioOutputI2S           audioOutput;
AudioConnection          patchCord1(playFSWav, 0, audioOutput, 1);
AudioConnection          patchCord2(playFSWav, 1, audioOutput, 0);

 
void setup() {
  pinMode(LED_BUILTIN, OUTPUT);
  digitalWrite(LED_BUILTIN, HIGH); // Show we're awake
  Serial.begin(115200);
  while(!Serial); // wait for Arduino Serial Monitor (native USB boards)
  Serial.println("Crash Demo!");
  Serial.println();
}

//watch the regular pattern change after a certain number of cycles (sleep drops out)
void loop() {
  Serial.println("Going to sleep in one second...");
  Watchdog.sleep(64); // Sleep for 64 ms, this drops out after a while
  digitalWrite(LED_BUILTIN, LOW); // Show we're asleep
  Watchdog.sleep(64);  // Sleep for 64 ms, this drops out after a while
  digitalWrite(LED_BUILTIN, HIGH); // Show we're awake again
}

Hi Casey10110,

I presume you're using Paul Stoffregen's Audio library? It's a complex piece of code, but have you tried using the library's macros: AudioNoInterrupts() and AudioInterrupts(), in order to disable the interrupts prior to putting the processor to sleep, and enabling them afterwards?

...
AudioNoInterrupts();
Watchdog.sleep(64);
AudioInterrupts();
...

Update this appears to work:

// Minimal Crashing Example
// Sleep crashes after a certain (seemingly random) number of cycles

#include <Adafruit_SleepyDog.h>
#include <play_fs_wav.h>
#include <Audio.h>

AudioPlayFSWav playFSWav;
AudioOutputI2S audioOutput;
AudioConnection patchCord1(playFSWav, 0, audioOutput, 1);
AudioConnection patchCord2(playFSWav, 1, audioOutput, 0);

void setup() {
pinMode(LED_BUILTIN, OUTPUT);
digitalWrite(LED_BUILTIN, HIGH); // Show we’re awake
Serial.begin(115200);
while(!Serial); // wait for Arduino Serial Monitor (native USB boards)
Serial.println(“Crash Demo!”);
Serial.println();
}

//watch the regular pattern change after a certain number of cycles (sleep drops out)
void loop() {
Serial.println(“Going to sleep in one second…”);
AudioNoInterrupts();
DMAC->Channel[0].CHCTRLA.bit.ENABLE = 0;
Watchdog.sleep(64); // Sleep for 64 ms, this drops out after a while
OSCCTRL->DFLLVAL.reg = OSCCTRL->DFLLVAL.reg;
while ( OSCCTRL->DFLLSYNC.bit.DFLLVAL );
DMAC->Channel[0].CHCTRLA.bit.ENABLE = 1;
AudioInterrupts();
digitalWrite(LED_BUILTIN, LOW); // Show we’re asleep
AudioNoInterrupts();
DMAC->Channel[0].CHCTRLA.bit.ENABLE = 0;
Watchdog.sleep(64); // Sleep for 64 ms, this drops out after a while
OSCCTRL->DFLLVAL.reg = OSCCTRL->DFLLVAL.reg;
while ( OSCCTRL->DFLLSYNC.bit.DFLLVAL );
DMAC->Channel[0].CHCTRLA.bit.ENABLE = 1;
AudioInterrupts();
digitalWrite(LED_BUILTIN, HIGH); // Show we’re awake again
}

Hi Casey10110,

I was wondering why you were writing the DFLLVAL register to itself:

OSCCTRL->DFLLVAL.reg = OSCCTRL->DFLLVAL.reg;
while ( OSCCTRL->DFLLSYNC.bit.DFLLVAL );

Now I see that it's mentioned in the SAMD51 errata sheet and is necessary to kickstart the 48MHz DFLL (Digital Frequency Locked Loop) at the correct frequency. Thanks.

Yes, Martin, that took me a very long time to figure out. I was getting crashes whenever using Adafruit's SPIFlash library and sleep together and that solves it (was actually a separate issue from that described above). Strange looking piece of code to see something equal itself, ha ha ...