I've just recieved an XIAO BLE Sense board based on the nrf52840 and am trying to output some audio using I2S. I am close, as I can make out the words spoken into a recording, but they are all distorted and I can barely make them out. I have tried adjusting everything, using 8-bit audio, 16-bit audio, but its all distorted and crappy. I must be missing something simple or else this board is not working correctly. Here is some sample code that reproduces the issue. I have a file on SD card called 'calibrat.wav' and it is 8-bit mono. I also have 16-bit samples that I've tested with 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);
}
}
If anyone has insight on this that would be fantastic!