From 2a00ec13e5a23e87686428c78e12a03373edcf16 Mon Sep 17 00:00:00 2001 From: Marian Buschsieweke Date: Wed, 27 Dec 2023 19:37:03 +0100 Subject: [PATCH] drivers/periph/gpio_ll: shrink gpio_conf_t This commit optimizes the `gpio_conf_t` type in the following regards: - The "base" `gpio_conf_t` is stripped from members that only some platforms support, e.g. drive strength, slew rate, and disabling of the Schmitt Trigger are no longer universally available but platform-specific extensions - The `gpio_conf_t` is now crammed into a bit-field that is 8 bit or 16 bit wide. This allows for storing lots of them e.g. in `driver_foo_params_t` or `uart_conf_t` etc. - A `union` of the `struct` with bit-field members and a `bits` is used to allow accessing all bits in a simple C statement and to ensure alignment for efficient handling of the type Co-authored-by: Gunar Schorcht --- cpu/atmega_common/include/periph_cpu_common.h | 8 -- cpu/atmega_common/periph/gpio_ll.c | 4 - cpu/efm32/periph/gpio_ll.c | 11 -- cpu/esp32/include/periph_cpu.h | 59 +++++++- cpu/esp32/periph/gpio_ll.c | 24 +++- cpu/gd32v/include/periph_cpu.h | 76 +++++++++++ cpu/gd32v/periph/gpio_ll.c | 27 ++++ cpu/nrf5x_common/include/periph_cpu_common.h | 67 ++++++++- cpu/nrf5x_common/periph/gpio_ll.c | 24 +++- cpu/stm32/include/periph/cpu_gpio_ll.h | 94 ++++++++++++- cpu/stm32/periph/gpio_ll.c | 34 +++++ drivers/include/periph/gpio_ll.h | 128 ++++++++---------- drivers/periph_common/gpio_ll.c | 63 --------- tests/bench/periph_gpio_ll/main.c | 3 - 14 files changed, 449 insertions(+), 173 deletions(-) diff --git a/cpu/atmega_common/include/periph_cpu_common.h b/cpu/atmega_common/include/periph_cpu_common.h index 4a9910a535..bf6627072f 100644 --- a/cpu/atmega_common/include/periph_cpu_common.h +++ b/cpu/atmega_common/include/periph_cpu_common.h @@ -231,14 +231,6 @@ typedef enum { GPIO_TRIGGER_LEVEL_HIGH = 0xff, /**< not supported */ } gpio_irq_trig_t; -#define HAVE_GPIO_PULL_T -typedef enum { - GPIO_FLOATING = 0, - GPIO_PULL_UP = 1, - GPIO_PULL_DOWN = 0xfe, /*< not supported */ - GPIO_PULL_KEEP = 0xff, /*< not supported */ -} gpio_pull_t; - #define HAVE_GPIO_LL_PREPARE_WRITE_ALL_PINS #define HAVE_GPIO_LL_PREPARE_WRITE diff --git a/cpu/atmega_common/periph/gpio_ll.c b/cpu/atmega_common/periph/gpio_ll.c index 405a3589a6..fc3b9b2538 100644 --- a/cpu/atmega_common/periph/gpio_ll.c +++ b/cpu/atmega_common/periph/gpio_ll.c @@ -84,10 +84,6 @@ void gpio_ll_query_conf(gpio_conf_t *dest, gpio_port_t port, uint8_t pin) { assert(dest); memset(dest, 0, sizeof(*dest)); - /* E.g. the schematics in figure 14-5 in the ATmega328P datasheet shows that - * a Schmitt Trigger is always connected before the digital input signal. - * Let's assume this is also true for all other ATmegas */ - dest->schmitt_trigger = true; if (_is_output(port, pin)) { dest->state = GPIO_OUTPUT_PUSH_PULL; dest->initial_value = (gpio_ll_read_output(port) >> pin) & 1U; diff --git a/cpu/efm32/periph/gpio_ll.c b/cpu/efm32/periph/gpio_ll.c index f076bc770a..ba347007fb 100644 --- a/cpu/efm32/periph/gpio_ll.c +++ b/cpu/efm32/periph/gpio_ll.c @@ -141,16 +141,5 @@ void gpio_ll_query_conf(gpio_conf_t *dest, gpio_port_t port, uint8_t pin) break; } - /* as good as any */ - dest->slew_rate = GPIO_SLEW_FAST; - - /* It's always on as long as they're in a mode in which it matters, judging - * from https://www.silabs.com/documents/public/application-notes/an0027.pdf */ - dest->schmitt_trigger = true; - dest->initial_value = (gpio_ll_read_output(port) >> pin) & 1; - - /* Using 'strong' her already as that fits with what the hardware has - * (lowest, low, standard, high) */ - dest->drive_strength = GPIO_DRIVE_STRONG; } diff --git a/cpu/esp32/include/periph_cpu.h b/cpu/esp32/include/periph_cpu.h index 6e0623533f..b00f0ee6d1 100644 --- a/cpu/esp32/include/periph_cpu.h +++ b/cpu/esp32/include/periph_cpu.h @@ -190,7 +190,7 @@ typedef enum { GPIO_FLOATING = 0, GPIO_PULL_UP = 1, GPIO_PULL_DOWN = 2, - GPIO_PULL_KEEP = 0xff /*< not supported */ + GPIO_PULL_KEEP = 3 /*< not supported */ } gpio_pull_t; /** @@ -212,9 +212,64 @@ typedef enum { #define GPIO_DRIVE_20 GPIO_DRIVE_STRONG /**< 20 mA (default) */ #define GPIO_DRIVE_30 GPIO_DRIVE_STRONGEST /**< 30 mA */ -/* END: GPIO LL overwrites */ +#define HAVE_GPIO_STATE_T +typedef enum { + GPIO_OUTPUT_PUSH_PULL, + GPIO_OUTPUT_OPEN_DRAIN, + GPIO_OUTPUT_OPEN_SOURCE, + GPIO_INPUT, + GPIO_USED_BY_PERIPHERAL, + GPIO_DISCONNECT, +} gpio_state_t; + +#define HAVE_GPIO_CONF_T +typedef union gpio_conf_esp32 gpio_conf_t; #endif /* ndef DOXYGEN */ + +/** + * @brief GPIO pin configuration for ESP32/ESP32Cx/ESP32Sx MCUs + * @ingroup drivers_periph_gpio_ll + */ +union gpio_conf_esp32 { + uint8_t bits; /**< the raw bits */ + struct { + /** + * @brief State of the pin + */ + gpio_state_t state : 3; + /** + * @brief Pull resistor configuration + */ + gpio_pull_t pull : 2; + /** + * @brief Drive strength of the GPIO + * + * @warning If the requested drive strength is not available, the closest + * fit supported will be configured instead. + * + * This value is ignored when @ref gpio_conf_esp32::state is configured + * to @ref GPIO_INPUT or @ref GPIO_DISCONNECT. + */ + gpio_drive_strength_t drive_strength : 2; + /** + * @brief Initial value of the output + * + * Ignored if @ref gpio_conf_esp32::state is set to @ref GPIO_INPUT or + * @ref GPIO_DISCONNECT. If the pin was previously in a high impedance + * state, it is guaranteed to directly transition to the given initial + * value. + * + * @ref gpio_ll_query_conf will write the current value of the specified + * pin here, which is read from the input register when the state is + * @ref GPIO_INPUT, otherwise the state from the output register is + * consulted. + */ + bool initial_value : 1; + }; +}; + +/* END: GPIO LL overwrites */ /** @} */ /** diff --git a/cpu/esp32/periph/gpio_ll.c b/cpu/esp32/periph/gpio_ll.c index af02263d30..582debd8e3 100644 --- a/cpu/esp32/periph/gpio_ll.c +++ b/cpu/esp32/periph/gpio_ll.c @@ -38,6 +38,15 @@ #include "esp_idf_api/gpio.h" +#ifdef MODULE_FMT +#include "fmt.h" +#else +static inline void print_str(const char *str) +{ + fputs(str, stdout); +} +#endif + /* variables that have to be used together with periph/gpio */ #ifdef ESP_PM_WUP_PINS extern bool _gpio_pin_pu[GPIO_PIN_NUMOF]; @@ -142,7 +151,6 @@ int gpio_ll_init(gpio_port_t port, uint8_t pin, const gpio_conf_t *conf) /* since we can't read back the configuration, we have to save it */ _gpio_conf[gpio] = *conf; - _gpio_conf[gpio].schmitt_trigger = false; if (esp_idf_gpio_config(&cfg) != ESP_OK) { return -ENOTSUP; @@ -196,3 +204,17 @@ void gpio_ll_query_conf(gpio_conf_t *dest, gpio_port_t port, uint8_t pin) } irq_restore(state); } + +void gpio_ll_print_conf(const gpio_conf_t *conf) +{ + static const char *drive_strs[] = { + [GPIO_DRIVE_WEAKEST] = "weakest", + [GPIO_DRIVE_WEAK] = "weak", + [GPIO_DRIVE_STRONG] = "strong", + [GPIO_DRIVE_STRONGEST] = "strongest", + }; + + gpio_ll_print_conf_common(conf); + print_str(", drive: "); + print_str(drive_strs[conf->drive_strength]); +} diff --git a/cpu/gd32v/include/periph_cpu.h b/cpu/gd32v/include/periph_cpu.h index 17431ea199..ec6e437610 100644 --- a/cpu/gd32v/include/periph_cpu.h +++ b/cpu/gd32v/include/periph_cpu.h @@ -187,6 +187,24 @@ void gpio_init_analog(gpio_t pin); * public view on type */ #ifndef DOXYGEN +#define HAVE_GPIO_STATE_T +typedef enum { + GPIO_OUTPUT_PUSH_PULL, + GPIO_OUTPUT_OPEN_DRAIN, + GPIO_OUTPUT_OPEN_SOURCE, + GPIO_INPUT, + GPIO_USED_BY_PERIPHERAL, + GPIO_DISCONNECT, +} gpio_state_t; + +#define HAVE_GPIO_PULL_T +typedef enum { + GPIO_FLOATING, + GPIO_PULL_UP, + GPIO_PULL_DOWN, + GPIO_PULL_KEEP, +} gpio_pull_t; + #define HAVE_GPIO_PULL_STRENGTH_T typedef enum { GPIO_PULL_WEAKEST = 0, @@ -211,8 +229,66 @@ typedef enum { GPIO_SLEW_FASTEST = 2, } gpio_slew_t; +#define HAVE_GPIO_CONF_T +typedef union gpio_conf_gd32v gpio_conf_t; + #endif /* !DOXYGEN */ +/** + * @brief GPIO pin configuration for GD32V MCUs. + * @ingroup drivers_periph_gpio_ll + */ +union gpio_conf_gd32v { + uint16_t bits; /**< the raw bits */ + struct { + /** + * @brief State of the pin + */ + gpio_state_t state : 3; + /** + * @brief Pull resistor configuration + */ + gpio_pull_t pull : 2; + /** + * @brief Configure the slew rate of outputs + * + * @warning If the requested slew rate is not available, the closest fit + * supported will be configured instead. + * + * This value is ignored *unless* @ref gpio_conf_stm32::state is + * configured to @ref GPIO_OUTPUT_PUSH_PULL or @ref GPIO_OUTPUT_OPEN_DRAIN. + */ + gpio_slew_t slew_rate : 2; + /** + * @brief Whether to disable the input Schmitt trigger + * + * @details This could be called `schmitt_trigger` with inverse + * meaning, but the API contract says that additional + * members in the structure should have a sane + * default when zero. + * + * This value is ignored *unless* @ref gpio_conf_stm32::state is + * configured to @ref GPIO_INPUT. + */ + bool schmitt_trigger_disabled : 1; + /** + * @brief Initial value of the output + * + * Ignored if @ref gpio_conf_stm32::state is set to @ref GPIO_INPUT or + * @ref GPIO_DISCONNECT. If the pin was previously in a high impedance + * state, it is guaranteed to directly transition to the given initial + * value. + * + * @ref gpio_ll_query_conf will write the current value of the specified + * pin here, which is read from the input register when the state is + * @ref GPIO_INPUT, otherwise the state from the output register is + * consulted. + */ + bool initial_value : 1; + uint8_t : 7; /*< padding */ + }; +}; + /** * @brief Available number of ADC devices */ diff --git a/cpu/gd32v/periph/gpio_ll.c b/cpu/gd32v/periph/gpio_ll.c index 03ec181fa5..a1cd73b9f5 100644 --- a/cpu/gd32v/periph/gpio_ll.c +++ b/cpu/gd32v/periph/gpio_ll.c @@ -28,6 +28,15 @@ #define ENABLE_DEBUG 0 #include "debug.h" +#ifdef MODULE_FMT +#include "fmt.h" +#else +static inline void print_str(const char *str) +{ + fputs(str, stdout); +} +#endif + uint16_t pin_used[GPIO_PORT_NUMOF] = {}; int gpio_ll_init(gpio_port_t port, uint8_t pin, const gpio_conf_t *conf) @@ -152,3 +161,21 @@ void gpio_ll_query_conf(gpio_conf_t *dest, gpio_port_t port, uint8_t pin) } irq_restore(state); } + +void gpio_ll_print_conf(const gpio_conf_t *conf) +{ + static const char *slew_strs[] = { + [GPIO_SLEW_SLOWEST] = "slowest", + [GPIO_SLEW_SLOW] = "medium", + [GPIO_SLEW_FASTEST] = "fastest", + "invalid" + }; + + gpio_ll_print_conf_common(conf); + print_str(", slew: "); + print_str(slew_strs[conf->slew_rate]); + + if (conf->schmitt_trigger_disabled) { + print_str(", Schmitt trigger disabled"); + } +} diff --git a/cpu/nrf5x_common/include/periph_cpu_common.h b/cpu/nrf5x_common/include/periph_cpu_common.h index c6ab6d51cc..b37a4a8b1d 100644 --- a/cpu/nrf5x_common/include/periph_cpu_common.h +++ b/cpu/nrf5x_common/include/periph_cpu_common.h @@ -138,12 +138,69 @@ typedef enum { #define HAVE_GPIO_PULL_T typedef enum { GPIO_FLOATING = 0, - GPIO_PULL_UP = GPIO_PIN_CNF_PULL_Pullup << GPIO_PIN_CNF_PULL_Pos, - GPIO_PULL_DOWN = GPIO_PIN_CNF_PULL_Pulldown << GPIO_PIN_CNF_PULL_Pos, - /* GPIO_PULL_KEEP is not supported by, gpio_ll_init() returns -ENOTSUP */ - GPIO_PULL_KEEP = 0xff + GPIO_PULL_UP = GPIO_PIN_CNF_PULL_Pullup, + GPIO_PULL_DOWN = GPIO_PIN_CNF_PULL_Pulldown, + GPIO_PULL_KEEP = 2, } gpio_pull_t; -#endif /* END: GPIO LL overwrites */ + +#define HAVE_GPIO_STATE_T +typedef enum { + GPIO_OUTPUT_PUSH_PULL, + GPIO_OUTPUT_OPEN_DRAIN, + GPIO_OUTPUT_OPEN_SOURCE, + GPIO_INPUT, + GPIO_USED_BY_PERIPHERAL, + GPIO_DISCONNECT, +} gpio_state_t; + +#define HAVE_GPIO_CONF_T +typedef union gpio_conf_nrf5x gpio_conf_t; + +#endif + +/** + * @brief GPIO pin configuration for nRF5x MCUs + * @ingroup drivers_periph_gpio_ll + */ +union gpio_conf_nrf5x { + uint8_t bits; /**< the raw bits */ + struct { + /** + * @brief State of the pin + */ + gpio_state_t state : 3; + /** + * @brief Pull resistor configuration + */ + gpio_pull_t pull : 2; + /** + * @brief Drive strength of the GPIO + * + * @warning If the requested drive strength is not available, the + * closest fit supported will be configured instead. + * + * This value is ignored when @ref gpio_conf_nrf5x::state is configured + * to @ref GPIO_INPUT or @ref GPIO_DISCONNECT. + */ + gpio_drive_strength_t drive_strength : 1; + /** + * @brief Initial value of the output + * + * Ignored if @ref gpio_conf_nrf5x::state is set to @ref GPIO_INPUT or + * @ref GPIO_DISCONNECT. If the pin was previously in a high impedance + * state, it is guaranteed to directly transition to the given initial + * value. + * + * @ref gpio_ll_query_conf will write the current value of the specified + * pin here, which is read from the input register when the state is + * @ref GPIO_INPUT, otherwise the state from the output register is + * consulted. + */ + bool initial_value : 1; + uint8_t : 1; /*< padding */ + }; +}; +/* END: GPIO LL overwrites */ #if !defined(DOXYGEN) && (defined(CPU_NRF53) || defined(CPU_NRF9160)) /** diff --git a/cpu/nrf5x_common/periph/gpio_ll.c b/cpu/nrf5x_common/periph/gpio_ll.c index c03e19437c..3541f939ad 100644 --- a/cpu/nrf5x_common/periph/gpio_ll.c +++ b/cpu/nrf5x_common/periph/gpio_ll.c @@ -36,13 +36,22 @@ #include "periph_cpu.h" #include "periph_conf.h" +#ifdef MODULE_FMT +#include "fmt.h" +#else +static inline void print_str(const char *str) +{ + fputs(str, stdout); +} +#endif + int gpio_ll_init(gpio_port_t port, uint8_t pin, const gpio_conf_t *conf) { if (conf->pull == GPIO_PULL_KEEP) { return -ENOTSUP; } - uint32_t pin_cnf = conf->pull; + uint32_t pin_cnf = (unsigned)conf->pull << GPIO_PIN_CNF_PULL_Pos; switch (conf->state) { case GPIO_OUTPUT_PUSH_PULL: /* INPUT bit needs to be *CLEARED* in input mode, so set to disconnect input buffer */ @@ -115,7 +124,6 @@ void gpio_ll_query_conf(gpio_conf_t *dest, gpio_port_t port, uint8_t pin) * no matches. Assuming Schmitt trigger cannot be disabled for the * nRF5x MCU. */ - dest->schmitt_trigger = true; dest->state = GPIO_INPUT; NRF_GPIO_Type *p = (NRF_GPIO_Type *)port; @@ -197,3 +205,15 @@ void gpio_ll_query_conf(gpio_conf_t *dest, gpio_port_t port, uint8_t pin) dest->initial_value = (gpio_ll_read_output(port) >> pin) & 1UL; } } + +void gpio_ll_print_conf(const gpio_conf_t *conf) +{ + static const char *drive_strs[] = { + [GPIO_DRIVE_WEAK] = "weak", + [GPIO_DRIVE_STRONG] = "strong", + }; + + gpio_ll_print_conf_common(conf); + print_str(", drive: "); + print_str(drive_strs[conf->drive_strength]); +} diff --git a/cpu/stm32/include/periph/cpu_gpio_ll.h b/cpu/stm32/include/periph/cpu_gpio_ll.h index 5ae238ab24..741604048e 100644 --- a/cpu/stm32/include/periph/cpu_gpio_ll.h +++ b/cpu/stm32/include/periph/cpu_gpio_ll.h @@ -19,6 +19,7 @@ #ifndef PERIPH_CPU_GPIO_LL_H #define PERIPH_CPU_GPIO_LL_H +#include #include #include "periph_cpu.h" @@ -46,8 +47,6 @@ typedef enum { GPIO_DRIVE_STRONGEST = 0 } gpio_drive_strength_t; -#if defined(GPIO_OSPEEDR_OSPEED0) || defined(GPIO_OSPEEDER_OSPEEDR0) \ - || defined(GPIO_OSPEEDER_OSPEED0) || defined(GPIO_OSPEEDR_OSPEEDR0) /* Modern STM32 GPIO config registers with the OSPEEDR register support full * 4 slew rates, legacy STM32F1 style only have three slew rates. We define * slow and fast to the same value, so that we have three options: @@ -55,11 +54,26 @@ typedef enum { * 2. SLOW: 10 MHZ * 3. FAST/FASTEST: 50 MHz */ +#if defined(GPIO_OSPEEDR_OSPEED0) || defined(GPIO_OSPEEDER_OSPEEDR0) \ + || defined(GPIO_OSPEEDER_OSPEED0) || defined(GPIO_OSPEEDR_OSPEEDR0) +# define STM32_HAS_OSPEED 1 +#else +# define STM32_HAS_OSPEED 0 +#endif + #define HAVE_GPIO_SLEW_T +#if STM32_HAS_OSPEED typedef enum { GPIO_SLEW_SLOWEST = 0, GPIO_SLEW_SLOW = 1, GPIO_SLEW_FAST = 2, + GPIO_SLEW_FASTEST = 3, +} gpio_slew_t; +#else +typedef enum { + GPIO_SLEW_SLOWEST = 0, + GPIO_SLEW_SLOW = 0, + GPIO_SLEW_FAST = 1, GPIO_SLEW_FASTEST = 2, } gpio_slew_t; #endif @@ -88,8 +102,84 @@ typedef enum { GPIO_TRIGGER_LEVEL_LOW = GPIO_TRIGGER_LEVEL | GPIO_TRIGGER_EDGE_FALLING, } gpio_irq_trig_t; +#define HAVE_GPIO_STATE_T +typedef enum { + GPIO_OUTPUT_PUSH_PULL, + GPIO_OUTPUT_OPEN_DRAIN, + GPIO_OUTPUT_OPEN_SOURCE, + GPIO_INPUT, + GPIO_USED_BY_PERIPHERAL, + GPIO_DISCONNECT, +} gpio_state_t; + +#define HAVE_GPIO_PULL_T +typedef enum { + GPIO_FLOATING, + GPIO_PULL_UP, + GPIO_PULL_DOWN, + GPIO_PULL_KEEP, +} gpio_pull_t; + +#define HAVE_GPIO_CONF_T +typedef union gpio_conf_stm32 gpio_conf_t; + #endif /* ndef Doxygen */ +/** + * @brief GPIO pin configuration for STM32 MCUs. + * @ingroup drivers_periph_gpio_ll + */ +union gpio_conf_stm32 { + uint16_t bits; /**< the raw bits */ + struct { + /** + * @brief State of the pin + */ + gpio_state_t state : 3; + /** + * @brief Pull resistor configuration + */ + gpio_pull_t pull : 2; + /** + * @brief Configure the slew rate of outputs + * + * @warning If the requested slew rate is not available, the closest fit + * supported will be configured instead. + * + * This value is ignored *unless* @ref gpio_conf_stm32::state is + * configured to @ref GPIO_OUTPUT_PUSH_PULL or @ref GPIO_OUTPUT_OPEN_DRAIN. + */ + gpio_slew_t slew_rate : 2; + /** + * @brief Whether to disable the input Schmitt trigger + * + * @details This could be called `schmitt_trigger` with inverse + * meaning, but the API contract says that additional + * members in the structure should have a sane + * default when zero. + * + * This value is ignored *unless* @ref gpio_conf_stm32::state is + * configured to @ref GPIO_INPUT. + */ + bool schmitt_trigger_disabled : 1; + /** + * @brief Initial value of the output + * + * Ignored if @ref gpio_conf_stm32::state is set to @ref GPIO_INPUT or + * @ref GPIO_DISCONNECT. If the pin was previously in a high impedance + * state, it is guaranteed to directly transition to the given initial + * value. + * + * @ref gpio_ll_query_conf will write the current value of the specified + * pin here, which is read from the input register when the state is + * @ref GPIO_INPUT, otherwise the state from the output register is + * consulted. + */ + bool initial_value : 1; + uint8_t : 7; /*< padding */ + }; +}; + #ifdef __cplusplus } #endif diff --git a/cpu/stm32/periph/gpio_ll.c b/cpu/stm32/periph/gpio_ll.c index 439b77e564..6d4139f760 100644 --- a/cpu/stm32/periph/gpio_ll.c +++ b/cpu/stm32/periph/gpio_ll.c @@ -35,6 +35,15 @@ #include "bitarithm.h" #include "periph/gpio_ll.h" +#ifdef MODULE_FMT +#include "fmt.h" +#else +static inline void print_str(const char *str) +{ + fputs(str, stdout); +} +#endif + #ifdef RCC_AHBENR_GPIOAEN # define GPIO_BUS AHB # define GPIOAEN RCC_AHBENR_GPIOAEN @@ -356,3 +365,28 @@ void gpio_ll_query_conf(gpio_conf_t *dest, gpio_port_t port, uint8_t pin) } irq_restore(state); } + +void gpio_ll_print_conf(const gpio_conf_t *conf) +{ + static const char *slew_strs[] = { + [GPIO_SLEW_SLOWEST] = "slowest", +#if STM32_HAS_OSPEED + [GPIO_SLEW_SLOW] = "slow", +#endif + [GPIO_SLEW_FAST] = "fast", + [GPIO_SLEW_FASTEST] = "fastest", +/* If only three slew rates are supported, a fourth value would be + * representable with the two-bit field. Let's be rather safe than sorry */ +#if !STM32_HAS_OSPEED + "invalid" +#endif + }; + + gpio_ll_print_conf_common(conf); + print_str(", slew: "); + print_str(slew_strs[conf->slew_rate]); + + if (conf->schmitt_trigger_disabled) { + print_str(", Schmitt trigger disabled"); + } +} diff --git a/drivers/include/periph/gpio_ll.h b/drivers/include/periph/gpio_ll.h index f436a77926..94b17d6b57 100644 --- a/drivers/include/periph/gpio_ll.h +++ b/drivers/include/periph/gpio_ll.h @@ -299,7 +299,57 @@ typedef enum { + (GPIO_SLEW_SLOW != GPIO_SLEW_FAST) \ + (GPIO_SLEW_FAST != GPIO_SLEW_FASTEST)) -#if !defined(HAVE_GPIO_CONF_T) || defined(DOXYGEN) +/** + * @brief Public members of `gpio_conf_t` + * + * The type `gpio_conf_t` is implementation specific, but will in any case + * be a union of an unsigned integer of implementation defined width named + * `bits` (to access all bits of the configuration in one read/write), and + * an anonymous `struct` to access the actual configuration via its members. + * + * The members given here have to be present in every implementation. Make sure + * to use the `enum` names to assign them, as the actual numeric representation + * again is implementation specific. + * + * It is explicitly intended that code makes use of implementation specific + * extensions to `gpio_conf_t`. However, portable code should be restricted + * to only use the members detailed here and make sure that all other bits + * are initialized with zero. + */ +union gpio_conf_minimal { + uint8_t bits; /**< The raw bits of the configuration */ + struct { + /** + * @brief State of the pin + */ + gpio_state_t state : 3; + /** + * @brief Pull resistor configuration + */ + gpio_pull_t pull : 2; + /** + * @brief Initial value of the output + * + * Ignored if @ref gpio_conf_minimal::state is set to @ref GPIO_INPUT or + * @ref GPIO_DISCONNECT. If the pin was previously in a high impedance + * state, it is guaranteed to directly transition to the given initial + * value. + * + * @ref gpio_ll_query_conf will write the current value of the specified + * pin here, which is read from the input register when the state is + * @ref GPIO_INPUT, otherwise the state from the output register is + * consulted. + */ + bool initial_value : 1; + uint8_t : 2; /*< padding */ + }; +}; + +#if !defined(HAVE_GPIO_CONF_T) && !defined(DOXYGEN) +typedef union gpio_conf_minimal gpio_conf_t; +#endif + +#ifdef DOXYGEN /** * @brief GPIO pin configuration * @@ -309,75 +359,9 @@ typedef enum { * initializers or zeroing out the whole contents using `memset() * before initializing the individual fields. * - * It is fully valid that an implementation extends this structure with - * additional implementation specific fields. For example, it could be useful - * to also include fields to configure routing of a GPIO pin to other - * peripherals (e.g. for us as an TXD pin of an UART). These implementation - * specific fields **MUST** however have reasonable defaults when initialized - * with zero (e.g. pin is not routed to another peripheral but to be used as - * regular GPIO). For obvious reasons, portable code cannot rely on the - * presence and semantic of any implementation specific fields. Additionally, - * out-of-tree users should not use these fields, as the implementation - * specific fields cannot be considered a stable API. + * See @ref gpio_conf_minimal for the minimal structure fields to expect. */ -typedef struct { - gpio_state_t state; /**< State of the pin */ - gpio_pull_t pull; /**< Pull resistor configuration */ - /** - * @brief Configure the slew rate of outputs - * - * @warning If the requested slew rate is not available, the closest fit - * supported will be configured instead. - * - * This value is ignored *unless* @ref gpio_conf_t::state is configured - * to @ref GPIO_OUTPUT_PUSH_PULL or @ref GPIO_OUTPUT_OPEN_DRAIN. - */ - gpio_slew_t slew_rate; - /** - * @brief Whether to enable the input Schmitt trigger - * - * @warning If the requested Schmitt trigger setting is not available, it - * will be ignored. - * - * This value is ignored *unless* @ref gpio_conf_t::state is configured - * to @ref GPIO_INPUT. - */ - bool schmitt_trigger; - /** - * @brief Initial value of the output - * - * Ignored if @ref gpio_conf_t::state is set to @ref GPIO_INPUT or - * @ref GPIO_DISCONNECT. If the pin was previously in a high impedance - * state, it is guaranteed to directly transition to the given initial - * value. - * - * @ref gpio_ll_query_conf will write the current value of the specified - * pin here, which is read from the input register when the state is - * @ref GPIO_INPUT, otherwise the state from the output register is - * consulted. - */ - bool initial_value; - /** - * @brief Strength of the pull up/down resistor - * - * @warning If the requested pull strength is not available, the closest fit - * supported will be configured instead. - * - * This value is ignored when @ref gpio_conf_t::pull is configured to - * @ref GPIO_FLOATING. - */ - gpio_pull_strength_t pull_strength; - /** - * @brief Drive strength of the GPIO - * - * @warning If the requested drive strength is not available, the closest - * fit supported will be configured instead. - * - * This value is ignored when @ref gpio_conf_t::state is configured to - * @ref GPIO_INPUT or @ref GPIO_DISCONNECT. - */ - gpio_drive_strength_t drive_strength; -} gpio_conf_t; +typedef /* implementation specific */ gpio_conf_t; #endif /** @@ -488,9 +472,9 @@ int gpio_ll_init(gpio_port_t port, uint8_t pin, const gpio_conf_t *conf); * @pre @p port and @p pin refer to an existing GPIO pin and @p dest can * be written to. Expect blowing assertions otherwise. * - * @note @ref gpio_conf_t::initial_value should be set to the current value - * of the pin, so that no shadow log of the initial value is needed to - * consult. + * @note @ref gpio_conf_minimal::initial_value should be set to the current + * value of the pin, so that no shadow log of the initial value is + * needed to consult. */ void gpio_ll_query_conf(gpio_conf_t *dest, gpio_port_t port, uint8_t pin); diff --git a/drivers/periph_common/gpio_ll.c b/drivers/periph_common/gpio_ll.c index a90a6695c0..aa357b2955 100644 --- a/drivers/periph_common/gpio_ll.c +++ b/drivers/periph_common/gpio_ll.c @@ -32,47 +32,37 @@ static inline void print_str(const char *str) const gpio_conf_t gpio_ll_in = { .state = GPIO_INPUT, .pull = GPIO_FLOATING, - .schmitt_trigger = true, }; const gpio_conf_t gpio_ll_in_pd = { .state = GPIO_INPUT, .pull = GPIO_PULL_DOWN, - .schmitt_trigger = true, }; const gpio_conf_t gpio_ll_in_pu = { .state = GPIO_INPUT, .pull = GPIO_PULL_UP, - .schmitt_trigger = true, }; const gpio_conf_t gpio_ll_in_pk = { .state = GPIO_INPUT, .pull = GPIO_PULL_KEEP, - .schmitt_trigger = true, }; const gpio_conf_t gpio_ll_out = { .state = GPIO_OUTPUT_PUSH_PULL, - .drive_strength = GPIO_DRIVE_STRONGEST, - .slew_rate = GPIO_SLEW_FASTEST, .initial_value = false, }; const gpio_conf_t gpio_ll_pd = { .state = GPIO_OUTPUT_OPEN_DRAIN, .pull = GPIO_FLOATING, - .drive_strength = GPIO_DRIVE_STRONGEST, - .slew_rate = GPIO_SLEW_FASTEST, .initial_value = true, }; const gpio_conf_t gpio_ll_pd_pu = { .state = GPIO_OUTPUT_OPEN_DRAIN, .pull = GPIO_PULL_UP, - .drive_strength = GPIO_DRIVE_STRONGEST, - .slew_rate = GPIO_SLEW_FASTEST, .initial_value = true, }; @@ -103,39 +93,6 @@ void gpio_ll_print_conf_common(const gpio_conf_t *conf) break; } - if (conf->state != GPIO_INPUT) { - if (GPIO_DRIVE_NUMOF > 1) { - print_str(", drive: "); - if (conf->drive_strength == GPIO_DRIVE_WEAK) { - print_str("weak"); - } - else if (conf->drive_strength == GPIO_DRIVE_WEAKEST) { - print_str("weakest"); - } - else if (conf->drive_strength == GPIO_DRIVE_STRONG) { - print_str("strong"); - } - else { - print_str("strongest"); - } - } - if (GPIO_SLEW_NUMOF > 1) { - print_str(", slew: "); - if (conf->slew_rate == GPIO_SLEW_SLOW) { - print_str("slow"); - } - else if (conf->slew_rate == GPIO_SLEW_SLOWEST) { - print_str("slowest"); - } - else if (conf->slew_rate == GPIO_SLEW_FAST) { - print_str("fast"); - } - else { - print_str("fastest"); - } - } - } - if (conf->state != GPIO_OUTPUT_PUSH_PULL) { print_str(", pull: "); switch (conf->pull) { @@ -153,26 +110,6 @@ void gpio_ll_print_conf_common(const gpio_conf_t *conf) print_str("keep"); break; } - - if ((conf->pull != GPIO_FLOATING) && (GPIO_PULL_NUMOF > 1)) { - print_str(" ("); - if (conf->pull_strength == GPIO_PULL_WEAK) { - print_str("weak"); - } - else if (conf->pull_strength == GPIO_PULL_WEAKEST) { - print_str("weakest"); - } - else if (conf->pull_strength == GPIO_PULL_STRONG) { - print_str("strong"); - } - else { - print_str("strongest"); - } - print_str(")"); - } - - print_str(", schmitt trigger: "); - print_str(off_on[conf->schmitt_trigger]); } print_str(", value: "); diff --git a/tests/bench/periph_gpio_ll/main.c b/tests/bench/periph_gpio_ll/main.c index 17c3999858..e476f4822b 100644 --- a/tests/bench/periph_gpio_ll/main.c +++ b/tests/bench/periph_gpio_ll/main.c @@ -157,7 +157,6 @@ int main(void) "-------------------------------------------------------"); gpio_conf_t conf = { .state = GPIO_OUTPUT_PUSH_PULL, - .slew_rate = GPIO_SLEW_FASTEST }; expect(0 == gpio_ll_init(port_out, PIN_OUT_0, &conf)); expect(0 == gpio_ll_init(port_out, PIN_OUT_1, &conf)); @@ -211,7 +210,6 @@ int main(void) "-----------------------------------------"); gpio_conf_t conf = { .state = GPIO_OUTPUT_PUSH_PULL, - .slew_rate = GPIO_SLEW_FASTEST }; expect(0 == gpio_ll_init(port_out, PIN_OUT_0, &conf)); expect(0 == gpio_ll_init(port_out, PIN_OUT_1, &conf)); @@ -265,7 +263,6 @@ int main(void) "----------------------------------------"); gpio_conf_t conf = { .state = GPIO_OUTPUT_PUSH_PULL, - .slew_rate = GPIO_SLEW_FASTEST }; expect(0 == gpio_ll_init(port_out, PIN_OUT_0, &conf)); expect(0 == gpio_ll_init(port_out, PIN_OUT_1, &conf));