1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2025-12-15 17:43:51 +01:00

cpu/stm32: Implement gpio_ll for STM32F1

This provides basic GPIO LL support. IRQ support will be added as
follow up.
This commit is contained in:
Marian Buschsieweke 2023-03-17 14:34:44 +01:00
parent c4400e8964
commit 4a0c462ec3
No known key found for this signature in database
GPG Key ID: CB8E3238CE715A94
7 changed files with 359 additions and 102 deletions

View File

@ -11,9 +11,9 @@ FEATURES_PROVIDED += periph_timer_periodic
FEATURES_PROVIDED += periph_rtt_overflow
FEATURES_PROVIDED += periph_uart_modecfg
FEATURES_PROVIDED += periph_uart_nonblocking
FEATURES_PROVIDED += periph_gpio_ll
ifneq ($(CPU_FAM),f1)
FEATURES_PROVIDED += periph_gpio_ll
FEATURES_PROVIDED += periph_gpio_ll_irq
FEATURES_PROVIDED += periph_gpio_ll_irq_level_triggered_high
FEATURES_PROVIDED += periph_gpio_ll_irq_level_triggered_low

View File

@ -71,7 +71,14 @@ static inline void gpio_ll_set(gpio_port_t port, uword_t mask)
static inline void gpio_ll_clear(gpio_port_t port, uword_t mask)
{
GPIO_TypeDef *p = (GPIO_TypeDef *)port;
/* The STM32F4 vendor header files do include defines for accessing the
* BRR register, but do not have a BRR register.
* See https://github.com/STMicroelectronics/cmsis_device_f4/pull/7 */
#if defined(GPIO_BRR_BR0) && !defined(CPU_FAM_STM32F4)
p->BRR = mask;
#else
p->BSRR = mask << 16;
#endif
}
static inline void gpio_ll_toggle(gpio_port_t port, uword_t mask)

View File

@ -20,7 +20,7 @@
#define PERIPH_CPU_GPIO_LL_H
#include <stdint.h>
#include "cpu.h"
#include "periph_cpu.h"
#ifdef __cplusplus
extern "C" {
@ -46,6 +46,24 @@ 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:
* 1. SLOWEST: 2 MHZ
* 2. SLOW: 10 MHZ
* 3. FAST/FASTEST: 50 MHz
*/
#define HAVE_GPIO_SLEW_T
typedef enum {
GPIO_SLEW_SLOWEST = 0,
GPIO_SLEW_SLOW = 1,
GPIO_SLEW_FAST = 2,
GPIO_SLEW_FASTEST = 2,
} gpio_slew_t;
#endif
#define HAVE_GPIO_IRQ_TRIG_T
/*
* Layout:

View File

@ -19,6 +19,8 @@
#ifndef PERIPH_F1_PERIPH_CPU_H
#define PERIPH_F1_PERIPH_CPU_H
#include "cpu_conf.h"
#ifdef __cplusplus
extern "C" {
#endif
@ -63,6 +65,48 @@ extern "C" {
*/
#define ADC_DEVS (2U)
/**
* @name GPIO Definitions Missing in Vendor Files
* @{
*/
/**
* @brief Possible values of the MODE0 field in the GPIO CRL register
*
* The MODE1 to MODE7 fields have the same values. Don't forget to shift the
* constants to the field position for MODE1 to MODE7 by 4 times n bits, where
* n is the pin number.
*
* In addition the MODE8 to MODE15 fields in the CRH register have the same
* layout and semantics as the MODE0 to MODE 7 fields in the CRL register.
*/
enum {
GPIO_CRL_MODE0_INPUT = (0x0 << GPIO_CRL_MODE0_Pos),
GPIO_CRL_MODE0_OUTPUT_10MHZ = (0x1 << GPIO_CRL_MODE0_Pos),
GPIO_CRL_MODE0_OUTPUT_2MHZ = (0x2 << GPIO_CRL_MODE0_Pos),
GPIO_CRL_MODE0_OUTPUT_50MHZ = (0x3 << GPIO_CRL_MODE0_Pos),
};
/**
* @brief Possible values of the CNF0 field in the GPIO CRL register
*
* The CNF1 to CNF7 fields have the same values. Don't forget to shift the
* constants to the field position for CNF1 to CNF7 by 4 times n bits, where
* n is the pin number.
*
* In addition the CNF8 to CNF15 fields in the CRH register have the same
* layout and semantics as the CNF0 to CNF 7 fields in the CRL register.
*/
enum {
GPIO_CRL_CNF0_INPUT_ANALOG = (0x0 << GPIO_CRL_CNF0_Pos),
GPIO_CRL_CNF0_INPUT_FLOATING = (0x1 << GPIO_CRL_CNF0_Pos),
GPIO_CRL_CNF0_INPUT_PULL = (0x2 << GPIO_CRL_CNF0_Pos),
GPIO_CRL_CNF0_OUTPUT_PUSH_PULL = (0x0 << GPIO_CRL_CNF0_Pos),
GPIO_CRL_CNF0_OUTPUT_OPEN_DRAIN = (0x1 << GPIO_CRL_CNF0_Pos),
GPIO_CRL_CNF0_AF_PUSH_PULL = (0x2 << GPIO_CRL_CNF0_Pos),
GPIO_CRL_CNF0_AF_OPEN_DRAIN = (0x3 << GPIO_CRL_CNF0_Pos),
};
/** @} */
#ifdef __cplusplus
}
#endif

View File

@ -16,6 +16,7 @@ config CPU_FAM_F1
select HAS_PERIPH_FLASHPAGE_IN_ADDRESS_SPACE
select HAS_PERIPH_FLASHPAGE_PAGEWISE
select HAS_PERIPH_FLASHPAGE_RAW
select HAS_PERIPH_GPIO_LL
select HAS_PERIPH_RTT_SET_COUNTER
select HAS_PERIPH_WDT
select HAVE_SHARED_PERIPH_RTT_PERIPH_RTC

View File

@ -35,32 +35,42 @@
#include "bitarithm.h"
#include "periph/gpio_ll.h"
#if defined(CPU_FAM_STM32F0) || defined(CPU_FAM_STM32F3) || defined(CPU_FAM_STM32L1)
#ifdef RCC_AHBENR_GPIOAEN
# define GPIO_BUS AHB
# define GPIOAEN RCC_AHBENR_GPIOAEN
#elif defined(CPU_FAM_STM32L0) || defined(CPU_FAM_STM32G0)
# define GPIO_BUS IOP
# define GPIOAEN RCC_IOPENR_GPIOAEN
#elif defined(CPU_FAM_STM32L4) || defined(CPU_FAM_STM32WB) || \
defined(CPU_FAM_STM32G4) || defined(CPU_FAM_STM32L5) || \
defined(CPU_FAM_STM32U5) || defined(CPU_FAM_STM32WL)
#define GPIO_BUS AHB2
# if defined(CPU_FAM_STM32U5)
# define GPIOAEN RCC_AHB2ENR1_GPIOAEN
# else
# define GPIOAEN RCC_AHB2ENR_GPIOAEN
# endif
# ifdef PWR_CR2_IOSV
# define PORTG_REQUIRES_EXTERNAL_POWER
# endif
#elif defined(CPU_FAM_STM32MP1)
# define GPIO_BUS AHB4
# define GPIOAEN RCC_MC_AHB4ENSETR_GPIOAEN
#else
#endif
#ifdef RCC_AHB1ENR_GPIOAEN
# define GPIO_BUS AHB1
# define GPIOAEN RCC_AHB1ENR_GPIOAEN
#endif
#ifdef RCC_AHB2ENR1_GPIOAEN
# define GPIO_BUS AHB2
# define GPIOAEN RCC_AHB2ENR1_GPIOAEN
#endif
#ifdef RCC_AHB2ENR_GPIOAEN
# define GPIO_BUS AHB2
# define GPIOAEN RCC_AHB2ENR_GPIOAEN
#endif
#ifdef RCC_MC_AHB4ENSETR_GPIOAEN
# define GPIO_BUS AHB4
# define GPIOAEN RCC_MC_AHB4ENSETR_GPIOAEN
#endif
#ifdef RCC_IOPENR_GPIOAEN
# define GPIO_BUS IOP
# define GPIOAEN RCC_IOPENR_GPIOAEN
#endif
#ifdef RCC_APB2ENR_IOPAEN
# define GPIO_BUS APB2
# define GPIOAEN RCC_APB2ENR_IOPAEN
#endif
static void _init_clock(gpio_port_t port)
{
periph_clk_en(GPIO_BUS, (GPIOAEN << GPIO_PORT_NUM(port)));
@ -73,6 +83,7 @@ static void _init_clock(gpio_port_t port)
#endif
}
#if defined(GPIO_MODER_MODER0) || defined(GPIO_MODER_MODE0)
static void _set_dir(gpio_port_t port, uint8_t pin, bool output)
{
GPIO_TypeDef *p = (void *)port;
@ -83,63 +94,10 @@ static void _set_dir(gpio_port_t port, uint8_t pin, bool output)
}
p->MODER = tmp;
}
#endif
static void _set_output_type(gpio_port_t port, uint8_t pin, bool open_drain)
{
GPIO_TypeDef *p = (void *)port;
if (open_drain) {
p->OTYPER |= 1UL << pin;
}
else {
p->OTYPER &= ~(1UL << pin);
}
}
static void _set_pull_config(gpio_port_t port, uint8_t pin, gpio_pull_t pull)
{
GPIO_TypeDef *p = (void *)port;
/* being more verbose here so that compiler doesn't generate two loads and stores when accessing
* volatile variable */
uint32_t pupdr = p->PUPDR;
pupdr &= ~(0x3UL << (2 * pin));
pupdr |= (uint32_t)pull << (2 * pin);
p->PUPDR = pupdr;
}
static void _set_slew_rate(gpio_port_t port, uint8_t pin, gpio_slew_t slew_rate)
{
GPIO_TypeDef *p = (void *)port;
/* being more verbose here so that compiler doesn't generate two loads and
* stores when accessing volatile variable */
uint32_t ospeedr = p->OSPEEDR;
ospeedr &= ~(3UL << (2 * pin));
ospeedr |= (uint32_t)slew_rate << (2 * pin);
p->OSPEEDR = ospeedr;
}
int gpio_ll_init(gpio_port_t port, uint8_t pin, const gpio_conf_t *conf)
{
if ((conf->pull == GPIO_PULL_KEEP) || (conf->state == GPIO_OUTPUT_OPEN_SOURCE)) {
return -ENOTSUP;
}
unsigned state = irq_disable();
_init_clock(port);
if (conf->initial_value) {
gpio_ll_set(port, 1UL << pin);
}
else {
gpio_ll_clear(port, 1UL << pin);
}
_set_output_type(port, pin, conf->state == GPIO_OUTPUT_OPEN_DRAIN);
_set_pull_config(port, pin, conf->pull);
_set_slew_rate(port, pin, conf->slew_rate);
_set_dir(port, pin, conf->state < GPIO_INPUT);
irq_restore(state);
return 0;
}
#if (defined(GPIO_MODER_MODER0) || defined(GPIO_MODER_MODE0)) \
&& (defined(GPIO_OTYPER_OT0) || defined(GPIO_OTYPER_OT_0))
static gpio_state_t _get_state(gpio_port_t port, uint8_t pin)
{
GPIO_TypeDef *p = (void *)port;
@ -153,6 +111,32 @@ static gpio_state_t _get_state(gpio_port_t port, uint8_t pin)
}
return GPIO_USED_BY_PERIPHERAL;
}
#endif
#if defined(GPIO_OTYPER_OT0) || defined(GPIO_OTYPER_OT_0)
static void _set_output_type(gpio_port_t port, uint8_t pin, bool open_drain)
{
GPIO_TypeDef *p = (void *)port;
if (open_drain) {
p->OTYPER |= 1UL << pin;
}
else {
p->OTYPER &= ~(1UL << pin);
}
}
#endif
#if defined(GPIO_PUPDR_PUPDR0) || defined(GPIO_PUPDR_PUPD0)
static void _set_pull_config(gpio_port_t port, uint8_t pin, gpio_pull_t pull)
{
GPIO_TypeDef *p = (void *)port;
/* being more verbose here so that compiler doesn't generate two loads and stores when accessing
* volatile variable */
uint32_t pupdr = p->PUPDR;
pupdr &= ~(0x3UL << (2 * pin));
pupdr |= (uint32_t)pull << (2 * pin);
p->PUPDR = pupdr;
}
static gpio_pull_t _get_pull_config(gpio_port_t port, uint8_t pin)
{
@ -160,6 +144,20 @@ static gpio_pull_t _get_pull_config(gpio_port_t port, uint8_t pin)
uint32_t pupdr = (p->PUPDR >> (2 * pin)) & 0x3UL;
return (gpio_pull_t)pupdr;
}
#endif
#if defined(GPIO_OSPEEDR_OSPEED0) || defined(GPIO_OSPEEDER_OSPEEDR0) \
|| defined(GPIO_OSPEEDER_OSPEED0) || defined(GPIO_OSPEEDR_OSPEEDR0)
static void _set_slew_rate(gpio_port_t port, uint8_t pin, gpio_slew_t slew_rate)
{
GPIO_TypeDef *p = (void *)port;
/* being more verbose here so that compiler doesn't generate two loads and
* stores when accessing volatile variable */
uint32_t ospeedr = p->OSPEEDR;
ospeedr &= ~(3UL << (2 * pin));
ospeedr |= (uint32_t)slew_rate << (2 * pin);
p->OSPEEDR = ospeedr;
}
static gpio_slew_t _get_slew_rate(gpio_port_t port, uint8_t pin)
{
@ -167,15 +165,190 @@ static gpio_slew_t _get_slew_rate(gpio_port_t port, uint8_t pin)
uint32_t ospeedr = (p->OSPEEDR >> (2 * pin)) & 0x3UL;
return (gpio_slew_t)ospeedr;
}
#endif
#ifdef GPIO_CRL_MODE
static void _set_legacy_f1_config(gpio_port_t port, uint8_t pin,
const gpio_conf_t *conf)
{
/* STM32F1 style config register mix output mode and slew rate into the
* same field. This look up table can be used to look up the correct
* output mode by slew rate */
static const uint8_t output_mode_by_slew_rate[] = {
[GPIO_SLEW_SLOWEST] = GPIO_CRL_MODE0_OUTPUT_2MHZ,
[GPIO_SLEW_FAST] = GPIO_CRL_MODE0_OUTPUT_10MHZ,
[GPIO_SLEW_FASTEST] = GPIO_CRL_MODE0_OUTPUT_50MHZ,
};
GPIO_TypeDef *p = (void *)port;
/* There is low control register (CRL) for pins 0-7, and a high control
* register (CRH) for pins 8-15. `offset` is the offset within the
* registers, `high_reg` is true if CRH is to be used */
unsigned offset = (pin & 0x7U) << 2;
bool high_reg = pin > 7;
uint32_t control = high_reg ? p->CRH : p -> CRL;
assert((unsigned)conf->slew_rate < ARRAY_SIZE(output_mode_by_slew_rate));
/* prepare bis in cnf and mode fields for given pin */
uint32_t cnf_mode = 0;
switch (conf->state) {
default:
case GPIO_DISCONNECT:
/* Keeping GPIO in analog mode is said to reduce power consumption.
* This is plausible, as the Schmitt trigger and the input buffer could
* be disabled. */
cnf_mode = GPIO_CRL_MODE0_INPUT | GPIO_CRL_CNF0_INPUT_ANALOG;
break;
case GPIO_INPUT:
switch (conf->pull) {
default:
case GPIO_FLOATING:
cnf_mode = GPIO_CRL_MODE0_INPUT | GPIO_CRL_CNF0_INPUT_FLOATING;
break;
case GPIO_PULL_UP:
cnf_mode = GPIO_CRL_MODE0_INPUT | GPIO_CRL_CNF0_INPUT_PULL;
/* ODR controls pull resistor in input mode. We access ODR via
* BSRR to atomically set the bit (mostly to safe ROM and CPU
* cycles, IRQs are disabled anyway) */
p->BSRR = 1U << pin;
break;
case GPIO_PULL_DOWN:
cnf_mode = GPIO_CRL_MODE0_INPUT | GPIO_CRL_CNF0_INPUT_PULL;
/* ODR controls pull resistor in input mode. We access ODR via
* BSRR to atomically clear the bit (mostly to safe ROM and CPU
* cycles, IRQs are disabled anyway) */
p->BSRR = 1U << (pin | 0x10);
}
break;
case GPIO_OUTPUT_PUSH_PULL:
cnf_mode = GPIO_CRL_CNF0_OUTPUT_PUSH_PULL
| output_mode_by_slew_rate[conf->slew_rate];
break;
case GPIO_OUTPUT_OPEN_DRAIN:
cnf_mode = GPIO_CRL_CNF0_OUTPUT_OPEN_DRAIN
| output_mode_by_slew_rate[conf->slew_rate];
}
/* clear old values of cnf and mode fields in config reg */
control &= ~(0xFU << offset);
/* apply new values of cnf and mode fields in config reg */
control |= cnf_mode << offset;
if (high_reg) {
p->CRH = control;
}
else {
p->CRL = control;
}
}
static void _get_legacy_f1_config(gpio_conf_t *dest, gpio_port_t port,
uint8_t pin)
{
GPIO_TypeDef *p = (void *)port;
unsigned offset = (pin & 0x7U) << 2;
bool high_reg = pin > 7;
uint32_t control = high_reg ? p->CRH : p ->CRL;
uint32_t cnf_mode = control >> offset;
uint32_t cnf = cnf_mode & GPIO_CRL_CNF0_Msk;
uint32_t mode = cnf_mode & GPIO_CRL_MODE0_Msk;
switch (mode) {
default:
case GPIO_CRL_MODE0_INPUT:
switch (cnf) {
default:
case GPIO_CRL_CNF0_INPUT_ANALOG:
dest->state = GPIO_DISCONNECT;
break;
case GPIO_CRL_CNF0_INPUT_FLOATING:
dest->state = GPIO_INPUT;
break;
case GPIO_CRL_CNF0_INPUT_PULL:
dest->state = GPIO_INPUT;
dest->pull = GPIO_PULL_DOWN;
if (p->ODR & (1U << pin)) {
dest->pull = GPIO_PULL_UP;
}
}
return;
case GPIO_CRL_MODE0_OUTPUT_2MHZ:
dest->slew_rate = GPIO_SLEW_SLOWEST;
break;
case GPIO_CRL_MODE0_OUTPUT_10MHZ:
dest->slew_rate = GPIO_SLEW_FAST;
break;
case GPIO_CRL_MODE0_OUTPUT_50MHZ:
dest->slew_rate = GPIO_SLEW_FASTEST;
break;
}
switch (cnf) {
case GPIO_CRL_CNF0_OUTPUT_PUSH_PULL:
dest->state = GPIO_OUTPUT_PUSH_PULL;
break;
case GPIO_CRL_CNF0_OUTPUT_OPEN_DRAIN:
dest->state = GPIO_OUTPUT_OPEN_DRAIN;
break;
default:
case GPIO_CRL_CNF0_AF_PUSH_PULL:
case GPIO_CRL_CNF0_AF_OPEN_DRAIN:
dest->state = GPIO_USED_BY_PERIPHERAL;
}
}
#endif
int gpio_ll_init(gpio_port_t port, uint8_t pin, const gpio_conf_t *conf)
{
if ((conf->pull == GPIO_PULL_KEEP) || (conf->state == GPIO_OUTPUT_OPEN_SOURCE)) {
return -ENOTSUP;
}
#ifndef GPIO_PUPDR_PUPDR0
/* without dedicated pull up / pull down register, pull resistors can only
* be used with input pins */
if ((conf->state == GPIO_OUTPUT_OPEN_DRAIN) && (conf->pull != GPIO_FLOATING)) {
return -ENOTSUP;
}
#endif
unsigned state = irq_disable();
_init_clock(port);
if (conf->initial_value) {
gpio_ll_set(port, 1UL << pin);
}
else {
gpio_ll_clear(port, 1UL << pin);
}
#ifdef GPIO_CRL_MODE
/* old STM32F1 style GPIO configuration register layout */
_set_legacy_f1_config(port, pin, conf);
#else
/* modern STM32 style GPIO configuration register layout */
_set_output_type(port, pin, conf->state == GPIO_OUTPUT_OPEN_DRAIN);
_set_pull_config(port, pin, conf->pull);
_set_slew_rate(port, pin, conf->slew_rate);
_set_dir(port, pin, conf->state < GPIO_INPUT);
#endif
irq_restore(state);
return 0;
}
void gpio_ll_query_conf(gpio_conf_t *dest, gpio_port_t port, uint8_t pin)
{
assert(dest);
unsigned state = irq_disable();
memset(dest, 0, sizeof(*dest));
#ifdef GPIO_CRL_MODE
/* old STM32F1 style GPIO configuration register layout */
_get_legacy_f1_config(dest, port, pin);
#else
/* modern STM32 style GPIO configuration register layout */
dest->state = _get_state(port, pin);
dest->pull = _get_pull_config(port, pin);
dest->slew_rate = _get_slew_rate(port, pin);
#endif
if (dest->state == GPIO_INPUT) {
dest->initial_value = (gpio_ll_read(port) >> pin) & 1UL;
}

View File

@ -40,30 +40,44 @@
#define EXTI_NUMOF (16U)
#define EXTI_MASK (0xFFFF)
#if defined(CPU_FAM_STM32L4) || defined(CPU_FAM_STM32WB) || \
defined(CPU_FAM_STM32G4) || defined(CPU_FAM_STM32G0) || \
defined(CPU_FAM_STM32L5) || defined(CPU_FAM_STM32U5) || \
defined(CPU_FAM_STM32WL)
# define EXTI_REG_RTSR (EXTI->RTSR1)
# define EXTI_REG_FTSR (EXTI->FTSR1)
# define EXTI_REG_SWIER (EXTI->SWIER1)
# define EXTI_REG_IMR (EXTI->IMR1)
# if !defined(CPU_FAM_STM32G0) && !defined(CPU_FAM_STM32L5) && \
!defined(CPU_FAM_STM32U5) && !defined(CPU_FAM_STM32MP1)
# define EXTI_REG_PR (EXTI->PR1)
# endif
#elif defined(CPU_FAM_STM32MP1)
# define EXTI_REG_RTSR (EXTI->RTSR1)
# define EXTI_REG_FTSR (EXTI->FTSR1)
# define EXTI_REG_PR (EXTI->PR1)
# define EXTI_REG_SWIER (EXTI->SWIER1)
# define EXTI_REG_IMR (EXTI_C2->IMR1)
#else
# define EXTI_REG_RTSR (EXTI->RTSR)
# define EXTI_REG_FTSR (EXTI->FTSR)
# define EXTI_REG_PR (EXTI->PR)
# define EXTI_REG_SWIER (EXTI->SWIER)
# define EXTI_REG_IMR (EXTI->IMR)
#if defined(EXTI_SWIER_SWI0) || defined(EXTI_SWIER_SWIER0)
#define EXTI_REG_SWIER (EXTI->SWIER)
#endif
#if defined(EXTI_SWIER1_SWI0) || defined(EXTI_SWIER1_SWIER0)
#define EXTI_REG_SWIER (EXTI->SWIER1)
#endif
#if defined(EXTI_RTSR_RT0) || defined(EXTI_RTSR_TR0)
#define EXTI_REG_RTSR (EXTI->RTSR)
#endif
#if defined(EXTI_RTSR1_RT0) || defined(EXTI_RTSR1_TR0)
#define EXTI_REG_RTSR (EXTI->RTSR1)
#endif
#if defined(EXTI_FTSR_FT0) || defined(EXTI_FTSR_TR0)
#define EXTI_REG_FTSR (EXTI->FTSR)
#endif
#if defined(EXTI_FTSR1_FT0) || defined (EXTI_FTSR1_TR0)
#define EXTI_REG_FTSR (EXTI->FTSR1)
#endif
#ifdef EXTI_PR_PR0
#define EXTI_REG_PR (EXTI->PR)
#endif
#ifdef EXTI_PR1_PIF0
#define EXTI_REG_PR (EXTI->PR1)
#endif
#if defined(EXTI_C2_BASE)
# define EXTI_REG_IMR (EXTI_C2->IMR1)
#elif defined(EXTI_IMR_IM0)
# define EXTI_REG_IMR (EXTI->IMR)
#elif defined(EXTI_IMR1_IM0)
# define EXTI_REG_IMR (EXTI->IMR1)
#endif
void gpio_ll_irq_mask(gpio_port_t port, uint8_t pin)