1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2025-12-25 14:33:52 +01:00

cpu/stm32: implement periph_gpio_ll_switch_dir

This implements periph_gpio_ll_switch_dir for STM32 except for STM32F1,
which has a different register layout.
This commit is contained in:
Marian Buschsieweke 2024-08-02 19:57:36 +02:00
parent af61cf40e3
commit 8839ccbe50
No known key found for this signature in database
GPG Key ID: 77AA882EC78084E6
4 changed files with 75 additions and 0 deletions

View File

@ -25,6 +25,7 @@ FEATURES_PROVIDED += periph_uart_nonblocking
ifneq (f1,$(CPU_FAM))
FEATURES_PROVIDED += periph_gpio_ll_open_drain_pull_up
FEATURES_PROVIDED += periph_gpio_ll_switch_dir
endif
ifneq (,$(filter $(CPU_FAM),c0 f0 f1 f3 g0 g4 l0 l1 l4 l5 u5 wb wl))

View File

@ -142,6 +142,31 @@ static inline void gpio_ll_write(gpio_port_t port, uword_t value)
p->ODR = value;
}
#ifdef MODULE_PERIPH_GPIO_LL_SWITCH_DIR
static inline uword_t gpio_ll_prepare_switch_dir(uword_t mask)
{
/* implementation too large to always inline */
extern uword_t gpio_ll_prepare_switch_dir_impl(uword_t mask);
return gpio_ll_prepare_switch_dir_impl(mask);
}
static inline void gpio_ll_switch_dir_output(gpio_port_t port, uword_t pins)
{
GPIO_TypeDef *p = (GPIO_TypeDef *)port;
unsigned irq_state = irq_disable();
p->MODER |= pins;
irq_restore(irq_state);
}
static inline void gpio_ll_switch_dir_input(gpio_port_t port, uword_t pins)
{
GPIO_TypeDef *p = (GPIO_TypeDef *)port;
unsigned irq_state = irq_disable();
p->MODER &= ~pins;
irq_restore(irq_state);
}
#endif
static inline gpio_port_t gpio_get_port(gpio_t pin)
{
return pin & 0xfffffff0LU;

View File

@ -31,6 +31,11 @@ extern "C" {
* public view on type */
#ifndef DOXYGEN
#if !defined(CPU_FAM_STM32F1)
/* For the STM32F1 GPIO peripheral, the gpio_ll_switch_dir is not supported */
# define HAVE_GPIO_LL_PREPARE_SWITCH_DIR
#endif
#define HAVE_GPIO_PULL_STRENGTH_T
typedef enum {
GPIO_PULL_WEAKEST = 0,

View File

@ -361,6 +361,50 @@ int gpio_ll_init(gpio_port_t port, uint8_t pin, gpio_conf_t conf)
return 0;
}
uword_t gpio_ll_prepare_switch_dir_impl(uword_t mask)
{
/* Mask contains a bitmask containing the pins needed to change
* the direction of. E.g. for pins 0 to 3 it looks like:
*
* 3 2 1 0
* +----+----+----+----+
* | P3 | P2 | P1 | P0 |
* +----+----+----+----+
*
* We need to update the GPIOX->MODER register, which for pins 0 to 3
* looks like this:
*
* 7 6 5 4 3 2 1 0
* +---------+---------+---------+---------+
* | MODE3 | MODE2 | MODE1 | MODE0 |
* +---------+---------+---------+---------+
*
* Where each mode field will have the value `0b00` for input or `0b01`
* for output (the others two values are for alternate function mode and
* analog mode, which are both not relevant here). So, we need a way to
* efficiently set and clear every second bit. Specifically, a bitmask
* that looks like this is our goal:
*
* 7 6 5 4 3 2 1 0
* +----+----+----+----+----+----+----+----+
* | 0 | P3 | 0 | P2 | 0 | P1 | 0 | P0 |
* +----+----+----+----+----+----+----+----+
*
* This is what below bit magic magic does (but for 16 pins instead of
* 4).
*/
uword_t output = mask & 0xFFFF;
output |= output << 8;
output &= 0x00FF00FF;
output |= output << 4;
output &= 0x0F0F0F0F;
output |= output << 2;
output &= 0x33333333;
output |= output << 1;
output &= 0x55555555;
return output;
}
gpio_conf_t gpio_ll_query_conf(gpio_port_t port, uint8_t pin)
{
gpio_conf_t result = { 0 };