Skip to content

Commit dba3d21

Browse files
Clean up I2S PIO program selection, automatic swap
Redo the multiple convoluted ifs + nested trinaries into a simpler switch() statement to make it easier to work on the library. Remove all hardcoded "swap" versions of PIO programs because they are a headache and need to manually be sync'd with the normal pinout programs. Instead, dynamically swap the sideset pins from the single normal-mode version. Removes 1/2 of the PIO programs.
1 parent a1077c0 commit dba3d21

File tree

4 files changed

+199
-468
lines changed

4 files changed

+199
-468
lines changed

libraries/I2S/src/I2S.cpp

Lines changed: 105 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ I2S::I2S(PinMode direction, pin_size_t bclk, pin_size_t data, pin_size_t mclk, p
6666
_swapClocks = false;
6767
_multMCLK = 256;
6868
_isSlave = false;
69+
_pgm = nullptr;
6970
}
7071

7172
I2S::~I2S() {
@@ -262,20 +263,94 @@ void I2S::MCLKbegin() {
262263
pio_sm_set_enabled(_pioMCLK, _smMCLK, true);
263264
}
264265

266+
pio_program_t *I2S::swap2SideSet(const pio_program_t *in) {
267+
int size = (sizeof(*in) + 4) % ~3; // Round up to nearest 32b word for alignment purposes
268+
size += in->length * sizeof(uint16_t);
269+
uint8_t *mem = (uint8_t *)malloc(size);
270+
memcpy(mem, in, sizeof(*in));
271+
pio_program_t *out = (pio_program_t *)mem;
272+
out->instructions = (uint16_t *)(mem + ((sizeof(*in) + 4) % ~3)); // Place the insn memory right after the struct
273+
uint16_t *dest = (uint16_t *)out->instructions;
274+
const uint16_t *src = in->instructions;
275+
for (int i = 0; i < in->length; i++) {
276+
uint16_t insn = src[i];
277+
uint16_t a = (insn & (1 << 12)) >> 1; // Move upper sideset bit down
278+
uint16_t b = (insn & (1 << 11)) << 1; // Move lower sideset bit up
279+
insn &= ~((1 << 12) | (1 << 11));
280+
insn |= a | b;
281+
dest[i] = insn;
282+
}
283+
return out;
284+
}
285+
286+
265287
bool I2S::begin() {
288+
if (_running) {
289+
return false;
290+
}
291+
266292
_running = true;
267293
_hasPeeked = false;
268294
_isHolding = 0;
269-
int off = 0;
270-
if (!_swapClocks) {
271-
if (!_isSlave) {
272-
_i2s = new PIOProgram(_isOutput ? (_isInput ? (_isTDM ? &pio_tdm_inout_program : &pio_i2s_inout_program) : (_isTDM ? &pio_tdm_out_program : (_isLSBJ ? &pio_lsbj_out_program : &pio_i2s_out_program))) : &pio_i2s_in_program);
295+
296+
// Determine the proper base program for the PIO
297+
// Probably should have started API with an enum/bitmask instead of bunch of flags, but we're here now...
298+
const pio_program *pgm;
299+
void (*init)(PIO pio, uint sm, uint offset, uint data_in_pin, uint data_out_pin, uint clock_pin_base, uint bits, uint channels);
300+
const int _SLAVE = 1 << 0;
301+
const int _OUTPUT = 1 << 1;
302+
const int _INPUT = 1 << 2;
303+
const int _TDM = 1 << 3;
304+
const int _LSBJ = 1 << 4;
305+
int selector = (_isSlave ? _SLAVE : 0) | (_isOutput ? _OUTPUT : 0) | (_isInput ? _INPUT : 0) | (_isTDM ? _TDM : 0) | (_isLSBJ ? _LSBJ : 0);
306+
switch (selector) {
307+
case _SLAVE + _OUTPUT:
308+
if (_bps > 16) {
309+
pgm = &pio_i2s_out_slave_32_program;
273310
} else {
274-
_i2s = new PIOProgram(_bps > 16 ? &pio_i2s_out_slave_32_program : &pio_i2s_out_slave_16_program);
311+
pgm = &pio_i2s_out_slave_16_program;
275312
}
313+
init = pio_i2s_out_slave_program_init;
314+
break;
315+
case _OUTPUT + _INPUT + _TDM:
316+
pgm = &pio_tdm_inout_program;
317+
init = pio_tdm_inout_program_init;
318+
break;
319+
case _OUTPUT + _INPUT:
320+
pgm = &pio_i2s_inout_program;
321+
init = pio_i2s_inout_program_init;
322+
break;
323+
case _OUTPUT + _TDM:
324+
pgm = &pio_tdm_out_program;
325+
init = pio_tdm_out_program_init;
326+
break;
327+
case _OUTPUT + _LSBJ:
328+
pgm = &pio_lsbj_out_program;
329+
init = pio_lsbj_out_program_init;
330+
break;
331+
case _OUTPUT:
332+
pgm = &pio_i2s_out_program;
333+
init = pio_i2s_out_program_init;
334+
break;
335+
case _INPUT:
336+
pgm = &pio_i2s_in_program;
337+
init = pio_i2s_in_program_init;
338+
break;
339+
default:
340+
// Unsupported combination!
341+
_running = false;
342+
return false;
343+
}
344+
345+
if (_swapClocks) {
346+
// If we swap, we need to save the generated bits and free on ::end
347+
_pgm = swap2SideSet(pgm);
348+
_i2s = new PIOProgram(_pgm);
276349
} else {
277-
_i2s = new PIOProgram(_isOutput ? (_isInput ? (_isTDM ? &pio_tdm_inout_swap_program : &pio_i2s_inout_swap_program) : (_isTDM ? &pio_tdm_out_swap_program : (_isLSBJ ? &pio_lsbj_out_swap_program : &pio_i2s_out_swap_program))) : &pio_i2s_in_swap_program);
350+
_i2s = new PIOProgram(pgm);
278351
}
352+
353+
// With the RP2350B there is a GPIO-base offset for PIOs, so need to pass the min/max pins to PIOProgram to set appropriately
279354
int minpin, maxpin;
280355
if (_isOutput && _isInput) {
281356
minpin = std::min(std::min((int)_pinDOUT, (int)_pinDIN), (int)_pinBCLK);
@@ -287,53 +362,48 @@ bool I2S::begin() {
287362
minpin = std::min((int)_pinDIN, (int)_pinBCLK);
288363
maxpin = std::max((int)_pinDIN, (int)_pinBCLK + 1);
289364
}
365+
366+
int off = 0;
290367
if (!_i2s->prepare(&_pio, &_sm, &off, minpin, maxpin - minpin + 1)) {
291368
_running = false;
369+
free(_pgm);
292370
delete _i2s;
293371
_i2s = nullptr;
294372
return false;
295373
}
296-
if (_isOutput) {
297-
if (_isInput) {
298-
if (_isTDM) {
299-
pio_tdm_inout_program_init(_pio, _sm, off, _pinDIN, _pinDOUT, _pinBCLK, _bps, _swapClocks, _tdmChannels);
300-
} else {
301-
pio_i2s_inout_program_init(_pio, _sm, off, _pinDIN, _pinDOUT, _pinBCLK, _bps, _swapClocks);
302-
}
303-
} else if (_isTDM) {
304-
pio_tdm_out_program_init(_pio, _sm, off, _pinDOUT, _pinBCLK, _bps, _swapClocks, _tdmChannels);
305-
} else if (_isLSBJ) {
306-
pio_lsbj_out_program_init(_pio, _sm, off, _pinDOUT, _pinBCLK, _bps, _swapClocks);
307-
} else {
308-
if (_isSlave) {
309-
pio_i2s_out_slave_program_init(_pio, _sm, off, _pinDOUT, _pinBCLK, _bps, _swapClocks);
310-
} else {
311-
pio_i2s_out_program_init(_pio, _sm, off, _pinDOUT, _pinBCLK, _bps, _swapClocks);
312-
}
313-
}
314-
} else {
315-
pio_i2s_in_program_init(_pio, _sm, off, _pinDIN, _pinBCLK, _bps, _swapClocks);
316-
}
374+
375+
init(_pio, _sm, off, _pinDIN, _pinDOUT, _pinBCLK, _bps, _tdmChannels);
376+
317377
setFrequency(_freq);
378+
379+
// Start MCLK if needed
318380
if (_MCLKenabled) {
319381
MCLKbegin();
320382
}
383+
384+
// Calculate what to send on under/overflow based on the bits
321385
if (_bps == 8) {
322386
uint8_t a = _silenceSample & 0xff;
323387
_silenceSample = (a << 24) | (a << 16) | (a << 8) | a;
324388
} else if (_bps == 16) {
325389
uint16_t a = _silenceSample & 0xffff;
326390
_silenceSample = (a << 16) | a;
327391
}
392+
393+
// Ensure a safe minimum if no buffersize is set
328394
if (!_bufferWords) {
329395
_bufferWords = 64 * (_bps == 32 ? 2 : 1);
330396
}
397+
398+
// Generate the input ARB for DMA to RAM
331399
if (_isInput) {
332400
_arbInput = new AudioBufferManager(_buffers, _bufferWords, _silenceSample, INPUT);
333401
if (!_arbInput->begin(pio_get_dreq(_pio, _sm, false), (volatile void*)&_pio->rxf[_sm])) {
334402
_running = false;
335403
delete _arbInput;
336404
_arbInput = nullptr;
405+
free(_pgm);
406+
_pgm = nullptr;
337407
delete _i2s;
338408
_i2s = nullptr;
339409
return false;
@@ -344,6 +414,8 @@ bool I2S::begin() {
344414
_arbInput->setCallback(_cbInput);
345415
}
346416
}
417+
418+
// Generate the output ARB to dump from RAM to I2S
347419
if (_isOutput) {
348420
_arbOutput = new AudioBufferManager(_buffers, _bufferWords, _silenceSample, OUTPUT);
349421
if (!_arbOutput->begin(pio_get_dreq(_pio, _sm, true), &_pio->txf[_sm])) {
@@ -352,6 +424,8 @@ bool I2S::begin() {
352424
_arbOutput = nullptr;
353425
delete _arbInput;
354426
_arbInput = nullptr;
427+
free(_pgm);
428+
_pgm = nullptr;
355429
delete _i2s;
356430
_i2s = nullptr;
357431
return false;
@@ -362,6 +436,8 @@ bool I2S::begin() {
362436
_arbOutput->setCallback(_cbOutput);
363437
}
364438
}
439+
440+
// Start the ball rolling!
365441
pio_sm_set_enabled(_pio, _sm, true);
366442

367443
return true;
@@ -380,6 +456,8 @@ bool I2S::end() {
380456
_arbOutput = nullptr;
381457
delete _arbInput;
382458
_arbInput = nullptr;
459+
free(_pgm);
460+
_pgm = nullptr;
383461
delete _i2s;
384462
_i2s = nullptr;
385463
}

libraries/I2S/src/I2S.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,11 +182,14 @@ class I2S : public Stream, public AudioOutputBase {
182182

183183
AudioBufferManager *_arbInput;
184184
AudioBufferManager *_arbOutput;
185+
pio_program_t *swap2SideSet(const pio_program_t *src);
186+
pio_program_t *_pgm;
185187
PIOProgram *_i2s;
186188
PIOProgram *_i2sMCLK;
187189
PIO _pio, _pioMCLK;
188190
int _sm, _smMCLK;
189191

190192
static const int I2SSYSCLK_44_1 = 135600; // 44.1, 88.2 kHz sample rates
191193
static const int I2SSYSCLK_8 = 153600; // 8k, 16, 32, 48, 96, 192 kHz
194+
192195
};

0 commit comments

Comments
 (0)