-
Notifications
You must be signed in to change notification settings - Fork 233
OutputPort proposal #134
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
OutputPort proposal #134
Conversation
Shamefully stolen from @japaric's issue rust-embedded#30
Thanks for the pull request, and welcome! The Rust team is excited to review your changes, and you should hear from @ryankurte (or someone else) soon. If any changes to this PR are deemed necessary, please add them as extra commits. This ensures that the reviewer can see what has changed since they last reviewed the code. Due to the way GitHub handles out-of-date commits, this should also make it reasonably obvious what issues have or haven't been addressed. Large or tricky changes may require several passes of review and changes. Please see the contribution instructions for more information. |
Here's a sample implementation: impl OutputPort<u32> for Port {
fn write(&mut self, word: u32) {
unsafe {
(*PORT::ptr()).outset.write(|bits| {
bits.bits(word)
});
}
}
} |
hey thanks for the PR! looks pretty good to me. the only question i have is how we define / configure buses of widths that are not an existing (standard integer width) type, and can this be made clear in the interface and/or documentation? (cc. #30) |
We talked about this a bit on IRC earlier today. I think there're few other problems with the comments and I'd certainly would like to see a slightly more complete impl doing something other than simply exposing the full |
Maybe we could have a mask parameter, or use the uX crate? |
#30 (comment) |
I've made testing implementation of |
Thanks for your test implementation @burrbull. /// A digital output "port"
///
/// `N` is number of pins in "port"
///
/// `Width` is the size of the data; it could be `u8` for an 8-bit parallel
/// port, `u16` for a 16-bit one, etc.
///
/// **NOTE** The "port" doesn't necessarily has to match a hardware GPIO port;
/// it could for instance be a 4-bit ports made up of non contiguous pins, say
/// `PA0`, `PA3`, `PA10` and `PA13`.
#[cfg(feature = "unproven")]
pub trait OutputPort<const N: u8, Width> {
/// Error type
type Error;
/// Outputs `word` on the port pins
///
/// # Contract
///
/// The state of all the port pins will change atomically ("at the same time"). This usually
/// means that state of all the pins will be changed in a single register operation.
fn write(&mut self, word: Width) -> Result<(), Self::Error>;
} A few questions/remarks:
|
What if limit trait to |
If you have looked at implementation example, you could see that it support all combinations of pins located on 1 real port. |
@eldruin #[cfg(feature = "unproven")]
pub trait OutputPort<const N: usize> {
/// Error type
type Error;
/// Outputs `word` on the port pins
///
/// # Contract
///
/// The state of all the port pins will change atomically ("at the same time"). This usually
/// means that state of all the pins will be changed in a single register operation.
fn write(&mut self, word: u8) -> Result<(), Self::Error>;
}
#[cfg(feature = "unproven")]
impl<PIN, const N: usize> OutputPort<N> for [PIN; N]
where
PIN: OutputPin,
{
type Error = PIN::Error;
fn write(&mut self, word: u8) -> Result<(), Self::Error> {
for i in 0..N {
if word & (1 << i) != 0 {
self[i].set_high()?;
} else {
self[i].set_low()?;
}
}
Ok(())
}
} and for tuples: macro_rules! port_tuple {
($N:literal, ($($i:literal),+), ($($P:ident),+), ($($b:ident),+)) => {
impl<$($P),+, E> OutputPort<$N> for ($($P),+)
where
$($P: OutputPin<Error=E>),+,
{
type Error = E;
fn write(&mut self, word: u8) -> Result<(), Self::Error> {
let ($($b),+) = self;
$(
if word & (1 << $i) != 0 {
$b.set_high()?;
} else {
$b.set_low()?;
}
)+
Ok(())
}
}
}
}
port_tuple!(2, (0, 1), (P0, P1), (b0, b1));
port_tuple!(3, (0, 1, 2), (P0, P1, P2), (b0, b1, b2));
port_tuple!(4, (0, 1, 2, 3), (P0, P1, P2, P3), (b0, b1, b2, b3));
port_tuple!(
5,
(0, 1, 2, 3, 4),
(P0, P1, P2, P3, P4),
(b0, b1, b2, b3, b4)
);
port_tuple!(
6,
(0, 1, 2, 3, 4, 5),
(P0, P1, P2, P3, P4, P5),
(b0, b1, b2, b3, b4, b5)
);
port_tuple!(
7,
(0, 1, 2, 3, 4, 5, 6),
(P0, P1, P2, P3, P4, P5, P6),
(b0, b1, b2, b3, b4, b5, b6)
);
port_tuple!(
8,
(0, 1, 2, 3, 4, 5, 6, 7),
(P0, P1, P2, P3, P4, P5, P6, P7),
(b0, b1, b2, b3, b4, b5, b6, b7)
); |
How does your
|
It is just dumb implementation above for HALs which don't have native support of OutputPort. More relevant implementation example is in https://github.com/stm32-rs/stm32f4xx-hal/blob/d94044188defb82583bd561fe8fb3363932454cb/src/gpio/outport.rs |
Thanks for the simplified macro here @burrbull. I understood now how it solves the combination problem nicely. These other questions remain:
It should be noted that we have generic |
This is main question. |
An example of a device that exposes 16 IO pins which can be driven at the same time is the classic I2C PCF8575. (The datasheet talks about two ports because the data for them is sent in two bytes but this note is irrelevant here.) |
426: OutputPort r=therealprof a=burrbull ~~This is draft of [`OutputPort<u8>`](rust-embedded/embedded-hal#134 Co-authored-by: Andrey Zgarbul <[email protected]>
Shamefully stolen from @japaric's issue #30.
I needs for my parallel LCDs.