I’ve been struggling for a long time now, trying to get I2S working properly on a XIAO Sense 52840, and the closest I’ve gotten is some distorted audio that can hardly be made out.
I’ve tried various pin combinations, libraries, MBED vs non-MBED cores, and writing my own library/code.
What I get seems close to the proper output, but it is all distorted and non-useable. I have however, been able to get it working with PWM output and PDM microphone.
My code is the GitHub - TMRh20/AutoAnalogAudio: Create a wide range of sampling and audio related applications with simple API for onboard DAC (or PWM), ADC, DMA & Timers on Arduino devices (AVR & SAM) library (need to open src/NRF52840/AutoAnalogAudio.cpp and un-comment #define USE_I2s )
Here is some old code that I believe should work as well:
#include <hal/nrf_pdm.h>
#include <hal/nrf_i2s.h>
#include <SD.h>
File myFile;
#define PIN_MCK (13)
#define PIN_SCK (14)
#define PIN_LRCK (15)
#define PIN_SDOUT (2)
uint8_t bigBuffer[16000 * 5];
uint8_t smallBuffer[32];
void setup() {
Serial.begin(115200);
while (!Serial) { delay(10); }
delay(5000);
if (!SD.begin(4)) {
Serial.println("initialization failed!");
while (1)
;
} else {
Serial.println("SD initialization success!");
}
myFile = SD.open("calibrat.wav");
if (myFile) {
Serial.print("FOK");
}
myFile.seek(44);
Serial.println("reading into buffer");
uint32_t counter = 0;
while (myFile.available()) {
bigBuffer[counter++] = myFile.read();
if (counter > 16000 * 5) { break; }
}
Serial.println("finished loading buffer");
myFile.close();
// 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);
// MCKFREQ = 4 MHz
//NRF_I2S->CONFIG.MCKFREQ = I2S_CONFIG_MCKFREQ_MCKFREQ_32MDIV11 << I2S_CONFIG_MCKFREQ_MCKFREQ_Pos;
NRF_I2S->CONFIG.MCKFREQ = I2S_CONFIG_MCKFREQ_MCKFREQ_32MDIV63 << I2S_CONFIG_MCKFREQ_MCKFREQ_Pos;
// Ratio = 64
NRF_I2S->CONFIG.RATIO = I2S_CONFIG_RATIO_RATIO_64X << I2S_CONFIG_RATIO_RATIO_Pos;
//NRF_I2S->CONFIG.RATIO = I2S_CONFIG_RATIO_RATIO_256X << I2S_CONFIG_RATIO_RATIO_Pos;
// 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_8BIT << I2S_CONFIG_SWIDTH_SWIDTH_Pos;
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 stereo
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->ENABLE = 1;
// Configure data pointer
NRF_I2S->TXD.PTR = (uint32_t)&smallBuffer[0];
NRF_I2S->RXTXD.MAXCNT = 32 / sizeof(uint32_t);
//NRF_I2S->TXD.PTR = (uint32_t)&sine_table[0];
//NRF_I2S->RXTXD.MAXCNT = sizeof(sine_table) / sizeof(uint32_t);
// Start transmitting I2S data
NRF_I2S->TASKS_START = 1;
}
uint32_t sampleCounter = 0;
void loop() {
if (sampleCounter < 16000 * 5) {
while (!nrf_i2s_event_check(NRF_I2S, NRF_I2S_EVENT_TXPTRUPD));
for (int i = 0; i < 32; i++) {
smallBuffer[i] = bigBuffer[sampleCounter++];
}
NRF_I2S->RXTXD.MAXCNT = 32 / sizeof(uint32_t);
nrf_i2s_event_clear(NRF_I2S, NRF_I2S_EVENT_TXPTRUPD);
}
}
It seems others are having success, so I’m just wondering what I’m possibly doing wrong here…
I’ve seen the thread at XIAO nrf52840 and I2S and still can’t get it working properly. Even bought an I2s amplifier, but that seems to have been a waste of money at this point.
This is on a barebones XIAO Sense 52840 connected to a grove shield, breadboard or other, it doesn’t matter, still doesn’t work quite properly.
Would really love to have this working for higher quality output than PWM can provide. Any advice is welcome!