This is an automated email from the ASF dual-hosted git repository. linguini pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/nuttx.git
commit 1513b3e20c2387732428d529b9d9046d163e608f Author: raiden00pl <[email protected]> AuthorDate: Sat Apr 11 12:30:33 2026 +0200 arch/nrf52|nrf53: add qencoder support add qencoder support for nrf52 and nrf53 Signed-off-by: raiden00pl <[email protected]> --- Documentation/platforms/arm/nrf52/index.rst | 8 +- Documentation/platforms/arm/nrf53/index.rst | 8 +- arch/arm/src/nrf52/CMakeLists.txt | 4 + arch/arm/src/nrf52/Kconfig | 21 ++ arch/arm/src/nrf52/Make.defs | 4 + arch/arm/src/nrf52/hardware/nrf52_qdec.h | 242 +++++++++++++++ arch/arm/src/nrf52/nrf52_qdec.c | 445 ++++++++++++++++++++++++++ arch/arm/src/nrf52/nrf52_qdec.h | 52 ++++ arch/arm/src/nrf53/CMakeLists.txt | 8 + arch/arm/src/nrf53/Kconfig | 28 ++ arch/arm/src/nrf53/Make.defs | 4 + arch/arm/src/nrf53/hardware/nrf53_qdec.h | 215 +++++++++++++ arch/arm/src/nrf53/nrf53_qdec.c | 464 ++++++++++++++++++++++++++++ arch/arm/src/nrf53/nrf53_qdec.h | 52 ++++ 14 files changed, 1553 insertions(+), 2 deletions(-) diff --git a/Documentation/platforms/arm/nrf52/index.rst b/Documentation/platforms/arm/nrf52/index.rst index 5d4bb0144c6..f2fd75315a6 100644 --- a/Documentation/platforms/arm/nrf52/index.rst +++ b/Documentation/platforms/arm/nrf52/index.rst @@ -62,7 +62,7 @@ NFCT No PDM No PPI Yes PWM Yes -QDEC No +QDEC Yes Quadrature encoder QSPI Yes RADIO Yes BLE, IEEE 802.15.4 RNG Yes @@ -158,6 +158,12 @@ PWM PWM is supported via standard driver. This means that more advanced features such as complex sequences or waveform modes are not yet supported. +QDEC +---- + +The QDEC (Quadrature Decoder) peripheral is supported via the standard qencoder driver +interface. The LED output is not supported now. + QSPI ---- diff --git a/Documentation/platforms/arm/nrf53/index.rst b/Documentation/platforms/arm/nrf53/index.rst index 50ae98fee05..4ea834de622 100644 --- a/Documentation/platforms/arm/nrf53/index.rst +++ b/Documentation/platforms/arm/nrf53/index.rst @@ -21,7 +21,7 @@ NFCT No PDM No DPPI No PWM Yes -QDEC No +QDEC Yes Quadrature encoder QSPI Yes RADIO No RNG No @@ -102,6 +102,12 @@ PWM PWM is supported via standard driver. This means that more advanced features such as complex sequences or waveform modes are not yet supported. +QDEC +---- + +The QDEC (Quadrature Decoder) peripheral is supported via the standard qencoder driver +interface. The LED output is not supported now. + QSPI ---- diff --git a/arch/arm/src/nrf52/CMakeLists.txt b/arch/arm/src/nrf52/CMakeLists.txt index 66858f547ee..4fcf2e6fb81 100644 --- a/arch/arm/src/nrf52/CMakeLists.txt +++ b/arch/arm/src/nrf52/CMakeLists.txt @@ -132,6 +132,10 @@ if(CONFIG_NRF52_SAADC) list(APPEND SRCS nrf52_adc.c) endif() +if(CONFIG_NRF52_QDEC) + list(APPEND SRCS nrf52_qdec.c) +endif() + if(CONFIG_PM) list(APPEND SRCS nrf52_pminitialize.c) endif() diff --git a/arch/arm/src/nrf52/Kconfig b/arch/arm/src/nrf52/Kconfig index eb2d0847bdb..3c6ac405212 100644 --- a/arch/arm/src/nrf52/Kconfig +++ b/arch/arm/src/nrf52/Kconfig @@ -170,6 +170,10 @@ config NRF52_RTC bool default n +config NRF52_QDEC + bool + default n + menu "nRF52 Peripheral Selection" config NRF52_I2C0_MASTER @@ -803,6 +807,23 @@ menu "USBDEV Configuration" endmenu # USBDEV Configuration +config NRF52_QDEC0 + bool "Qencoder (QDEC) support" + default n + select NRF52_QDEC + ---help--- + Enable Qencoder support. + +config NRF52_QENCODER_INDEX + bool "Enable QDEC index pin support" + default n + depends on NRF52_GPIOTE && NRF52_QDEC + ---help--- + Enable index/Z channel support for quadrature encoder. + Uses a GPIO interrupt to detect index pulses and reset + position to configured index value. Requires one GPIOTE + channel for interrupt detection. + menuconfig NRF52_SOFTDEVICE_CONTROLLER bool "SoftDevice Controller" depends on ALLOW_BSDNORDIC_COMPONENTS diff --git a/arch/arm/src/nrf52/Make.defs b/arch/arm/src/nrf52/Make.defs index 10118d1753d..a8eb228fdc6 100644 --- a/arch/arm/src/nrf52/Make.defs +++ b/arch/arm/src/nrf52/Make.defs @@ -123,6 +123,10 @@ ifeq ($(CONFIG_NRF52_SAADC),y) CHIP_CSRCS += nrf52_adc.c endif +ifeq ($(CONFIG_NRF52_QDEC),y) +CHIP_CSRCS += nrf52_qdec.c +endif + ifeq ($(CONFIG_PM),y) CHIP_CSRCS += nrf52_pminitialize.c endif diff --git a/arch/arm/src/nrf52/hardware/nrf52_qdec.h b/arch/arm/src/nrf52/hardware/nrf52_qdec.h new file mode 100644 index 00000000000..2cbe799dc92 --- /dev/null +++ b/arch/arm/src/nrf52/hardware/nrf52_qdec.h @@ -0,0 +1,242 @@ +/**************************************************************************** + * arch/arm/src/nrf52/hardware/nrf52_qdec.h + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +#ifndef __ARCH_ARM_SRC_NRF52_HARDWARE_NRF52_QDEC_H +#define __ARCH_ARM_SRC_NRF52_HARDWARE_NRF52_QDEC_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <nuttx/config.h> +#include "hardware/nrf52_memorymap.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Register offsets *********************************************************/ + +#define NRF52_QDEC_TASKS_START_OFFSET 0x0000 /* Task starting the quadrature decoder */ +#define NRF52_QDEC_TASKS_STOP_OFFSET 0x0004 /* Task stopping the quadrature decoder */ +#define NRF52_QDEC_TASKS_READCLRACC_OFFSET 0x0008 /* Read and clear ACC and ACCDBL */ +#define NRF52_QDEC_TASKS_RDCLRACC_OFFSET 0x000c /* Read and clear ACC */ +#define NRF52_QDEC_TASKS_RDCLRDBL_OFFSET 0x0010 /* Read and clear ACCDBL */ + +#define NRF52_QDEC_EVENTS_SAMPLERDY_OFFSET 0x0100 /* Event being generated for every new sample value */ +#define NRF52_QDEC_EVENTS_REPORTRDY_OFFSET 0x0104 /* Non-null report ready */ +#define NRF52_QDEC_EVENTS_ACCOF_OFFSET 0x0108 /* ACC or ACCDBL register overflow */ +#define NRF52_QDEC_EVENTS_DBLRDY_OFFSET 0x010c /* Double displacement(s) detected */ +#define NRF52_QDEC_EVENTS_STOPPED_OFFSET 0x0110 /* QDEC has been stopped */ + +#define NRF52_QDEC_SHORTS_OFFSET 0x0200 /* Shortcut register */ +#define NRF52_QDEC_INTENSET_OFFSET 0x0304 /* Enable interrupt */ +#define NRF52_QDEC_INTENCLR_OFFSET 0x0308 /* Disable interrupt */ +#define NRF52_QDEC_ENABLE_OFFSET 0x0500 /* Enable the quadrature decoder */ +#define NRF52_QDEC_LEDPOL_OFFSET 0x0504 /* LED output pin polarity */ +#define NRF52_QDEC_SAMPLEPER_OFFSET 0x0508 /* Sample period */ +#define NRF52_QDEC_SAMPLE_OFFSET 0x050c /* Motion sample value */ +#define NRF52_QDEC_REPORTPER_OFFSET 0x0510 /* Number of samples to be taken before REPORTRDY and DBLRDY events */ +#define NRF52_QDEC_ACC_OFFSET 0x0514 /* Register accumulating the valid transitions */ +#define NRF52_QDEC_ACCREAD_OFFSET 0x0518 /* Snapshot of the ACC register */ +#define NRF52_QDEC_PSEL_LED_OFFSET 0x051c /* Pin select for LED signal */ +#define NRF52_QDEC_PSEL_A_OFFSET 0x0520 /* Pin select for A signal */ +#define NRF52_QDEC_PSEL_B_OFFSET 0x0524 /* Pin select for B signal */ +#define NRF52_QDEC_DBFEN_OFFSET 0x0528 /* Enable input debounce filters */ +#define NRF52_QDEC_LEDPRE_OFFSET 0x0540 /* Time period the LED is switched ON prior to sampling */ +#define NRF52_QDEC_ACCDBL_OFFSET 0x0544 /* Register accumulating the number of detected double transitions */ +#define NRF52_QDEC_ACCDBLREAD_OFFSET 0x0548 /* Snapshot of the ACCDBL register */ + +/* Register addresses *******************************************************/ + +#define NRF52_QDEC_TASKS_START (NRF52_QDEC_BASE + NRF52_QDEC_TASKS_START_OFFSET) +#define NRF52_QDEC_TASKS_STOP (NRF52_QDEC_BASE + NRF52_QDEC_TASKS_STOP_OFFSET) +#define NRF52_QDEC_TASKS_READCLRACC (NRF52_QDEC_BASE + NRF52_QDEC_TASKS_READCLRACC_OFFSET) +#define NRF52_QDEC_TASKS_RDCLRACC (NRF52_QDEC_BASE + NRF52_QDEC_TASKS_RDCLRACC_OFFSET) +#define NRF52_QDEC_TASKS_RDCLRDBL (NRF52_QDEC_BASE + NRF52_QDEC_TASKS_RDCLRDBL_OFFSET) + +#define NRF52_QDEC_EVENTS_SAMPLERDY (NRF52_QDEC_BASE + NRF52_QDEC_EVENTS_SAMPLERDY_OFFSET) +#define NRF52_QDEC_EVENTS_REPORTRDY (NRF52_QDEC_BASE + NRF52_QDEC_EVENTS_REPORTRDY_OFFSET) +#define NRF52_QDEC_EVENTS_ACCOF (NRF52_QDEC_BASE + NRF52_QDEC_EVENTS_ACCOF_OFFSET) +#define NRF52_QDEC_EVENTS_DBLRDY (NRF52_QDEC_BASE + NRF52_QDEC_EVENTS_DBLRDY_OFFSET) +#define NRF52_QDEC_EVENTS_STOPPED (NRF52_QDEC_BASE + NRF52_QDEC_EVENTS_STOPPED_OFFSET) + +#define NRF52_QDEC_SHORTS (NRF52_QDEC_BASE + NRF52_QDEC_SHORTS_OFFSET) +#define NRF52_QDEC_INTENSET (NRF52_QDEC_BASE + NRF52_QDEC_INTENSET_OFFSET) +#define NRF52_QDEC_INTENCLR (NRF52_QDEC_BASE + NRF52_QDEC_INTENCLR_OFFSET) +#define NRF52_QDEC_ENABLE (NRF52_QDEC_BASE + NRF52_QDEC_ENABLE_OFFSET) +#define NRF52_QDEC_LEDPOL (NRF52_QDEC_BASE + NRF52_QDEC_LEDPOL_OFFSET) +#define NRF52_QDEC_SAMPLEPER (NRF52_QDEC_BASE + NRF52_QDEC_SAMPLEPER_OFFSET) +#define NRF52_QDEC_SAMPLE (NRF52_QDEC_BASE + NRF52_QDEC_SAMPLE_OFFSET) +#define NRF52_QDEC_REPORTPER (NRF52_QDEC_BASE + NRF52_QDEC_REPORTPER_OFFSET) +#define NRF52_QDEC_ACC (NRF52_QDEC_BASE + NRF52_QDEC_ACC_OFFSET) +#define NRF52_QDEC_ACCREAD (NRF52_QDEC_BASE + NRF52_QDEC_ACCREAD_OFFSET) +#define NRF52_QDEC_PSEL_LED (NRF52_QDEC_BASE + NRF52_QDEC_PSEL_LED_OFFSET) +#define NRF52_QDEC_PSEL_A (NRF52_QDEC_BASE + NRF52_QDEC_PSEL_A_OFFSET) +#define NRF52_QDEC_PSEL_B (NRF52_QDEC_BASE + NRF52_QDEC_PSEL_B_OFFSET) +#define NRF52_QDEC_DBFEN (NRF52_QDEC_BASE + NRF52_QDEC_DBFEN_OFFSET) +#define NRF52_QDEC_LEDPRE (NRF52_QDEC_BASE + NRF52_QDEC_LEDPRE_OFFSET) +#define NRF52_QDEC_ACCDBL (NRF52_QDEC_BASE + NRF52_QDEC_ACCDBL_OFFSET) +#define NRF52_QDEC_ACCDBLREAD (NRF52_QDEC_BASE + NRF52_QDEC_ACCDBLREAD_OFFSET) + +/* Register Bitfield Definitions ********************************************/ + +/* TASKS_START Register */ + +#define QDEC_TASKS_START (1 << 0) /* Bit 0: Start QDEC */ + +/* TASKS_STOP Register */ + +#define QDEC_TASKS_STOP (1 << 0) /* Bit 0: Stop QDEC */ + +/* TASKS_READCLRACC Register */ + +#define QDEC_TASKS_READCLRACC (1 << 0) /* Bit 0: Read and clear ACC and ACCDBL */ + +/* TASKS_RDCLRACC Register */ + +#define QDEC_TASKS_RDCLRACC (1 << 0) /* Bit 0: Read and clear ACC */ + +/* TASKS_RDCLRDBL Register */ + +#define QDEC_TASKS_RDCLRDBL (1 << 0) /* Bit 0: Read and clear ACCDBL */ + +/* EVENTS_SAMPLERDY Register */ + +#define QDEC_EVENTS_SAMPLERDY (1 << 0) /* Bit 0: Sample ready event */ + +/* EVENTS_REPORTRDY Register */ + +#define QDEC_EVENTS_REPORTRDY (1 << 0) /* Bit 0: Report ready event */ + +/* EVENTS_ACCOF Register */ + +#define QDEC_EVENTS_ACCOF (1 << 0) /* Bit 0: Accumulator overflow event */ + +/* EVENTS_DBLRDY Register */ + +#define QDEC_EVENTS_DBLRDY (1 << 0) /* Bit 0: Double displacement ready event */ + +/* EVENTS_STOPPED Register */ + +#define QDEC_EVENTS_STOPPED (1 << 0) /* Bit 0: QDEC stopped event */ + +/* SHORTS Register */ + +#define QDEC_SHORTS_REPORTRDY_READCLRACC (1 << 0) /* Bit 0: Shortcut between REPORTRDY event and READCLRACC task */ +#define QDEC_SHORTS_SAMPLERDY_STOP (1 << 1) /* Bit 1: Shortcut between SAMPLERDY event and STOP task */ +#define QDEC_SHORTS_REPORTRDY_RDCLRACC (1 << 2) /* Bit 2: Shortcut between REPORTRDY event and RDCLRACC task */ +#define QDEC_SHORTS_REPORTRDY_STOP (1 << 3) /* Bit 3: Shortcut between REPORTRDY event and STOP task */ +#define QDEC_SHORTS_DBLRDY_RDCLRDBL (1 << 4) /* Bit 4: Shortcut between DBLRDY event and RDCLRDBL task */ +#define QDEC_SHORTS_DBLRDY_STOP (1 << 5) /* Bit 5: Shortcut between DBLRDY event and STOP task */ +#define QDEC_SHORTS_SAMPLERDY_READCLRACC (1 << 6) /* Bit 6: Shortcut between SAMPLERDY event and READCLRACC task */ + +/* INTENSET/INTENCLR Register */ + +#define QDEC_INT_SAMPLERDY (1 << 0) /* Bit 0: Interrupt for SAMPLERDY event */ +#define QDEC_INT_REPORTRDY (1 << 1) /* Bit 1: Interrupt for REPORTRDY event */ +#define QDEC_INT_ACCOF (1 << 2) /* Bit 2: Interrupt for ACCOF event */ +#define QDEC_INT_DBLRDY (1 << 3) /* Bit 3: Interrupt for DBLRDY event */ +#define QDEC_INT_STOPPED (1 << 4) /* Bit 4: Interrupt for STOPPED event */ + +/* ENABLE Register */ + +#define QDEC_ENABLE_DISABLE (0 << 0) /* Bit 0: Disable QDEC */ +#define QDEC_ENABLE_ENABLE (1 << 0) /* Bit 0: Enable QDEC */ + +/* LEDPOL Register */ + +#define QDEC_LEDPOL_ACTIVELOW (0 << 0) /* Bit 0: LED active on output pin low */ +#define QDEC_LEDPOL_ACTIVEHIGH (1 << 0) /* Bit 0: LED active on output pin high */ + +/* SAMPLEPER Register */ + +#define QDEC_SAMPLEPER_SHIFT (0) /* Bits 0-3: Sample period */ +#define QDEC_SAMPLEPER_MASK (0xf << QDEC_SAMPLEPER_SHIFT) +# define QDEC_SAMPLEPER_128US (0 << QDEC_SAMPLEPER_SHIFT) /* 128 us */ +# define QDEC_SAMPLEPER_256US (1 << QDEC_SAMPLEPER_SHIFT) /* 256 us */ +# define QDEC_SAMPLEPER_512US (2 << QDEC_SAMPLEPER_SHIFT) /* 512 us */ +# define QDEC_SAMPLEPER_1024US (3 << QDEC_SAMPLEPER_SHIFT) /* 1024 us */ +# define QDEC_SAMPLEPER_2048US (4 << QDEC_SAMPLEPER_SHIFT) /* 2048 us */ +# define QDEC_SAMPLEPER_4096US (5 << QDEC_SAMPLEPER_SHIFT) /* 4096 us */ +# define QDEC_SAMPLEPER_8192US (6 << QDEC_SAMPLEPER_SHIFT) /* 8192 us */ +# define QDEC_SAMPLEPER_16384US (7 << QDEC_SAMPLEPER_SHIFT) /* 16384 us */ +# define QDEC_SAMPLEPER_32MS (8 << QDEC_SAMPLEPER_SHIFT) /* 32768 us */ +# define QDEC_SAMPLEPER_65MS (9 << QDEC_SAMPLEPER_SHIFT) /* 65536 us */ +# define QDEC_SAMPLEPER_131MS (10 << QDEC_SAMPLEPER_SHIFT) /* 131072 us */ + +/* SAMPLE Register */ + +#define QDEC_SAMPLE_MASK (0xffffffff) + +/* REPORTPER Register */ + +#define QDEC_REPORTPER_SHIFT (0) /* Bits 0-3: Report period */ +#define QDEC_REPORTPER_MASK (0xf << QDEC_REPORTPER_SHIFT) +# define QDEC_REPORTPER_10SMPL (0 << QDEC_REPORTPER_SHIFT) /* 10 samples */ +# define QDEC_REPORTPER_40SMPL (1 << QDEC_REPORTPER_SHIFT) /* 40 samples */ +# define QDEC_REPORTPER_80SMPL (2 << QDEC_REPORTPER_SHIFT) /* 80 samples */ +# define QDEC_REPORTPER_120SMPL (3 << QDEC_REPORTPER_SHIFT) /* 120 samples */ +# define QDEC_REPORTPER_160SMPL (4 << QDEC_REPORTPER_SHIFT) /* 160 samples */ +# define QDEC_REPORTPER_200SMPL (5 << QDEC_REPORTPER_SHIFT) /* 200 samples */ +# define QDEC_REPORTPER_240SMPL (6 << QDEC_REPORTPER_SHIFT) /* 240 samples */ +# define QDEC_REPORTPER_280SMPL (7 << QDEC_REPORTPER_SHIFT) /* 280 samples */ +# define QDEC_REPORTPER_DISABLED (8 << QDEC_REPORTPER_SHIFT) /* Disabled */ + +/* ACC Register */ + +#define QDEC_ACC_MASK (0xffffffff) + +/* ACCREAD Register */ + +#define QDEC_ACCREAD_MASK (0xffffffff) + +/* PSEL Registers */ + +#define QDEC_PSEL_PIN_SHIFT (0) /* Bits 0-4: Pin number */ +#define QDEC_PSEL_PIN_MASK (0x1f << QDEC_PSEL_PIN_SHIFT) +#define QDEC_PSEL_PORT_SHIFT (5) /* Bit 5: Port number */ +#define QDEC_PSEL_PORT_MASK (0x1 << QDEC_PSEL_PORT_SHIFT) +#define QDEC_PSEL_CONNECT_SHIFT (31) /* Bit 31: Connection */ +#define QDEC_PSEL_CONNECT_MASK (0x1 << QDEC_PSEL_CONNECT_SHIFT) +# define QDEC_PSEL_CONNECTED (0 << QDEC_PSEL_CONNECT_SHIFT) +# define QDEC_PSEL_DISCONNECTED (1 << QDEC_PSEL_CONNECT_SHIFT) + +/* DBFEN Register */ + +#define QDEC_DBFEN_DISABLE (0 << 0) /* Bit 0: Debounce input filters disabled */ +#define QDEC_DBFEN_ENABLE (1 << 0) /* Bit 0: Debounce input filters enabled */ + +/* LEDPRE Register */ + +#define QDEC_LEDPRE_SHIFT (0) /* Bits 0-8: LED pre time */ +#define QDEC_LEDPRE_MASK (0x1ff << QDEC_LEDPRE_SHIFT) + +/* ACCDBL Register */ + +#define QDEC_ACCDBL_MASK (0xf) + +/* ACCDBLREAD Register */ + +#define QDEC_ACCDBLREAD_MASK (0xf) + +#endif /* __ARCH_ARM_SRC_NRF52_HARDWARE_NRF52_QDEC_H */ diff --git a/arch/arm/src/nrf52/nrf52_qdec.c b/arch/arm/src/nrf52/nrf52_qdec.c new file mode 100644 index 00000000000..128f978f99e --- /dev/null +++ b/arch/arm/src/nrf52/nrf52_qdec.c @@ -0,0 +1,445 @@ +/**************************************************************************** + * arch/arm/src/nrf52/nrf52_qdec.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <nuttx/config.h> + +#include <stdint.h> +#include <assert.h> +#include <errno.h> +#include <debug.h> + +#include <nuttx/arch.h> +#include <nuttx/spinlock.h> +#include <nuttx/sensors/qencoder.h> + +#include <arch/board/board.h> + +#include "arm_internal.h" +#include "nrf52_gpio.h" +#include "nrf52_qdec.h" + +#include "hardware/nrf52_qdec.h" +#include "hardware/nrf52_utils.h" + +#ifdef CONFIG_NRF52_QENCODER_INDEX +# include <nuttx/irq.h> +# include "nrf52_gpiote.h" +#endif + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct nrf52_qdec_s +{ + const struct qe_ops_s *ops; + const struct nrf52_qeconfig_s *config; + uint32_t base; + uint32_t pin_a; + uint32_t pin_b; + int32_t position; + uint32_t posmax; + bool inuse; + bool posmax_enabled; +#ifdef CONFIG_NRF52_QENCODER_INDEX + uint16_t index_count; + uint32_t pin_index; + uint32_t index_pos; +#endif + spinlock_t lock; +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static inline void nrf52_qdec_putreg(struct nrf52_qdec_s *priv, + uint32_t offset, uint32_t value); +static inline uint32_t nrf52_qdec_getreg(struct nrf52_qdec_s *priv, + uint32_t offset); + +static int nrf52_qdec_setup(struct qe_lowerhalf_s *lower); +static int nrf52_qdec_shutdown(struct qe_lowerhalf_s *lower); +static int nrf52_qdec_position(struct qe_lowerhalf_s *lower, int32_t *pos); +static int nrf52_qdec_setposmax(struct qe_lowerhalf_s *lower, uint32_t pos); +static int nrf52_qdec_reset(struct qe_lowerhalf_s *lower); +static int nrf52_qdec_setindex(struct qe_lowerhalf_s *lower, uint32_t pos); +static int nrf52_qdec_ioctl(struct qe_lowerhalf_s *lower, int cmd, + unsigned long arg); + +#ifdef CONFIG_NRF52_QENCODER_INDEX +static int nrf52_qdec_index_interrupt(int irq, void *context, void *arg); +#endif + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static const struct qe_ops_s g_nrf52_qdecops = +{ + .setup = nrf52_qdec_setup, + .shutdown = nrf52_qdec_shutdown, + .position = nrf52_qdec_position, + .setposmax = nrf52_qdec_setposmax, + .reset = nrf52_qdec_reset, + .setindex = nrf52_qdec_setindex, + .ioctl = nrf52_qdec_ioctl, +}; + +#ifdef CONFIG_NRF52_QDEC0 +struct nrf52_qdec_s g_nrf52_qdec0 = +{ + .ops = &g_nrf52_qdecops, + .base = NRF52_QDEC_BASE, + .pin_a = BOARD_QDEC0_A_PIN, + .pin_b = BOARD_QDEC0_B_PIN, +#ifdef CONFIG_NRF52_QENCODER_INDEX + .pin_index = BOARD_QDEC0_INDEX_PIN, +#endif +}; +#endif + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: nrf52_qdec_putreg + ****************************************************************************/ + +static inline void nrf52_qdec_putreg(struct nrf52_qdec_s *priv, + uint32_t offset, uint32_t value) +{ + putreg32(value, priv->base + offset); +} + +/**************************************************************************** + * Name: nrf52_qdec_getreg + ****************************************************************************/ + +static inline uint32_t nrf52_qdec_getreg(struct nrf52_qdec_s *priv, + uint32_t offset) +{ + return getreg32(priv->base + offset); +} + +/**************************************************************************** + * Name: nrf52_qdec_setup + ****************************************************************************/ + +static int nrf52_qdec_setup(struct qe_lowerhalf_s *lower) +{ + struct nrf52_qdec_s *priv = (struct nrf52_qdec_s *)lower; + uint32_t pin; + uint32_t port; + uint32_t regval; + irqstate_t flags; + int ret; + + flags = spin_lock_irqsave(&priv->lock); + + if (priv->inuse) + { + ret = -EBUSY; + goto errout; + } + + nrf52_gpio_config(priv->pin_a); + nrf52_gpio_config(priv->pin_b); + + /* Configure PSEL A */ + + pin = GPIO_PIN_DECODE(priv->pin_a); + port = GPIO_PORT_DECODE(priv->pin_a); + regval = (port << QDEC_PSEL_PORT_SHIFT); + regval |= (pin << QDEC_PSEL_PIN_SHIFT); + nrf52_qdec_putreg(priv, NRF52_QDEC_PSEL_A_OFFSET, regval); + + /* Configure PSEL B */ + + pin = GPIO_PIN_DECODE(priv->pin_b); + port = GPIO_PORT_DECODE(priv->pin_b); + regval = (port << QDEC_PSEL_PORT_SHIFT); + regval |= (pin << QDEC_PSEL_PIN_SHIFT); + nrf52_qdec_putreg(priv, NRF52_QDEC_PSEL_B_OFFSET, regval); + + /* Disable LED */ + + nrf52_qdec_putreg(priv, NRF52_QDEC_PSEL_LED_OFFSET, + QDEC_PSEL_DISCONNECTED); + + /* Configure decoder */ + + nrf52_qdec_putreg(priv, NRF52_QDEC_SAMPLEPER_OFFSET, + priv->config->sample_period & QDEC_SAMPLEPER_MASK); + nrf52_qdec_putreg(priv, NRF52_QDEC_REPORTPER_OFFSET, + priv->config->report_period & QDEC_REPORTPER_MASK); + + nrf52_qdec_putreg(priv, NRF52_QDEC_DBFEN_OFFSET, + priv->config->enable_debounce ? QDEC_DBFEN_ENABLE : + QDEC_DBFEN_DISABLE); + + /* Start decoder */ + + nrf52_qdec_putreg(priv, NRF52_QDEC_ENABLE_OFFSET, QDEC_ENABLE_ENABLE); + nrf52_qdec_putreg(priv, NRF52_QDEC_TASKS_START_OFFSET, QDEC_TASKS_START); + +#ifdef CONFIG_NRF52_QENCODER_INDEX + /* Configure Index pin */ + + nrf52_gpio_config(priv->pin_index); + ret = nrf52_gpiote_set_event(priv->pin_index, true, false, + nrf52_qdec_index_interrupt, priv); + if (ret < 0) + { + snerr("ERROR: Failed to configure index pin interrupt: %d\n", ret); + goto errout; + } +#endif + + priv->inuse = true; + priv->position = 0; + priv->posmax_enabled = false; +#ifdef CONFIG_NRF52_QENCODER_INDEX + priv->index_pos = 0; + priv->index_count = 0; +#endif + ret = OK; + +errout: + spin_unlock_irqrestore(&priv->lock, flags); + return ret; +} + +/**************************************************************************** + * Name: nrf52_qdec_shutdown + ****************************************************************************/ + +static int nrf52_qdec_shutdown(struct qe_lowerhalf_s *lower) +{ + struct nrf52_qdec_s *priv = (struct nrf52_qdec_s *)lower; + irqstate_t flags; + + flags = spin_lock_irqsave(&priv->lock); + + nrf52_qdec_putreg(priv, NRF52_QDEC_TASKS_STOP_OFFSET, QDEC_TASKS_STOP); + nrf52_qdec_putreg(priv, NRF52_QDEC_ENABLE_OFFSET, QDEC_ENABLE_DISABLE); + + nrf52_gpio_unconfig(priv->pin_a); + nrf52_gpio_unconfig(priv->pin_b); + +#ifdef CONFIG_NRF52_QENCODER_INDEX + nrf52_gpiote_set_event(priv->pin_index, false, false, NULL, NULL); + nrf52_gpio_unconfig(priv->pin_index); +#endif + + priv->inuse = false; + + spin_unlock_irqrestore(&priv->lock, flags); + return OK; +} + +/**************************************************************************** + * Name: nrf52_qdec_position + ****************************************************************************/ + +static int nrf52_qdec_position(struct qe_lowerhalf_s *lower, int32_t *pos) +{ + struct nrf52_qdec_s *priv = (struct nrf52_qdec_s *)lower; + irqstate_t flags; + int32_t accread; + + DEBUGASSERT(lower && pos); + + flags = spin_lock_irqsave(&priv->lock); + + nrf52_qdec_putreg(priv, NRF52_QDEC_TASKS_RDCLRACC_OFFSET, + QDEC_TASKS_RDCLRACC); + + accread = (int32_t)nrf52_qdec_getreg(priv, NRF52_QDEC_ACCREAD_OFFSET); + + priv->position += accread; + + if (priv->posmax_enabled && priv->posmax > 0) + { + priv->position = priv->position % (int32_t)priv->posmax; + if (priv->position < 0) + { + priv->position += (int32_t)priv->posmax; + } + } + + *pos = priv->position; + + spin_unlock_irqrestore(&priv->lock, flags); + return OK; +} + +/**************************************************************************** + * Name: nrf52_qdec_setposmax + ****************************************************************************/ + +static int nrf52_qdec_setposmax(struct qe_lowerhalf_s *lower, uint32_t pos) +{ + struct nrf52_qdec_s *priv = (struct nrf52_qdec_s *)lower; + irqstate_t flags; + + flags = spin_lock_irqsave(&priv->lock); + + priv->posmax = pos; + priv->posmax_enabled = (pos > 0); + + spin_unlock_irqrestore(&priv->lock, flags); + return OK; +} + +/**************************************************************************** + * Name: nrf52_qdec_reset + ****************************************************************************/ + +static int nrf52_qdec_reset(struct qe_lowerhalf_s *lower) +{ + struct nrf52_qdec_s *priv = (struct nrf52_qdec_s *)lower; + irqstate_t flags; + + flags = spin_lock_irqsave(&priv->lock); + + nrf52_qdec_putreg(priv, NRF52_QDEC_TASKS_READCLRACC_OFFSET, + QDEC_TASKS_READCLRACC); + + priv->position = 0; + + spin_unlock_irqrestore(&priv->lock, flags); + return OK; +} + +/**************************************************************************** + * Name: nrf52_qdec_setindex + ****************************************************************************/ + +static int nrf52_qdec_setindex(struct qe_lowerhalf_s *lower, uint32_t pos) +{ +#ifdef CONFIG_NRF52_QENCODER_INDEX + struct nrf52_qdec_s *priv = (struct nrf52_qdec_s *)lower; + irqstate_t flags; + + flags = spin_lock_irqsave(&priv->lock); + priv->index_pos = pos; + spin_unlock_irqrestore(&priv->lock, flags); + + return OK; +#else + return -ENOTTY; +#endif +} + +#ifdef CONFIG_NRF52_QENCODER_INDEX + +/**************************************************************************** + * Name: nrf52_qdec_index_interrupt + ****************************************************************************/ + +static int nrf52_qdec_index_interrupt(int irq, void *context, void *arg) +{ + struct nrf52_qdec_s *priv = (struct nrf52_qdec_s *)arg; + irqstate_t flags; + + flags = spin_lock_irqsave(&priv->lock); + + priv->position = priv->index_pos; + priv->index_count++; + + spin_unlock_irqrestore(&priv->lock, flags); + + return OK; +} + +#endif + +/**************************************************************************** + * Name: nrf52_qdec_ioctl + ****************************************************************************/ + +static int nrf52_qdec_ioctl(struct qe_lowerhalf_s *lower, int cmd, + unsigned long arg) +{ +#ifdef CONFIG_NRF52_QENCODER_INDEX + struct nrf52_qdec_s *priv = (struct nrf52_qdec_s *)lower; + struct qe_index_s *index; + + if (cmd == QEIOC_GETINDEX) + { + index = (struct qe_index_s *)arg; + index->qenc_pos = priv->position; + index->indx_pos = priv->index_pos; + index->indx_cnt = priv->index_count; + return OK; + } +#endif + + return -ENOTTY; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: nrf52_qeinitialize + ****************************************************************************/ + +struct qe_lowerhalf_s * +nrf52_qeinitialize(int qdec, const struct nrf52_qeconfig_s *config) +{ + struct nrf52_qdec_s *priv = NULL; + + DEBUGASSERT(config != NULL); + + switch (qdec) + { +#ifdef CONFIG_NRF52_QDEC0 + case 0: + { + priv = &g_nrf52_qdec0; + break; + } +#endif + + default: + { + snerr("ERROR: Invalid QDEC instance: %d\n", qdec); + return NULL; + } + } + + priv->config = config; + + return (struct qe_lowerhalf_s *)priv; +} diff --git a/arch/arm/src/nrf52/nrf52_qdec.h b/arch/arm/src/nrf52/nrf52_qdec.h new file mode 100644 index 00000000000..3dce1dabdc7 --- /dev/null +++ b/arch/arm/src/nrf52/nrf52_qdec.h @@ -0,0 +1,52 @@ +/**************************************************************************** + * arch/arm/src/nrf52/nrf52_qdec.h + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +#ifndef __ARCH_ARM_SRC_NRF52_NRF52_QENCODER_H +#define __ARCH_ARM_SRC_NRF52_NRF52_QENCODER_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <nuttx/config.h> +#include <stdint.h> + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +struct nrf52_qeconfig_s +{ + uint8_t sample_period; + uint8_t report_period; + bool enable_debounce; +}; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +struct qe_lowerhalf_s; +struct qe_lowerhalf_s * +nrf52_qeinitialize(int qdec, const struct nrf52_qeconfig_s *config); + +#endif /* __ARCH_ARM_SRC_NRF52_NRF52_QENCODER_H */ diff --git a/arch/arm/src/nrf53/CMakeLists.txt b/arch/arm/src/nrf53/CMakeLists.txt index 900c52430c1..49d99a0548f 100644 --- a/arch/arm/src/nrf53/CMakeLists.txt +++ b/arch/arm/src/nrf53/CMakeLists.txt @@ -107,6 +107,14 @@ if(CONFIG_NRF53_QSPI) list(APPEND SRCS nrf53_qspi.c) endif() +if(CONFIG_NRF53_QDEC) + list(APPEND SRCS nrf53_qdec.c) +endif() + +if(CONFIG_SENSORS_QENCODER) + list(APPEND SRCS nrf53_qdec.c) +endif() + if(CONFIG_NRF53_SOFTDEVICE_CONTROLLER) set(NRFXLIB_VER "2.3.0") diff --git a/arch/arm/src/nrf53/Kconfig b/arch/arm/src/nrf53/Kconfig index a629b2d5a23..b951cd9bde1 100644 --- a/arch/arm/src/nrf53/Kconfig +++ b/arch/arm/src/nrf53/Kconfig @@ -182,6 +182,10 @@ config NRF53_RTC bool default n +config NRF53_QDEC + bool + default n + menu "nRF53 Peripheral Selection" config NRF53_GPIOTE @@ -714,6 +718,30 @@ endif # NRF53_QSPI endmenu # QSPI Configuration +config NRF53_QDEC0 + bool "Qencoder 0 (QDEC0) support" + default n + select NRF53_QDEC + ---help--- + Enable Qencoder 0 support. + +config NRF53_QDEC1 + bool "Qencoder 1 (QDEC1) support" + default n + select NRF53_QDEC + ---help--- + Enable Qencoder 1 support. + +config NRF53_QENCODER_INDEX + bool "Enable QDEC index pin support" + default n + depends on NRF53_GPIOTE && NRF53_QDEC + ---help--- + Enable index/Z channel support for quadrature encoder. + Uses a GPIO interrupt to detect index pulses and reset + position to configured index value. Requires one GPIOTE + channel for interrupt detection. + menuconfig NRF53_SOFTDEVICE_CONTROLLER bool "SoftDevice Controller" depends on ALLOW_BSDNORDIC_COMPONENTS diff --git a/arch/arm/src/nrf53/Make.defs b/arch/arm/src/nrf53/Make.defs index d91f55b12e6..9a0a63827c3 100644 --- a/arch/arm/src/nrf53/Make.defs +++ b/arch/arm/src/nrf53/Make.defs @@ -101,6 +101,10 @@ ifeq ($(CONFIG_NRF53_QSPI),y) CHIP_CSRCS += nrf53_qspi.c endif +ifeq ($(CONFIG_NRF53_QDEC),y) +CHIP_CSRCS += nrf53_qdec.c +endif + ifeq ($(CONFIG_NRF53_SOFTDEVICE_CONTROLLER),y) NRFXLIB_UNPACK := sdk-nrfxlib diff --git a/arch/arm/src/nrf53/hardware/nrf53_qdec.h b/arch/arm/src/nrf53/hardware/nrf53_qdec.h new file mode 100644 index 00000000000..a2d45933b87 --- /dev/null +++ b/arch/arm/src/nrf53/hardware/nrf53_qdec.h @@ -0,0 +1,215 @@ +/**************************************************************************** + * arch/arm/src/nrf53/hardware/nrf53_qdec.h + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +#ifndef __ARCH_ARM_SRC_NRF53_HARDWARE_NRF53_QDEC_H +#define __ARCH_ARM_SRC_NRF53_HARDWARE_NRF53_QDEC_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <nuttx/config.h> +#include "hardware/nrf53_memorymap_cpuapp.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* QDEC Base Addresses */ + +#define NRF53_QDEC0_BASE NRF53_QDEC_BASE /* QDEC0: 0x50033000 */ +#define NRF53_QDEC1_BASE 0x50034000 /* QDEC1: 0x50034000 */ + +/* Register offsets *********************************************************/ + +#define NRF53_QDEC_TASKS_START_OFFSET 0x0000 /* Task starting the quadrature decoder */ +#define NRF53_QDEC_TASKS_STOP_OFFSET 0x0004 /* Task stopping the quadrature decoder */ +#define NRF53_QDEC_TASKS_READCLRACC_OFFSET 0x0008 /* Read and clear ACC and ACCDBL */ +#define NRF53_QDEC_TASKS_RDCLRACC_OFFSET 0x000c /* Read and clear ACC */ +#define NRF53_QDEC_TASKS_RDCLRDBL_OFFSET 0x0010 /* Read and clear ACCDBL */ + +#define NRF53_QDEC_EVENTS_SAMPLERDY_OFFSET 0x0100 /* Event being generated for every new sample value */ +#define NRF53_QDEC_EVENTS_REPORTRDY_OFFSET 0x0104 /* Non-null report ready */ +#define NRF53_QDEC_EVENTS_ACCOF_OFFSET 0x0108 /* ACC or ACCDBL register overflow */ +#define NRF53_QDEC_EVENTS_DBLRDY_OFFSET 0x010c /* Double displacement(s) detected */ +#define NRF53_QDEC_EVENTS_STOPPED_OFFSET 0x0110 /* QDEC has been stopped */ + +#define NRF53_QDEC_SHORTS_OFFSET 0x0200 /* Shortcut register */ +#define NRF53_QDEC_INTENSET_OFFSET 0x0304 /* Enable interrupt */ +#define NRF53_QDEC_INTENCLR_OFFSET 0x0308 /* Disable interrupt */ +#define NRF53_QDEC_ENABLE_OFFSET 0x0500 /* Enable the quadrature decoder */ +#define NRF53_QDEC_LEDPOL_OFFSET 0x0504 /* LED output pin polarity */ +#define NRF53_QDEC_SAMPLEPER_OFFSET 0x0508 /* Sample period */ +#define NRF53_QDEC_SAMPLE_OFFSET 0x050c /* Motion sample value */ +#define NRF53_QDEC_REPORTPER_OFFSET 0x0510 /* Number of samples to be taken before REPORTRDY and DBLRDY events */ +#define NRF53_QDEC_ACC_OFFSET 0x0514 /* Register accumulating the valid transitions */ +#define NRF53_QDEC_ACCREAD_OFFSET 0x0518 /* Snapshot of the ACC register */ +#define NRF53_QDEC_PSEL_LED_OFFSET 0x051c /* Pin select for LED signal */ +#define NRF53_QDEC_PSEL_A_OFFSET 0x0520 /* Pin select for A signal */ +#define NRF53_QDEC_PSEL_B_OFFSET 0x0524 /* Pin select for B signal */ +#define NRF53_QDEC_DBFEN_OFFSET 0x0528 /* Enable input debounce filters */ +#define NRF53_QDEC_LEDPRE_OFFSET 0x0540 /* Time period the LED is switched ON prior to sampling */ +#define NRF53_QDEC_ACCDBL_OFFSET 0x0544 /* Register accumulating the number of detected double transitions */ +#define NRF53_QDEC_ACCDBLREAD_OFFSET 0x0548 /* Snapshot of the ACCDBL register */ + +/* Register Bitfield Definitions ********************************************/ + +/* TASKS_START Register */ + +#define QDEC_TASKS_START (1 << 0) /* Bit 0: Start QDEC */ + +/* TASKS_STOP Register */ + +#define QDEC_TASKS_STOP (1 << 0) /* Bit 0: Stop QDEC */ + +/* TASKS_READCLRACC Register */ + +#define QDEC_TASKS_READCLRACC (1 << 0) /* Bit 0: Read and clear ACC and ACCDBL */ + +/* TASKS_RDCLRACC Register */ + +#define QDEC_TASKS_RDCLRACC (1 << 0) /* Bit 0: Read and clear ACC */ + +/* TASKS_RDCLRDBL Register */ + +#define QDEC_TASKS_RDCLRDBL (1 << 0) /* Bit 0: Read and clear ACCDBL */ + +/* EVENTS_SAMPLERDY Register */ + +#define QDEC_EVENTS_SAMPLERDY (1 << 0) /* Bit 0: Sample ready event */ + +/* EVENTS_REPORTRDY Register */ + +#define QDEC_EVENTS_REPORTRDY (1 << 0) /* Bit 0: Report ready event */ + +/* EVENTS_ACCOF Register */ + +#define QDEC_EVENTS_ACCOF (1 << 0) /* Bit 0: Accumulator overflow event */ + +/* EVENTS_DBLRDY Register */ + +#define QDEC_EVENTS_DBLRDY (1 << 0) /* Bit 0: Double displacement ready event */ + +/* EVENTS_STOPPED Register */ + +#define QDEC_EVENTS_STOPPED (1 << 0) /* Bit 0: QDEC stopped event */ + +/* SHORTS Register */ + +#define QDEC_SHORTS_REPORTRDY_READCLRACC (1 << 0) /* Bit 0: Shortcut between REPORTRDY event and READCLRACC task */ +#define QDEC_SHORTS_SAMPLERDY_STOP (1 << 1) /* Bit 1: Shortcut between SAMPLERDY event and STOP task */ +#define QDEC_SHORTS_REPORTRDY_RDCLRACC (1 << 2) /* Bit 2: Shortcut between REPORTRDY event and RDCLRACC task */ +#define QDEC_SHORTS_REPORTRDY_STOP (1 << 3) /* Bit 3: Shortcut between REPORTRDY event and STOP task */ +#define QDEC_SHORTS_DBLRDY_RDCLRDBL (1 << 4) /* Bit 4: Shortcut between DBLRDY event and RDCLRDBL task */ +#define QDEC_SHORTS_DBLRDY_STOP (1 << 5) /* Bit 5: Shortcut between DBLRDY event and STOP task */ +#define QDEC_SHORTS_SAMPLERDY_READCLRACC (1 << 6) /* Bit 6: Shortcut between SAMPLERDY event and READCLRACC task */ + +/* INTENSET/INTENCLR Register */ + +#define QDEC_INT_SAMPLERDY (1 << 0) /* Bit 0: Interrupt for SAMPLERDY event */ +#define QDEC_INT_REPORTRDY (1 << 1) /* Bit 1: Interrupt for REPORTRDY event */ +#define QDEC_INT_ACCOF (1 << 2) /* Bit 2: Interrupt for ACCOF event */ +#define QDEC_INT_DBLRDY (1 << 3) /* Bit 3: Interrupt for DBLRDY event */ +#define QDEC_INT_STOPPED (1 << 4) /* Bit 4: Interrupt for STOPPED event */ + +/* ENABLE Register */ + +#define QDEC_ENABLE_DISABLE (0 << 0) /* Bit 0: Disable QDEC */ +#define QDEC_ENABLE_ENABLE (1 << 0) /* Bit 0: Enable QDEC */ + +/* LEDPOL Register */ + +#define QDEC_LEDPOL_ACTIVELOW (0 << 0) /* Bit 0: LED active on output pin low */ +#define QDEC_LEDPOL_ACTIVEHIGH (1 << 0) /* Bit 0: LED active on output pin high */ + +/* SAMPLEPER Register */ + +#define QDEC_SAMPLEPER_SHIFT (0) /* Bits 0-3: Sample period */ +#define QDEC_SAMPLEPER_MASK (0xf << QDEC_SAMPLEPER_SHIFT) +# define QDEC_SAMPLEPER_128US (0 << QDEC_SAMPLEPER_SHIFT) /* 128 us */ +# define QDEC_SAMPLEPER_256US (1 << QDEC_SAMPLEPER_SHIFT) /* 256 us */ +# define QDEC_SAMPLEPER_512US (2 << QDEC_SAMPLEPER_SHIFT) /* 512 us */ +# define QDEC_SAMPLEPER_1024US (3 << QDEC_SAMPLEPER_SHIFT) /* 1024 us */ +# define QDEC_SAMPLEPER_2048US (4 << QDEC_SAMPLEPER_SHIFT) /* 2048 us */ +# define QDEC_SAMPLEPER_4096US (5 << QDEC_SAMPLEPER_SHIFT) /* 4096 us */ +# define QDEC_SAMPLEPER_8192US (6 << QDEC_SAMPLEPER_SHIFT) /* 8192 us */ +# define QDEC_SAMPLEPER_16384US (7 << QDEC_SAMPLEPER_SHIFT) /* 16384 us */ +# define QDEC_SAMPLEPER_32MS (8 << QDEC_SAMPLEPER_SHIFT) /* 32768 us */ +# define QDEC_SAMPLEPER_65MS (9 << QDEC_SAMPLEPER_SHIFT) /* 65536 us */ +# define QDEC_SAMPLEPER_131MS (10 << QDEC_SAMPLEPER_SHIFT) /* 131072 us */ + +/* SAMPLE Register */ + +#define QDEC_SAMPLE_MASK (0xffffffff) + +/* REPORTPER Register */ + +#define QDEC_REPORTPER_SHIFT (0) /* Bits 0-3: Report period */ +#define QDEC_REPORTPER_MASK (0xf << QDEC_REPORTPER_SHIFT) +# define QDEC_REPORTPER_10SMPL (0 << QDEC_REPORTPER_SHIFT) /* 10 samples */ +# define QDEC_REPORTPER_40SMPL (1 << QDEC_REPORTPER_SHIFT) /* 40 samples */ +# define QDEC_REPORTPER_80SMPL (2 << QDEC_REPORTPER_SHIFT) /* 80 samples */ +# define QDEC_REPORTPER_120SMPL (3 << QDEC_REPORTPER_SHIFT) /* 120 samples */ +# define QDEC_REPORTPER_160SMPL (4 << QDEC_REPORTPER_SHIFT) /* 160 samples */ +# define QDEC_REPORTPER_200SMPL (5 << QDEC_REPORTPER_SHIFT) /* 200 samples */ +# define QDEC_REPORTPER_240SMPL (6 << QDEC_REPORTPER_SHIFT) /* 240 samples */ +# define QDEC_REPORTPER_280SMPL (7 << QDEC_REPORTPER_SHIFT) /* 280 samples */ +# define QDEC_REPORTPER_DISABLED (8 << QDEC_REPORTPER_SHIFT) /* Disabled */ + +/* ACC Register */ + +#define QDEC_ACC_MASK (0xffffffff) + +/* ACCREAD Register */ + +#define QDEC_ACCREAD_MASK (0xffffffff) + +/* PSEL Registers */ + +#define QDEC_PSEL_PIN_SHIFT (0) /* Bits 0-4: Pin number */ +#define QDEC_PSEL_PIN_MASK (0x1f << QDEC_PSEL_PIN_SHIFT) +#define QDEC_PSEL_PORT_SHIFT (5) /* Bit 5: Port number */ +#define QDEC_PSEL_PORT_MASK (0x1 << QDEC_PSEL_PORT_SHIFT) +#define QDEC_PSEL_CONNECT_SHIFT (31) /* Bit 31: Connection */ +#define QDEC_PSEL_CONNECT_MASK (0x1 << QDEC_PSEL_CONNECT_SHIFT) +# define QDEC_PSEL_CONNECTED (0 << QDEC_PSEL_CONNECT_SHIFT) +# define QDEC_PSEL_DISCONNECTED (1 << QDEC_PSEL_CONNECT_SHIFT) + +/* DBFEN Register */ + +#define QDEC_DBFEN_DISABLE (0 << 0) /* Bit 0: Debounce input filters disabled */ +#define QDEC_DBFEN_ENABLE (1 << 0) /* Bit 0: Debounce input filters enabled */ + +/* LEDPRE Register */ + +#define QDEC_LEDPRE_SHIFT (0) /* Bits 0-8: LED pre time */ +#define QDEC_LEDPRE_MASK (0x1ff << QDEC_LEDPRE_SHIFT) + +/* ACCDBL Register */ + +#define QDEC_ACCDBL_MASK (0xf) + +/* ACCDBLREAD Register */ + +#define QDEC_ACCDBLREAD_MASK (0xf) + +#endif /* __ARCH_ARM_SRC_NRF53_HARDWARE_NRF53_QDEC_H */ diff --git a/arch/arm/src/nrf53/nrf53_qdec.c b/arch/arm/src/nrf53/nrf53_qdec.c new file mode 100644 index 00000000000..91c6e3fc43d --- /dev/null +++ b/arch/arm/src/nrf53/nrf53_qdec.c @@ -0,0 +1,464 @@ +/**************************************************************************** + * arch/arm/src/nrf53/nrf53_qdec.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <nuttx/config.h> + +#include <stdint.h> +#include <assert.h> +#include <errno.h> +#include <debug.h> + +#include <nuttx/arch.h> +#include <nuttx/spinlock.h> +#include <nuttx/sensors/qencoder.h> + +#include <arch/board/board.h> + +#include "arm_internal.h" +#include "nrf53_gpio.h" +#include "nrf53_qdec.h" + +#include "hardware/nrf53_qdec.h" +#include "hardware/nrf53_utils.h" + +#ifdef CONFIG_NRF53_QENCODER_INDEX +# include <nuttx/irq.h> +# include "nrf53_gpiote.h" +#endif + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct nrf53_qdec_s +{ + const struct qe_ops_s *ops; + const struct nrf53_qeconfig_s *config; + uint32_t base; + uint32_t pin_a; + uint32_t pin_b; + int32_t position; + uint32_t posmax; + bool inuse; + bool posmax_enabled; +#ifdef CONFIG_NRF53_QENCODER_INDEX + uint16_t index_count; + uint32_t pin_index; + uint32_t index_pos; +#endif + spinlock_t lock; +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static inline void nrf53_qdec_putreg(struct nrf53_qdec_s *priv, + uint32_t offset, uint32_t value); +static inline uint32_t nrf53_qdec_getreg(struct nrf53_qdec_s *priv, + uint32_t offset); + +static int nrf53_qdec_setup(struct qe_lowerhalf_s *lower); +static int nrf53_qdec_shutdown(struct qe_lowerhalf_s *lower); +static int nrf53_qdec_position(struct qe_lowerhalf_s *lower, int32_t *pos); +static int nrf53_qdec_setposmax(struct qe_lowerhalf_s *lower, uint32_t pos); +static int nrf53_qdec_reset(struct qe_lowerhalf_s *lower); +static int nrf53_qdec_setindex(struct qe_lowerhalf_s *lower, uint32_t pos); +static int nrf53_qdec_ioctl(struct qe_lowerhalf_s *lower, int cmd, + unsigned long arg); + +#ifdef CONFIG_NRF53_QENCODER_INDEX +static int nrf53_qdec_index_interrupt(int irq, void *context, void *arg); +#endif + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static const struct qe_ops_s g_nrf53_qdecops = +{ + .setup = nrf53_qdec_setup, + .shutdown = nrf53_qdec_shutdown, + .position = nrf53_qdec_position, + .setposmax = nrf53_qdec_setposmax, + .reset = nrf53_qdec_reset, + .setindex = nrf53_qdec_setindex, + .ioctl = nrf53_qdec_ioctl, +}; + +#ifdef CONFIG_NRF53_QDEC0 +struct nrf53_qdec_s g_nrf53_qdec0 = +{ + .ops = &g_nrf53_qdecops, + .base = NRF53_QDEC0_BASE, + .pin_a = BOARD_QDEC0_A_PIN, + .pin_b = BOARD_QDEC0_B_PIN, +#ifdef CONFIG_NRF53_QENCODER_INDEX + .pin_index = BOARD_QDEC0_INDEX_PIN, +#endif +}; +#endif + +#ifdef CONFIG_NRF53_QDEC1 +struct nrf53_qdec_s g_nrf53_qdec1 = +{ + .ops = &g_nrf53_qdecops, + .base = NRF53_QDEC1_BASE, + .pin_a = BOARD_QDEC1_A_PIN, + .pin_b = BOARD_QDEC1_B_PIN, +#ifdef CONFIG_NRF53_QENCODER_INDEX + .pin_index = BOARD_QDEC1_INDEX_PIN, +#endif +}; +#endif + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: nrf53_qdec_putreg + ****************************************************************************/ + +static inline void nrf53_qdec_putreg(struct nrf53_qdec_s *priv, + uint32_t offset, uint32_t value) +{ + putreg32(value, priv->base + offset); +} + +/**************************************************************************** + * Name: nrf53_qdec_getreg + ****************************************************************************/ + +static inline uint32_t nrf53_qdec_getreg(struct nrf53_qdec_s *priv, + uint32_t offset) +{ + return getreg32(priv->base + offset); +} + +/**************************************************************************** + * Name: nrf53_qdec_setup + ****************************************************************************/ + +static int nrf53_qdec_setup(struct qe_lowerhalf_s *lower) +{ + struct nrf53_qdec_s *priv = (struct nrf53_qdec_s *)lower; + uint32_t pin; + uint32_t port; + uint32_t regval; + irqstate_t flags; + int ret; + + flags = spin_lock_irqsave(&priv->lock); + + if (priv->inuse) + { + ret = -EBUSY; + goto errout; + } + + nrf53_gpio_config(priv->pin_a); + nrf53_gpio_config(priv->pin_b); + + /* Configure PSEL A */ + + pin = GPIO_PIN_DECODE(priv->pin_a); + port = GPIO_PORT_DECODE(priv->pin_a); + regval = (port << QDEC_PSEL_PORT_SHIFT); + regval |= (pin << QDEC_PSEL_PIN_SHIFT); + nrf53_qdec_putreg(priv, NRF53_QDEC_PSEL_A_OFFSET, regval); + + /* Configure PSEL B */ + + pin = GPIO_PIN_DECODE(priv->pin_b); + port = GPIO_PORT_DECODE(priv->pin_b); + regval = (port << QDEC_PSEL_PORT_SHIFT); + regval |= (pin << QDEC_PSEL_PIN_SHIFT); + nrf53_qdec_putreg(priv, NRF53_QDEC_PSEL_B_OFFSET, regval); + + /* Disable LED */ + + nrf53_qdec_putreg(priv, NRF53_QDEC_PSEL_LED_OFFSET, + QDEC_PSEL_DISCONNECTED); + + /* Configure decoder */ + + nrf53_qdec_putreg(priv, NRF53_QDEC_SAMPLEPER_OFFSET, + priv->config->sample_period & QDEC_SAMPLEPER_MASK); + nrf53_qdec_putreg(priv, NRF53_QDEC_REPORTPER_OFFSET, + priv->config->report_period & QDEC_REPORTPER_MASK); + + nrf53_qdec_putreg(priv, NRF53_QDEC_DBFEN_OFFSET, + priv->config->enable_debounce ? QDEC_DBFEN_ENABLE : + QDEC_DBFEN_DISABLE); + + /* Start decoder */ + + nrf53_qdec_putreg(priv, NRF53_QDEC_ENABLE_OFFSET, QDEC_ENABLE_ENABLE); + nrf53_qdec_putreg(priv, NRF53_QDEC_TASKS_START_OFFSET, QDEC_TASKS_START); + +#ifdef CONFIG_NRF53_QENCODER_INDEX + nrf53_gpio_config(priv->pin_index); + ret = nrf53_gpiote_set_event(priv->pin_index, true, false, + nrf53_qdec_index_interrupt, priv); + if (ret < 0) + { + snerr("ERROR: Failed to configure index pin interrupt: %d\n", ret); + goto errout; + } +#endif + + priv->inuse = true; + priv->position = 0; + priv->posmax_enabled = false; +#ifdef CONFIG_NRF53_QENCODER_INDEX + priv->index_pos = 0; + priv->index_count = 0; +#endif + ret = OK; + +errout: + spin_unlock_irqrestore(&priv->lock, flags); + return ret; +} + +/**************************************************************************** + * Name: nrf53_qdec_shutdown + ****************************************************************************/ + +static int nrf53_qdec_shutdown(struct qe_lowerhalf_s *lower) +{ + struct nrf53_qdec_s *priv = (struct nrf53_qdec_s *)lower; + irqstate_t flags; + + flags = spin_lock_irqsave(&priv->lock); + + nrf53_qdec_putreg(priv, NRF53_QDEC_TASKS_STOP_OFFSET, QDEC_TASKS_STOP); + nrf53_qdec_putreg(priv, NRF53_QDEC_ENABLE_OFFSET, QDEC_ENABLE_DISABLE); + + nrf53_gpio_unconfig(priv->pin_a); + nrf53_gpio_unconfig(priv->pin_b); + +#ifdef CONFIG_NRF53_QENCODER_INDEX + nrf53_gpiote_set_event(priv->pin_index, false, false, NULL, NULL); + nrf53_gpio_unconfig(priv->pin_index); +#endif + + priv->inuse = false; + + spin_unlock_irqrestore(&priv->lock, flags); + return OK; +} + +/**************************************************************************** + * Name: nrf53_qdec_position + ****************************************************************************/ + +static int nrf53_qdec_position(struct qe_lowerhalf_s *lower, int32_t *pos) +{ + struct nrf53_qdec_s *priv = (struct nrf53_qdec_s *)lower; + irqstate_t flags; + int32_t accread; + + DEBUGASSERT(lower && pos); + + flags = spin_lock_irqsave(&priv->lock); + + nrf53_qdec_putreg(priv, NRF53_QDEC_TASKS_RDCLRACC_OFFSET, + QDEC_TASKS_RDCLRACC); + + accread = (int32_t)nrf53_qdec_getreg(priv, NRF53_QDEC_ACCREAD_OFFSET); + + priv->position += accread; + + if (priv->posmax_enabled && priv->posmax > 0) + { + priv->position = priv->position % (int32_t)priv->posmax; + if (priv->position < 0) + { + priv->position += (int32_t)priv->posmax; + } + } + + *pos = priv->position; + + spin_unlock_irqrestore(&priv->lock, flags); + return OK; +} + +/**************************************************************************** + * Name: nrf53_qdec_setposmax + ****************************************************************************/ + +static int nrf53_qdec_setposmax(struct qe_lowerhalf_s *lower, uint32_t pos) +{ + struct nrf53_qdec_s *priv = (struct nrf53_qdec_s *)lower; + irqstate_t flags; + + flags = spin_lock_irqsave(&priv->lock); + + priv->posmax = pos; + priv->posmax_enabled = (pos > 0); + + spin_unlock_irqrestore(&priv->lock, flags); + return OK; +} + +/**************************************************************************** + * Name: nrf53_qdec_reset + ****************************************************************************/ + +static int nrf53_qdec_reset(struct qe_lowerhalf_s *lower) +{ + struct nrf53_qdec_s *priv = (struct nrf53_qdec_s *)lower; + irqstate_t flags; + + flags = spin_lock_irqsave(&priv->lock); + + nrf53_qdec_putreg(priv, NRF53_QDEC_TASKS_READCLRACC_OFFSET, + QDEC_TASKS_READCLRACC); + + priv->position = 0; + + spin_unlock_irqrestore(&priv->lock, flags); + return OK; +} + +/**************************************************************************** + * Name: nrf53_qdec_setindex + ****************************************************************************/ + +static int nrf53_qdec_setindex(struct qe_lowerhalf_s *lower, uint32_t pos) +{ +#ifdef CONFIG_NRF53_QENCODER_INDEX + struct nrf53_qdec_s *priv = (struct nrf53_qdec_s *)lower; + irqstate_t flags; + + flags = spin_lock_irqsave(&priv->lock); + priv->index_pos = pos; + spin_unlock_irqrestore(&priv->lock, flags); + + return OK; +#else + return -ENOTTY; +#endif +} + +#ifdef CONFIG_NRF53_QENCODER_INDEX + +/**************************************************************************** + * Name: nrf53_qdec_index_interrupt + ****************************************************************************/ + +static int nrf53_qdec_index_interrupt(int irq, void *context, void *arg) +{ + struct nrf53_qdec_s *priv = (struct nrf53_qdec_s *)arg; + irqstate_t flags; + + flags = spin_lock_irqsave(&priv->lock); + + priv->position = priv->index_pos; + priv->index_count++; + + spin_unlock_irqrestore(&priv->lock, flags); + + return OK; +} + +#endif + +/**************************************************************************** + * Name: nrf53_qdec_ioctl + ****************************************************************************/ + +static int nrf53_qdec_ioctl(struct qe_lowerhalf_s *lower, int cmd, + unsigned long arg) +{ +#ifdef CONFIG_NRF53_QENCODER_INDEX + struct nrf53_qdec_s *priv = (struct nrf53_qdec_s *)lower; + struct qe_index_s *index; + + if (cmd == QEIOC_GETINDEX) + { + index = (struct qe_index_s *)arg; + index->qenc_pos = priv->position; + index->indx_pos = priv->index_pos; + index->indx_cnt = priv->index_count; + return OK; + } +#endif + + return -ENOTTY; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: nrf53_qeinitialize + ****************************************************************************/ + +struct qe_lowerhalf_s * +nrf53_qeinitialize(int qdec, const struct nrf53_qeconfig_s *config) +{ + struct nrf53_qdec_s *priv = NULL; + + DEBUGASSERT(config != NULL); + + switch (qdec) + { +#ifdef CONFIG_NRF53_QDEC0 + case 0: + { + priv = &g_nrf53_qdec0; + break; + } +#endif + +#ifdef CONFIG_NRF53_QDEC1 + case 1: + { + priv = &g_nrf53_qdec1; + break; + } +#endif + + default: + { + snerr("ERROR: Invalid QDEC instance: %d\n", qdec); + return NULL; + } + } + + priv->config = config; + + return (struct qe_lowerhalf_s *)priv; +} diff --git a/arch/arm/src/nrf53/nrf53_qdec.h b/arch/arm/src/nrf53/nrf53_qdec.h new file mode 100644 index 00000000000..19b86351175 --- /dev/null +++ b/arch/arm/src/nrf53/nrf53_qdec.h @@ -0,0 +1,52 @@ +/**************************************************************************** + * arch/arm/src/nrf53/nrf53_qdec.h + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +#ifndef __ARCH_ARM_SRC_NRF53_NRF53_QENCODER_H +#define __ARCH_ARM_SRC_NRF53_NRF53_QENCODER_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <nuttx/config.h> +#include <stdint.h> + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +struct nrf53_qeconfig_s +{ + uint8_t sample_period; + uint8_t report_period; + bool enable_debounce; +}; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +struct qe_lowerhalf_s; +struct qe_lowerhalf_s * +nrf53_qeinitialize(int qdec, const struct nrf53_qeconfig_s *config); + +#endif /* __ARCH_ARM_SRC_NRF53_NRF53_QENCODER_H */
