-
Notifications
You must be signed in to change notification settings - Fork 2k
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
[POC/RFC/WIP] periph/gpio: Proposal for the change of the GPIO API #12877
Conversation
This looks pretty solid. I especially like how you made the compiler be able to optimize most of it away in the common case :) I see that updating all the ports & drivers will be a larger undertaking, so is there some fall-back possible so we don't have to update everything at once? |
Yeah! +100. And @gschorcht, thanks for your persistence and patience!! |
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.
OK. Lets sum up the pros and cons:
- 👍👍👍 this finally allows to use GPIOs regardless of whether the GPIOs a peripheral or external ones
- 👍 this provides access to whole GPIO ports, a feature lacking in the current GPIO API
- 👍 the internal API reflects the inner workings of most GPIO hardware very well, which eases porting RIOT to other devices
- 👍 the GPIO pin API is now shared code implemented on top of the GPIO port access API, which allows to share code
- 👎 the implementation increases ROM size
- The increased ROM size is due to the additional features provided and justified by them
- 👎 the time to access GPIO pins using the GPIO pin API without LTO is a bit slow
- With LTO this is likely no longer the case
- Using the GPIO port API directly is at least as fast and sometimes faster then the current GPIO API; the very few uses cases requiring fast GPIO access can use this API instead
- A high level GPIO API is objectively not the right tool for "as fast as possible" GPIO access
So to me, the pros clearly outweight the cons and this is the route to go.
I see two ways to tackle this:
|
I will resume my work on this in next few weeks. I think, I have a possible approach.
|
b1cb9e9
to
a09e49e
Compare
something's odd with the disque instance on riot-ci, I'm on it. |
The changes in `cpu/stm32f1` depend on `cpu/stm_common` and are not compilable separately.
7732a30
to
23783b7
Compare
I really would like to have this in. And now with the release branch just having detached from The approach of v5 seems to be settled upon. However, I would like to change the approach of getting this in, as a complete ad-hoc replacement of all GPIO implementations and all users IMHO just does not scale. Thus, I propose to do it similar to how it is planned for
|
Thank you for picking up the ball again 😄 I still have it on my ToDo list and I would also like to get it in. I agree that it's now the best time to restart the work on it. My plan was to open a new PR with approach V5 next days (or next weeks 😟).
It is not really clear to me why a different approach is necessary. In fact, the approach proposed in this PR is probably very similar to that you suggested. It isn't an ad-hoc approach. It will let coexist old As a proof of this concept I only changed the implementations for AVR and STM32 and kept the others untouched. Murdock was happy with compilation including all tests and all existing drivers for all platforms. What are the steps of the approach proposed and already realized by this PR?
Since all drivers and all existing applications always use the high level API (with the exception of the From my point of view, a realistic goal for the next release could be to define the new low level API and to provide the MCU implementations for AVR and STM32. |
Perfect :-)
Sounds good. Can you split this into three PRs? One for the basic API, and one for each implementation? I think if more people help out with it, it should also be possible to get some implementations more into master. Maybe we should add a tracking issue for the implementations as check-box list with assigned coders. That could prevent people from accidentally doing the same work without contributing to each other. (Btw: I have boards featuring the following MCU familys at hand: AVR, STM32 (F0, F1, F3, F4, F7), nRF51, nRF52, ESP8266, ESP32, and SAM3. So I could review / test / implement for some of those.) |
Sure, that was my plan 😉 as I wrote in #12877 (comment):
Since this PR served as POC/RFC, it was better to have all commits in one PR.
That would be great. These are exactly the same platforms I have, except for STM32 F0, F3 and F7. |
@gschorcht, it would be good if we could move forward here. With @fjmolinas, we have a lot of hardware for testing (stm32f0/l0/l1/l4/g4/wb, sam0, kinetis, nrf, esp, efm32). |
I will resume the work on in in next days. |
I'm going to resume my work on this PR now. All approaches tested here use some preprocessor conditionals to optimize the call of the low-level MCU functions regarding ROM size, RAM size and performance when no GPIO extenders are used. I wonder if we should really make such a difference or if the low-level MCU functions should always be called the same way, regardless if GPIO extenders are used or not, to ensure the same timing. Having always the same timing could be important when implementing bit-banging protocols. |
Both GPIO expanders and bit-banging are rather niche use cases. Let's not over-complicate / degrade things for the unlikely case that both are used at the same time. |
Both ESP32 as well as ESP8266 are using a bit-banging implementation 😉 ESP8266 because there is no hardware controller, the ESP32 because the hardware implemention is buggy and supports only |
Well, I once tried to implement bit-banging the single-wire protocol used by the WS281x RGB-LEDs. I was able to get this working on STM32F4 and STM32F7 (and it also works on ESP32), even though the generated signal was actually out of spec. But on Cortex M0 and Cortex M0+, the GPIO API does not allow matching the timing constraints well enough, as the overhead of the (With that API the WS281x driver works reliable on a bluepill and the timings where indeed within spec.) So: With the API not being useful for bit-banging with strict timing constraints anyway, I don't think we should worry about this here. |
When ported to the GPIO ABC API, those could move to (And also when more I2C buses are needed, a software solution would be quite handy as well.) |
@aabadie When I tried to rebase this PR, I realized that the GPIO port definitions for STM32 MCUs had been restructured. In previous versions, there was a definition of available ports as an enumeration for each MCU in For the proposed GPIO API, we need a definition of RIOT/cpu/stm32l0/include/periph_cpu.h Lines 39 to 60 in 23783b7
GPIO_PORT_EXT was just defined as the last value the enumeration of avialable ports.
Do you think it would be better to define |
Closed in favor of PR #14602 |
Contribution description
Based on the discussion and the ideas of @maribu and @kaspar030 discussed in PR #12333, this PR provides a proposal and a proof of concept for a change to the GPIO API. The design goals of the change are:
GPIO_PIN(port, pin)
. The access is always done with the functionsgpio_*
.For that purpose, the GPIO API is divided into a low-level API and a high-level API:
The low-level API provides functions for port-oriented access to the GPIO pins. It must be implemented by any hardware component that provides GPIO pins. The port-oriented functions of the low-level API are
The functions of the low-level API are used via a
gpio_driver_t
type driver. This driver defines the interfaces of the low-level functions and contains references to these functions of the respective hardware component. It is thus used for mapping the low-level function calls to the implementation of the hardware component.The low-level API implementation of the MCU has to be done in the functions
gpio_cpu_*
. For performance reasons these functions are used to access the GPIO ports of the MCU via a specialgpio_driver_t
instance, thegpio_cpu_driver
driver.The high-level API is the previous GPIO API and remains unchanged. It is intended for use by the application and allows pin-oriented access to the GPIO pins.
However, the implementation of the high-level API is no longer realized directly by the MCU. Rather, the functions of the high-level API are implemented as static inline functions that use the functions of the low-level API of the respective GPIO hardware component.
Implementation Details
A GPIO pin of type
gpio_t
is now defined by a GPIO port of typegpio_port_t
and a pin number.gpio_t
can't be overridden anymore.The GPIO port of type
gpio_port_t
can be eithergpio_dev_t
which defines any GPIO hardware component, e.g. a GPIO expander device.The device data structure of type
gpio_dev_t
containsgpio_driver_t
for using the device where the driver which maps the function calls of the low-level API to the implementation of the GPIO device.In case of MCU GPIO ports, the port defines just the register address. The mapping to the functions of the low-level API is realized by the special driver instance
gpio_cpu_driver
which is created automatically from the MCU low-level functionsgpio_cpu_*
.Port-oriented functions of low-level API
use a mask or multiple bits that represent the pins of a GPIO port. The width of this mask has to be the maximum width of all different GPIO ports used in the system. For this purpose, each component that provides GPIO ports (MCUs and GPIO device drivers) must enable the corresponding pseudo module that specifies the width of its GPIO ports:
gpio_mask_8bit
,gpio_mask_16bit
,gpio_mask_32bit
.Due to the structured GPIO data type, the comfort functions
gpio_is_equal
andgpio_is_undef
are introduced to check whether GPIOs are equal or undefined.MCU changes
The following changes are required for each MCU:
gpio_cpu_*
according to the interface defined ingpio_driver_t
. In most cases, only a very small number of changes of existing MCU implementations are required.gpio_mask_8bit
,gpio_mask_16bit
orgpio_mask_32bit
has to be enabled.cpu/*/include/gpio_arch.h
. These are:GPIO_CPU_REG_MASK
as the part of a given address that uniquely identifies the address as a register address.GPIO_CPU_REG_GPIO
that identifies an address masked withGPIO_CPU_REG_MASK
as GPIO register address.See for example the file
cpu/stm32_common/include/gpio_arch.h
.cpu/*/include/periph_cpu.h
must defineGPIO_CPU_PORTS
. The index of the port with the register address must match the defined ordinal number of the port. This Definition is used to generate the port listgpio_ports[]
.PORT_EXT
as last definition in the port enumeration type. This will make possible to use port names for GPIO expander devices.See for example the file
cpu/stm32l0/include/periph_cpu.h
.All defined ports are stored in array
gpio_ports
.The MCU and GPIO expander drivers have to implement function according to the prototypes defined as members of the GPIO device driver
gpio_driver_t
.The PR provides the changes for STM32 and ATmega MCUs as examples. The changes of all peripheral drivers of these MCUs are already changed an tested.
Board Changes
Many boards should work without changes. Only boards that use direct register access for GPIOs, e.g. to handle the LEDs, have to be changed.
Driver Changes
Many drivers should work without changes. Only drivers that compare a pin directly to
GPIO_UNDEF
need to be changed. The comparison has to be replaced by the functiongpio_is_undef
.Testing procedure
Sinec the PR provides the changes for STM32 and ATmega MCUs as examples, Nucleo-64 boards and ATmega boards can be used to try the concept, for example:
The latter one enables the possibility to use GPIO expander devices with small code size increase and small performance decrease.
Issues/PRs references
Initial ideas of a GPIO extension API were discussed in issue #9690 and PRs #9860 and #9958.
An updated version of a GPIO extension API was provided and discussed in #12333.
With the availability of a GPIO extension API, the drivers in PRs #10430 and #10688 could used.