-
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
[RFC] digital::OutputPort
, atomically drive several pins
#30
Comments
Never thought about this, but you are right, without an atomic set the communication timing could become broken by interrupts. But, wouldn't we than need an |
Possibly. You let me know what you need for your driver. :-) |
I like the idea of being able to drive multiple pins separately as a single atomic thing -- for the timing reasons you've mentioned. However, for the purpose of HD44780 LCD, I think, timings on data output are less important than being able to use pins from different GPIO ports (like GPIOA/GPIOB). Even if bit operation is interrupted by, well, interrupt, it does not matter for HD44780 as data is transferred via dedicated "pulse" on EN bit after all data pins are set. Given that flexibility of LCD, it would be tempting to assign pins to it which are "left" from the more demanding devices (like those requiring timer PWM, atomic operations on across multiple pins, etc), which could lead to the situation when all pins are taken from different ports. Theoretically, you can design this trait to be able to span multiple ports and make it "do its best" to do as little operations as possible... |
@idubrov in that case I think we can split the atomic property into a marker trait so that |
I have done some quick testing and @idubrov seems to be right, for the HD44780 LCD driver the timing doesn't matter. The only situation where the output can become corrupted is an Interrupt that changes the state of the display Pins. Now, the question is, should preventing that be a part of the embedded-hal crate? If not I will go by with the |
Well, in an ideal world, where you can have a perfect cut & slice on your GPIO (like all pins of GPIOA+GPIOB+GPIOC => Slice1 + Slice2 + Slice3), your interrupt should not have "mutable"/"exclusive" access to the same slice as your non-interrupt code writing to the display. Regarding Atomic, my concern (could be unfounded, I haven't really done a lot of thinking), is that if you go for that "perfect solution" so to speak, with all these Atomic traits, ability to mix and match multiple pins, etc, wouldn't the complexity make it hard to use/implement? |
Though, if you can have it, I would certainly love the solution that allows me to:
|
I see you guys are all very ST centric. ;) Are few things are more awkward in the ST world (like having different GPIO banks and sometimes high/low registers) while others are quite a bit more comfortable, like not having to use 10 different registers to send/receive data on an I2C port or having to manually reset events after them being triggered. However one thing that pretty much all devices have in common is that often registers have to be split into individual bits or group of bits and ideally the control over the bits should be movable into abstracted types which cannot easily be done at the moment. This should be abstracted in a way that allows a type to exercise control over exactly the required bits but nothing more which in some cases may mean it be done atomically (either due to the availability of bitbanding or separate set/clear registers) while in other cases it will mean that RMW is required. |
@idubrov all those sound like configuration details that both the trait and the generic driver author don't have to concern themselves about. Do you think the proposed trait would get in the way of implementing any / all of that configurability? I don't think so but won't know for sure until I sit down and try to implement it. I don't have a use case that requires implementing something so elaborated though so don't count on me implementing that.
if you don't have bit banding you can do the RMW operation in a critical section and not require the token. |
Bump. Parallel writes to output pins are a necessity for many usecases, especially LCD drivers. |
Shamefully stolen from @japaric's issue rust-embedded#30
@sajattack In the mean time, if you can add additional hardware, you can use a cheap I/O port expander, which allows you to set all 8/16 outputs at once. See pcf857x. |
Consider the likely performance difference between consecutive and non-consecutive pins in a bus. Say we have 32 pins controlled by a single u32 register, GPIO->DATA_OUT. Where P0 is controlled by bit 0, P1 is controlled by bit 1, etc. If we want to write a u8 to P8:P15 or P19:P26 (in the same bit to pin order), all we need to do is left-shift that u8, mask the controlled pins out of the current register value, and logical-or the two results to get the new register value. This only requires we know:
If we want to write a u8 to P0, P2, P4, P6, P8, P10, P14, and P16 (regardless of order), it gets much more complicated and less optimizable. There are two options: (1) lookup table taking up a lot of memory or (2) bit-wise iteration. I think 1 is mostly self-explanatory, but for 2 you'd need to know:
Then you have to iterate through each bit of the u8, testing if the bit is a 1 and logical-or ing the corresponding masks together with the masked old register value. The performance/memory implications of this increase linearly with the size of the values to write and read. I think there should be two types of output bus, in order to accommodate these two very different cases. |
hmm, interesting. afaik most processors have some bitband registers that should make that more efficient than otherwise, but it's still not always going to be efficient. what about a single bus type (so drivers don't have to care) with a marker trait to indicate / support a requirement for pseudo-atomic / single operation implementations? |
My main concern is to make it abundantly clear if such a penalty will be required for a given setup. Another confounding factor is that some micros provide a layer with which to remap certain pins to different bits on the bus. For instance, P11 could be remapped to bit 3. |
I think we need two traits to support discontiguous pins: OutputPort and FlexibleOutputPort. OutputPort has a the I've been thinking of ways to implement this in an actual hal. I like the idea of passing a range of If they aren't contiguous then an array of OutputPins would be passed into |
Should this trait be: pub trait OutputPort<Width> {
type Error;
fn output(&mut self, word: Width) -> Result<(), Self::Error>;
} or pub trait OutputPort {
type Error;
type Width;
fn output(&mut self, word: Self::Width) -> Result<(), Self::Error>;
} in |
I think any given OutputPort should only support one width, so the second makes the most sense idiomatically in rust. If anyone can think of an instance where that wouldn't hold, please say so. Additionally, with the associated type, you could share the width between the OutputPort and FlexibleOutputPort if it's decided to go in that direction. |
Maybe we should get a reference design of this feature; that could be built into stm32f30x-hal crate or somewhere else. |
I have another use case for changing the output value of several pins at once: |
30: Add support for character device GPIO r=posborne a=almusil Co-authored-by: Ales Musil <aedvin1@gmail.com>
Has there been any progress on this? It's incredibly common to do this in C. |
There are several questions needed to be solved: #134 (comment) You could discuss them on Tuesday matrix chat meeting. |
In the v0.1.0 release of this crate we have a
digital::OutputPin
that represents a single digital output pin. That trait is useful for, e.g., implementing the NSS pin of a SPI interface but not enough to implement 8 or 16 bit parallel port interfaces where all the pins need to change at the same time (atomically) to meet timing requirements (e.g. LCD interfaces) -- using 16impl OutputPin
would result in them changing state at different time intervals.I think the obvious trait to use would be the following:
cc @kunerd
The text was updated successfully, but these errors were encountered: