mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2025-01-17 09:52:43 +01:00
cpu/nrf5x_common: implement periph/gpio_ll{,_irq}
Co-authored-by: Gunar Schorcht <gunar@schorcht.net>
This commit is contained in:
parent
a427d630f1
commit
f0586dbf8f
@ -13,6 +13,9 @@ config CPU_COMMON_NRF5X
|
|||||||
select HAS_PERIPH_FLASHPAGE_PAGEWISE
|
select HAS_PERIPH_FLASHPAGE_PAGEWISE
|
||||||
select HAS_PERIPH_GPIO
|
select HAS_PERIPH_GPIO
|
||||||
select HAS_PERIPH_GPIO_IRQ
|
select HAS_PERIPH_GPIO_IRQ
|
||||||
|
select HAS_PERIPH_GPIO_LL
|
||||||
|
select HAS_PERIPH_GPIO_LL_IRQ
|
||||||
|
select HAS_PERIPH_GPIO_LL_IRQ_UNMASK
|
||||||
select HAS_PERIPH_HWRNG
|
select HAS_PERIPH_HWRNG
|
||||||
select HAS_PERIPH_TEMPERATURE
|
select HAS_PERIPH_TEMPERATURE
|
||||||
select HAS_PERIPH_TIMER_PERIODIC
|
select HAS_PERIPH_TIMER_PERIODIC
|
||||||
|
@ -4,6 +4,7 @@ FEATURES_PROVIDED += periph_flashpage
|
|||||||
FEATURES_PROVIDED += periph_flashpage_in_address_space
|
FEATURES_PROVIDED += periph_flashpage_in_address_space
|
||||||
FEATURES_PROVIDED += periph_flashpage_pagewise
|
FEATURES_PROVIDED += periph_flashpage_pagewise
|
||||||
FEATURES_PROVIDED += periph_gpio periph_gpio_irq
|
FEATURES_PROVIDED += periph_gpio periph_gpio_irq
|
||||||
|
FEATURES_PROVIDED += periph_gpio_ll periph_gpio_ll_irq periph_gpio_ll_irq_unmask
|
||||||
FEATURES_PROVIDED += periph_timer_periodic
|
FEATURES_PROVIDED += periph_timer_periodic
|
||||||
FEATURES_PROVIDED += periph_uart_modecfg
|
FEATURES_PROVIDED += periph_uart_modecfg
|
||||||
|
|
||||||
|
157
cpu/nrf5x_common/include/gpio_ll_arch.h
Normal file
157
cpu/nrf5x_common/include/gpio_ll_arch.h
Normal file
@ -0,0 +1,157 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2015 Jan Wagner <mail@jwagner.eu>
|
||||||
|
* 2015-2016 Freie Universität Berlin
|
||||||
|
* 2019 Inria
|
||||||
|
*
|
||||||
|
* This file is subject to the terms and conditions of the GNU Lesser
|
||||||
|
* General Public License v2.1. See the file LICENSE in the top level
|
||||||
|
* directory for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ingroup cpu_nrf5x_common
|
||||||
|
* @ingroup drivers_periph_gpio_ll
|
||||||
|
* @{
|
||||||
|
*
|
||||||
|
* @file
|
||||||
|
* @brief CPU specific part of the Peripheral GPIO Low-Level API
|
||||||
|
*
|
||||||
|
* @note This GPIO driver implementation supports only one pin to be
|
||||||
|
* defined as external interrupt.
|
||||||
|
*
|
||||||
|
* @author Christian Kühling <kuehling@zedat.fu-berlin.de>
|
||||||
|
* @author Timo Ziegler <timo.ziegler@fu-berlin.de>
|
||||||
|
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
|
||||||
|
* @author Jan Wagner <mail@jwagner.eu>
|
||||||
|
* @author Alexandre Abadie <alexandre.abadie@inria.fr>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef GPIO_LL_ARCH_H
|
||||||
|
#define GPIO_LL_ARCH_H
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
#include "cpu.h"
|
||||||
|
#include "irq.h"
|
||||||
|
#include "periph_cpu.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef DOXYGEN /* hide implementation specific details from Doxygen */
|
||||||
|
|
||||||
|
#define PORT_BIT (1 << 5)
|
||||||
|
#define PIN_MASK (0x1f)
|
||||||
|
#define NRF5X_IO_AREA_START (0x40000000UL)
|
||||||
|
|
||||||
|
/* Compatibility wrapper defines for nRF9160 */
|
||||||
|
#ifdef NRF_P0_S
|
||||||
|
#define NRF_P0 NRF_P0_S
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(CPU_FAM_NRF51)
|
||||||
|
#define GPIO_PORT(num) ((gpio_port_t)NRF_GPIO)
|
||||||
|
#define GPIO_PORT_NUM(port) 0
|
||||||
|
#elif defined(NRF_P1)
|
||||||
|
#define GPIO_PORT(num) ((num) ? (gpio_port_t)NRF_P1 : (gpio_port_t)NRF_P0)
|
||||||
|
#define GPIO_PORT_NUM(port) ((port == (gpio_port_t)NRF_P1) ? 1 : 0)
|
||||||
|
#else
|
||||||
|
#define GPIO_PORT(num) ((gpio_port_t)NRF_P0)
|
||||||
|
#define GPIO_PORT_NUM(port) 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static inline uword_t gpio_ll_read(gpio_port_t port)
|
||||||
|
{
|
||||||
|
NRF_GPIO_Type *p = (NRF_GPIO_Type *)port;
|
||||||
|
return p->IN;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uword_t gpio_ll_read_output(gpio_port_t port)
|
||||||
|
{
|
||||||
|
NRF_GPIO_Type *p = (NRF_GPIO_Type *)port;
|
||||||
|
return p->OUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void gpio_ll_set(gpio_port_t port, uword_t mask)
|
||||||
|
{
|
||||||
|
NRF_GPIO_Type *p = (NRF_GPIO_Type *)port;
|
||||||
|
p->OUTSET = mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void gpio_ll_clear(gpio_port_t port, uword_t mask)
|
||||||
|
{
|
||||||
|
NRF_GPIO_Type *p = (NRF_GPIO_Type *)port;
|
||||||
|
p->OUTCLR = mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void gpio_ll_toggle(gpio_port_t port, uword_t mask)
|
||||||
|
{
|
||||||
|
NRF_GPIO_Type *p = (NRF_GPIO_Type *)port;
|
||||||
|
unsigned state = irq_disable();
|
||||||
|
p->OUT ^= mask;
|
||||||
|
irq_restore(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void gpio_ll_write(gpio_port_t port, uword_t value)
|
||||||
|
{
|
||||||
|
NRF_GPIO_Type *p = (NRF_GPIO_Type *)port;
|
||||||
|
p->OUT = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline gpio_port_t gpio_get_port(gpio_t pin)
|
||||||
|
{
|
||||||
|
#if defined(NRF_P1)
|
||||||
|
return GPIO_PORT(pin >> 5);
|
||||||
|
#else
|
||||||
|
(void)pin;
|
||||||
|
return GPIO_PORT(0);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint8_t gpio_get_pin_num(gpio_t pin)
|
||||||
|
{
|
||||||
|
#if defined(NRF_P1)
|
||||||
|
return pin & PIN_MASK;
|
||||||
|
#else
|
||||||
|
return (uint8_t)pin;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline gpio_port_t gpio_port_pack_addr(void *addr)
|
||||||
|
{
|
||||||
|
return (gpio_port_t)addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void * gpio_port_unpack_addr(gpio_port_t port)
|
||||||
|
{
|
||||||
|
/* NRF5X_IO_AREA_START is the start of the memory mapped I/O area. Both data
|
||||||
|
* and flash are mapped before it. So if it is an I/O address, it
|
||||||
|
* cannot be a packed data address and (hopefully) is a GPIO port */
|
||||||
|
if (port >= NRF5X_IO_AREA_START) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (void *)port;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool is_gpio_port_num_valid(uint_fast8_t num)
|
||||||
|
{
|
||||||
|
switch (num) {
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
case 0:
|
||||||
|
#if defined(NRF_P1)
|
||||||
|
case 1:
|
||||||
|
#endif
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* DOXYGEN */
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* GPIO_LL_ARCH_H */
|
||||||
|
/** @} */
|
@ -80,6 +80,50 @@ extern "C" {
|
|||||||
*/
|
*/
|
||||||
#define GPIO_MODE(oe, ic, pr, dr) (oe | (ic << 1) | (pr << 2) | (dr << 8))
|
#define GPIO_MODE(oe, ic, pr, dr) (oe | (ic << 1) | (pr << 2) | (dr << 8))
|
||||||
|
|
||||||
|
#ifndef DOXYGEN /* BEGIN: GPIO LL overwrites */
|
||||||
|
#define HAVE_GPIO_SLEW_T
|
||||||
|
typedef enum {
|
||||||
|
GPIO_SLEW_SLOWEST = 0,
|
||||||
|
GPIO_SLEW_SLOW = 0,
|
||||||
|
GPIO_SLEW_FAST = 0,
|
||||||
|
GPIO_SLEW_FASTEST = 0,
|
||||||
|
} gpio_slew_t;
|
||||||
|
|
||||||
|
#define HAVE_GPIO_PULL_STRENGTH_T
|
||||||
|
typedef enum {
|
||||||
|
GPIO_PULL_WEAKEST = 0,
|
||||||
|
GPIO_PULL_WEAK = 0,
|
||||||
|
GPIO_PULL_STRONG = 0,
|
||||||
|
GPIO_PULL_STRONGEST = 0
|
||||||
|
} gpio_pull_strength_t;
|
||||||
|
|
||||||
|
#define HAVE_GPIO_DRIVE_STRENGTH_T
|
||||||
|
typedef enum {
|
||||||
|
GPIO_DRIVE_WEAKEST = 0,
|
||||||
|
GPIO_DRIVE_WEAK = 0,
|
||||||
|
GPIO_DRIVE_STRONG = 1,
|
||||||
|
GPIO_DRIVE_STRONGEST = 1
|
||||||
|
} gpio_drive_strength_t;
|
||||||
|
|
||||||
|
#define HAVE_GPIO_IRQ_TRIG_T
|
||||||
|
typedef enum {
|
||||||
|
GPIO_TRIGGER_EDGE_RISING = GPIOTE_CONFIG_POLARITY_LoToHi << GPIOTE_CONFIG_POLARITY_Pos,
|
||||||
|
GPIO_TRIGGER_EDGE_FALLING = GPIOTE_CONFIG_POLARITY_HiToLo << GPIOTE_CONFIG_POLARITY_Pos,
|
||||||
|
GPIO_TRIGGER_EDGE_BOTH = GPIO_TRIGGER_EDGE_RISING | GPIO_TRIGGER_EDGE_FALLING,
|
||||||
|
GPIO_TRIGGER_LEVEL_HIGH = 0, /**< unsupported */
|
||||||
|
GPIO_TRIGGER_LEVEL_LOW = 0, /**< unsupported */
|
||||||
|
} gpio_irq_trig_t;
|
||||||
|
|
||||||
|
#define HAVE_GPIO_PULL_T
|
||||||
|
typedef enum {
|
||||||
|
GPIO_FLOATING = 0,
|
||||||
|
GPIO_PULL_UP = GPIO_PIN_CNF_PULL_Pullup << GPIO_PIN_CNF_PULL_Pos,
|
||||||
|
GPIO_PULL_DOWN = GPIO_PIN_CNF_PULL_Pulldown << GPIO_PIN_CNF_PULL_Pos,
|
||||||
|
/* GPIO_PULL_KEEP is not supported by, gpio_ll_init() returns -ENOTSUP */
|
||||||
|
GPIO_PULL_KEEP = 0xff
|
||||||
|
} gpio_pull_t;
|
||||||
|
#endif /* END: GPIO LL overwrites */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief No support for HW chip select...
|
* @brief No support for HW chip select...
|
||||||
*/
|
*/
|
||||||
|
199
cpu/nrf5x_common/periph/gpio_ll.c
Normal file
199
cpu/nrf5x_common/periph/gpio_ll.c
Normal file
@ -0,0 +1,199 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2015 Jan Wagner <mail@jwagner.eu>
|
||||||
|
* 2015-2016 Freie Universität Berlin
|
||||||
|
* 2019 Inria
|
||||||
|
* 2021 Otto-von-Guericke-Universität Magdeburg
|
||||||
|
*
|
||||||
|
* This file is subject to the terms and conditions of the GNU Lesser
|
||||||
|
* General Public License v2.1. See the file LICENSE in the top level
|
||||||
|
* directory for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ingroup cpu_nrf5x_common
|
||||||
|
* @ingroup drivers_periph_gpio_ll
|
||||||
|
* @{
|
||||||
|
*
|
||||||
|
* @file
|
||||||
|
* @brief Peripheral GPIO Low-Level API implementation for the nRF5x MCU family
|
||||||
|
*
|
||||||
|
* @author Christian Kühling <kuehling@zedat.fu-berlin.de>
|
||||||
|
* @author Timo Ziegler <timo.ziegler@fu-berlin.de>
|
||||||
|
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
|
||||||
|
* @author Jan Wagner <mail@jwagner.eu>
|
||||||
|
* @author Alexandre Abadie <alexandre.abadie@inria.fr>
|
||||||
|
* @author Marian Buschsieweke <marian.buschsieweke@ovgu.de>
|
||||||
|
*
|
||||||
|
* @}
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "cpu.h"
|
||||||
|
#include "periph/gpio_ll.h"
|
||||||
|
#include "periph_cpu.h"
|
||||||
|
#include "periph_conf.h"
|
||||||
|
|
||||||
|
int gpio_ll_init(gpio_port_t port, uint8_t pin, const gpio_conf_t *conf)
|
||||||
|
{
|
||||||
|
if (conf->pull == GPIO_PULL_KEEP) {
|
||||||
|
return -ENOTSUP;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t pin_cnf = conf->pull;
|
||||||
|
switch (conf->state) {
|
||||||
|
case GPIO_OUTPUT_PUSH_PULL:
|
||||||
|
/* INPUT bit needs to be *CLEARED* in input mode, so set to disconnect input buffer */
|
||||||
|
pin_cnf |= GPIO_PIN_CNF_DIR_Msk | GPIO_PIN_CNF_INPUT_Msk;
|
||||||
|
if (conf->drive_strength) {
|
||||||
|
pin_cnf |= GPIO_PIN_CNF_DRIVE_H0H1 << GPIO_PIN_CNF_DRIVE_Pos;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case GPIO_OUTPUT_OPEN_DRAIN:
|
||||||
|
pin_cnf |= GPIO_PIN_CNF_DIR_Msk;
|
||||||
|
if (conf->drive_strength) {
|
||||||
|
pin_cnf |= GPIO_PIN_CNF_DRIVE_H0D1 << GPIO_PIN_CNF_DRIVE_Pos;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
pin_cnf |= GPIO_PIN_CNF_DRIVE_S0D1 << GPIO_PIN_CNF_DRIVE_Pos;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case GPIO_OUTPUT_OPEN_SOURCE:
|
||||||
|
pin_cnf |= GPIO_PIN_CNF_DIR_Msk;
|
||||||
|
if (conf->drive_strength) {
|
||||||
|
pin_cnf |= GPIO_PIN_CNF_DRIVE_D0H1 << GPIO_PIN_CNF_DRIVE_Pos;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
pin_cnf |= GPIO_PIN_CNF_DRIVE_D0S1 << GPIO_PIN_CNF_DRIVE_Pos;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case GPIO_INPUT:
|
||||||
|
break;
|
||||||
|
case GPIO_DISCONNECT:
|
||||||
|
default:
|
||||||
|
/* INPUT bit needs to be *CLEARED* in input mode, so set to disconnect input buffer */
|
||||||
|
pin_cnf |= GPIO_PIN_CNF_INPUT_Msk;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (conf->state != GPIO_OUTPUT_PUSH_PULL) {
|
||||||
|
switch (conf->pull) {
|
||||||
|
default:
|
||||||
|
case GPIO_FLOATING:
|
||||||
|
break;
|
||||||
|
case GPIO_PULL_UP:
|
||||||
|
pin_cnf |= GPIO_PIN_CNF_PULL_Pullup << GPIO_PIN_CNF_PULL_Pos;
|
||||||
|
break;
|
||||||
|
case GPIO_PULL_DOWN:
|
||||||
|
pin_cnf |= GPIO_PIN_CNF_PULL_Pulldown << GPIO_PIN_CNF_PULL_Pos;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NRF_GPIO_Type *p = (NRF_GPIO_Type *)port;
|
||||||
|
if (conf->initial_value) {
|
||||||
|
p->OUTSET = 1UL << pin;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
p->OUTCLR = 1UL << pin;
|
||||||
|
}
|
||||||
|
p->PIN_CNF[pin] = pin_cnf;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void gpio_ll_query_conf(gpio_conf_t *dest, gpio_port_t port, uint8_t pin)
|
||||||
|
{
|
||||||
|
assert((dest != NULL)
|
||||||
|
&& (NULL == gpio_port_unpack_addr(port))
|
||||||
|
&& (pin < 32));
|
||||||
|
memset(dest, 0, sizeof(*dest));
|
||||||
|
/* Searching "Schmitt" in
|
||||||
|
* https://infocenter.nordicsemi.com/pdf/nRF52840_OPS_v0.5.pdf yields
|
||||||
|
* no matches. Assuming Schmitt trigger cannot be disabled for the
|
||||||
|
* nRF5x MCU.
|
||||||
|
*/
|
||||||
|
dest->schmitt_trigger = true;
|
||||||
|
dest->state = GPIO_INPUT;
|
||||||
|
|
||||||
|
NRF_GPIO_Type *p = (NRF_GPIO_Type *)port;
|
||||||
|
uint32_t cnf = p->PIN_CNF[pin];
|
||||||
|
|
||||||
|
if (cnf & GPIO_PIN_CNF_DIR_Msk) {
|
||||||
|
/* some kind of output, determine which: */
|
||||||
|
switch ((cnf >> GPIO_PIN_CNF_DRIVE_Pos) & 0xf) {
|
||||||
|
default:
|
||||||
|
/* push-pull with mix of high drive and standard drive (e.g. high
|
||||||
|
* drive for 0, standard drive for 1) is also possible
|
||||||
|
* hardware-wise, but not supported via the API. Anyways, if someone
|
||||||
|
* bypassed the API to set this, get at least the closest match to
|
||||||
|
* the configuration.
|
||||||
|
*/
|
||||||
|
case GPIO_PIN_CNF_DRIVE_S0S1:
|
||||||
|
/* standard drive 0, standard drive 1
|
||||||
|
* --> push pull with weak drive */
|
||||||
|
dest->state = GPIO_OUTPUT_PUSH_PULL;
|
||||||
|
dest->drive_strength = GPIO_DRIVE_WEAK;
|
||||||
|
break;
|
||||||
|
case GPIO_PIN_CNF_DRIVE_H0H1:
|
||||||
|
/* high drive 0, high drive 1
|
||||||
|
* --> push pull with high drive */
|
||||||
|
dest->state = GPIO_OUTPUT_PUSH_PULL;
|
||||||
|
dest->drive_strength = GPIO_DRIVE_STRONG;
|
||||||
|
break;
|
||||||
|
case GPIO_PIN_CNF_DRIVE_S0D1:
|
||||||
|
/* standard drive 0, disconnect at 1
|
||||||
|
* --> open drain with weak drive */
|
||||||
|
dest->state = GPIO_OUTPUT_OPEN_DRAIN;
|
||||||
|
dest->drive_strength = GPIO_DRIVE_WEAK;
|
||||||
|
break;
|
||||||
|
case GPIO_PIN_CNF_DRIVE_H0D1:
|
||||||
|
/* high drive 0, disconnect at 1
|
||||||
|
* --> open drain with strong drive */
|
||||||
|
dest->state = GPIO_OUTPUT_OPEN_DRAIN;
|
||||||
|
dest->drive_strength = GPIO_DRIVE_STRONG;
|
||||||
|
break;
|
||||||
|
case GPIO_PIN_CNF_DRIVE_D0S1:
|
||||||
|
/* disconnect at 0, standard drive 1
|
||||||
|
* --> open emitter with weak drive */
|
||||||
|
dest->state = GPIO_OUTPUT_OPEN_SOURCE;
|
||||||
|
dest->drive_strength = GPIO_DRIVE_WEAK;
|
||||||
|
break;
|
||||||
|
case GPIO_PIN_CNF_DRIVE_D0H1:
|
||||||
|
/* disconnect at 0, high drive 1
|
||||||
|
* --> open emitter with strong drive */
|
||||||
|
dest->state = GPIO_OUTPUT_OPEN_SOURCE;
|
||||||
|
dest->drive_strength = GPIO_DRIVE_STRONG;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (cnf & GPIO_PIN_CNF_INPUT_Msk) {
|
||||||
|
/* input buffer is disconnected and pin is not in output mode
|
||||||
|
* --> GPIO pin is off
|
||||||
|
*/
|
||||||
|
dest->state = GPIO_DISCONNECT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch ((cnf & GPIO_PIN_CNF_PULL_Msk) >> GPIO_PIN_CNF_PULL_Pos) {
|
||||||
|
case GPIO_PIN_CNF_PULL_Pullup:
|
||||||
|
dest->pull = GPIO_PULL_UP;
|
||||||
|
break;
|
||||||
|
case GPIO_PIN_CNF_PULL_Pulldown:
|
||||||
|
dest->pull = GPIO_PULL_DOWN;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
dest->pull = GPIO_FLOATING;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dest->state == GPIO_INPUT) {
|
||||||
|
dest->initial_value = (gpio_ll_read(port) >> pin) & 1UL;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
dest->initial_value = (gpio_ll_read_output(port) >> pin) & 1UL;
|
||||||
|
}
|
||||||
|
}
|
221
cpu/nrf5x_common/periph/gpio_ll_irq.c
Normal file
221
cpu/nrf5x_common/periph/gpio_ll_irq.c
Normal file
@ -0,0 +1,221 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2015 Jan Wagner <mail@jwagner.eu>
|
||||||
|
* 2015-2016 Freie Universität Berlin
|
||||||
|
* 2019 Inria
|
||||||
|
* 2021 Otto-von-Guericke-Universität Magdeburg
|
||||||
|
*
|
||||||
|
* This file is subject to the terms and conditions of the GNU Lesser
|
||||||
|
* General Public License v2.1. See the file LICENSE in the top level
|
||||||
|
* directory for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ingroup cpu_nrf5x_common
|
||||||
|
* @ingroup drivers_periph_gpio_ll_irq
|
||||||
|
* @{
|
||||||
|
*
|
||||||
|
* @file
|
||||||
|
* @brief IRQ implementation of the GPIO Low-Level API for the nRF5x MCU family
|
||||||
|
*
|
||||||
|
* @note This GPIO driver implementation supports only one pin to be
|
||||||
|
* defined as external interrupt.
|
||||||
|
*
|
||||||
|
* @author Christian Kühling <kuehling@zedat.fu-berlin.de>
|
||||||
|
* @author Timo Ziegler <timo.ziegler@fu-berlin.de>
|
||||||
|
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
|
||||||
|
* @author Jan Wagner <mail@jwagner.eu>
|
||||||
|
* @author Alexandre Abadie <alexandre.abadie@inria.fr>
|
||||||
|
* @author Marian Buschsieweke <marian.buschsieweke@ovgu.de>
|
||||||
|
*
|
||||||
|
* @}
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
#include "cpu.h"
|
||||||
|
#include "periph/gpio_ll.h"
|
||||||
|
#include "periph/gpio_ll_irq.h"
|
||||||
|
#include "periph_conf.h"
|
||||||
|
#include "periph_cpu.h"
|
||||||
|
|
||||||
|
#ifdef NRF_GPIOTE0_S
|
||||||
|
#define NRF_GPIOTE NRF_GPIOTE0_S
|
||||||
|
#define GPIOTE_IRQn GPIOTE0_IRQn
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if CPU_FAM_NRF51
|
||||||
|
#define GPIOTE_CHAN_NUMOF (4U)
|
||||||
|
#else
|
||||||
|
#define GPIOTE_CHAN_NUMOF (8U)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Place to store the interrupt context
|
||||||
|
*/
|
||||||
|
struct isr_ctx {
|
||||||
|
gpio_ll_cb_t cb;
|
||||||
|
void *arg;
|
||||||
|
};
|
||||||
|
static struct isr_ctx isr_ctx[GPIOTE_CHAN_NUMOF];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief get the GPIOTE channel used to monitor the given pin
|
||||||
|
*
|
||||||
|
* @return the GPIOTE channel monitoring the specified pin
|
||||||
|
* @retval GPIOTE_CHAN_NUMOF no GPIOTE channel is monitoring the given pin
|
||||||
|
*/
|
||||||
|
static unsigned get_channel_of_pin(uint8_t port_num, uint8_t pin)
|
||||||
|
{
|
||||||
|
/* port_num unused for nrf51 */
|
||||||
|
(void)port_num;
|
||||||
|
for (unsigned i = 0; i < GPIOTE_CHAN_NUMOF; i++) {
|
||||||
|
uint32_t conf = NRF_GPIOTE->CONFIG[i];
|
||||||
|
uint32_t mode = (conf & GPIOTE_CONFIG_MODE_Msk) >> GPIOTE_CONFIG_MODE_Pos;
|
||||||
|
if (mode == GPIOTE_CONFIG_MODE_Event) {
|
||||||
|
uint8_t pinsel = (conf & GPIOTE_CONFIG_PSEL_Msk) >> GPIOTE_CONFIG_PSEL_Pos;
|
||||||
|
#ifdef GPIOTE_CONFIG_PORT_Msk
|
||||||
|
uint8_t portsel = (conf & GPIOTE_CONFIG_PORT_Msk) >> GPIOTE_CONFIG_PORT_Pos;
|
||||||
|
#endif
|
||||||
|
if ((pinsel == pin)
|
||||||
|
#ifdef GPIOTE_CONFIG_PORT_Msk
|
||||||
|
&& (portsel == port_num)
|
||||||
|
#endif
|
||||||
|
) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return GPIOTE_CHAN_NUMOF;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief select a GPIOTE channel suitable for managing the irq for the given
|
||||||
|
* pin
|
||||||
|
*
|
||||||
|
* @return if one channel is already used for the given pin, return that.
|
||||||
|
* Otherwise return a free channel
|
||||||
|
* @retval GPIOTE_CHAN_NUMOF all GPIOTE channels occupied by pins different
|
||||||
|
* to the selected one
|
||||||
|
*/
|
||||||
|
static unsigned get_channel_for_pin(uint8_t port_num, uint8_t pin)
|
||||||
|
{
|
||||||
|
unsigned result = get_channel_of_pin(port_num, pin);
|
||||||
|
if (result != GPIOTE_CHAN_NUMOF) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* no channel devoted to the pin yet, return first free channel instead */
|
||||||
|
for (unsigned i = 0; i < GPIOTE_CHAN_NUMOF; i++) {
|
||||||
|
uint32_t conf = NRF_GPIOTE->CONFIG[i];
|
||||||
|
uint32_t mode = (conf & GPIOTE_CONFIG_MODE_Msk) >> GPIOTE_CONFIG_MODE_Pos;
|
||||||
|
if (mode != GPIOTE_CONFIG_MODE_Event) {
|
||||||
|
/* free channel found */
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return GPIOTE_CHAN_NUMOF;
|
||||||
|
}
|
||||||
|
|
||||||
|
int gpio_ll_irq(gpio_port_t port, uint8_t pin,
|
||||||
|
gpio_irq_trig_t trig, gpio_ll_cb_t cb, void *arg)
|
||||||
|
{
|
||||||
|
/* param port is not used on nRF5x variants with only one GPIO port */
|
||||||
|
(void)port;
|
||||||
|
uint8_t port_num = GPIO_PORT_NUM(port);
|
||||||
|
uint8_t channel = get_channel_for_pin(port_num, pin);
|
||||||
|
assert((trig != GPIO_TRIGGER_LEVEL_HIGH) && (trig != GPIO_TRIGGER_LEVEL_LOW));
|
||||||
|
|
||||||
|
if (channel == GPIOTE_CHAN_NUMOF) {
|
||||||
|
return -EBUSY;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* mask IRQ */
|
||||||
|
NRF_GPIOTE->INTENCLR = GPIOTE_INTENSET_IN0_Msk << channel;
|
||||||
|
|
||||||
|
isr_ctx[channel].cb = cb;
|
||||||
|
isr_ctx[channel].arg = arg;
|
||||||
|
|
||||||
|
/* use event mode */
|
||||||
|
uint32_t config = GPIOTE_CONFIG_MODE_Event << GPIOTE_CONFIG_MODE_Pos;
|
||||||
|
/* set pin and (nRF52 only) port */
|
||||||
|
config |= (uint32_t)pin << GPIOTE_CONFIG_PSEL_Pos;
|
||||||
|
#ifdef GPIOTE_CONFIG_PORT_Pos
|
||||||
|
config |= (uint32_t)port_num << GPIOTE_CONFIG_PORT_Pos;
|
||||||
|
#endif
|
||||||
|
/* set trigger */
|
||||||
|
config |= (uint32_t)trig & GPIOTE_CONFIG_POLARITY_Msk;
|
||||||
|
/* apply config */
|
||||||
|
NRF_GPIOTE->CONFIG[channel] = config;
|
||||||
|
/* enable IRQ */
|
||||||
|
NVIC_EnableIRQ(GPIOTE_IRQn);
|
||||||
|
/* clear any spurious IRQ still present */
|
||||||
|
NRF_GPIOTE->EVENTS_IN[channel] = 0;
|
||||||
|
/* unmask IRQ */
|
||||||
|
NRF_GPIOTE->INTENSET = GPIOTE_INTENSET_IN0_Msk << channel;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void gpio_ll_irq_mask(gpio_port_t port, uint8_t pin)
|
||||||
|
{
|
||||||
|
/* param port is not used on nRF5x variants with only one GPIO port */
|
||||||
|
(void)port;
|
||||||
|
uint8_t port_num = GPIO_PORT_NUM(port);
|
||||||
|
unsigned channel = get_channel_of_pin(port_num, pin);
|
||||||
|
assert(channel != GPIOTE_CHAN_NUMOF);
|
||||||
|
if (channel != GPIOTE_CHAN_NUMOF) {
|
||||||
|
NRF_GPIOTE->INTENCLR = GPIOTE_INTENCLR_IN0_Msk << channel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void gpio_ll_irq_unmask(gpio_port_t port, uint8_t pin)
|
||||||
|
{
|
||||||
|
/* param port is not used on nRF5x variants with only one GPIO port */
|
||||||
|
(void)port;
|
||||||
|
uint8_t port_num = GPIO_PORT_NUM(port);
|
||||||
|
unsigned channel = get_channel_of_pin(port_num, pin);
|
||||||
|
assert(channel != GPIOTE_CHAN_NUMOF);
|
||||||
|
if (channel != GPIOTE_CHAN_NUMOF) {
|
||||||
|
NRF_GPIOTE->INTENSET = GPIOTE_INTENCLR_IN0_Msk << channel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void gpio_ll_irq_unmask_and_clear(gpio_port_t port, uint8_t pin)
|
||||||
|
{
|
||||||
|
/* param port is not used on nRF5x variants with only one GPIO port */
|
||||||
|
(void)port;
|
||||||
|
uint8_t port_num = GPIO_PORT_NUM(port);
|
||||||
|
unsigned channel = get_channel_of_pin(port_num, pin);
|
||||||
|
assert(channel != GPIOTE_CHAN_NUMOF);
|
||||||
|
if (channel != GPIOTE_CHAN_NUMOF) {
|
||||||
|
NRF_GPIOTE->EVENTS_IN[channel] = 0;
|
||||||
|
NRF_GPIOTE->INTENSET = GPIOTE_INTENCLR_IN0_Msk << channel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void gpio_ll_irq_off(gpio_port_t port, uint8_t pin)
|
||||||
|
{
|
||||||
|
/* param port is not used on nRF5x variants with only one GPIO port */
|
||||||
|
(void)port;
|
||||||
|
uint8_t port_num = GPIO_PORT_NUM(port);
|
||||||
|
unsigned channel = get_channel_of_pin(port_num, pin);
|
||||||
|
assert(channel != GPIOTE_CHAN_NUMOF);
|
||||||
|
if (channel != GPIOTE_CHAN_NUMOF) {
|
||||||
|
NRF_GPIOTE->INTENSET = GPIOTE_INTENCLR_IN0_Msk << channel;
|
||||||
|
NRF_GPIOTE->CONFIG[channel] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void isr_gpiote(void)
|
||||||
|
{
|
||||||
|
for (unsigned int i = 0; i < GPIOTE_CHAN_NUMOF; ++i) {
|
||||||
|
if (NRF_GPIOTE->EVENTS_IN[i] == 1) {
|
||||||
|
NRF_GPIOTE->EVENTS_IN[i] = 0;
|
||||||
|
isr_ctx[i].cb(isr_ctx[i].arg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cortexm_isr_end();
|
||||||
|
}
|
@ -13,6 +13,9 @@ config CPU_FAM_NRF9160
|
|||||||
select HAS_PERIPH_FLASHPAGE_PAGEWISE
|
select HAS_PERIPH_FLASHPAGE_PAGEWISE
|
||||||
select HAS_PERIPH_GPIO
|
select HAS_PERIPH_GPIO
|
||||||
select HAS_PERIPH_GPIO_IRQ
|
select HAS_PERIPH_GPIO_IRQ
|
||||||
|
select HAS_PERIPH_GPIO_LL
|
||||||
|
select HAS_PERIPH_GPIO_LL_IRQ
|
||||||
|
select HAS_PERIPH_GPIO_LL_IRQ_UNMASK
|
||||||
select HAS_PERIPH_TIMER_PERIODIC
|
select HAS_PERIPH_TIMER_PERIODIC
|
||||||
select HAS_PERIPH_UART_MODECFG
|
select HAS_PERIPH_UART_MODECFG
|
||||||
select HAS_PERIPH_SPI_GPIO_MODE
|
select HAS_PERIPH_SPI_GPIO_MODE
|
||||||
|
Loading…
Reference in New Issue
Block a user