1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00
RIOT/drivers/include/periph/gpio_ll.h
2023-02-04 11:33:44 +01:00

740 lines
29 KiB
C

/*
* Copyright (C) 2020 Gunar Schorcht
* 2021 Otto-von-Guericke-Universität Magdeburg
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @defgroup drivers_periph_gpio_ll GPIO Low-Level API
* @ingroup drivers_periph
* @brief Peripheral GPIO Low-Level API
*
* @warning This API is not stable yet and intended for internal use only
* as of now.
*
* # Design Goals
*
* This API aims to provide low-level access to GPIOs with as little
* abstraction and overhead in place as possible for the hot code paths, while
* providing a relatively high-level and feature complete API for the
* configuration of GPIO pins. The former is to enable sophisticated use cases
* such at bit-banging parallel protocols, bit-banging at high data rates,
* bit-banging with strict timing requirements, or any combination of these.
* The latter is to expose as much of the features the (arguably) most
* important peripheral of the MCU as possible.
*
* It is possible to implement the high level pin-based GPIO API of RIOT, @ref
* drivers_periph_gpio, on top of this API. It is expected that for many use
* cases the high level API will still remain the API of choice, since it is
* more concise an easier to use.
*
* Note that this API is likely to be faster moving than the high level GPIO
* API, as it intents to match the hardware features more closely. Hence, new
* support for new MCUs are more likely to result in API changes here. This is
* another reason why only users interested in the low level access to GPIOs
* should be using the this low level API, while the high level API will cater
* all the LED blinking and button sensing use cases with a more convenient
* and more very stable interface.
*
* # Thread Safety
*
* All functions provided by this API have to be implemented in a thread-safe
* manner, except for @ref gpio_ll_prepare_write and
* @ref gpio_ll_prepare_write_all_outputs. If the read-modify-write operations
* @ref gpio_ll_set, @ref gpio_ll_clear, and @ref gpio_ll_toggle can be done
* atomically in hardware with predictable timing, this must be used in the
* implementation. Otherwise IRQs have to be disabled during read-modify-write
* sequences. Calls to @ref gpio_ll_write are inherently thread-safe and
* lock-less, but sharing pins on the same port between threads is still
* requires a lock between the calls to @ref gpio_ll_prepare_write and
* @ref gpio_ll_write in the general case.
*
* Under no circumstances two threads may call @ref gpio_ll_init on the same
* port / pin combination concurrently.
*
* @{
* @file
* @brief Peripheral GPIO Low-Level API
*
* @author Gunar Schorcht <gunar@schorcht.net>
* @author Marian Buschsieweke <marian.buschsieweke@ovgu.de>
*
* @warning This API is not stable yet and intended for internal use only as
* of now.
*/
#ifndef PERIPH_GPIO_LL_H
#define PERIPH_GPIO_LL_H
#include <inttypes.h>
#include <stdbool.h>
#include <stdint.h>
#include "architecture.h"
#include "periph_cpu.h"
#include "periph/gpio.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief GPIO port type
*/
typedef uintptr_t gpio_port_t;
#if !defined(GPIO_PORT_UNDEF) || defined(DOXYGEN)
/**
* @brief Magic "undefined GPIO port" value
*/
#define GPIO_PORT_UNDEF UINTPTR_MAX
#endif
#ifdef DOXYGEN
/**
* @brief Get the @ref gpio_port_t value of the port identified by @p num
*
* @note If @p num is a compile time constant, this is guaranteed to be
* suitable for a constant initializer.
*
* Typically this will be something like `(GPIO_BASE_ADDR + num * sizeof(struct
* vendor_gpio_reg))`
*/
#define GPIO_PORT(num) implementation_specific
#endif
#ifdef DOXYGEN
/**
* @brief Get the number of the GPIO port belonging to the given @ref
* gpio_port_t value
*
* @note If @p port is a compile time constant, this is guaranteed to be
* suitable for a constant initializer.
*
* @pre @p port is the return value of @ref GPIO_PORT
*
* For every supported port number *n* the following `assert()` must not blow
* up:
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.c}
* assert(n == GPIO_PORT_NUM(GPIO_PORT(n)));
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
#define GPIO_PORT_NUM(port) implementation_specific
#endif
#if !defined(HAVE_GPIO_STATE_T) || defined(DOXYGEN)
/**
* @brief Enumeration of GPIO states (direction)
*/
typedef enum {
/**
* @brief Use pin as output in push-pull configuration
*
* | Logical Value | Electrical Behavior |
* |:-------------- |:--------------------------------- |
* | `0` | Low |
* | `1` | High |
*/
GPIO_OUTPUT_PUSH_PULL,
/**
* @brief Use pin as output in open collector configuration
*
* | Logical Value | Electrical Behavior |
* |:-------------- |:--------------------------------- |
* | `0` | Low |
* | `1` | High Impedance (Disconnected) |
*/
GPIO_OUTPUT_OPEN_DRAIN,
/**
* @brief Use pin as output in open emitter configuration
*
* | Logical Value | Electrical Behavior |
* |:-------------- |:--------------------------------- |
* | `0` | High Impedance (Disconnected) |
* | `1` | High |
*/
GPIO_OUTPUT_OPEN_SOURCE,
GPIO_INPUT, /**< Use pin as input */
/**
* @brief The GPIO pin is used by a peripheral
*
* Note that calling @ref gpio_ll_init with this state is implementation
* defined behavior, as implementation specific additional details
* (such as which peripheral to connect the pin to) are needed. An
* implementation may choose to not support this at all.
*
* However, it is *strongly* encouraged that @ref gpio_ll_query_conf uses
* this state to indicate a GPIO pin is currently used by a peripheral (e.g.
* as ADC input, I2C data/clock pin, etc.).
*/
GPIO_USED_BY_PERIPHERAL,
/**
* @brief Disconnect pin from all peripherals
*
* The implementation should aim to reduce power consumption of the pin
* when this state is entered, if this is feasible.
*
* @note Pull resistors can still be requested in this mode. This can be
* useful e.g. for keeping an UART TXD pin from emitting noise
* while the UART peripheral is powered off. But not every
* implementation will support this.
*
* @details Once all GPIOs of a GPIO port are disconnected, the
* implementation is allowed to power off the whole GPIO port again
* to conserve power.
*/
GPIO_DISCONNECT,
} gpio_state_t;
#endif
#if !defined(HAVE_GPIO_PULL_T) || defined(DOXYGEN)
/**
* @brief Enumeration of pull resistor configurations
*/
typedef enum {
GPIO_FLOATING, /**< No pull ups nor pull downs enabled */
GPIO_PULL_UP, /**< Pull up resistor enabled */
GPIO_PULL_DOWN, /**< Pull down resistor enabled */
GPIO_PULL_KEEP, /**< Keep the signal at current logic level with pull
up/down resistors */
} gpio_pull_t;
#endif
#if !defined(HAVE_GPIO_PULL_STRENGTH_T) || defined(DOXYGEN)
/**
* @brief Enumeration of pull resistor values
*
* @note Depending on the implementation, some (or even all!) constants can
* have the same numeric value if less than four pull resistors per
* direction are provided. For obvious reasons, only neighboring values
* are allowed to have the same numeric value.
*/
typedef enum {
GPIO_PULL_WEAKEST, /**< Use the weakest (highest Ohm value) resistor */
GPIO_PULL_WEAK, /**< Use a weak pull resistor */
GPIO_PULL_STRONG, /**< Use a strong pull resistor */
GPIO_PULL_STRONGEST, /**< Use the strongest pull resistor */
} gpio_pull_strength_t;
#endif
/**
* @brief The number of distinct supported pull resistor strengths
*
* This equals the number of pull resistor strengths actually supported and can
* be less than four, if one or more enumeration values in @ref
* gpio_pull_strength_t have the same numeric value. Note that: a) some pins
* might have more options than others and b) it could be possible that there
* are e.g. two pull up resistors to pick from, but only one pull down
* resistor.
*/
#define GPIO_PULL_NUMOF (1U + (GPIO_PULL_WEAKEST != GPIO_PULL_WEAK) \
+ (GPIO_PULL_WEAK != GPIO_PULL_STRONG) \
+ (GPIO_PULL_STRONG != GPIO_PULL_STRONGEST))
#if !defined(HAVE_GPIO_DRIVE_STRENGTH_T) || defined(DOXYGEN)
/**
* @brief Enumeration of drive strength options
*
* @note Depending on the implementation, some (or even all!) constants can
* have the same numeric value if less than four drive strength options
* to pick from. For obvious reasons, only neighboring values are
* allowed to have the same numeric value.
*/
typedef enum {
GPIO_DRIVE_WEAKEST, /**< Use the weakest drive strength */
GPIO_DRIVE_WEAK, /**< Use a weak drive strength */
GPIO_DRIVE_STRONG, /**< Use a strong drive strength */
GPIO_DRIVE_STRONGEST, /**< Use the strongest drive strength */
} gpio_drive_strength_t;
#endif
/**
* @brief The number of distinct supported drive strengths
*
* This equals the number of drive strengths actually supported and can be less
* than four, if one or more enumeration values in @ref gpio_drive_strength_t
* have the same numeric value. Note that some pins might have more options
* than others.
*/
#define GPIO_DRIVE_NUMOF (1U + (GPIO_DRIVE_WEAKEST != GPIO_DRIVE_WEAK) \
+ (GPIO_DRIVE_WEAK != GPIO_DRIVE_STRONG) \
+ (GPIO_DRIVE_STRONG != GPIO_DRIVE_STRONGEST))
#if !defined(HAVE_GPIO_SLEW_T) || defined(DOXYGEN)
/**
* @brief Enumeration of slew rate settings
*
* Reducing the slew rate can be useful to limit the high frequency noise
* emitted by a GPIO pin. On the other hand, a high frequency signal cannot be
* generated if the slew rate is too slow.
*
* @warning The numeric values are implementation defined and multiple
* constants can have the same numeric value, if an implementation
* supports fewer slew rates. An implementation only supporting a
* single slew rate can have all constants set to a value of zero.
*/
typedef enum {
GPIO_SLEW_SLOWEST, /**< let the output voltage level rise/fall as slow as
possible */
GPIO_SLEW_SLOW, /**< let the output voltage level rise/fall slowly */
GPIO_SLEW_FAST, /**< let the output voltage level rise/fall fast */
GPIO_SLEW_FASTEST, /**< let the output voltage level rise/fall as fast as
possible */
} gpio_slew_t;
#endif
/**
* @brief The number of distinct supported slew rates
*
* This equals the number of slew rates actually supported and can be less than
* four, if one or more enumeration values in @ref gpio_drive_strength_t have
* the same numeric value. Note that some pins might have more options than
* others.
*/
#define GPIO_SLEW_NUMOF (1U + (GPIO_SLEW_SLOWEST != GPIO_SLEW_SLOW) \
+ (GPIO_SLEW_SLOW != GPIO_SLEW_FAST) \
+ (GPIO_SLEW_FAST != GPIO_SLEW_FASTEST))
#if !defined(HAVE_GPIO_CONF_T) || defined(DOXYGEN)
/**
* @brief GPIO pin configuration
*
* @warning The layout of this structure is implementation dependent and
* additional implementation specific fields might be present. For this
* reason, this structure must be initialized using designated
* 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.
*/
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;
#endif
/**
* @brief A standard configuration for a generic floating input pin
*/
extern const gpio_conf_t gpio_ll_in;
/**
* @brief A standard configuration for a generic input pin with pull down
* resistor
*/
extern const gpio_conf_t gpio_ll_in_pd;
/**
* @brief A standard configuration for a generic input pin with pull up
* resistor
*/
extern const gpio_conf_t gpio_ll_in_pu;
/**
* @brief A standard configuration for a generic input pin with pull
* resistor to keep signal at bus level
*
* This means, when the input reaches a 0, a pull down resistor is applied. If
* input reaches 1, a pull up is applied instead.
*/
extern const gpio_conf_t gpio_ll_in_pk;
/**
* @brief A standard configuration for a generic push-pull output pin
*
* @note The pin will have an initial value of 0.
*/
extern const gpio_conf_t gpio_ll_out;
/**
* @brief A standard configuration for a generic floating open drain output
*
* @note The pin will have an initial value of 1 (which in absence of an
* external pull up resistor will be high impedance).
*/
extern const gpio_conf_t gpio_ll_od;
/**
* @brief A standard configuration for a generic open drain output with pull
* up
*
* @note The pin will have an initial value of 1 (so that the pull up will
* pill the line high).
*/
extern const gpio_conf_t gpio_ll_od_pu;
/**
* @brief Check if the given number is a valid argument for @ref GPIO_PORT
*
* @param[in] num port number to check
* @retval true the MCU used has a GPIO port with that number
* @retval false the MCU used has ***NO*** GPIO port with that number
*/
static inline bool is_gpio_port_num_valid(uint_fast8_t num);
/**
* @brief Initialize the given GPIO pin as specified
*
* @param[in] port port the pin to initialize belongs to
* @param[in] pin number of the pin to initialize
* @param[in] conf configuration to apply
*
* @retval 0 success
* @retval -ENOTSUP GPIO state or pull resistor configuration not supported
*
* @warning If the configuration of the Schmitt trigger, the drive strength, or
* the pull resistor strength are not supported, the closest supported
* value will be chosen instead and `0` is returned.
* @warning Note that hardware GPIO peripherals may have shared building
* blocks. Those *SHOULD* be handed out by the implementation in
* first-come-first-served fashion. (E.g. if there is only one pull up
* resistor per port that can be connected to any pin of that port,
* typically the first pin on the port configured as pull up will
* succeed and subsequent configuration as pull ups for other pins on
* that port will get an `-ENOTSUP`.) For that reason, an application
* might need to optimize the order in which it configures GPIO pins
* to get the most suitable overall configuration supported by the
* hardware.
* @note An application having strict requirements can use
* @ref gpio_ll_query_conf afterwards to verify that the used
* configuration is indeed within spec. It is often sensible to omit
* these checks if `DEVELHELP` is disabled. An application can rely on
* that the configuration of pin @p pin at port @p port will not be
* changed as side-effect of operations performed on other pins. That
* is, once @ref gpio_ll_init returns the configuration details are
* settled and may only change due to subsequent calls to
* @ref gpio_ll_init with the same values for @p port and @p pin.
* @pre No concurrent context is calling this function for the same
* combination of @p port and @p pin - concurrent initialization of
* different pins on the same port is supported. The underlying
* implementation might perform locking where needed.
*/
int gpio_ll_init(gpio_port_t port, uint8_t pin, const gpio_conf_t *conf);
/**
* @brief Retrieve the current configuration of a GPIO pin
*
* @param[out] dest Write the current config of the given GPIO here
* @param[in] port GPIO port the pin to query is located at
* @param[in] pin Number of the pin to query within @p port
*
* @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.
*/
void gpio_ll_query_conf(gpio_conf_t *dest, gpio_port_t port, uint8_t pin);
/**
* @brief INTERNAL, use @ref gpio_ll_print_conf instead
*
* This function prints the public API part of @ref gpio_conf_t to stdio. The
* intention is that implementations that extend @ref gpio_conf_t to contain
* more members overwrite @ref gpio_ll_print_conf and call this function to
* print the common members
*/
void gpio_ll_print_conf_common(const gpio_conf_t *conf);
/**
* @brief Utility function to print a given GPIO configuration to stdio
* @param[in] conf Configuration to print
*/
void gpio_ll_print_conf(const gpio_conf_t *conf);
/**
* @brief Get the current input value of all GPIO pins of the given port as
* bitmask
*
* @param[in] port port to read
*
* @return The current value of the input register of the given
* GPIO port
*
* @note The value of unconfigured pins and pins configured as @ref
* GPIO_DISCONNECT or @ref GPIO_OUTPUT_PUSH_PULL is implementation
* defined.
* @details Unless technically impossible, this must be implemented as a single
* read instruction.
*/
static inline uword_t gpio_ll_read(gpio_port_t port);
/**
* @brief Get the current output value of all GPIO pins of the given port as
* bitmask
*
* @param[in] port port to read
*
* @return The current value of the output register of the given
* GPIO port
*
* @note The value of unconfigured pins and pins configured as @ref
* GPIO_INPUT or @ref GPIO_OUTPUT_PUSH_PULL is implementation
* defined.
* @details Unless technically impossible, this must be implemented as a single
* read instruction.
*/
static inline uword_t gpio_ll_read_output(gpio_port_t port);
/**
* @brief Perform an `reg |= mask` operation on the I/O register of the port
*
* @note The behavior regarding pins not configured as
* @ref GPIO_OUTPUT_PUSH_PULL or as
* @ref GPIO_OUTPUT_OPEN_DRAIN is implementation defined.
* @warning Portable code must set the bits in @p mask that do not correspond to
* pins configured as output to zero.
* @details On hardware that supports implementing this as a single write
* instruction, this must be implemented as such. Otherwise this
* read-modify-write will disable IRQs to still behave atomically.
*
* @param[in] port port to modify
* @param[in] mask bitmask containing the pins to set
*/
static inline void gpio_ll_set(gpio_port_t port, uword_t mask);
/**
* @brief Perform an `reg &= ~mask` operation on the I/O register of the port
*
* @note The behavior regarding pins not configured as
* @ref GPIO_OUTPUT_PUSH_PULL or as
* @ref GPIO_OUTPUT_OPEN_DRAIN is implementation defined.
* @warning Portable code must set the bits in @p mask that do not correspond to
* pins configured as output to zero.
* @details On hardware that supports implementing this as a single write
* instruction, this must be implemented as such. Otherwise this
* read-modify-write will disable IRQs to still behave atomically.
*
* @param[in] port port to modify
* @param[in] mask bitmask containing the pins to clear
*/
static inline void gpio_ll_clear(gpio_port_t port, uword_t mask);
/**
* @brief Perform an `reg ^= mask` operation on the I/O register of the port
*
* @note The behavior regarding pins not configured as
* @ref GPIO_OUTPUT_PUSH_PULL or as
* @ref GPIO_OUTPUT_OPEN_DRAIN is implementation defined.
* @warning Portable code must set the bits in @p mask that do not correspond to
* pins configured as output to zero.
* @details On hardware that supports implementing this as a single write
* instruction, this must be implemented as such. Otherwise this
* read-modify-write will disable IRQs to still behave atomically.
*
* @param[in] port port to modify
* @param[in] mask bitmask containing the pins to toggle
*/
static inline void gpio_ll_toggle(gpio_port_t port, uword_t mask);
#if defined(DOXYGEN) || !defined(HAVE_GPIO_LL_PREPARE_WRITE_ALL_PINS)
/**
* @brief Same as `gpio_ll_prepare_write(port, UWORD_MAX, value)`, but
* faster
* @param[in] port port that should be written to
* @param[in] value value to write to port
* @return Value to call @ref gpio_ll_write with
*
* @details On most platforms this function will just pass @p value through
* unmodified, becoming a no-op (and costing neither CPU cycles,
* nor RAM, nor ROM). Hence, this function will only cost you
* if your platform really requires preparation of any sorts.
*
* @note If all pins on @p port are known to be configured as output,
* calls to this functions can be omitted.
* @details The caller needs to make sure that no concurrent changes
* (this includes configuration changes, writing, clearing, setting
* or toggling GPIO pins) are performed.
*
* This function can be used to prevent side-effects on non-output pins of a
* port when writing to it, e.g. when the output buffer is multiplexed with
* the pull configuration for input pins (such as on ATmega MCUs).
*/
static inline uword_t gpio_ll_prepare_write_all_outputs(gpio_port_t port,
uword_t value)
{
(void)port;
return value;
}
#endif
#if defined(DOXYGEN) || !defined(HAVE_GPIO_LL_PREPARE_WRITE)
/**
* @brief Helper to use @ref gpio_ll_write side-effect free
* @param[in] port port that should be written to
* @param[in] mask bitmask of the pins to write to
* @param[in] value value to write to port
* @return Value to call @ref gpio_ll_write with
*
* @details The caller needs to make sure that no concurrent changes
* (this includes configuration changes, writing, clearing, setting
* or toggling GPIO pins) are performed.
*
* See @ref gpio_ll_write on how to use this function
*/
static inline uword_t gpio_ll_prepare_write(gpio_port_t port, uword_t mask,
uword_t value)
{
return value | (gpio_ll_read_output(port) & (~mask));
}
#endif
/**
* @brief Perform a masked write operation on the I/O register of the port
*
* Some platforms multiplex the "write" I/O register with additional
* functions e.g. for input pin configuration. To prevent unintentional
* side effects prepare a value using @ref gpio_ll_prepare_write that
* will set the bits of non-output pins as needed to not have side
* effects on the state the GPIO port had when calling
* @ref gpio_ll_prepare_write .
*
* @param[in] port port to modify
* @param[in] state Opaque value produced by @ref gpio_ll_prepare_write
*
* Usage:
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.c}
* // emit a square wave from two pins with a phase shift of pi between the
* // waves, e.g.:
* // pin1: _⎍_⎍_⎍_⎍_⎍_⎍_⎍_⎍_⎍
* // pin2: ⎍_⎍_⎍_⎍_⎍_⎍_⎍_⎍_⎍_
* void square_wave(gpio_port_t port, uint8_t pin1, uint8_t pin2)
* {
* uword_t mask = (1U << pin1) | (1U << pin2);
* uword_t state1 = gpio_ll_prepare_write(port, mask, 1U << pin1);
* uword_t state2 = gpio_ll_prepare_write(port, mask, 1U << pin2);
* while (1) {
* gpio_ll_write(port, state1);
* gpio_ll_write(port, state2);
* }
* }
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* @note If the configuration of the used port changes between the calls
* of @ref gpio_ll_prepare_write and @ref gpio_ll_write the write will
* still have undesired side effects on the configuration of input pins
* if the platform multiplexes the write register with configuration
* for input pins. It is expected that the user of the API either
* exclusively uses a GPIO port or synchronizes with other users to
* update the prepared value on configuration changes.
* @details Unless technically impossible, this must be implemented as a single
* write instruction.
*/
static inline void gpio_ll_write(gpio_port_t port, uword_t state);
/**
* @brief Extract the `gpio_port_t` from a `gpio_t`
*/
static inline gpio_port_t gpio_get_port(gpio_t pin);
/**
* @brief Extract the pin number from a `gpio_t`
*/
static inline uint8_t gpio_get_pin_num(gpio_t pin);
/**
* @brief Pack a pointer into a @ref gpio_port_t
*
* @pre The address in @p addr is not `NULL` ***and*** points to a regular
* memory region (e.g. anywhere in `.data`, `.rodata`, `.bss` or an
* address returned by `malloc()` and friends)
*
* @return A value that is distinguishable from any valid port reference and
* from which @p addr can be reconstructed by calling
* @ref gpio_port_unpack_addr
*/
static inline gpio_port_t gpio_port_pack_addr(void *addr);
/**
* @brief Extract a data pointer that was packed by @ref gpio_port_pack_addr
*
* @return The exact address previously packed by @ref gpio_port_pack_addr
* @retval NULL @p port is a valid GPIO port
*
* The motivation is that a high level API can multiplex peripheral GPIOs and
* GPIOs provided by port extenders. That high level API could pack pointers to
* the device descriptors of port extenders into a @ref gpio_port_t and use this
* function to check if the port is a peripheral port (the return value is
* `NULL`), or retrieve the device descriptor.
*/
static inline void * gpio_port_unpack_addr(gpio_port_t port);
#ifdef __cplusplus
}
#endif
/* the hardware specific implementation relies on the types such as gpio_port_t
* to be provided */
#include "gpio_ll_arch.h"
#endif /* PERIPH_GPIO_LL_H */
/** @} */