mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
Merge #19412
19412: cpu/stm32/periph: Add GPIO LL IRQ support for STM32F1 r=gschorcht a=maribu ### Contribution description As the title says ### Testing procedure <details><summary><code>make BOARD=nucleo-f103rb flash test-with-config -C tests/periph_gpio_ll</code></summary> ``` make: Entering directory '/home/maribu/Repos/software/RIOT/tests/periph_gpio_ll' Building application "tests_periph_gpio_ll" for "nucleo-f103rb" with MCU "stm32". "make" -C /home/maribu/Repos/software/RIOT/boards/common/init "make" -C /home/maribu/Repos/software/RIOT/boards/nucleo-f103rb "make" -C /home/maribu/Repos/software/RIOT/boards/common/nucleo "make" -C /home/maribu/Repos/software/RIOT/core "make" -C /home/maribu/Repos/software/RIOT/core/lib "make" -C /home/maribu/Repos/software/RIOT/cpu/stm32 "make" -C /home/maribu/Repos/software/RIOT/cpu/cortexm_common "make" -C /home/maribu/Repos/software/RIOT/cpu/cortexm_common/periph "make" -C /home/maribu/Repos/software/RIOT/cpu/stm32/periph "make" -C /home/maribu/Repos/software/RIOT/cpu/stm32/stmclk "make" -C /home/maribu/Repos/software/RIOT/cpu/stm32/vectors "make" -C /home/maribu/Repos/software/RIOT/drivers "make" -C /home/maribu/Repos/software/RIOT/drivers/periph_common "make" -C /home/maribu/Repos/software/RIOT/sys "make" -C /home/maribu/Repos/software/RIOT/sys/auto_init "make" -C /home/maribu/Repos/software/RIOT/sys/div "make" -C /home/maribu/Repos/software/RIOT/sys/frac "make" -C /home/maribu/Repos/software/RIOT/sys/isrpipe "make" -C /home/maribu/Repos/software/RIOT/sys/libc "make" -C /home/maribu/Repos/software/RIOT/sys/malloc_thread_safe "make" -C /home/maribu/Repos/software/RIOT/sys/newlib_syscalls_default "make" -C /home/maribu/Repos/software/RIOT/sys/pm_layered "make" -C /home/maribu/Repos/software/RIOT/sys/preprocessor "make" -C /home/maribu/Repos/software/RIOT/sys/stdio_uart "make" -C /home/maribu/Repos/software/RIOT/sys/test_utils/interactive_sync "make" -C /home/maribu/Repos/software/RIOT/sys/test_utils/print_stack_usage "make" -C /home/maribu/Repos/software/RIOT/sys/tsrb "make" -C /home/maribu/Repos/software/RIOT/sys/ztimer text data bss dec hex filename 20760 176 2728 23664 5c70 /home/maribu/Repos/software/RIOT/tests/periph_gpio_ll/bin/nucleo-f103rb/tests_periph_gpio_ll.elf /home/maribu/Repos/software/RIOT/dist/tools/openocd/openocd.sh flash /home/maribu/Repos/software/RIOT/tests/periph_gpio_ll/bin/nucleo-f103rb/tests_periph_gpio_ll.elf ### Flashing Target ### Open On-Chip Debugger 0.12.0+dev-snapshot (2023-03-13-08:56) Licensed under GNU GPL v2 For bug reports, read http://openocd.org/doc/doxygen/bugs.html DEPRECATED! use 'adapter serial' not 'hla_serial' hla_swd Info : The selected transport took over low-level target control. The results might differ compared to plain JTAG/SWD srst_only separate srst_nogate srst_open_drain connect_assert_srst Info : clock speed 1000 kHz Info : STLINK V2J29M18 (API v2) VID:PID 0483:374B Info : Target voltage: 3.252984 Info : [stm32f1x.cpu] Cortex-M3 r1p1 processor detected Info : [stm32f1x.cpu] target has 6 breakpoints, 4 watchpoints Info : starting gdb server for stm32f1x.cpu on 0 Info : Listening on port 32843 for gdb connections TargetName Type Endian TapName State -- ------------------ ---------- ------ ------------------ ------------ 0* stm32f1x.cpu hla_target little stm32f1x.cpu unknown [stm32f1x.cpu] halted due to debug-request, current mode: Thread xPSR: 0x01000000 pc: 0x08001ae4 msp: 0x20000200 Info : device id = 0x20036410 Info : flash size = 128 KiB Warn : Adding extra erase range, 0x080051c8 .. 0x080053ff auto erase enabled wrote 20936 bytes from file /home/maribu/Repos/software/RIOT/tests/periph_gpio_ll/bin/nucleo-f103rb/tests_periph_gpio_ll.elf in 1.192543s (17.144 KiB/s) verified 20936 bytes in 0.333379s (61.328 KiB/s) shutdown command invoked Done flashing r /home/maribu/Repos/software/RIOT/dist/tools/pyterm/pyterm -p "/dev/ttyACM1" -b "115200" --no-reconnect --noprefix --no-repeat-command-on-empty-line Connect to serial port /dev/ttyACM1 Welcome to pyterm! Type '/exit' to exit. READY s START main(): This is RIOT! (Version: 2023.04-devel-685-gd0d5d-cpu/stm32/periph/gpio_ll_irq) Test / Hardware Details: ======================== Cabling: (INPUT -- OUTPUT) P2.10 (PC10) -- P1.8 (PB8) P2.12 (PC12) -- P1.9 (PB9) Number of pull resistor values supported: 1 Number of drive strengths supported: 1 Number of slew rates supported: 4 Valid GPIO ports: - PORT 0 (PORT A) - PORT 1 (PORT B) - PORT 2 (PORT C) - PORT 3 (PORT D) - PORT 4 (PORT E) Testing gpio_port_pack_addr() ============================= All OK Testing gpip_ng_init() ====================== Testing is_gpio_port_num_valid() is true for PORT_OUT and PORT_IN: Testing input configurations for PIN_IN_0: Support for input with pull up: yes state: in, pull: up, schmitt trigger: off, value: on Support for input with pull down: yes state: in, pull: down, schmitt trigger: off, value: off Support for input with pull to bus level: no Support for floating input (no pull resistors): yes state: in, pull: none, schmitt trigger: off, value: off Testing output configurations for PIN_OUT_0: Support for output (push-pull) with initial value of LOW: yes state: out-pp, slew: slowest, value: off Output is indeed LOW: yes state: out-pp, slew: slowest, value: on Output can be pushed HIGH: yes Support for output (push-pull) with initial value of HIGH: yes state: out-pp, slew: slowest, value: on Output is indeed HIGH: yes Support for output (open drain with pull up) with initial value of LOW: no Support for output (open drain with pull up) with initial value of HIGH: no Support for output (open drain) with initial value of LOW: yes state: out-od, slew: slowest, pull: none, schmitt trigger: off, value: off Output is indeed LOW: yes Support for output (open drain) with initial value of HIGH: yes state: out-od, slew: slowest, pull: none, schmitt trigger: off, value: on state: in, pull: down, schmitt trigger: off, value: off Output can indeed be pulled LOW: yes state: in, pull: up, schmitt trigger: off, value: on Output can indeed be pulled HIGH: yes Support for output (open source) with initial value of LOW: no Support for output (open source) with initial value of HIGH: no Support for output (open source with pull up) with initial value of HIGH: no Support for output (open source with pull up) with initial value of LOW: no Support for disconnecting GPIO: yes Output can indeed be pulled LOW: yes Output can indeed be pulled HIGH: yes Testing Reading/Writing GPIO Ports ================================== testing initial value of 0 after init ...OK testing setting both outputs_optional simultaneously ...OK testing clearing both outputs_optional simultaneously ...OK testing toggling first output (0 --> 1) ...OK testing toggling first output (1 --> 0) ...OK testing toggling second output (0 --> 1) ...OK testing toggling second output (1 --> 0) ...OK testing setting first output and clearing second with write ...OK testing setting second output and clearing first with write ...OK All input/output operations worked as expected Testing External IRQs ===================== Testing rising edge on PIN_IN_0 ... OK Testing falling edge on PIN_IN_0 ... OK Testing both edges on PIN_IN_0 ... OK Testing masking of IRQs (still both edges on PIN_IN_0) ... OK Testing level-triggered on HIGH on PIN_IN_0 (when input is LOW when setting up IRQ) ... OK Testing level-triggered on HIGH on PIN_IN_0 (when input is HIGH when setting up IRQ) ... OK Testing level-triggered on LOW on PIN_IN_0 (when input is HIGH when setting up IRQ) ... OK Testing level-triggered on LOW on PIN_IN_0 (when input is LOW when setting up IRQ) ... OK TEST SUCCEEDED ``` </details> ### Issues/PRs references Depends on https://github.com/RIOT-OS/RIOT/pull/19407 Co-authored-by: Marian Buschsieweke <marian.buschsieweke@ovgu.de>
This commit is contained in:
commit
9405bf7a26
@ -12,12 +12,9 @@ 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_irq
|
||||
FEATURES_PROVIDED += periph_gpio_ll_irq_level_triggered_high
|
||||
FEATURES_PROVIDED += periph_gpio_ll_irq_level_triggered_low
|
||||
endif
|
||||
FEATURES_PROVIDED += periph_gpio_ll_irq
|
||||
FEATURES_PROVIDED += periph_gpio_ll_irq_level_triggered_high
|
||||
FEATURES_PROVIDED += periph_gpio_ll_irq_level_triggered_low
|
||||
|
||||
ifneq (,$(filter $(CPU_FAM),f0 f1 f3 g0 g4 l0 l1 l4 l5 u5 wb wl))
|
||||
FEATURES_PROVIDED += periph_flashpage
|
||||
|
@ -17,6 +17,9 @@ config CPU_FAM_F1
|
||||
select HAS_PERIPH_FLASHPAGE_PAGEWISE
|
||||
select HAS_PERIPH_FLASHPAGE_RAW
|
||||
select HAS_PERIPH_GPIO_LL
|
||||
select HAS_PERIPH_GPIO_LL_IRQ
|
||||
select HAS_PERIPH_GPIO_LL_IRQ_LEVEL_TRIGGERED_HIGH
|
||||
select HAS_PERIPH_GPIO_LL_IRQ_LEVEL_TRIGGERED_LOW
|
||||
select HAS_PERIPH_RTT_SET_COUNTER
|
||||
select HAS_PERIPH_WDT
|
||||
select HAVE_SHARED_PERIPH_RTT_PERIPH_RTC
|
||||
|
@ -41,43 +41,87 @@
|
||||
#define EXTI_MASK (0xFFFF)
|
||||
|
||||
#if defined(EXTI_SWIER_SWI0) || defined(EXTI_SWIER_SWIER0)
|
||||
#define EXTI_REG_SWIER (EXTI->SWIER)
|
||||
# define EXTI_REG_SWIER (EXTI->SWIER)
|
||||
#endif
|
||||
|
||||
#if defined(EXTI_SWIER1_SWI0) || defined(EXTI_SWIER1_SWIER0)
|
||||
#define EXTI_REG_SWIER (EXTI->SWIER1)
|
||||
# define EXTI_REG_SWIER (EXTI->SWIER1)
|
||||
#endif
|
||||
|
||||
#if defined(EXTI_RTSR_RT0) || defined(EXTI_RTSR_TR0)
|
||||
#define EXTI_REG_RTSR (EXTI->RTSR)
|
||||
# define EXTI_REG_RTSR (EXTI->RTSR)
|
||||
#endif
|
||||
|
||||
#if defined(EXTI_RTSR1_RT0) || defined(EXTI_RTSR1_TR0)
|
||||
#define EXTI_REG_RTSR (EXTI->RTSR1)
|
||||
# define EXTI_REG_RTSR (EXTI->RTSR1)
|
||||
#endif
|
||||
|
||||
#if defined(EXTI_FTSR_FT0) || defined(EXTI_FTSR_TR0)
|
||||
#define EXTI_REG_FTSR (EXTI->FTSR)
|
||||
# define EXTI_REG_FTSR (EXTI->FTSR)
|
||||
#endif
|
||||
|
||||
#if defined(EXTI_FTSR1_FT0) || defined (EXTI_FTSR1_TR0)
|
||||
#define EXTI_REG_FTSR (EXTI->FTSR1)
|
||||
# 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)
|
||||
#if defined(EXTI_PR_PR0)
|
||||
# define EXTI_REG_PR (EXTI->PR)
|
||||
#elif defined(EXTI_PR1_PIF0)
|
||||
# define EXTI_REG_PR (EXTI->PR1)
|
||||
#else
|
||||
# define EXTI_REG_FPR (EXTI->FPR1)
|
||||
# define EXTI_REG_RPR (EXTI->RPR1)
|
||||
#endif
|
||||
|
||||
#if defined(EXTI_C2_BASE)
|
||||
# define EXTI_REG_IMR (EXTI_C2->IMR1)
|
||||
# define EXTI_REG_IMR (EXTI_C2->IMR1)
|
||||
#elif defined(EXTI_IMR_IM0)
|
||||
# define EXTI_REG_IMR (EXTI->IMR)
|
||||
# define EXTI_REG_IMR (EXTI->IMR)
|
||||
#elif defined(EXTI_IMR1_IM0)
|
||||
# define EXTI_REG_IMR (EXTI->IMR1)
|
||||
# define EXTI_REG_IMR (EXTI->IMR1)
|
||||
#endif
|
||||
|
||||
#ifdef RCC_APB2ENR_SYSCFGCOMPEN
|
||||
# define SYSFG_CLOCK APB2
|
||||
# define SYSFG_ENABLE_MASK RCC_APB2ENR_SYSCFGCOMPEN
|
||||
#endif
|
||||
|
||||
#ifdef RCC_APBENR2_SYSCFGEN
|
||||
# define SYSFG_ENABLE_MASK RCC_APBENR2_SYSCFGEN
|
||||
# ifdef APB12
|
||||
# define SYSFG_CLOCK APB12
|
||||
# else
|
||||
# define SYSFG_CLOCK APB2
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifdef RCC_APB3ENR_SYSCFGEN
|
||||
# define SYSFG_CLOCK APB3
|
||||
# define SYSFG_ENABLE_MASK RCC_APB3ENR_SYSCFGEN
|
||||
#endif
|
||||
|
||||
#ifdef EXTI_EXTICR1_EXTI0
|
||||
# define EXTICR_REG(num) (EXTI->EXTICR[(num) >> 2])
|
||||
#endif
|
||||
|
||||
#ifdef SYSCFG_EXTICR1_EXTI0
|
||||
# define EXTICR_REG(num) (SYSCFG->EXTICR[(num) >> 2])
|
||||
#endif
|
||||
|
||||
#ifdef AFIO_EXTICR1_EXTI0
|
||||
# define EXTICR_REG(num) (AFIO->EXTICR[(num) >> 2])
|
||||
#endif
|
||||
|
||||
#ifdef SYSCFG_EXTICR1_EXTI1_Pos
|
||||
# define EXTICR_FIELD_SIZE SYSCFG_EXTICR1_EXTI1_Pos
|
||||
#endif
|
||||
|
||||
#ifdef EXTI_EXTICR1_EXTI1_Pos
|
||||
# define EXTICR_FIELD_SIZE EXTI_EXTICR1_EXTI1_Pos
|
||||
#endif
|
||||
|
||||
#ifdef AFIO_EXTICR1_EXTI1_Pos
|
||||
# define EXTICR_FIELD_SIZE AFIO_EXTICR1_EXTI1_Pos
|
||||
#endif
|
||||
|
||||
void gpio_ll_irq_mask(gpio_port_t port, uint8_t pin)
|
||||
@ -102,6 +146,8 @@ static uint16_t level_triggered;
|
||||
|
||||
static IRQn_Type get_irqn(uint8_t pin)
|
||||
{
|
||||
/* TODO: Come up with a way that this doesn't need updates whenever a new
|
||||
* MCU family gets added */
|
||||
#if defined(CPU_FAM_STM32L5) || defined(CPU_FAM_STM32U5)
|
||||
return EXTI0_IRQn + pin;
|
||||
#elif defined(CPU_FAM_STM32F0) || defined(CPU_FAM_STM32L0) || \
|
||||
@ -155,48 +201,29 @@ static IRQn_Type get_irqn(uint8_t pin)
|
||||
|
||||
static void clear_pending_irqs(uint8_t pin)
|
||||
{
|
||||
#if defined(CPU_FAM_STM32G0) || defined(CPU_FAM_STM32L5) || \
|
||||
defined(CPU_FAM_STM32U5) || defined(CPU_FAM_STM32MP1)
|
||||
/* clear any pending requests */
|
||||
EXTI->RPR1 = (1 << pin);
|
||||
EXTI->FPR1 = (1 << pin);
|
||||
#ifdef EXTI_REG_PR
|
||||
/* same IRQ flag no matter if falling or rising edge detected */
|
||||
EXTI_REG_PR = (1U << pin);
|
||||
#else
|
||||
/* clear any pending requests */
|
||||
EXTI_REG_PR = (1 << pin);
|
||||
/* distinct IRQ flags for falling and rising edge, clearing both */
|
||||
EXTI_REG_FPR = (1U << pin);
|
||||
EXTI_REG_RPR = (1U << pin);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void set_exti_port(uint8_t exti_num, uint8_t port_num)
|
||||
{
|
||||
#if defined(CPU_FAM_STM32G0) || defined(CPU_FAM_STM32L5) || \
|
||||
defined(CPU_FAM_STM32U5)
|
||||
/* enable specific pin as exti sources */
|
||||
EXTI->EXTICR[exti_num >> 2] &= ~(0xf << ((exti_num & 0x03) * 8));
|
||||
EXTI->EXTICR[exti_num >> 2] |= (port_num << ((exti_num & 0x03) * 8));
|
||||
#elif defined(CPU_FAM_STM32MP1)
|
||||
/* enable specific pin as exti sources */
|
||||
EXTI->EXTICR[exti_num >> 2] &= ~(0xf << ((exti_num & 0x03) * 4));
|
||||
EXTI->EXTICR[exti_num >> 2] |= (port_num << ((exti_num & 0x03) * 4));
|
||||
#else
|
||||
/* enable specific pin as exti sources */
|
||||
SYSCFG->EXTICR[exti_num >> 2] &= ~(0xf << ((exti_num & 0x03) * 4));
|
||||
SYSCFG->EXTICR[exti_num >> 2] |= (port_num << ((exti_num & 0x03) * 4));
|
||||
#endif
|
||||
uint32_t tmp = EXTICR_REG(exti_num);
|
||||
tmp &= ~(0xf << ((exti_num & 0x03) * EXTICR_FIELD_SIZE));
|
||||
tmp |= (port_num << ((exti_num & 0x03) * EXTICR_FIELD_SIZE));
|
||||
EXTICR_REG(exti_num) = tmp;
|
||||
}
|
||||
|
||||
static uint8_t get_exti_port(uint8_t exti_num)
|
||||
{
|
||||
#if defined(CPU_FAM_STM32G0) || defined(CPU_FAM_STM32L5) || \
|
||||
defined(CPU_FAM_STM32U5)
|
||||
/* enable specific pin as exti sources */
|
||||
return 0xf & (EXTI->EXTICR[exti_num >> 2] >> ((exti_num & 0x03) * 8));
|
||||
#elif defined(CPU_FAM_STM32MP1)
|
||||
/* enable specific pin as exti sources */
|
||||
return 0xf & (EXTI->EXTICR[exti_num >> 2] >> ((exti_num & 0x03) * 4));
|
||||
#else
|
||||
/* enable specific pin as exti sources */
|
||||
return 0xf & (SYSCFG->EXTICR[exti_num >> 2] >> ((exti_num & 0x03) * 4));
|
||||
#endif
|
||||
uint32_t reg = EXTICR_REG(exti_num);
|
||||
reg >>= (exti_num & 0x03) * EXTICR_FIELD_SIZE;
|
||||
return reg & 0xf;
|
||||
}
|
||||
|
||||
int gpio_ll_irq(gpio_port_t port, uint8_t pin, gpio_irq_trig_t trig, gpio_ll_cb_t cb, void *arg)
|
||||
@ -209,17 +236,8 @@ int gpio_ll_irq(gpio_port_t port, uint8_t pin, gpio_irq_trig_t trig, gpio_ll_cb_
|
||||
isr_ctx[pin].arg = arg;
|
||||
|
||||
/* enable clock of the SYSCFG module for EXTI configuration */
|
||||
#if !defined(CPU_FAM_STM32WB) && !defined(CPU_FAM_STM32MP1) && \
|
||||
!defined(CPU_FAM_STM32WL)
|
||||
#ifdef CPU_FAM_STM32F0
|
||||
periph_clk_en(APB2, RCC_APB2ENR_SYSCFGCOMPEN);
|
||||
#elif defined(CPU_FAM_STM32G0)
|
||||
periph_clk_en(APB12, RCC_APBENR2_SYSCFGEN);
|
||||
#elif defined(CPU_FAM_STM32U5)
|
||||
periph_clk_en(APB3, RCC_APB3ENR_SYSCFGEN);
|
||||
#else
|
||||
periph_clk_en(APB2, RCC_APB2ENR_SYSCFGEN);
|
||||
#endif
|
||||
#ifdef SYSFG_CLOCK
|
||||
periph_clk_en(SYSFG_CLOCK, SYSFG_ENABLE_MASK);
|
||||
#endif
|
||||
|
||||
/* enable global pin interrupt */
|
||||
@ -262,30 +280,35 @@ int gpio_ll_irq(gpio_port_t port, uint8_t pin, gpio_irq_trig_t trig, gpio_ll_cb_
|
||||
return 0;
|
||||
}
|
||||
|
||||
void isr_exti(void)
|
||||
static uint32_t get_and_clear_pending_irqs(void)
|
||||
{
|
||||
#if defined(CPU_FAM_STM32G0) || defined(CPU_FAM_STM32L5) || \
|
||||
defined(CPU_FAM_STM32U5) || defined(CPU_FAM_STM32MP1)
|
||||
/* get all interrupts handled by this ISR */
|
||||
uint32_t pending_rising_isr = (EXTI->RPR1 & EXTI_MASK);
|
||||
uint32_t pending_falling_isr = (EXTI->FPR1 & EXTI_MASK);
|
||||
#ifdef EXTI_REG_PR
|
||||
/* only one pending IRQ flag register for both falling and rising flanks */
|
||||
uint32_t pending_isr = (EXTI_REG_PR & EXTI_MASK);
|
||||
|
||||
/* clear by writing a 1 */
|
||||
EXTI_REG_PR = pending_isr;
|
||||
return pending_isr;
|
||||
#else
|
||||
/* distinct registers for pending IRQ flags depending on rising or falling
|
||||
* flank */
|
||||
uint32_t pending_rising_isr = (EXTI_REG_RPR & EXTI_MASK);
|
||||
uint32_t pending_falling_isr = (EXTI_REG_FPR & EXTI_MASK);
|
||||
|
||||
/* clear by writing a 1 */
|
||||
EXTI->RPR1 = pending_rising_isr;
|
||||
EXTI->FPR1 = pending_falling_isr;
|
||||
|
||||
/* only generate interrupts against lines which have their IMR set */
|
||||
uint32_t pending_isr = (pending_rising_isr | pending_falling_isr) & EXTI_REG_IMR;
|
||||
#else
|
||||
/* read all pending interrupts wired to isr_exti */
|
||||
uint32_t pending_isr = (EXTI_REG_PR & EXTI_MASK);
|
||||
return pending_rising_isr | pending_falling_isr;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* clear by writing a 1 */
|
||||
EXTI_REG_PR = pending_isr;
|
||||
void isr_exti(void)
|
||||
{
|
||||
uint32_t pending_isr = get_and_clear_pending_irqs();
|
||||
|
||||
/* only generate soft interrupts against lines which have their IMR set */
|
||||
pending_isr &= EXTI_REG_IMR;
|
||||
#endif
|
||||
|
||||
/* iterate over all set bits */
|
||||
uint8_t pin = 0;
|
||||
|
Loading…
Reference in New Issue
Block a user