@@ -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,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+
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+ 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 }
0 commit comments