stm32wl5: add EXTI support for GPIO

This patch implements working support for EXTI GPIO.

Signed-off-by: Michał Łyszczek <michal.lyszczek@bofc.pl>

--
v1 -> v2:
Suggested by: Petro Karashchenko
- change (1 << n) to (1 << (n)) in macro definition
- change 1 << X to (1 << X) in code
- fix alignment

v2 -> v3:
Suggested by: Petro Karashchenko
- I was supposed to change (1 << pin) to 1 << pin, not the other way around:)
This commit is contained in:
Michał Łyszczek 2022-06-12 14:06:22 +02:00 committed by Xiang Xiao
parent cb8c992914
commit 288b57d5ca
4 changed files with 370 additions and 51 deletions

View File

@ -30,14 +30,14 @@ Peripheral Support Notes
========== ======= =====
IRQs Yes
GPIO Yes
EXTI No
EXTI Yes
HSE Yes
PLL Yes Tested @ 48MHz
HSI Yes Not tested
MSI Yes Not tested
LSE Yes Not tested
RCC Yes All registers defined, not all peripherals enabled
SYSCFG Yes All registers defined, remapping not tested
SYSCFG Yes All registers defined, GPIO EXTI works, remapping not tested
USART Yes
LPUART Yes full speed with HSE works, low power mode with LSE not implemented
DMA No
@ -102,7 +102,7 @@ SYSCFG
------
System configuration controller. Can be used to remap memory or
manage external interrupts.
manage GPIO multiplexer for EXTI.
GPIO
----
@ -123,6 +123,16 @@ IPCC
Inter-processor communication controller. IPCC is used to exchange data
between Cortex-M4 and Cortex-M0 CPUs.
EXTI
----
Extended interrupts and event controller. Extends interrupts not provided
by NVIC. For example, there is only one interrupt for GPIO5..9 in NVIC,
but thanks to EXTI we can differentiate which GPIO caused interrupt. Such
interrupt first goes through EXTI and is then forwarded to main NVIC.
EXTI for gpio can be enabled via `stm32wl5_gpiosetevent` function.
Supported Boards
================

View File

