Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature request: Add full-duplex mode for I2S peripheral #1055

Open
DatanoiseTV opened this issue Dec 20, 2022 · 6 comments
Open

Feature request: Add full-duplex mode for I2S peripheral #1055

DatanoiseTV opened this issue Dec 20, 2022 · 6 comments
Labels
enhancement New feature or request

Comments

@DatanoiseTV
Copy link
Contributor

Add full-duplex (in+out) mode for I2S using PIO.

@earlephilhower
Copy link
Owner

There is already input and output I2S support. You can instantiate both (or up to 4 total I2S interfaces, in fact...limited by DMA channels) with the same or different params. I don't think there'd be any savings in terms of compute, RAM, etc. in somehow merging the 2 together (and you'd have 1/2 the PIO FIFO space which may be a problem).

@earlephilhower earlephilhower added the enhancement New feature or request label Dec 21, 2022
@DatanoiseTV
Copy link
Contributor Author

I think the benefit of processing the OSR and ISR (Input and output shift registers) of the PIO at once would be saving at least the PIO SMs. Also, in that case the CB could be used to process both the incoming audio and generating the audio output stream.

I have at least a dozen of use cases (including Vult DSP), which would greatly benefit from this.

@earlephilhower
Copy link
Owner

From my experience, it's the PIO instruction memory that gets exhausted first, not the SMs (c.f. the cool PIO-USB stuff which uses all 32 insns on a single SM). :) Because there are no nops in the instruction stream (https://github.com/earlephilhower/arduino-pico/blob/master/libraries/I2S/src/pio_i2s.pio) you'd need to double the clock and insert nops every other cycle except for the one IN PINS, 1 so it's the same instruction count as an INPUT and OUTPUT I2S.

You would save a couple pins, though, which might be handy.

Also, two DMAs would be required and so you'd have 2 separate IRQs to handle, anyway. There's no bidir DMA option. You might be able to just assume the other one will fire, too, but it's awkward and open-loop at that point which feels unstable.

@palmerr23
Copy link
Contributor

Another way to achieve a similar outcome would be to implement slave input - using the same BCKL/LRCLK GPIO pins as the output PIO program is setting. The PIO code would have a interrupts on each of these signals.

@earlephilhower
Copy link
Owner

implement slave input - using the same BCKL/LRCLK GPIO pins

Unfortunately it's not possible to drive a PIO off of an input pin, only the system clocks. So any kind of slave mode is troublesome. I think you've got to run several multiples the frequency of the signal (to ensure you sample at proper setup/hold time). The PIO serial ports have this architecture, but they run at a much lower frequency...at I2S bit frequencies I don't think it's possible.

To get something like this you'd probably need a new I2S input PIO program and start it at exactly the same time as the I2S output clock. They would run in lockstep and you'd be operating in open loop mode, shifting in at the computed times. There are methods of triggering both starts off of an IRQ, too, but in the end your I2S input is really just an open loop shift-in program.

@palmerr23
Copy link
Contributor

Thanks Earle,

You are absolutely correct for trying to synchronise to an external master clock for outputs. It would be very difficult to meet the I2S timing requirements if the PIO clock wasn't locked to some multiple of BCLK. Even then, just one PIO cycle delay may be too long to meet the BCLK edge to DOUT ready timing requirement (which is around 20nS for several CODECs I use regularly).

However, I think slave inputs are still worth considering - if only to save two GPIO pins on a CODEC setup. I was assuming that I could trigger actions via interrupts off GPIO pins for LRCLK and BCLK. With a master-mode Pico output, the issue of clock drift is avoided.

2.19.3. Interrupts An interrupt can be generated for every GPIO pin in four scenarios: ...

3.2.5. Pin Mapping... Each range can cover any of the GPIOs accessible to a given PIO block (on RP2040 this is the 30 user GPIOs), and the ranges can overlap.

Yes, the PIO clock does need to be greater than BCLK, but a factor of anything more than 4 (and possibly 2) should provide enough accuracy to meet the data setup time requirements, despite clock drift or complete lack of synchronisation. I think a possible issue would likely be that the number of registers available might be exhausted, if an extra counter was needed to position the read near the middle of the bit cycle. Or, maybe just waiting one PIO clock cycle after the BCLK interrupt would be good enough to get a clean bit read.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

3 participants