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

i2c: Add I2C Controller implementation #27

Merged
merged 10 commits into from
Feb 27, 2025
Merged

Conversation

astapleton
Copy link
Contributor

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 Instance concept 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)

Copy link
Member

@ryan-summers ryan-summers left a 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
@@ -50,6 +50,7 @@ embedded-hal = "1.0.0"
defmt = { version = "0.3.8", optional = true }
paste = "1.0.15"
log = { version = "0.4.20", optional = true}
nb = "1.1.0"
Copy link
Member

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.

Copy link
Contributor Author

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

Copy link
Contributor Author

@astapleton astapleton Jan 9, 2025

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.

Copy link
Contributor Author

@astapleton astapleton Feb 4, 2025

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?

Copy link
Member

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))?;
Copy link
Member

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
Copy link
Member

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

@astapleton
Copy link
Contributor Author

astapleton commented Jan 9, 2025

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

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.

@astapleton
Copy link
Contributor Author

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.

@astapleton astapleton force-pushed the as/i2c-controller branch 2 times, most recently from 34c9dc5 to 9ada869 Compare February 22, 2025 03:09
@astapleton
Copy link
Contributor Author

@ryan-summers I've removed nb crate and the public non-blocking functions as you suggested, as well as anything to do with interrupts for which they would have been used. Should be ready to go now.

Copy link
Member

@ryan-summers ryan-summers left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! Nicely organized.

@astapleton astapleton merged commit d26c531 into master Feb 27, 2025
19 checks passed
@astapleton astapleton deleted the as/i2c-controller branch February 27, 2025 00:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants