1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00

drivers/periph/gpio_ll: Add API to switch direction

This adds two functions:

    void gpio_ll_switch_dir_output(gpio_port_t port, uword_t outputs);
    void gpio_ll_switch_dir_input(gpio_port_t port, uword_t inputs);

The first configures GPIO pins specified by a bitmask as output, the
second configures the specified pins as input.

The main use case is to allow bit-banging bidirectional protocols using
more basic GPIO peripherals that do not implement open drain mode, such
as found e.g. on MSP430, ATmega, or SAM0.

It is not intended to implement this feature on modern MCUs with
sophisticated GPIO peripherals.
This commit is contained in:
Marian Buschsieweke 2024-01-22 16:51:46 +01:00
parent 8bf61336a2
commit 6fb369d4fc
No known key found for this signature in database
GPG Key ID: 77AA882EC78084E6
2 changed files with 78 additions and 3 deletions

View File

@ -69,13 +69,13 @@
#ifndef PERIPH_GPIO_LL_H
#define PERIPH_GPIO_LL_H
#include <inttypes.h>
#include <assert.h>
#include <stdbool.h>
#include <stdint.h>
#include "architecture.h"
#include "periph_cpu.h"
#include "periph/gpio.h"
#include "periph_cpu.h"
#ifdef __cplusplus
extern "C" {
@ -706,6 +706,47 @@ static inline uword_t gpio_ll_prepare_write(gpio_port_t port, uword_t mask,
}
#endif
/**
* @brief Turn GPIO pins specified by the bitmask @p outputs to outputs
*
* @param[in] port GPIO port to modify
* @param[in] outputs Bitmask specifying the GPIO pins to set in output
* mode
* @pre The feature `gpio_ll_switch_dir` is available
* @pre Each affected GPIO pin is either configured as input or as
* push-pull output.
*
* @note This is a makeshift solution to implement bit-banging of
* bidirectional protocols on less sophisticated GPIO peripherals
* that do not support open drain mode.
* @warning Use open drain mode instead, if supported.
*/
static inline void gpio_ll_switch_dir_output(gpio_port_t port, uword_t outputs);
/**
* @brief Turn GPIO pins specified by the bitmask @p inputs to inputs
*
* @param[in] port GPIO port to modify
* @param[in] inputs Bitmask specifying the GPIO pins to set in input
* mode
* @pre The feature `gpio_ll_switch_dir` is available
* @pre Each affected GPIO pin is either configured as input or as
* push-pull output.
*
* @warning The state of the output register may be intermixed with the
* input configuration. Specifically, on AVR the output register
* enables/disables the internal pull up, on SAM0 MCUs the output
* register controls the pull resistor direction (if the pull
* resistor is enabled). Hence, the bits in the output
* register of the pins switched to input should be restored
* just after this call.
* @note This is a makeshift solution to implement bit-banging of
* bidirectional protocols on less sophisticated GPIO peripherals
* that do not support open drain mode.
* @warning Use open drain mode instead, if supported.
*/
static inline void gpio_ll_switch_dir_input(gpio_port_t port, uword_t inputs);
/**
* @brief Perform a masked write operation on the I/O register of the port
*
@ -786,6 +827,39 @@ static inline gpio_port_t gpio_port_pack_addr(void *addr);
*/
static inline void * gpio_port_unpack_addr(gpio_port_t port);
#ifndef DOXYGEN
#if !MODULE_PERIPH_GPIO_LL_SWITCH_DIR
static inline void gpio_ll_switch_dir_output(gpio_port_t port, uword_t outputs)
{
(void)port;
(void)outputs;
/* Hack: If this function is only used guarded by some
*
* if (IS_USED(MODULE_PERIPH_GPIO_LL_SWITCH_DIR)) {
* ...
* }
*
* as intended, all calls to the following fake function will be optimized
* due to the elimination of dead branches. If used incorrectly, a linking
* failure will be the result. The error message will not be ideal, but a
* compile time error is much better than a runtime error.
*/
extern void gpio_ll_switch_dir_output_used_but_feature_gpio_ll_switch_dir_is_not_provided(void);
gpio_ll_switch_dir_output_used_but_feature_gpio_ll_switch_dir_is_not_provided();
}
static inline void gpio_ll_switch_dir_input(gpio_port_t port, uword_t inputs)
{
(void)port;
(void)inputs;
/* Same hack as above */
extern void gpio_ll_switch_dir_input_used_but_feature_gpio_ll_switch_dir_is_not_provided(void);
gpio_ll_switch_dir_input_used_but_feature_gpio_ll_switch_dir_is_not_provided();
}
#endif /* !MODULE_PERIPH_GPIO_LL_SWITCH_DIR */
#endif /* !DOXYGEN */
#ifdef __cplusplus
}
#endif

View File

@ -1,14 +1,15 @@
# Always use hardware features, if available
ifneq (,$(filter periph_gpio_ll%,$(USEMODULE)))
FEATURES_OPTIONAL += periph_gpio_ll_disconnect
FEATURES_OPTIONAL += periph_gpio_ll_irq_level_triggered_high
FEATURES_OPTIONAL += periph_gpio_ll_input_pull_down
FEATURES_OPTIONAL += periph_gpio_ll_input_pull_keep
FEATURES_OPTIONAL += periph_gpio_ll_input_pull_up
FEATURES_OPTIONAL += periph_gpio_ll_irq_level_triggered_high
FEATURES_OPTIONAL += periph_gpio_ll_irq_level_triggered_low
FEATURES_OPTIONAL += periph_gpio_ll_irq_unmask
FEATURES_OPTIONAL += periph_gpio_ll_open_drain
FEATURES_OPTIONAL += periph_gpio_ll_open_drain_pull_up
FEATURES_OPTIONAL += periph_gpio_ll_open_source
FEATURES_OPTIONAL += periph_gpio_ll_open_source_pull_down
FEATURES_OPTIONAL += periph_gpio_ll_switch_dir
endif