-
Notifications
You must be signed in to change notification settings - Fork 2k
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
[WIP] periph/i2s: Add I2S device peripheral interface #15131
base: master
Are you sure you want to change the base?
Conversation
A |
For later analysis: |
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. If you want me to ignore this issue, please mark it with the "State: don't stale" label. Thank you for your contributions. |
I'm willing to pick this one up again soonish for a personal project and I'm still looking for feedback and ideas on the api here. |
Would probably be handy to align this with the DAC DDS API (example app) so an app could seamlessly switch between internal DAC and external I2S based DAC. With the DAC DDS API you get a callback when the next audio frame can be queued for transmission - does this also make sense for I2S? I suppose this could also come in handy as I2S can also be used for input, not just output. |
The idea I have in mind is to submit buffers, or transactions, with audio samples to read from or write in to the I2S peripheral and register a callback to be called when the next buffer should be submitted. For this it makes sense to me to have always two buffers for one direction, one that's currently used and one that is the next buffer that should be used. The user would then get the callback as soon as the peripheral switches buffers (moving to the next transaction) and the thread would have quite some time to prepare the next transaction and submit it to the peripheral. |
Looking now at the documentation of the DAC DDS API, this exactly matches what I put above. |
That's exactly what |
For a hobby project I picked this up again, pairing a tlv320aic3204 codec with two SPI/I2S peripherals on the stm32f446re. After some initial poking around I got a relative clean sine wave out of the codec. Based on this I have some observations of the peripheral and the other available peripherals from different vendors. It seems that the stm32 SPI peripheral in I2S mode is pretty much worst case in terms of effort per audio sample. I've only looked at the nRF52840, the stm32 and the atsam peripherals. I have no idea how the esp32 operates it's peripheral. Peripheral modesnRF52840The nRF52840 has a single I2S peripheral with simultaneous transmit and stm32The stm32 has both the SPI peripheral in I2S mode and the SAI blocks. Each SPI atsamThe same54 has a I2S peripheral consisting of a transmit and a receive block. Data formatnRF52840The nRF52840 supports 8, 16 and 24 bit modes and when using 24 bit mode it stm32The SPI peripheral has a 16 bit data register. This is inconvenient when atsamThe same54 has a flexible data format. DMAIt is almost mandatory to couple the peripheral with a DMA stream to have some nRF52840The EasyDMA of the nRF52840 automatically resolves this. Care has to be taken stm32The stm32 peripheral can be coupled with DMA streams. To get reliable atsamThe atsam DMA uses in RAM descriptors for the DMA transfers. These can be set ConclusionsFixed transaction buffersA number of these peripherals put restrictions on when the number of items in Splitting the peripheralsThe most flexible way to model the peripherals is to guarantee at least Data FormatAs the peripherals differ in what they expect, a conversion function is |
6fc016d
to
efd58f1
Compare
One more thing I noticed while developing on this: Currently the architecture uses a linked list of transactions, each transaction containing a preconfigured number of samples. This makes for a flexible API where different chunks of memory can be chained to construct the audio stream (as long as they all have the same size). However I doubt whether this is really useful for the end user. I noticed for myself that I would usually allocate one slab of memory and divide that over a set of transactions that I would feed the peripheral. Depending on the origin of the data (static array of samples or USB audio stream), I would manually keep track of which transactions have finished and write the next chunk of samples to it and feed it back to the peripheral. See also the test application included here for an example. What would greatly simplify the usage of the API is to include a memory region in the config struct together with how many equal sized regions it should be divided into. The implementation would then just have to keep track of a read and write pointer and signal in the callback when a chunk of the memory region has been fully consumed. The downside is that we lose some flexibility in where we get the memory regions from. On the other hand it would simplify the DMA requirements as these could in the simplest case run in circular mode and notify at the halfway points. The buffer write and read functions would still exist, but would write directly into the provided buffer without the intermediate transaction step |
The ST SAI peripheral also consists of two blocks, but it doesn't have the issue described above as the two blocks are fully symmetrical and can run both as transmit and receive. |
Contribution description
This is an initial draft of an I2S audio interface API.
The goal is to have an API to fit the peripherals on different platforms. Furthermore, due to the streaming nature of I2S data, it should be suitable for DMA usage and should allow for chaining transfers as not to cause glitches in the middle of a stream.
All properties of an I2S stream should be reconfigurable at runtime. This includes the sample width and the sample rate. Possibly also a switch between mono/stereo.
Testing procedure
It's only an API so far, I need an implementation or two to test it.
Issues/PRs references
None
Development
I don't have a lot of time at the moment to work on this, but lets use the branch here as a common development point. Feel free to open PRs for implementations on top of this branch or push fixes to the API directly to this branch.