@@ -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
7172I2S::~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+
265287bool 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 }
0 commit comments