Skip to content

Commit 58f9847

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 58f9847

File tree

4 files changed

+208
-468
lines changed

4 files changed

+208
-468
lines changed

libraries/I2S/src/I2S.cpp

Lines changed: 114 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,103 @@ 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+
int sz;
300+
void (*init)(PIO pio, uint sm, uint offset, uint data_in_pin, uint data_out_pin, uint clock_pin_base, uint bits, uint channels);
301+
const int _SLAVE = 1 << 0;
302+
const int _OUTPUT = 1 << 1;
303+
const int _INPUT = 1 << 2;
304+
const int _TDM = 1 << 3;
305+
const int _LSBJ = 1 << 4;
306+
int selector = (_isSlave ? _SLAVE : 0) | (_isOutput ? _OUTPUT : 0) | (_isInput ? _INPUT : 0) | (_isTDM ? _TDM : 0) | (_isLSBJ ? _LSBJ : 0);
307+
switch (selector) {
308+
case _SLAVE + _OUTPUT:
309+
if (_bps > 16) {
310+
pgm = &pio_i2s_out_slave_32_program;
311+
sz = sizeof(pio_i2s_out_slave_32_program);
273312
} else {
274-
_i2s = new PIOProgram(_bps > 16 ? &pio_i2s_out_slave_32_program : &pio_i2s_out_slave_16_program);
313+
pgm = &pio_i2s_out_slave_16_program;
314+
sz = sizeof(pio_i2s_out_slave_16_program);
275315
}
316+
init = pio_i2s_out_slave_program_init;
317+
break;
318+
case _OUTPUT + _INPUT + _TDM:
319+
pgm = &pio_tdm_inout_program;
320+
sz = sizeof(pio_tdm_inout_program);
321+
init = pio_tdm_inout_program_init;
322+
break;
323+
case _OUTPUT + _INPUT:
324+
pgm = &pio_i2s_inout_program;
325+
sz = sizeof(pio_i2s_inout_program);
326+
init = pio_i2s_inout_program_init;
327+
break;
328+
case _OUTPUT + _TDM:
329+
pgm = &pio_tdm_out_program;
330+
sz = sizeof(pio_tdm_out_program);
331+
init = pio_tdm_out_program_init;
332+
break;
333+
case _OUTPUT + _LSBJ:
334+
pgm = &pio_lsbj_out_program;
335+
sz = sizeof(pio_lsbj_out_program);
336+
init = pio_lsbj_out_program_init;
337+
break;
338+
case _OUTPUT:
339+
pgm = &pio_i2s_out_program;
340+
sz = sizeof(pio_i2s_out_program);
341+
init = pio_i2s_out_program_init;
342+
break;
343+
case _INPUT:
344+
pgm = &pio_i2s_in_program;
345+
sz = sizeof(pio_i2s_in_program);
346+
init = pio_i2s_in_program_init;
347+
break;
348+
default:
349+
// Unsupported combination!
350+
_running = false;
351+
return false;
352+
}
353+
354+
if (_swapClocks) {
355+
// If we swap, we need to save the generated bits and free on ::end
356+
_pgm = swap2SideSet(pgm);
357+
_i2s = new PIOProgram(_pgm);
276358
} 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);
359+
_i2s = new PIOProgram(pgm);
278360
}
361+
362+
// With the RP2350B there is a GPIO-base offset for PIOs, so need to pass the min/max pins to PIOProgram to set appropriately
279363
int minpin, maxpin;
280364
if (_isOutput && _isInput) {
281365
minpin = std::min(std::min((int)_pinDOUT, (int)_pinDIN), (int)_pinBCLK);
@@ -287,53 +371,48 @@ bool I2S::begin() {
287371
minpin = std::min((int)_pinDIN, (int)_pinBCLK);
288372
maxpin = std::max((int)_pinDIN, (int)_pinBCLK + 1);
289373
}
374+
375+
int off = 0;
290376
if (!_i2s->prepare(&_pio, &_sm, &off, minpin, maxpin - minpin + 1)) {
291377
_running = false;
378+
free(_pgm);
292379
delete _i2s;
293380
_i2s = nullptr;
294381
return false;
295382
}
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-
}
383+
384+
init(_pio, _sm, off, _pinDIN, _pinDOUT, _pinBCLK, _bps, _tdmChannels);
385+
317386
setFrequency(_freq);
387+
388+
// Start MCLK if needed
318389
if (_MCLKenabled) {
319390
MCLKbegin();
320391
}
392+
393+
// Calculate what to send on under/overflow based on the bits
321394
if (_bps == 8) {
322395
uint8_t a = _silenceSample & 0xff;
323396
_silenceSample = (a << 24) | (a << 16) | (a << 8) | a;
324397
} else if (_bps == 16) {
325398
uint16_t a = _silenceSample & 0xffff;
326399
_silenceSample = (a << 16) | a;
327400
}
401+
402+
// Ensure a safe minimum if no buffersize is set
328403
if (!_bufferWords) {
329404
_bufferWords = 64 * (_bps == 32 ? 2 : 1);
330405
}
406+
407+
// Generate the input ARB for DMA to RAM
331408
if (_isInput) {
332409
_arbInput = new AudioBufferManager(_buffers, _bufferWords, _silenceSample, INPUT);
333410
if (!_arbInput->begin(pio_get_dreq(_pio, _sm, false), (volatile void*)&_pio->rxf[_sm])) {
334411
_running = false;
335412
delete _arbInput;
336413
_arbInput = nullptr;
414+
free(_pgm);
415+
_pgm = nullptr;
337416
delete _i2s;
338417
_i2s = nullptr;
339418
return false;
@@ -344,6 +423,8 @@ bool I2S::begin() {
344423
_arbInput->setCallback(_cbInput);
345424
}
346425
}
426+
427+
// Generate the output ARB to dump from RAM to I2S
347428
if (_isOutput) {
348429
_arbOutput = new AudioBufferManager(_buffers, _bufferWords, _silenceSample, OUTPUT);
349430
if (!_arbOutput->begin(pio_get_dreq(_pio, _sm, true), &_pio->txf[_sm])) {
@@ -352,6 +433,8 @@ bool I2S::begin() {
352433
_arbOutput = nullptr;
353434
delete _arbInput;
354435
_arbInput = nullptr;
436+
free(_pgm);
437+
_pgm = nullptr;
355438
delete _i2s;
356439
_i2s = nullptr;
357440
return false;
@@ -362,6 +445,8 @@ bool I2S::begin() {
362445
_arbOutput->setCallback(_cbOutput);
363446
}
364447
}
448+
449+
// Start the ball rolling!
365450
pio_sm_set_enabled(_pio, _sm, true);
366451

367452
return true;
@@ -380,6 +465,8 @@ bool I2S::end() {
380465
_arbOutput = nullptr;
381466
delete _arbInput;
382467
_arbInput = nullptr;
468+
free(_pgm);
469+
_pgm = nullptr;
383470
delete _i2s;
384471
_i2s = nullptr;
385472
}

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)