heidi02
October 26, 2025, 11:13am
1
I can play .wav files on my nrf52840 via I2S. It works well without problems.
I'm having trouble detecting the TXPTRUPD-event via interrupt.
As soon as the interrupts are enabled, the board immediately freezes completely.
Can anyone give me a hint as to where I'm going wrong?
#include <nrf.h>
#include <bluefruit.h>
#define PIN_MCK 11
#define PIN_SCK 20 //
#define PIN_LRCK 22 // sw
#define PIN_SDOUT 24 // ws
byte tx_array[100000];
int array_size = 8000;
void setup() {
pinMode(15, OUTPUT);
digitalWrite(15, LOW);
Serial.begin(115200);
while (!Serial) { delay(10); }
Serial.println("Hello!");
delay(500);
// === Setup NRF52840 ===
// Enable transmission
NRF_I2S->CONFIG.TXEN = (I2S_CONFIG_TXEN_TXEN_ENABLE << I2S_CONFIG_TXEN_TXEN_Pos);
// Enable MCK generator
NRF_I2S->CONFIG.MCKEN = (I2S_CONFIG_MCKEN_MCKEN_ENABLE << I2S_CONFIG_MCKEN_MCKEN_Pos);
// Master clock frequency
NRF_I2S->CONFIG.MCKFREQ = I2S_CONFIG_MCKFREQ_MCKFREQ_32MDIV63 << I2S_CONFIG_MCKFREQ_MCKFREQ_Pos; //0.5076365 MHz
// LRCK Ratio
NRF_I2S->CONFIG.RATIO = I2S_CONFIG_RATIO_RATIO_64X << I2S_CONFIG_RATIO_RATIO_Pos; //LRCK = MCK/64 = 45.45 kHz or 8 kHz
// Master mode, 16Bit, left aligned
NRF_I2S->CONFIG.MODE = I2S_CONFIG_MODE_MODE_MASTER << I2S_CONFIG_MODE_MODE_Pos;
NRF_I2S->CONFIG.SWIDTH = I2S_CONFIG_SWIDTH_SWIDTH_16BIT << I2S_CONFIG_SWIDTH_SWIDTH_Pos; //this defines BCLK as LRCLK * 32
NRF_I2S->CONFIG.ALIGN = I2S_CONFIG_ALIGN_ALIGN_LEFT << I2S_CONFIG_ALIGN_ALIGN_Pos;
// Format = I2S
NRF_I2S->CONFIG.FORMAT = I2S_CONFIG_FORMAT_FORMAT_I2S << I2S_CONFIG_FORMAT_FORMAT_Pos;
// Use mono left
NRF_I2S->CONFIG.CHANNELS = I2S_CONFIG_CHANNELS_CHANNELS_Left << I2S_CONFIG_CHANNELS_CHANNELS_Pos;
// Configure pins
NRF_I2S->PSEL.MCK = (PIN_MCK << I2S_PSEL_MCK_PIN_Pos);
NRF_I2S->PSEL.SCK = (PIN_SCK << I2S_PSEL_SCK_PIN_Pos);
NRF_I2S->PSEL.LRCK = (PIN_LRCK << I2S_PSEL_LRCK_PIN_Pos);
NRF_I2S->PSEL.SDOUT = (PIN_SDOUT << I2S_PSEL_SDOUT_PIN_Pos);
NRF_I2S->TXD.PTR = (uint32_t)&tx_array[0];
NRF_I2S->RXTXD.MAXCNT = array_size / sizeof(uint32_t);
/**************************************************************************/
// This is the problematic part
// As soon as these lines are active, the board freezes completely.
NRF_I2S->INTENSET = I2S_INTEN_TXPTRUPD_Enabled << I2S_INTEN_TXPTRUPD_Pos;
NVIC_EnableIRQ(I2S_IRQn);
/**************************************************************************/
NRF_I2S->ENABLE = 1;
// Start transmitting I2S data
NRF_I2S->TASKS_START = 1;
}
void loop() {
digitalWrite(15, !digitalRead(15));
delay(250);
}
extern "C" void I2S_IRQHandler(void) {
if (NRF_I2S->EVENTS_TXPTRUPD == 1) {
NRF_I2S->EVENTS_TXPTRUPD = 0;
NRF_I2S->TASKS_STOP = 1;
digitalWrite(15, !digitalRead(15));
Serial.println("IRQ!");
}
}
Thanks!
.…
While it's not directly comparable, there's a problem here with the same effect.
It crashes as soon as interrupts are activated. Is this perhaps a general interrupt problem?
https://forum.arduino.cc/t/board-crashed-on-upload-gpiote-irqhandler-v/935537
heidi02:
Serial.println("IRQ!");
NEVER print from within an ISR, nor call any other interrupt-dependent function.
Instead, set a global flag declared volatile, and exit immediately. The main code checks and handles the flag.
Of course you're right! My Faux pas.
I would be happy if that were my problem. The program crashes way ahead.
I've really tried many different variations.
The problem doesn't only occur when sending I2S data. If I activate instead the reception of I2S data and the corresponding interrupt instead, the same crash occurs.
NRF_I2S->CONFIG.RXEN = (I2S_CONFIG_RXEN_RXEN_ENABLE << I2S_CONFIG_RXEN_RXEN_Pos);
NRF_I2S->INTENSET = I2S_INTEN_RXPTRUPD_Enabled << I2S_INTEN_RXPTRUPD_Pos;
NVIC_EnableIRQ(I2S_IRQn);
Maybe @TMRh20 would like to say a few words about that. The interrupts are also disabled in his AAA library.
There is not enough information in your post to guess what the real problem might be.
TMRh20
October 26, 2025, 6:04pm
5
I’ve not actually attempted to use interrupts with my AAAudio Library.
However the following code works:
#include <AutoAnalogAudio.h>
AutoAnalog aaAudio;
#if defined(USE_TINYUSB)
// Needed for Serial.print on non-MBED enabled or adafruit-based nRF52 cores
#include "Adafruit_TinyUSB.h"
#endif
#define bufferSize 320
float volumeControl = 0.2;
/*********************************************************/
/* Tested with MAX98357A I2S breakout on XIAO 52840 Sense
/* BCLK connected to Arduino D1 (p0.03)
/* LRCK connected to Arduino D3 (p0.29)
/* DIN connected to Arduino D5 (p0.05)
/* SD connected to Arduino D6
/*********************************************************/
/* Tested with INMP441 I2S breakout on XIAO 52840 Sense
/* SCK connected to Arduino D1 (p0.03)
/* WS connected to Arduino D3 (p0.29)
/* SD connected to Arduino D4 (p0.04)
*/
volatile bool testIRQ = 0;
extern "C" {
__attribute__((__used__)) void I2S_IRQHandler(void)
{
testIRQ = 1;
NRF_I2S->EVENTS_TXPTRUPD = 0;
}
}
void setup() {
Serial.begin(115200);
aaAudio.maxBufferSize = bufferSize;
aaAudio.begin(2,2); //Setup aaAudio using I2s for Mic and Output
aaAudio.adcBitsPerSample = 24; // 24-bit samples input from I2S
aaAudio.dacBitsPerSample = 24; // 24-bit samples output to I2S
aaAudio.setSampleRate(44000, 0); // 44kHz Mono
pinMode(6, OUTPUT); //Connected to SD pin of MAX98357A
digitalWrite(6, HIGH);
NRF_I2S->INTENSET = I2S_INTEN_TXPTRUPD_Enabled << I2S_INTEN_TXPTRUPD_Pos;
NVIC_EnableIRQ(I2S_IRQn);
}
void loop() {
if(testIRQ == 1){
testIRQ = 0;
Serial.println("IRQ");
}
if (Serial.available()) {
char c = Serial.read();
if (c == '=') {
volumeControl += 0.1;
} else if (c == '-') {
volumeControl -= 0.1;
volumeControl = max(0.0, volumeControl);
}
Serial.println(volumeControl);
}
// For 24 bit samples
if (aaAudio.dacBitsPerSample == 24) {
aaAudio.getADC(bufferSize / 2); // Get data from I2S microphone
memcpy(aaAudio.dacBuffer16, aaAudio.adcBuffer16, bufferSize * 2); // Copy data into output buffer from input buffer
for (int i = 0; i < bufferSize; i += 2) {
int32_t test;
memcpy(&test, &aaAudio.dacBuffer16[i], 4);
test *= volumeControl;
memcpy(&aaAudio.dacBuffer16[i], &test, 4);
}
aaAudio.feedDAC(0, bufferSize / 2); // Feed the data to I2S output
// For 16-bit samples
} else {
aaAudio.getADC(bufferSize);
memcpy(aaAudio.dacBuffer16, aaAudio.adcBuffer16, bufferSize * 2);
for (int i = 0; i < bufferSize; i++) {
int16_t test = aaAudio.dacBuffer16[i];
test *= volumeControl;
aaAudio.dacBuffer16[i] = test;
}
aaAudio.feedDAC(0, bufferSize); // Feed the data to I2S output
}
}
I think its just a problem with not clearing the interrupt in the main IRQ function or not declaring it correctly:
volatile bool testIRQ = 0;
extern "C" {
__attribute__((__used__)) void I2S_IRQHandler(void)
{
testIRQ = 1;
NRF_I2S->EVENTS_TXPTRUPD = 0;
}
}
TMRh20's program worked flawlessly right away.
The most noticeable difference between TMRh20's program and mine was the different buffer size. So I set the buffer size in TMRh20's program to 10,000 and it also crashed.
Ah, the buffer size!
So I reduced the buffer size (the tx_array in my program) in my program and it worked too.
Now comes the strange part.
I gradually increased the buffer size in my program again to find the maximum possible value. But now it also works with the old value.
I can't find an explanation for this so far. But I will continue to try to get to the bottom of the problem. Any suggestions?
Thank you very much for your help and your efforts!!
PS: I chose such a large value for the tx_array because each sound to be played back should be copied completely from Flash to RAM.
TMRh20
October 27, 2025, 11:30am
7
Yeah, re-post your entire code. There is a logical reason why and its most likely in the code.
When you re-post your code also post an annotated schematic showing all connections and power sources as this could be part of your problem. Post links to technical information on your hardware items.
kirkwm
March 26, 2026, 11:47am
9
Would just like to add a comment here in the hope that it might save someone a TON of time. I had the same issue described above and was absolutely tearing my hair out over it for months.
I could get i2s IRQs working find on an Arduino Nano 33 BLE but on a nice!nano v2 board the same code would crash when firing the first interrupt.
The issue was that my IRQ handler wasn't named I2S_IRQHandler. Exactly the same code but with a different name fails! I haven't dug in, but I assume that there's something in a header file or linker script which forces a function of that name to be put into some IRQ-safe region of program memory.