-
Notifications
You must be signed in to change notification settings - Fork 6
i2c: Add I2C Controller implementation #27
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
Conversation
fc92300 to
2cd9a63
Compare
ryan-summers
left a comment
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.
Have you tested these on the H5 as well? I'd be a bit suspicious around some of the timing calcs, so at least one functional test would give some confidence.
Main comments in this are around the use of nb. I didn't closely look into the register calcs or device datasheet
Cargo.toml
Outdated
| defmt = { version = "0.3.8", optional = true } | ||
| paste = "1.0.15" | ||
| log = { version = "0.4.20", optional = true} | ||
| nb = "1.1.0" |
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 really would avoid using the nb crate nowadays. There's been talk of entirely deprecating it. It was intended to support embedded async before async was possible on embedded, but there's more modern approaches now with async frameworks targeting embedded.
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.
Ah, thanks for this context. I'll look at embedded-hal-async
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.
@ryan-summers I looked at embedded-hal-async, but I'm not sure how it solves the same problem? It doesn't provide any tool to manage repeatedly trying a blocking call. Is the suggestion to remove non-blocking calls entirely? Wouldn't they be useful in interrupts, or for creating a Future implementation for async?
Is it embedded-hal-nb that might be deprecated rather than the nb crate? That wasn't actually at 1.0 when I originally created this driver, so I didn't implement those APIs.
Turns out I did implement this for the SPI driver.
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.
@ryan-summers Coming back to this, is the recommendation to declare non-blocking/async methods that return a Future instead of using the nb crate? Or have non-blocking methods that just check a status and return bool/ Result<bool> instead of returning an nb::Error::WouldBlock and then have methods that can't be easily decomposed to a bool return a Future? If you're not using an async runtime, how would one implement interrupt based operations? Just manually invoking poll on a future?
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.
Generally, the way HALs do this is to have entirely blocking APIs for external embedded-hal traits. If you also want a non-blocking API, you'd use embedded-hal-async and could compose your software internally to handle both cases. nb doesn't really solve the problem at all.
src/i2c.rs
Outdated
| //! i2c.start(0x18, AddressMode::AddressMode7bit, write.len(), Stop::Automatic) | ||
| //! | ||
| //! for byte in write { | ||
| //! block!(i2c.write_nb(byte))?; |
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'd remove the nb implementation and instead go for embedded-hal-async personally, but up to you
src/i2c.rs
Outdated
| Event::AddressMatch => w.addrie().enabled(), | ||
| }); | ||
| let _ = self.i2c.cr1().read(); | ||
| let _ = self.i2c.cr1().read(); // Delay 2 peripheral clocks |
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.
Would be helpful to document why there's a delay here
The suspicion is warranted, I'll give you that. I honestly don't understand them properly and the reference manual doesn't explain them very well and just refers you to use STM32Cube to determine the timing values. However, I have tested these timing values quite extensively on the H503. I'm using them for a I2C target implementation (which I'll put up for review after I merge this) in pre-production hardware and they have worked well so far. I'll take another look a them though. |
2cd9a63 to
9ac41de
Compare
|
I've looked at the TIMINGR calculations again, and I still don't know where some of those numbers come from, but between the tests, the fact that they've been working pretty well for me, and seem to do a good job for the H7 and the fact that embassy uses a very similar derivation (probably copied from stm32h7xx-hal, based on timing and similarity), I think I'll stick with them as is. |
3fc911e to
cecddec
Compare
34c9dc5 to
9ada869
Compare
|
@ryan-summers I've removed |
9ada869 to
7d65a92
Compare
7d65a92 to
c26264d
Compare
ryan-summers
left a comment
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.
LGTM! Nicely organized.
This adds the I2C Controller driver implementation for the STM32H5. It implements a blocking, and non-blocking API, as well as the embedded-hal 1.0 traits.
It borrows a lot from the STM32H7 HAL (particularly the TIMINGR configuration, which is a bit inscrutable in the reference manual), but it borrows the
Instanceconcept from the STM32F4 project to minimize the need for macros.It uses the updated terminology of Controller/Target from the latest version of the I2C spec (Rev. 7.0)