From 9ce326ba7f31399c1422d7c1a18cae934ef26622 Mon Sep 17 00:00:00 2001 From: "Earle F. Philhower, III" Date: Tue, 18 Nov 2025 09:13:32 -0800 Subject: [PATCH 1/2] Add I2S input slave support Due to the way the PIO inputs work, slave input mode needs to have its pins defined in a fixed order: DIN, BCLK, LRCLK For slave mode, specify ``setDATA`` . ``setBCLK`` and ``setLRCLK`` are ignored. --- docs/i2s.rst | 7 ++-- libraries/I2S/src/I2S.cpp | 4 +++ libraries/I2S/src/pio_i2s.pio | 50 ++++++++++++++++++++++++++++ libraries/I2S/src/pio_i2s.pio.h | 58 +++++++++++++++++++++++++++++++++ 4 files changed, 117 insertions(+), 2 deletions(-) diff --git a/docs/i2s.rst b/docs/i2s.rst index d73ef85e1..3af36ad7e 100644 --- a/docs/i2s.rst +++ b/docs/i2s.rst @@ -39,8 +39,11 @@ any input or output can happen. bool setSlave() ~~~~~~~~~~~~~~~ Enables slave mode. BCLK and LRCLK are inputs and used to control the -timing of the DOUT output. Only normal I2S output mode is supported in -slave mode. +timing of the DOUT output. Only normal I2S output and input modes are +supported in slave mode. + +In I2S in slave mode, the clock pins and swapClocks are ignored. The pins +must be consecutive starting with DIN, then BCLK, then LRCLK. bool setBCLK(pin_size_t pin) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/libraries/I2S/src/I2S.cpp b/libraries/I2S/src/I2S.cpp index 103dfcf95..dec72bc56 100644 --- a/libraries/I2S/src/I2S.cpp +++ b/libraries/I2S/src/I2S.cpp @@ -332,6 +332,10 @@ bool I2S::begin() { pgm = &pio_i2s_out_program; init = pio_i2s_out_program_init; break; + case _SLAVE + _INPUT: + pgm = &pio_i2s_in_slave_program; + init = pio_i2s_in_slave_program_init; + break; case _INPUT: pgm = &pio_i2s_in_program; init = pio_i2s_in_program_init; diff --git a/libraries/I2S/src/pio_i2s.pio b/libraries/I2S/src/pio_i2s.pio index 3e8cca2a1..e3afb5cd2 100644 --- a/libraries/I2S/src/pio_i2s.pio +++ b/libraries/I2S/src/pio_i2s.pio @@ -182,6 +182,34 @@ right1: ; Loop back to beginning... +.program pio_i2s_in_slave +; The C code should update SHIFTCTRL to be 24 or 32 (for 16- and 32- bit mode) + +; IN 0 = DIN +; IN 1 = BCLK +; IN 2 = LRCLK + +waitfirstbit: + wait 0 pin 1 + wait 1 pin 1 + jmp pin waitfirstbit; LRCLK==1, so this is right channel...wait for left + +.wrap_target +readleft: + wait 0 pin 1 + wait 1 pin 1 + in pins, 1 ; read din on rising edge + jmp pin readright + jmp readleft + +readright: + wait 0 pin 1 + wait 1 pin 1 + in pins, 1 + jmp pin readright +.wrap + + .program pio_i2s_inout .side_set 2 ; 0 = bclk, 1=wclk @@ -399,6 +427,28 @@ static inline void pio_i2s_in_program_init I2S_INIT_PARAMS { pio_sm_exec(pio, sm, pio_encode_in(pio_pins, bits - 1)); // Shift in 1st R data modulo one bit, avoiding bit shift from #2037 } +static inline void pio_i2s_in_slave_program_init I2S_INIT_PARAMS { + I2S_INIT_PARAMS_VOID; + // Note that the CLOCK_BASE pin is ignored. It's DIN, BCLK, LRCLK consecutive due to PIO limitations + pio_gpio_init(pio, data_in_pin); + pio_gpio_init(pio, data_in_pin + 1); + pio_gpio_init(pio, data_in_pin + 2); + + pio_sm_config sm_config = pio_i2s_in_slave_program_get_default_config(offset); + + sm_config_set_in_pins(&sm_config, data_in_pin); + sm_config_set_in_pin_count(&sm_config, 3); + sm_config_set_in_shift(&sm_config, false, true, (bits <= 16) ? 2 * bits : bits); + sm_config_set_fifo_join(&sm_config, PIO_FIFO_JOIN_RX); + sm_config_set_jmp_pin(&sm_config, data_in_pin + 2); + + pio_sm_init(pio, sm, offset, &sm_config); + + pio_sm_set_consecutive_pindirs(pio, sm, data_in_pin, 3, false); +} + + + static inline void pio_i2s_inout_program_init I2S_INIT_PARAMS { I2S_INIT_PARAMS_VOID; pio_gpio_init(pio, data_in_pin); diff --git a/libraries/I2S/src/pio_i2s.pio.h b/libraries/I2S/src/pio_i2s.pio.h index 3be9db9a5..678a585e3 100644 --- a/libraries/I2S/src/pio_i2s.pio.h +++ b/libraries/I2S/src/pio_i2s.pio.h @@ -324,6 +324,49 @@ static inline pio_sm_config pio_i2s_in_program_get_default_config(uint offset) { } #endif +// ---------------- // +// pio_i2s_in_slave // +// ---------------- // + +#define pio_i2s_in_slave_wrap_target 3 +#define pio_i2s_in_slave_wrap 11 +#define pio_i2s_in_slave_pio_version 0 + +static const uint16_t pio_i2s_in_slave_program_instructions[] = { + 0x2021, // 0: wait 0 pin, 1 + 0x20a1, // 1: wait 1 pin, 1 + 0x00c0, // 2: jmp pin, 0 + // .wrap_target + 0x2021, // 3: wait 0 pin, 1 + 0x20a1, // 4: wait 1 pin, 1 + 0x4001, // 5: in pins, 1 + 0x00c8, // 6: jmp pin, 8 + 0x0003, // 7: jmp 3 + 0x2021, // 8: wait 0 pin, 1 + 0x20a1, // 9: wait 1 pin, 1 + 0x4001, // 10: in pins, 1 + 0x00c8, // 11: jmp pin, 8 + // .wrap +}; + +#if !PICO_NO_HARDWARE +static const struct pio_program pio_i2s_in_slave_program = { + .instructions = pio_i2s_in_slave_program_instructions, + .length = 12, + .origin = -1, + .pio_version = pio_i2s_in_slave_pio_version, +#if PICO_PIO_VERSION > 0 + .used_gpio_ranges = 0x0 +#endif +}; + +static inline pio_sm_config pio_i2s_in_slave_program_get_default_config(uint offset) { + pio_sm_config c = pio_get_default_sm_config(); + sm_config_set_wrap(&c, offset + pio_i2s_in_slave_wrap_target, offset + pio_i2s_in_slave_wrap); + return c; +} +#endif + // ------------- // // pio_i2s_inout // // ------------- // @@ -511,6 +554,21 @@ static inline void pio_i2s_in_program_init I2S_INIT_PARAMS { pio_sm_exec(pio, sm, pio_encode_in(pio_pins, bits)); // Shift in 1st L data pio_sm_exec(pio, sm, pio_encode_in(pio_pins, bits - 1)); // Shift in 1st R data modulo one bit, avoiding bit shift from #2037 } +static inline void pio_i2s_in_slave_program_init I2S_INIT_PARAMS { + I2S_INIT_PARAMS_VOID; + // Note that the CLOCK_BASE pin is ignored. It's DIN, BCLK, LRCLK consecutive due to PIO limitations + pio_gpio_init(pio, data_in_pin); + pio_gpio_init(pio, data_in_pin + 1); + pio_gpio_init(pio, data_in_pin + 2); + pio_sm_config sm_config = pio_i2s_in_slave_program_get_default_config(offset); + sm_config_set_in_pins(&sm_config, data_in_pin); + sm_config_set_in_pin_count(&sm_config, 3); + sm_config_set_in_shift(&sm_config, false, true, (bits <= 16) ? 2 * bits : bits); + sm_config_set_fifo_join(&sm_config, PIO_FIFO_JOIN_RX); + sm_config_set_jmp_pin(&sm_config, data_in_pin + 2); + pio_sm_init(pio, sm, offset, &sm_config); + pio_sm_set_consecutive_pindirs(pio, sm, data_in_pin, 3, false); +} static inline void pio_i2s_inout_program_init I2S_INIT_PARAMS { I2S_INIT_PARAMS_VOID; pio_gpio_init(pio, data_in_pin); From 9d0c6f77c2c332b45c784dc4d9edc2eccfa2cb34 Mon Sep 17 00:00:00 2001 From: "Earle F. Philhower, III" Date: Tue, 18 Nov 2025 10:37:00 -0800 Subject: [PATCH 2/2] Fix typo in I2S slave mode documentation --- docs/i2s.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/i2s.rst b/docs/i2s.rst index 3af36ad7e..93b8eeadf 100644 --- a/docs/i2s.rst +++ b/docs/i2s.rst @@ -42,7 +42,7 @@ Enables slave mode. BCLK and LRCLK are inputs and used to control the timing of the DOUT output. Only normal I2S output and input modes are supported in slave mode. -In I2S in slave mode, the clock pins and swapClocks are ignored. The pins +In I2S input slave mode, the clock pins and swapClocks are ignored. The pins must be consecutive starting with DIN, then BCLK, then LRCLK. bool setBCLK(pin_size_t pin)