-
Notifications
You must be signed in to change notification settings - Fork 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
[gpio] Runtime GPIO with virtual interface #631
base: develop
Are you sure you want to change the base?
Conversation
👍 for doing more research on this. I am not fully convinced yet a Generally value semantics and
This case could be prevented by making the assignment operator inaccessible but would not solve that users want to be able to do that. Or trying to put different gpio types into a container. You either have the slicing problem or have to store pointers. That's why I came up with #543 initially. I would strongly prefer a value-semantics interface. I am also unsure how an interface including port expanders could work in practice, especially with the concurrency model of resumable functions. Any operation on these gpio would need to be blocking and operations that fail can't report errors. I would like to see how the |
Config : uint32_t | ||
{ | ||
/// @cond | ||
_Mode = 0x8000'0000, |
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.
Identifiers starting with _
+ upper case letters are reserved in C++ for the standard library implementation.
I think if we have the overhead for runtime GPIO then we should accept 2-3 cycles overhead per virtual call. I need to check if the virtual tables get stored in Flash or RAM. I hope its Flash, otherwise it would be dumb…
Not just that. I've noticed particularly on STM32 that the differences between peripheral instances is just the The thing is that considering the overhead of the actual operation (ie. I2C transactions take forever, pushing a byte into UART isn't fast either), there's hardly any timing advantage for passing the Uart, Spi, I2c driver via template parameter, but excessive code bloat if you use the same external driver with different peripherals. For GPIO I want to have both static and runtime APIs, since things like SoftwareGpioPort are much faster with templates.
Yeah, I already fell into this trap… 😒 I think there needs to be a bunch of predefined extern modm::platform::Spi spi2;
extern modm::platform::Gpio gpioA2;
modm::IoExpander io_expander(/* modm::Spi& */ spi2, /* modm::Gpio& */ gpioA2);
modm::IoExpander::Gpio pin4(io_expander.pin(4));
modm::Driver driver(/* modm::Gpio& */ pin4);
Yes, unfortunately that's how the adapters works today. It uses Not sure about error reporting, that would probably need to be done with
Hm, not an expert at /*template<typename GpioTypes...>*/
Driver<modm::platform::Gpio, modm::IoExpander::Gpio>
driver(/*std::variant<GpioTypes...>*/ chip_select); |
I played around with gcc to figure out if it optimizes out virtual calls in debug mode where it knows at compile time which method to call. This is the case if the dynamic type is statically known or it can prove the called method can't be overridden ( |
For time-critical tasks like bit-bang I would remain with a template-based interface. It could be a solution to allow the user to specify the gpio type as a template argument and have them pass an instance to the constructor. The type could be any runtime gpio. the static gpio class or any other gpio class from a port expander. It could nevertheless be quite easy to use with class-type template argument deduction.
EDIT: in case you have the same template arguments this would still prevent code bloat, but you have the possibility to choose |
Yes, the use case was not entirely clear to me at that point. You would need to do that. |
This is a research PR to understand what is needed for a good runtime GPIO interface.
modm::Gpio
with a minimal virtual interface.modm::GpioPort
with a minimal virtual interface.modm::platform::RtGpio
runtime classmodm::platform::Gpio
unless that causes ambiguitymodm::GpioInput
,modm::GpioOutput
andmodm::GpioIO
with better namesThe new
configure(Config_t)
method allows configuring the GPIO in it's entirety with one 32-bit value (perhaps 16-bit on AVR). It's designed so that only the Flags that you put in get changed, so you can incrementally change the IO settings. Its extremely fast, since ARMv7-M uses the UBFX instruction to get the individual flags from the 32-bit value.This reduces the size of the virtual interface significantly. I would still keep the explicit interface for the static-type GPIO classes, since they cost nothing if not used, however also adding a configure method, since the compiler will likely optimize it all away for constant input values.
The
Config_t
values should ideally also be usable for other IO configs found on external IO expanders for example, that should then also be implemented using the virtual interface! This means there must be a minimal interface that may of course be value-optimized for the internal implementation, but still be useful for external implementations too.There are some issues about naming things. Intuitively I'd call the interface
modm::Gpio
and the implementationmodm::platform::Gpio
, but it may lead to some ambiguity, whenusing namespace modm::platform;
andusing namespace modm;
. Although the latter is not recommended. It's probably not a big issue in practice.cc @chris-durand @rleh