@ -32,43 +32,48 @@
* Pre-processor Definitions
****************************************************************************/
#define STM32WL5_NEXTI1 31
#define STM32WL5_EXTI1_MASK 0xffffffff
#define STM32WL5_NEXTI2 9
#define STM32WL5_EXTI2_MASK 0x000001ff
/* Register Offsets *********************************************************/
#define STM32WL5_EXTI_RTSR1_OFFSET 0x0000 /* Rising Trigger Selection 1 */
#define STM32WL5_EXTI_FTSR1_OFFSET 0x0004 /* Falling Trigger Selection 1 */
#define STM32WL5_EXTI_SWIER1_OFFSET 0x0008 /* Software Interrupt Event 1 */
#define STM32WL5_EXTI_PR1_OFFSET 0x000c /* Pending 1 */
#define STM32WL5_EXTI_RTSR2_OFFSET 0x0020 /* Rising Trigger Selection 2 */
#define STM32WL5_EXTI_FTSR2_OFFSET 0x0024 /* Falling Trigger Selection 2 */
#define STM32WL5_EXTI_SWIER2_OFFSET 0x0028 /* Software Interrupt Event 2 */
#define STM32WL5_EXTI_PR2_OFFSET 0x002c /* Pending 2 */
#define STM32WL5_EXTI_C1IMR1_OFFSET 0x0080 /* CPU Wakeup with Interrupt Mask 1 for cpu1 */
#define STM32WL5_EXTI_C1EMR1_OFFSET 0x0084 /* CPU Wakeup with Event Mask 1 for cpu1 */
#define STM32WL5_EXTI_C1IMR2_OFFSET 0x0090 /* CPU Wakeup with Interrupt Mask 2 for cpu1 */
#define STM32WL5_EXTI_C1EMR2_OFFSET 0x0094 /* CPU Wakeup with Event Mask 2 for cpu1 */
#define STM32WL5_EXTI_C2IMR1_OFFSET 0x00c0 /* CPU Wakeup with Interrupt Mask 1 for cpu2 */
#define STM32WL5_EXTI_C2EMR1_OFFSET 0x00c4 /* CPU Wakeup with Event Mask 1 for cpu2 */
#define STM32WL5_EXTI_C2IMR2_OFFSET 0x00d0 /* CPU Wakeup with Interrupt Mask 2 for cpu2 */
#define STM32WL5_EXTI_C2EMR2_OFFSET 0x00d4 /* CPU Wakeup with Event Mask 2 for cpu2 */
#define STM32WL5_EXTI_RTSR1_OFFSET 0x0000 /* Rising trigger selection 1 */
#define STM32WL5_EXTI_FTSR1_OFFSET 0x0004 /* Falling trigger selection 1 */
#define STM32WL5_EXTI_SWIER1_OFFSET 0x0008 /* Software interrupt event 1 */
#define STM32WL5_EXTI_PR1_OFFSET 0x000c /* Pending 1 */
#define STM32WL5_EXTI_RTSR2_OFFSET 0x0020 /* Rising trigger selection 2 */
#define STM32WL5_EXTI_FTSR2_OFFSET 0x0024 /* Falling trigger selection 2 */
#define STM32WL5_EXTI_SWIER2_OFFSET 0x0028 /* Software interrupt event 2 */
#define STM32WL5_EXTI_PR2_OFFSET 0x002c /* Pending 2 */
#define STM32WL5_EXTI_C1IMR1_OFFSET 0x0080 /* Interrupt mask 1 for cpu1 */
#define STM32WL5_EXTI_C1EMR1_OFFSET 0x0084 /* Event mask 1 for cpu1 */
#define STM32WL5_EXTI_C1IMR2_OFFSET 0x0090 /* Interrupt mask 2 for cpu1 */
#define STM32WL5_EXTI_C1EMR2_OFFSET 0x0094 /* Event mask 2 for cpu1 */
#define STM32WL5_EXTI_C2IMR1_OFFSET 0x00c0 /* Interrupt mask 1 for cpu2 */
#define STM32WL5_EXTI_C2EMR1_OFFSET 0x00c4 /* Event mask 1 for cpu2 */
#define STM32WL5_EXTI_C2IMR2_OFFSET 0x00d0 /* Interrupt mask 2 for cpu2 */
#define STM32WL5_EXTI_C2EMR2_OFFSET 0x00d4 /* Event mask 2 for cpu2 */
/* Register Addresses *******************************************************/
#define STM32WL5_EXTI_RTSR1 (STM32WL5_EXTI_BASE+STM32WL5_EXTI_RTSR1_OFFSET)
#define STM32WL5_EXTI_FTSR1 (STM32WL5_EXTI_BASE+STM32WL5_EXTI_FTSR1_OFFSET)
#define STM32WL5_EXTI_SWIER1 (STM32WL5_EXTI_BASE+STM32WL5_EXTI_SWIER1_OFFSET)
#define STM32WL5_EXTI_PR1 (STM32WL5_EXTI_BASE+STM32WL5_EXTI_PR1_OFFSET)
#define STM32WL5_EXTI_RTSR2 (STM32WL5_EXTI_BASE+STM32WL5_EXTI_RTSR2_OFFSET)
#define STM32WL5_EXTI_FTSR2 (STM32WL5_EXTI_BASE+STM32WL5_EXTI_FTSR2_OFFSET)
#define STM32WL5_EXTI_SWIER2 (STM32WL5_EXTI_BASE+STM32WL5_EXTI_SWIER2_OFFSET)
#define STM32WL5_EXTI_PR2 (STM32WL5_EXTI_BASE+STM32WL5_EXTI_PR2_OFFSET)
#define STM32WL5_EXTI_C1IMR1 (STM32WL5_EXTI_BASE+STM32WL5_EXTI_C1IMR1_OFFSET)
#define STM32WL5_EXTI_C1EMR1 (STM32WL5_EXTI_BASE+STM32WL5_EXTI_C1EMR1_OFFSET)
#define STM32WL5_EXTI_C1IMR2 (STM32WL5_EXTI_BASE+STM32WL5_EXTI_C1IMR2_OFFSET)
#define STM32WL5_EXTI_C1EMR2 (STM32WL5_EXTI_BASE+STM32WL5_EXTI_C1EMR2_OFFSET)
#define STM32WL5_EXTI_C2IMR1 (STM32WL5_EXTI_BASE+STM32WL5_EXTI_C2IMR1_OFFSET)
#define STM32WL5_EXTI_C2EMR1 (STM32WL5_EXTI_BASE+STM32WL5_EXTI_C2EMR1_OFFSET)
#define STM32WL5_EXTI_C2IMR2 (STM32WL5_EXTI_BASE+STM32WL5_EXTI_C2IMR2_OFFSET)
#define STM32WL5_EXTI_C2EMR2 (STM32WL5_EXTI_BASE+STM32WL5_EXTI_C2EMR2_OFFSET)
#define STM32WL5_EXTI_RTSR1 (STM32WL5_EXTI_BASE+STM32WL5_EXTI_RTSR1_OFFSET)
#define STM32WL5_EXTI_FTSR1 (STM32WL5_EXTI_BASE+STM32WL5_EXTI_FTSR1_OFFSET)
#define STM32WL5_EXTI_SWIER1 (STM32WL5_EXTI_BASE+STM32WL5_EXTI_SWIER1_OFFSET)
#define STM32WL5_EXTI_PR1 (STM32WL5_EXTI_BASE+STM32WL5_EXTI_PR1_OFFSET)
#define STM32WL5_EXTI_RTSR2 (STM32WL5_EXTI_BASE+STM32WL5_EXTI_RTSR2_OFFSET)
#define STM32WL5_EXTI_FTSR2 (STM32WL5_EXTI_BASE+STM32WL5_EXTI_FTSR2_OFFSET)
#define STM32WL5_EXTI_SWIER2 (STM32WL5_EXTI_BASE+STM32WL5_EXTI_SWIER2_OFFSET)
#define STM32WL5_EXTI_PR2 (STM32WL5_EXTI_BASE+STM32WL5_EXTI_PR2_OFFSET)
#define STM32WL5_EXTI_C1IMR1 (STM32WL5_EXTI_BASE+STM32WL5_EXTI_C1IMR1_OFFSET)
#define STM32WL5_EXTI_C1EMR1 (STM32WL5_EXTI_BASE+STM32WL5_EXTI_C1EMR1_OFFSET)
#define STM32WL5_EXTI_C1IMR2 (STM32WL5_EXTI_BASE+STM32WL5_EXTI_C1IMR2_OFFSET)
#define STM32WL5_EXTI_C1EMR2 (STM32WL5_EXTI_BASE+STM32WL5_EXTI_C1EMR2_OFFSET)
#define STM32WL5_EXTI_C2IMR1 (STM32WL5_EXTI_BASE+STM32WL5_EXTI_C2IMR1_OFFSET)
#define STM32WL5_EXTI_C2EMR1 (STM32WL5_EXTI_BASE+STM32WL5_EXTI_C2EMR1_OFFSET)
#define STM32WL5_EXTI_C2IMR2 (STM32WL5_EXTI_BASE+STM32WL5_EXTI_C2IMR2_OFFSET)
#define STM32WL5_EXTI_C2EMR2 (STM32WL5_EXTI_BASE+STM32WL5_EXTI_C2EMR2_OFFSET)
/* Register Bitfield Definitions ********************************************/
@ -103,4 +108,84 @@
#define EXTI2_RADIOBSY (1 << 14) /* EXTI line 45: Radio busy wakeup */
#define EXTI2_CDBGPWRUPREQ (1 << 15) /* EXTI line 46: Debug power-up request wakup */
/* Rising Trigger selection register */
#define EXTI_RTSR1_BIT(n) (1 << (n)) /* 1=Rising trigger enabled (for Event and Interrupt) for input line */
#define EXTI_RTSR1_SHIFT (0) /* Bits 0-X: Rising trigger event configuration bit for all lines */
#define EXTI_RTSR1_MASK (0x0061ffff)
#define EXTI_RTSR2_BIT(n) (1 << (n)) /* 1=Rising trigger enabled (for Event and Interrupt) for input line */
#define EXTI_RTSR2_SHIFT (0) /* Bits 0-X: Rising trigger event configuration bit for all lines */
#define EXTI_RTSR2_MASK (0x00002304)
/* Falling Trigger selection register */
#define EXTI_FTSR1_BIT(n) (1 << (n)) /* 1=Falling trigger enabled (for Event and Interrupt) for input line */
#define EXTI_FTSR1_SHIFT (0) /* Bits 0-X: Falling trigger event configuration bitfor all lines */
#define EXTI_FTSR1_MASK (0x0061ffff)
#define EXTI_FTSR2_BIT(n) (1 << (n)) /* 1=Falling trigger enabled (for Event and Interrupt) for input line */
#define EXTI_FTSR2_SHIFT (0) /* Bits 0-X: Falling trigger event configuration bitfor all lines */
#define EXTI_FTSR2_MASK (0x00002304)
/* Software interrupt event register */
#define EXTI_SWIER1_BIT(n) (1 << (n)) /* 1=Sets the corresponding pending bit in EXTI_PR */
#define EXTI_SWIER1_SHIFT (0) /* Bits 0-X: Software Interrupt for all lines */
#define EXTI_SWIER1_MASK (0x0061ffff)
#define EXTI_SWIER2_BIT(n) (1 << (n)) /* 1=Sets the corresponding pending bit in EXTI_PR */
#define EXTI_SWIER2_SHIFT (0) /* Bits 0-X: Software Interrupt for all lines */
#define EXTI_SWIER2_MASK (0x00002304)
/* Pending register */
#define EXTI_PR1_BIT(n) (1 << (n)) /* 1=Selected trigger request occurred */
#define EXTI_PR1_SHIFT (0) /* Bits 0-X: Pending bit for all lines */
#define EXTI_PR1_MASK (0x0061ffff)
#define EXTI_PR2_BIT(n) (1 << (n)) /* 1=Selected trigger request occurred */
#define EXTI_PR2_SHIFT (0) /* Bits 0-X: Pending bit for all lines */
#define EXTI_PR2_MASK (0x00002304)
/* Interrupt mask register */
#define EXTI_C1IMR1_BIT(n) (1 << (n)) /* 1=Interrupt request from line x is not masked */
#define EXTI_C1IMR1_SHIFT (0) /* Bits 0-X: Interrupt Mask for all lines */
#define EXTI_C1IMR1_MASK (0xffffffff)
#define EXTI_C1IMR2_BIT(n) (1 << (n)) /* 1=Interrupt request from line x is not masked */
#define EXTI_C1IMR2_SHIFT (0) /* Bits 0-X: Interrupt Mask for all lines */
#define EXTI_C1IMR2_MASK (0x007effff)
/* Event mask register */
#define EXTI_C1EMR1_BIT(n) (1 << (n)) /* 1=Event request from line x is not mask */
#define EXTI_C1EMR1_SHIFT (0) /* Bits Bits 0-X: Event Mask for all lines */
#define EXTI_C1EMR1_MASK (0x00007ff4)
#define EXTI_C1EMR2_BIT(n) (1 << (n)) /* 1=Event request from line x is not mask */
#define EXTI_C1EMR2_SHIFT (0) /* Bits Bits 0-X: Event Mask for all lines */
#define EXTI_C1EMR2_MASK (0x00000300)
/* Interrupt mask register */
#define EXTI_C2IMR1_BIT(n) (1 << (n)) /* 1=Interrupt request from line x is not masked */
#define EXTI_C2IMR1_SHIFT (0) /* Bits 0-X: Interrupt Mask for all lines */
#define EXTI_C2IMR1_MASK (0xffffffff)
#define EXTI_C2IMR2_BIT(n) (1 << (n)) /* 1=Interrupt request from line x is not masked */
#define EXTI_C2IMR2_SHIFT (0) /* Bits 0-X: Interrupt Mask for all lines */
#define EXTI_C2IMR2_MASK (0x007effff)
/* Event mask register */
#define EXTI_C2EMR1_BIT(n) (1 << (n)) /* 1=Event request from line x is not mask */
#define EXTI_C2EMR1_SHIFT (0) /* Bits Bits 0-X: Event Mask for all lines */
#define EXTI_C2EMR1_MASK (0x00007ff4)
#define EXTI_C2EMR2_BIT(n) (1 << (n)) /* 1=Event request from line x is not mask */
#define EXTI_C2EMR2_SHIFT (0) /* Bits Bits 0-X: Event Mask for all lines */
#define EXTI_C2EMR2_MASK (0x00000300)
#endif /* __ARCH_ARM_SRC_STM32WL5_HARDWARE_STM32WL5_EXTI_H */

