-
Notifications
You must be signed in to change notification settings - Fork 223
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
[Proposal] I2S traits #204
Conversation
r? @therealprof (rust_highfive has picked a reviewer for you, use r? to override) |
Thanks @eldruin for opening the proposal. I also got contacted by @jkristell about drivers for the CS43L22 (which uses I2S) which he also had been experimenting with. Maybe @jkristell also has some input to the discussion? |
src/blocking/i2s.rs
Outdated
type Error; | ||
|
||
/// Sends `left_words` and `right_words` to the slave. | ||
fn try_write<LW, RW>(&mut self, left_words: LW, right_words: RW) -> Result<(), Self::Error> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need LW
and RW
separate for some reason?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For PCM mode it’s not needed at all, for the other modes it’s not technically needed but could be semantically useful. For the other modes first left then right is written to the data register, or for Philips mode it’s synced to the WS clock. But I’m very new to this and have little practical knowledge. I guess the final decision depends on what abstraction level is the right one for the embedded-hal lib. The more high level left/right channel semantics (or even multichannel) can definitely be left to individual drivers to supply IMO.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Separate LW
and RW
would allow different types for the buffers as long as they can be converted to a W
iterator. I would rather not require them to be the same type if it is not necessary.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just out of curiosity, when would it make sense to have different types for the left and right channel? To my understanding they would always be interleaved when written and an application would most likely write both a left and right word at the same time.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Separate LW and RW would allow different types for the buffers as long as they can be converted to a W iterator.
Is there a situation in which this is useful? from my extremely limited i2s experience the left and right channels are always interleaved and usually going to be run at the same sampling rate / bit depth?
Afaik we usually have separate methods for iterator based approaches, rather than trying to be generic over both. For example, something like fn try_write<W>(&mut self, left_words: &[W], right_words: &[W]) -> Result<(), Self::Error>
vs. fn try_write_iter<W, I: Iterator<W>>(&mut self, left_words: I, right_words: I)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought separate types would improve support for 1-channel "mono" communication. e.g. maybe one wants to have a struct that generates numbers on the fly for the left channel and just loop over a zero-filled buffer for the right channel.
However, I just had a look at the multichannel description (TDM, although it is not standard) and it seems anything higher level than a raw buffer would not be a good fit.
I will update this PR soon.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds good. The new interface looks much cleaner BTW.
I'll try to play with an implementation of it in the stm32f4xx-hal crate I get some time, mostly for me to learn the ecosystem better.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Now when I have some more knowledge with I2S the separate left and right words might not be such a bad idea after all. What supports this is the fact that a full frame is considered transmitted only after both left and right has been sent. The word size can be 16, 24 or 32 bits. In the STM32F4 there is a special status flag (CHSIDE) which can be used together with TXE to determine which word to send next. It would be good to be able to take advantage of that.
I have also read the data sheet for the CS4385 which is a 8 channel DAC. In TDM mode it can receive all 8 channels over a SAI interface, which is basically a more modular version of I2S. In that case it can send 256 bits after only one WS trigger, but that behavior is not possible with standard I2S. My conclusion is that it’s perfectly ok for the HAL to focus on providing a 2 channel abstraction, which is all that standard I2S will be able to do.
Could you revert back the last change @eldruin? Sorry for the extra rounds 🙂
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@maxekman Very interesting :) Sure, no problem. I just reverted the last change in the i2s branch. The interleaved version can be found in the i2s-interleaved
branch.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks @eldruin, will give it a go later today.
I would like to bring up another topic; clock control. I know that setting up a device etc is outside the scope of the embedded-hal project, but I would like to gather the opinions on clock setup for I2S. Is there a way it can be done in the scope of embedded-hal? The clock setup plays such a major role for I2S often involving both a master clock for external ADCs and a word clock for the channels in addition to the ordinary serial clock. Any other thoughts on this topic? |
BTW, I have started work on the CS43L22 driver, which could be an example/reference implementation. I'll hopefully get it into the stm32f407g-disc crate eventually (https://github.com/stm32-rs/stm32f407g-disc). |
I have now also started to experiment with a I2S HAL implementation for the stm32f4xx-hal crate (https://github.com/stm32-rs/stm32f4xx-hal). My initial tests will be hardcoded only for the I2S 3 for the f407 which is what the 407g-disc board uses for audio out. Later all f4xx should be supported with the macro system that is used in those crates. |
I have working sound output from the F4 discovery board now! The next step will be to do the same thing but via this HAL proposal and see how that feels. Will feedback any discoveries on the way. |
I have published an early draft of a MCU HAL implementation: stm32-rs/stm32f4xx-hal#145 If you want you can update the todo in the description @eldruin. |
I have also published a WIP of a driver implementation for the CS43L22 DAC found on the STM32F4 Discovery board: https://github.com/maxekman/cs43l22 @eldruin You can add this to the description also if you want. |
Here is a running example of a simple frequency sweep of a inverted saw wave: https://github.com/maxekman/stm32f407g-disc/blob/dac-support-with-cs43l22/examples/audio.rs |
Cool stuff @maxekman! I updated the description accordingly. |
I backported this PR to embedded-hal 0.2.x for testing ease in the |
@eldruin That is cool, do you have some DAC chip to test with? |
@eldruin Do you think I should also implement the v2.x version of the HAL for stm32f4xx-hal? I'm not familiar with the timelines for when the current master of embedded-hal will be released. I did hear something that it may be released as v1.. |
Yes, the current I have a couple I2S DACs but had no chance to try them so far. I want to test my implementation with a MAX98357A which uses standard I2S (the only mode I implemented so far). If you want give it a try as well that would be great :) |
@eldruin Sounds good, I'll work on the v0.2.x HAL implementation first then, in any case. |
Is it just the stm32f4xx-hal implementation that's needed for this to go in? I've been playing around with audio on the stm32f407g-disc too, so it would be good to see this get merged and not use a selection of half finished branches. Happy to help that go in if you're no longer working on it @maxekman, as I see there hasn't been movement in a while. |
@RaoulHC I would love to finish this, I might be able to do it today. I have indeed not worked on this in a while due to the day job. If I don't get anything up soon, feel free to help out. |
@eldruin Could you rebase and push your i2s-v0.2.x branch (and possibly the i2s also)? Thanks! |
@maxekman I have rebased both branches now. FYI, I think it would be best to release this as part of |
@eldruin Are you suggesting that I drop the work towards v0.2.x and completely focuses on v1.x? I have splited all work in v0.2 and v1.0 branches for stm32f4xx-hal, stm32f407g-disco and my CSxxx DAC driver, which means we are flexible with it either way. |
That is not what I meant. I just wanted to let you know that this does not need to be done before the 1.0 release. |
Yep, thanks for the clarification @eldruin! |
Got some progress on the ref implementation / POC today |
265: Add I2S communication using SPI peripherals r=therealprof a=samcrow # Introduction Welcome to my first large pull request, which adds support for I2S audio communication using supported SPI peripherals. Like the way we support CAN with the bxcan library, I am proposing to support I2S using my [stm32_i2s_v12x](https://crates.io/crates/stm32_i2s_v12x) library. Although stm32_i2s_v12x is in a separate repository, we can also talk about it here. # Notes * The I2S module is available if the `i2s` feature is enabled, in order to not increase compile time for applications that don't use I2S. * All four modes are supported: master transmit, master receive, slave transmit, and slave receive. * Basic support for DMA, interrupts, and error detection is provided. * All STM32F4 models are supported. * I added two examples that run on the STM32F411E-DISCO board and send audio to the on-board DAC. One of them uses DMA. These changes are not perfect, so criticism and suggestions are welcome. # Limitations * No support for full-duplex communication * I have tested master transmit mode fairly thoroughly, but have not tested the other modes. * No support for embedded-hal I2S traits * [The I2S pull request](rust-embedded/embedded-hal#204) is still under review. The proposed traits look compatible with this driver code. Until that pull request gets merged and released, people can still use this driver code directly. # Related work * SAI driver pull request in this repository: #248 * Another, less complete, pull request for I2S in this repository: #212 * also #145 * embedded-hal pull request for I2S traits: rust-embedded/embedded-hal#204 Co-authored-by: Sam Crow <[email protected]> Co-authored-by: Sam Crow <[email protected]>
Maybe this is ready to be merged soon? I have not been able to finish my reference implementation but @samcrow did finish a similar implementation in stm32-rs/stm32f4xx-hal#265. |
@maxekman cool! |
I implemented some of these proposed traits in a branch of stm32f4xx-hal. The implementation is in another library: https://github.com/samcrow/stm32_i2s/blob/ehal1/src/ehal.rs . The corresponding version of stm32f4xx-hal, with an example that uses the The process was simple. Some notes:
|
Hi, i just discovered this I2S proposal. samcrow mentionned it because i'm rewriting I2s implementation for stm32f4xx. Is the discussion still open ? I have remarks, comments and suggestions. The first is the following: i see many times the terms "slave" to refer to the other I2s device. I think referring in this way is an error, "master" and "slave" just define who provide clocks, and the "master" is not even necessarily one of the I2s interface implied in data communication. |
I added some I2S traits to kick off the discussion started in #203.
The traits accept two words for left and right and are otherwise similar to SPI.
An interleaved version can be found in the
i2s-interleaved
branch. The data should be passed interleaved. e.g.[left0,right0,left1,right1,...]
A version of these traits for the embedded-hal 0.2.x version can be found in the
i2s-v0.2.x
branch.There is another MCU implementation in
stm32f4xx-hal
TODO:
try_
prefixThoughts?
cc. @maxekman