diff --git a/cpu/nrf5x_common/include/periph_cpu_common.h b/cpu/nrf5x_common/include/periph_cpu_common.h index cad1a919b1..a8d90229b2 100644 --- a/cpu/nrf5x_common/include/periph_cpu_common.h +++ b/cpu/nrf5x_common/include/periph_cpu_common.h @@ -188,6 +188,17 @@ typedef enum { #define NWDT_TIME_UPPER_LIMIT ((UINT32_MAX >> 15) * US_PER_MS + 1) /** @} */ +/** + * @brief Quadrature decoder configuration struct + */ +typedef struct { + gpio_t a_pin; /**< GPIO Pin for phase A */ + gpio_t b_pin; /**< GPIO Pin for phase B */ + gpio_t led_pin; /**< LED GPIO, GPIO_UNDEF to disable */ + uint8_t sample_period; /**< Sample period used, e.g. QDEC_SAMPLEPER_SAMPLEPER_128us */ + bool debounce_filter; /**< Enable/disable debounce filter */ +} qdec_conf_t; + /** * @brief Retrieve the exti(GPIOTE) channel associated with a gpio * diff --git a/cpu/nrf5x_common/periph/qdec.c b/cpu/nrf5x_common/periph/qdec.c new file mode 100644 index 0000000000..79e05f1f02 --- /dev/null +++ b/cpu/nrf5x_common/periph/qdec.c @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2021 Koen Zandberg + * + * 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_qdec + * @{ + * + * @file + * @brief Low-level QDEC driver implementation + * + * @author Koen Zandberg + * + * @} + */ +#include + +#include "cpu.h" +#include "periph_conf.h" +#include "periph/qdec.h" +#include "periph/gpio.h" + +#ifdef QDEC_NUMOF + + +/** + * @brief Interrupt context for each configured qdec + */ +static qdec_isr_ctx_t isr_ctx[QDEC_NUMOF]; + +static inline NRF_QDEC_Type* dev(qdec_t qdec) +{ + (void)qdec; + return NRF_QDEC; +} + +static inline const qdec_conf_t* conf(qdec_t qdec) +{ + return &qdec_config[qdec]; +} + +int32_t qdec_init(qdec_t qdec, qdec_mode_t mode, qdec_cb_t cb, void *arg) +{ + (void)mode; + /* Verify parameters */ + assert((qdec < QDEC_NUMOF)); + + /* The nrf5x peripheral counts all edges */ + if (mode != QDEC_X4) { + return -EINVAL; + } + + isr_ctx[qdec].cb = cb; + isr_ctx[qdec].arg = arg; + + if (cb) { + NVIC_EnableIRQ(QDEC_IRQn); + dev(qdec)->INTENSET = QDEC_INTENSET_ACCOF_Msk; + } + else { + NVIC_DisableIRQ(QDEC_IRQn); + dev(qdec)->INTENCLR = QDEC_INTENCLR_ACCOF_Msk; + } + + gpio_init(conf(qdec)->a_pin, GPIO_IN_PU); + gpio_init(conf(qdec)->b_pin, GPIO_IN_PU); + + dev(qdec)->PSEL.A = conf(qdec)->a_pin; + dev(qdec)->PSEL.B = conf(qdec)->b_pin; + + /** Optionally set or disable the LED */ + dev(qdec)->PSEL.LED = gpio_is_valid(conf(qdec)->led_pin) + ? QDEC_PSEL_LED_CONNECT_Msk + : conf(qdec)->led_pin; + + dev(qdec)->DBFEN = conf(qdec)->debounce_filter ? 1 : 0; + + /* Enable the peripheral */ + dev(qdec)->ENABLE = 1; + dev(qdec)->TASKS_START = 1; + return 0; +} + +int32_t qdec_read_and_reset(qdec_t qdec) +{ + /* Read and clear ACC register */ + dev(qdec)->TASKS_RDCLRACC = 1; + return dev(qdec)->ACCREAD; +} + +int32_t qdec_read(qdec_t qdec) +{ + return dev(qdec)->ACC; +} + +void qdec_start(qdec_t qdec) +{ + dev(qdec)->TASKS_START = 1; +} + +void qdec_stop(qdec_t qdec) +{ + dev(qdec)->TASKS_STOP = 1; + while (dev(qdec)->EVENTS_STOPPED == 0) {} + dev(qdec)->EVENTS_STOPPED = 0; +} + +void isr_qdec(void) +{ + qdec_t qdec = QDEC_DEV(0); /* only one available */ + dev(qdec)->EVENTS_ACCOF = 0; + isr_ctx[qdec].cb(isr_ctx[qdec].arg); +} +#endif