View File

@ -28,7 +28,6 @@
#include <stdint.h>
#include <stdbool.h>
#include <assert.h>
#include <errno.h>
#include <debug.h>
@ -62,28 +61,23 @@ static struct gpio_callback_s g_gpio_handlers[16];
****************************************************************************/
/****************************************************************************
* Interrupt Service Routine - Dispatcher
* Interrupt Service Routines - Dispatchers
****************************************************************************/
static int stm32wl5_exti0_15_isr(int irq, void *context, FAR void *arg)
static int stm32wl5_exti0_isr(int irq, void *context, void *arg)
{
int ret = OK;
int exti;
(void)arg;
exti = irq - STM32WL5_IRQ_EXTI0;
DEBUGASSERT((exti >= 0) && (exti <= 15));
/* Clear the pending interrupt */
/* Clear the pending interrupt for both rising and falling edges. */
putreg32(0x0001 << exti, STM32WL5_EXTI_PR1);
putreg32(0x0001, STM32WL5_EXTI_PR1);
/* And dispatch the interrupt to the handler */
if (g_gpio_handlers[exti].callback != NULL)
if (g_gpio_handlers[0].callback != NULL)
{
xcpt_t callback = g_gpio_handlers[exti].callback;
void *cbarg = g_gpio_handlers[exti].arg;
xcpt_t callback = g_gpio_handlers[0].callback;
void *cbarg = g_gpio_handlers[0].arg;
ret = callback(irq, context, cbarg);
}
@ -91,6 +85,144 @@ static int stm32wl5_exti0_15_isr(int irq, void *context, FAR void *arg)
return ret;
}
static int stm32wl5_exti1_isr(int irq, void *context, void *arg)
{
int ret = OK;
/* Clear the pending interrupt */
putreg32(0x0002, STM32WL5_EXTI_PR1);
/* And dispatch the interrupt to the handler */
if (g_gpio_handlers[1].callback != NULL)
{
xcpt_t callback = g_gpio_handlers[1].callback;
void *cbarg = g_gpio_handlers[1].arg;
ret = callback(irq, context, cbarg);
}
return ret;
}
static int stm32wl5_exti2_isr(int irq, void *context, void *arg)
{
int ret = OK;
/* Clear the pending interrupt */
putreg32(0x0004, STM32WL5_EXTI_PR1);
/* And dispatch the interrupt to the handler */
if (g_gpio_handlers[2].callback != NULL)
{
xcpt_t callback = g_gpio_handlers[2].callback;
void *cbarg = g_gpio_handlers[2].arg;
ret = callback(irq, context, cbarg);
}
return ret;
}
static int stm32wl5_exti3_isr(int irq, void *context, void *arg)
{
int ret = OK;
/* Clear the pending interrupt */
putreg32(0x0008, STM32WL5_EXTI_PR1);
/* And dispatch the interrupt to the handler */
if (g_gpio_handlers[3].callback != NULL)
{
xcpt_t callback = g_gpio_handlers[3].callback;
void *cbarg = g_gpio_handlers[3].arg;
ret = callback(irq, context, cbarg);
}
return ret;
}
static int stm32wl5_exti4_isr(int irq, void *context, void *arg)
{
int ret = OK;
/* Clear the pending interrupt */
putreg32(0x0010, STM32WL5_EXTI_PR1);
/* And dispatch the interrupt to the handler */
if (g_gpio_handlers[4].callback != NULL)
{
xcpt_t callback = g_gpio_handlers[4].callback;
void *cbarg = g_gpio_handlers[4].arg;
ret = callback(irq, context, cbarg);
}
return ret;
}
static int stm32wl5_exti_multiisr(int irq, void *context, void *arg,
int first, int last)
{
uint32_t pr;
int pin;
int ret = OK;
/* Examine the state of each pin in the group */
pr = getreg32(STM32WL5_EXTI_PR1);
/* And dispatch the interrupt to the handler */
for (pin = first; pin <= last; pin++)
{
/* Is an interrupt pending on this pin? */
uint32_t mask = 1 << pin;
if ((pr & mask) != 0)
{
/* Clear the pending interrupt */
putreg32(mask, STM32WL5_EXTI_PR1);
/* And dispatch the interrupt to the handler */
if (g_gpio_handlers[pin].callback != NULL)
{
xcpt_t callback = g_gpio_handlers[pin].callback;
void *cbarg = g_gpio_handlers[pin].arg;
int tmp;
tmp = callback(irq, context, cbarg);
if (tmp < 0)
{
ret = tmp;
}
}
}
}
return ret;
}
static int stm32wl5_exti95_isr(int irq, void *context, void *arg)
{
return stm32wl5_exti_multiisr(irq, context, arg, 5, 9);
}
static int stm32wl5_exti1510_isr(int irq, void *context, void *arg)
{
return stm32wl5_exti_multiisr(irq, context, arg, 10, 15);
}
/****************************************************************************
* Public Functions
****************************************************************************/
@ -121,9 +253,60 @@ static int stm32wl5_exti0_15_isr(int irq, void *context, FAR void *arg)
int stm32wl5_gpiosetevent(uint32_t pinset, bool risingedge, bool fallingedge,
bool event, xcpt_t func, void *arg)
{
struct gpio_callback_s *shared_cbs;
uint32_t pin = pinset & GPIO_PIN_MASK;
uint32_t exti = 1 << pin;
int irq = STM32WL5_IRQ_EXTI0 + pin;
int irq;
xcpt_t handler;
int nshared;
int i;
/* Select the interrupt handler for this EXTI pin */
if (pin < 5)
{
irq = pin + STM32WL5_IRQ_EXTI0;
nshared = 1;
shared_cbs = &g_gpio_handlers[pin];
switch (pin)
{
case 0:
handler = stm32wl5_exti0_isr;
break;
case 1:
handler = stm32wl5_exti1_isr;
break;
case 2:
handler = stm32wl5_exti2_isr;
break;
case 3:
handler = stm32wl5_exti3_isr;
break;
default:
handler = stm32wl5_exti4_isr;
break;
}
}
else if (pin < 10)
{
irq = STM32WL5_IRQ_EXTI95;
handler = stm32wl5_exti95_isr;
shared_cbs = &g_gpio_handlers[5];
nshared = 5;
}
else
{
irq = STM32WL5_IRQ_EXTI1510;
handler = stm32wl5_exti1510_isr;
shared_cbs = &g_gpio_handlers[10];
nshared = 6;
}
/* Get the previous GPIO IRQ handler; Save the new IRQ handler. */
g_gpio_handlers[pin].callback = func;
g_gpio_handlers[pin].arg = arg;
@ -132,12 +315,27 @@ int stm32wl5_gpiosetevent(uint32_t pinset, bool risingedge, bool fallingedge,
if (func)
{
irq_attach(irq, stm32wl5_exti0_15_isr, NULL);
irq_attach(irq, handler, NULL);
up_enable_irq(irq);
}
else
{
up_disable_irq(irq);
/* Only disable IRQ if shared handler does not have any active
* callbacks.
*/
for (i = 0; i < nshared; i++)
{
if (shared_cbs[i].callback != NULL)
{
break;
}
}
if (i == nshared)
{
up_disable_irq(irq);
}
}
/* Configure GPIO, enable EXTI line enabled if event or interrupt is
@ -169,5 +367,7 @@ int stm32wl5_gpiosetevent(uint32_t pinset, bool risingedge, bool fallingedge,
func ? 0 : exti,
func ? exti : 0);
/* Return the old IRQ handler */
return OK;
}

View File

@ -279,6 +279,30 @@ int stm32wl5_configgpio(uint32_t cfgset)
putreg32(regval, base + STM32WL5_GPIO_OTYPER_OFFSET);
/* Otherwise, it is an input pin. Should it configured as an
* EXTI interrupt?
*/
if (pinmode != GPIO_MODER_OUTPUT && (cfgset & GPIO_EXTI) != 0)
{
/* The selection of the EXTI line source is performed through the EXTIx
* bits in the SYSCFG_EXTICRx registers.
*/
uint32_t regaddr;
int shift;
/* Set the bits in the SYSCFG EXTICR register */
regaddr = STM32WL5_SYSCFG_EXTICR(pin);
regval = getreg32(regaddr);
shift = SYSCFG_EXTICR_EXTI_SHIFT(pin);
regval &= ~(SYSCFG_EXTICR_PORT_MASK << shift);
regval |= (((uint32_t)port) << shift);
putreg32(regval, regaddr);
}
leave_critical_section(flags);
return OK;
}