1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00
19407: cpu/stm32/periph: Implement GPIO LL for STM32F1 without IRQ support (yet) r=gschorcht a=maribu

### Contribution description

This implements GPIO LL support for the STM32F1 in the first commit. IRQ support is added with https://github.com/RIOT-OS/RIOT/pull/19412.

This sneaks in a second commit replacing the `expect()` calls in `tests/periph_gpio_ll` with a trivial five-liner that doesn't `panic()`, so that stdio output will still be delivered on high level stdio implementations. The tests provides a lot of useful output to aid debugging, so its a great usability improvement if the test makes sure to actually deliver that output.

### Testing procedure

<details><summary><code>make -C tests/periph_gpio_ll BOARD=nucleo-f103rb flash term</code></summary>

```
2023-03-17 18:55:09,188 # Help: Press s to start test, r to print it is ready
s
2023-03-17 18:55:10,299 # START
2023-03-17 18:55:10,307 # main(): This is RIOT! (Version: 2023.04-devel-683-g9c3812-cpu/stm32/periph/gpio_ll)
2023-03-17 18:55:10,309 # Test / Hardware Details:
2023-03-17 18:55:10,310 # ========================
2023-03-17 18:55:10,311 # Cabling:
2023-03-17 18:55:10,313 # (INPUT -- OUTPUT)
2023-03-17 18:55:10,315 #   P2.10 (PC10) -- P2.2 (PC2)
2023-03-17 18:55:10,318 #   P2.12 (PC12) -- P2.3 (PC3)
2023-03-17 18:55:10,322 # Number of pull resistor values supported: 1
2023-03-17 18:55:10,325 # Number of drive strengths supported: 1
2023-03-17 18:55:10,328 # Number of slew rates supported: 3
2023-03-17 18:55:10,330 # Valid GPIO ports:
2023-03-17 18:55:10,332 # - PORT 0 (PORT A)
2023-03-17 18:55:10,333 # - PORT 1 (PORT B)
2023-03-17 18:55:10,335 # - PORT 2 (PORT C)
2023-03-17 18:55:10,336 # - PORT 3 (PORT D)
2023-03-17 18:55:10,338 # - PORT 4 (PORT E)
2023-03-17 18:55:10,338 # 
2023-03-17 18:55:10,341 # Testing gpio_port_pack_addr()
2023-03-17 18:55:10,343 # =============================
2023-03-17 18:55:10,343 # 
2023-03-17 18:55:10,344 # All OK
2023-03-17 18:55:10,344 # 
2023-03-17 18:55:10,346 # Testing gpip_ng_init()
2023-03-17 18:55:10,348 # ======================
2023-03-17 18:55:10,348 # 
2023-03-17 18:55:10,354 # Testing is_gpio_port_num_valid() is true for PORT_OUT and PORT_IN:
2023-03-17 18:55:10,354 # 
2023-03-17 18:55:10,358 # Testing input configurations for PIN_IN_0:
2023-03-17 18:55:10,361 # Support for input with pull up: yes
2023-03-17 18:55:10,366 # state: in, pull: up, schmitt trigger: off, value: on
2023-03-17 18:55:10,369 # Support for input with pull down: yes
2023-03-17 18:55:10,374 # state: in, pull: down, schmitt trigger: off, value: off
2023-03-17 18:55:10,378 # Support for input with pull to bus level: no
2023-03-17 18:55:10,383 # Support for floating input (no pull resistors): yes
2023-03-17 18:55:10,388 # state: in, pull: none, schmitt trigger: off, value: off
2023-03-17 18:55:10,388 # 
2023-03-17 18:55:10,392 # Testing output configurations for PIN_OUT_0:
2023-03-17 18:55:10,397 # Support for output (push-pull) with initial value of LOW: yes
2023-03-17 18:55:10,401 # state: out-pp, slew: slowest, value: off
2023-03-17 18:55:10,404 # Output is indeed LOW: yes
2023-03-17 18:55:10,408 # state: out-pp, slew: slowest, value: on
2023-03-17 18:55:10,411 # Output can be pushed HIGH: yes
2023-03-17 18:55:10,417 # Support for output (push-pull) with initial value of HIGH: yes
2023-03-17 18:55:10,420 # state: out-pp, slew: slowest, value: on
2023-03-17 18:55:10,424 # Output is indeed HIGH: yes
2023-03-17 18:55:10,430 # Support for output (open drain with pull up) with initial value of LOW: no
2023-03-17 18:55:10,437 # Support for output (open drain with pull up) with initial value of HIGH: no
2023-03-17 18:55:10,443 # Support for output (open drain) with initial value of LOW: yes
2023-03-17 18:55:10,449 # state: out-od, slew: slowest, pull: none, schmitt trigger: off, value: off
2023-03-17 18:55:10,452 # Output is indeed LOW: yes
2023-03-17 18:55:10,458 # Support for output (open drain) with initial value of HIGH: yes
2023-03-17 18:55:10,465 # state: out-od, slew: slowest, pull: none, schmitt trigger: off, value: on
2023-03-17 18:55:10,470 # state: in, pull: down, schmitt trigger: off, value: off
2023-03-17 18:55:10,474 # Output can indeed be pulled LOW: yes
2023-03-17 18:55:10,478 # state: in, pull: up, schmitt trigger: off, value: on
2023-03-17 18:55:10,483 # Output can indeed be pulled HIGH: yes
2023-03-17 18:55:10,488 # Support for output (open source) with initial value of LOW: no
2023-03-17 18:55:10,494 # Support for output (open source) with initial value of HIGH: no
2023-03-17 18:55:10,501 # Support for output (open source with pull up) with initial value of HIGH: no
2023-03-17 18:55:10,508 # Support for output (open source with pull up) with initial value of LOW: no
2023-03-17 18:55:10,511 # Support for disconnecting GPIO: yes
2023-03-17 18:55:10,515 # Output can indeed be pulled LOW: yes
2023-03-17 18:55:10,519 # Output can indeed be pulled HIGH: yes
2023-03-17 18:55:10,519 # 
2023-03-17 18:55:10,523 # Testing Reading/Writing GPIO Ports
2023-03-17 18:55:10,526 # ==================================
2023-03-17 18:55:10,526 # 
2023-03-17 18:55:10,529 # testing initial value of 0 after init
2023-03-17 18:55:10,531 # ...OK
2023-03-17 18:55:10,535 # testing setting both outputs_optional simultaneously
2023-03-17 18:55:10,537 # ...OK
2023-03-17 18:55:10,541 # testing clearing both outputs_optional simultaneously
2023-03-17 18:55:10,543 # ...OK
2023-03-17 18:55:10,547 # testing toggling first output (0 --> 1)
2023-03-17 18:55:10,548 # ...OK
2023-03-17 18:55:10,552 # testing toggling first output (1 --> 0)
2023-03-17 18:55:10,553 # ...OK
2023-03-17 18:55:10,557 # testing toggling second output (0 --> 1)
2023-03-17 18:55:10,558 # ...OK
2023-03-17 18:55:10,562 # testing toggling second output (1 --> 0)
2023-03-17 18:55:10,563 # ...OK
2023-03-17 18:55:10,569 # testing setting first output and clearing second with write
2023-03-17 18:55:10,570 # ...OK
2023-03-17 18:55:10,575 # testing setting second output and clearing first with write
2023-03-17 18:55:10,576 # ...OK
2023-03-17 18:55:10,580 # All input/output operations worked as expected
2023-03-17 18:55:10,580 # 
2023-03-17 18:55:10,580 # 
2023-03-17 18:55:10,582 # TEST SUCCEEDED
2023-03-17 18:55:10,588 # { "threads": [{ "name": "main", "stack_size": 1536, "stack_used": 456 }]}
```

</details>

<details><summary><code>make -C tests/bench_periph_gpio_ll BOARD=nucleo-f103rb flash term</code></summary>

```
2023-03-17 18:55:42,192 # Help: Press s to start test, r to print it is ready
s
2023-03-17 18:55:44,616 # START
2023-03-17 18:55:44,624 # main(): This is RIOT! (Version: 2023.04-devel-683-g9c3812-cpu/stm32/periph/gpio_ll)
2023-03-17 18:55:44,624 # 
2023-03-17 18:55:44,626 # Benchmarking GPIO APIs
2023-03-17 18:55:44,628 # ======================
2023-03-17 18:55:44,628 # 
2023-03-17 18:55:44,632 # estimating loop overhead for compensation
2023-03-17 18:55:44,635 # -----------------------------------------
2023-03-17 18:55:44,642 # 4168 us for 50000 iterations
2023-03-17 18:55:44,642 # 
2023-03-17 18:55:44,647 # periph/gpio: Using 2x gpio_set() and 2x gpio_clear()
2023-03-17 18:55:44,651 # ---------------------------------------------------
2023-03-17 18:55:44,706 # 50000 iterations took 45840 us (50008 us uncompensated)
2023-03-17 18:55:44,713 # Two square waves pins at      1090750 Hz (      999840 Hz uncompensated)
2023-03-17 18:55:44,719 # ~66 CPU cycles per square wave period (~72 cycles uncompensated)
2023-03-17 18:55:44,719 # :'-(
2023-03-17 18:55:44,719 # 
2023-03-17 18:55:44,724 # periph/gpio_ll: Using gpio_ll_set() and gpio_ll_clear()
2023-03-17 18:55:44,729 # -------------------------------------------------------
2023-03-17 18:55:44,738 # 50000 iterations took 695 us (4863 us uncompensated)
2023-03-17 18:55:44,745 # Two square waves pins at     71942446 Hz (    10281719 Hz uncompensated)
2023-03-17 18:55:44,750 # ~1 CPU cycles per square wave period (~7 cycles uncompensated)
2023-03-17 18:55:44,751 # :-D
2023-03-17 18:55:44,751 # 
2023-03-17 18:55:44,755 # periph/gpio: Using 4x gpio_toggle()
2023-03-17 18:55:44,757 # -----------------------------------
2023-03-17 18:55:44,965 # 50000 iterations took 198646 us (202814 us uncompensated)
2023-03-17 18:55:44,972 # Two square waves pins at       251704 Hz (      246531 Hz uncompensated)
2023-03-17 18:55:44,977 # ~286 CPU cycles per square wave period (~292 cycles uncompensated)
2023-03-17 18:55:44,978 # :'-(
2023-03-17 18:55:44,978 # 
2023-03-17 18:55:44,982 # periph/gpio_ll: Using 2x gpio_ll_toggle()
2023-03-17 18:55:44,985 # -----------------------------------------
2023-03-17 18:55:45,010 # 50000 iterations took 15972 us (20140 us uncompensated)
2023-03-17 18:55:45,017 # Two square waves pins at      3130478 Hz (     2482621 Hz uncompensated)
2023-03-17 18:55:45,023 # ~23 CPU cycles per square wave period (~29 cycles uncompensated)
2023-03-17 18:55:45,023 # :'-(
2023-03-17 18:55:45,023 # 
2023-03-17 18:55:45,026 # periph/gpio: Using 4x gpio_write()
2023-03-17 18:55:45,029 # ----------------------------------
2023-03-17 18:55:45,097 # 50000 iterations took 58345 us (62513 us uncompensated)
2023-03-17 18:55:45,103 # Two square waves pins at       856971 Hz (      799833 Hz uncompensated)
2023-03-17 18:55:45,109 # ~84 CPU cycles per square wave period (~90 cycles uncompensated)
2023-03-17 18:55:45,109 # :'-(
2023-03-17 18:55:45,110 # 
2023-03-17 18:55:45,113 # periph/gpio_ll: Using 2x gpio_ll_write()
2023-03-17 18:55:45,117 # ----------------------------------------
2023-03-17 18:55:45,128 # 50000 iterations took 2777 us (6945 us uncompensated)
2023-03-17 18:55:45,135 # Two square waves pins at     18005041 Hz (     7199424 Hz uncompensated)
2023-03-17 18:55:45,141 # ~4 CPU cycles per square wave period (~10 cycles uncompensated)
2023-03-17 18:55:45,141 # :-)
2023-03-17 18:55:45,141 # 
2023-03-17 18:55:45,141 # 
2023-03-17 18:55:45,142 # TEST SUCCEEDED
2023-03-17 18:55:45,149 # { "threads": [{ "name": "main", "stack_size": 1536, "stack_used": 448 }]}
```

</details>

### Issues/PRs references

None


Co-authored-by: Marian Buschsieweke <marian.buschsieweke@ovgu.de>
This commit is contained in:
bors[bot] 2023-03-20 15:14:41 +00:00 committed by GitHub
commit 45c839dc98
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 374 additions and 104 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)

View File

@ -27,7 +27,6 @@
#include "mutex.h"
#include "periph/gpio_ll.h"
#include "periph/gpio_ll_irq.h"
#include "test_utils/expect.h"
#include "timex.h"
#include "ztimer.h"
@ -54,6 +53,19 @@ static void puts_optional(const char *str)
#define printf_optional(...) printf(__VA_ARGS__)
#endif
/* a custom expect that keeps the CPU alive makes debugging easier with
* stdio that requires RIOT to remain alive, e.g. USB CDC ACM */
static void expect_impl(int val, unsigned line)
{
if (!val) {
printf("expect failed at line %u\n", line);
fflush(stdout);
while(1) {}
}
}
#define expect(x) expect_impl((int)(x), __LINE__)
static void print_cabling(unsigned port1, unsigned pin1,
unsigned port2, unsigned pin2)
{
@ -66,7 +78,8 @@ static void print_details(void)
{
puts_optional("Test / Hardware Details:\n"
"========================\n"
"Cabling:");
"Cabling:\n"
"(INPUT -- OUTPUT)");
print_cabling(PORT_IN, PIN_IN_0, PORT_OUT, PIN_OUT_0);
print_cabling(PORT_IN, PIN_IN_1, PORT_OUT, PIN_OUT_1);
printf("Number of pull resistor values supported: %u\n", GPIO_PULL_NUMOF);