mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
Merge #19407
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:
commit
45c839dc98
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user