-
Notifications
You must be signed in to change notification settings - Fork 205
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
[Discussion] Non-blocking I2C Trait #50
Comments
I'd prefer statically checking validity of operation. Like this: pub trait Send {
type Error;
fn send(&mut self, byte: u8) -> nb::Result<(), Error<Self::Error>>;
}
pub trait Receive {
type Error;
fn receive(&mut self, send_ack: bool) -> nb::Result<u8, Error<Self::Error>>;
}
pub trait Master {
type Sender: Send;
type Receiver: Receive;
type Error;
fn send_start(&mut self, address: u8) -> nb::Result<&mut Self::Sender, Error<Self::Error>>;
fn receive_start(&mut self, address: u8) -> nb::Result<&mut Self::Receiver, Error<Self::Error>>;
} |
Are there any developments on the subject or has it been decided not to build a shared public interface for the non-blocking i2c? |
I'm not very experienced with this, but I'm wondering, why there is such a major distinction between this at all and why blocking is the default here. IdeaSo my naive idea of an I2C trait would be something like this:
A HAL implementation could then for example implement a non-blocking variant and implement the blocking variant by using busy-waiting or - if there can only be a blocking implementation - use the same function for both of it. I seems like this is the way stm32f1xx_hal implements it. Driver implementations would then be able to choose the according function for their implementation and save some (precious) μC cycles which are currently wasted by busy-waiting. ProblemsThis would be a breaking change. But don't we use semantic versioning for that very reason?
|
Because it's much easier to implement.
There're different kinds of "non-blocking". There's the kind where you basically just setup the data, point the peripheral to it and say do it and then just check whether the operation is done (i.e. poll the status) or receive a notification (i.e. an interrupt) that's how DMA works. Problem: You may or may not be able to set up multiple operations in flight so you have to be able to reject a new transaction until the previous one has finished. And the caller needs to be able to deal with that situation. Then there's a "software" implementation of the above. Problem here is: You need to have an internal buffer to handle store the in-flight data and you need to peridiocally poll the implementation (or use interrupts which really can not be done in a generic fashion internally) to make progress by spoon-feeding the next piece of data into the peripheral. Problems: Buffers are complicated to implement, especially on lowlier MCUs without atomic operations and sizing is always going to be a big fun. Also you usually cannot stretch the clock forever so you need to poll frequently to make progress. It's tricky to model an interface that works for both cases, and allows for (and actually requires) regular polling to check the status or make progress plus of course the application using that interface needs to be able to handle with errors which happen asynchronously. |
Thanks for the good summary 👍 . However, I still think, that a generic At the moment, every driver implementation requires the I2C object to implement |
Not sure I follow. When you say "driver", are you talking about the HAL impls? We're usally using the word driver for crate that takes an impl of the generic traits and converts them into a an API usable by applications. Of course the HAL impl needs to implement the whole interface at once which is why it has to be simple and generic.
This does not really work for I2C because you need to deal with the addressing and the conditions, which the original proposal in this issue did by exposing a low level interface. |
I think, we are talking about a different level of asynchronicity here. Indeed, this is not really nonblocking, because the software would still have to block when there is an ongoing I2C transfer and you want to initialize a second transfer. But it would at least speed up patterns like In contrast, I understood the original proposal like you need a FIFO and a distinct thread that starts the next transfer as soon as the previous one has finished.
That is what I meant by driver, too. |
I don't think so.
Sure. Depending on how you do your I2C communication you will still be able to do other computation.
It could be truly non-blocking in this case, but somehow you need to manage the data and DMA accesses and that is simply not easy to abstract over generically.
You absolutely need an software arbiter taking care of the bus accesses and feeding the data into the peripheral, just as I mentioned before. A separate task is a way of doing that and indeed how most embedded OSes do that but it's not really applicable here. There might easy specialized implementations with a trivial interface for some MCUs but again we're looking for a universally useful abstraction here.
Please note that there's literally no use in having a super driver that efficiently uses special traits for which there's exactly the two mandatory proving implementations. You might as well create and use a MCU specific optimized implementation of that driver then. I'm convinced there's a way to provide so an interface which is easy enough to use and can be universally implemented but it will require quite a bit of work and trial-and-error to come up with something that is acceptable for inclusion here. Please feel free to take on this task and come up a concrete proposal for such an interface. If you need help or testers, feel free to ping us on matrix or here and I'm sure there're plenty of people glad to help and figure this out with you. |
Would replacing all |
Will this still be relevant with the |
50: Embedded hal 1.0.0 alpha.3 r=ryankurte a=eldruin Builds on and superseeds rust-embedded#43 and rust-embedded#48. Co-authored-by: Diego Barrios Romero <[email protected]>
It's unfortunate that there's no nonblocking variant of the
i2c
HAL traits -- but that could easily change.My first impression is that it should look something like:
If this is stabilized, there could be default implementations of
blocking::i2c
in terms of it, simplifying driver writers' lives.Alternatives
The nonblocking trait could operate at a higher-level (basically just mirroring the traits exposed by
blocking::i2c
except with anb::Result
. The above trait has lots of potential for runtime errors, which may be undesirable.The text was updated successfully, but these errors were encountered: