diff --git a/arch/arm/src/nrf52/Kconfig b/arch/arm/src/nrf52/Kconfig index ff5413381e6..9c4cb8d8ca1 100644 --- a/arch/arm/src/nrf52/Kconfig +++ b/arch/arm/src/nrf52/Kconfig @@ -535,3 +535,61 @@ config NRF52_PWM3_CHANNEL endif # !NRF52_PWM_MULTICHAN endmenu # PWM configuration + +menu "SAADC Configuration" + +if NRF52_SAADC + +choice + prompt "SAADC trigger selection" + default NRF52_SAADC_TASK + ---help--- + Choose mode for sample rate control + +config NRF52_SAADC_TASK + bool "SAADC Task trigger" + +config NRF52_SAADC_TIMER + bool "SAADC Timer trigger" + +endchoice # SAADC trigger selection + +if NRF52_SAADC_TIMER + +config NRF52_SAADC_TIMER_CC + int "SAADC Timer CC" + default 0 + range 80 2047 + +endif #NRF52_SAADC_TIMER + +config NRF52_SAADC_OVERSAMPLE + int "SAADC oversample" + default 0 + range 0 8 + ---help--- + SAADC oversample control + +config NRF52_SAADC_RESOLUTION + int "SAADC resolution" + default 0 + range 0 3 + ---help--- + SAADC resolution 0 - 8 bits, 1 - 10 bits, 2 - 12 bits, 3 - 14 bits + +config NRF52_SAADC_CHANNELS + int "SAADC channels" + default 8 + range 0 8 + ---help--- + SAADC channels + +config NRF52_SAADC_LIMITS + bool "SAADC limits enable" + default n + ---help--- + SAADC limist enable + +endif # NRF52_SAADC + +endmenu # SAADC Configuration diff --git a/arch/arm/src/nrf52/Make.defs b/arch/arm/src/nrf52/Make.defs index d69903d7bf4..62acd3c9c0d 100644 --- a/arch/arm/src/nrf52/Make.defs +++ b/arch/arm/src/nrf52/Make.defs @@ -151,3 +151,7 @@ endif ifeq ($(CONFIG_NRF52_PWM),y) CHIP_CSRCS += nrf52_pwm.c endif + +ifeq ($(CONFIG_NRF52_SAADC),y) +CHIP_CSRCS += nrf52_adc.c +endif \ No newline at end of file diff --git a/arch/arm/src/nrf52/hardware/nrf52_saadc.h b/arch/arm/src/nrf52/hardware/nrf52_saadc.h index 3adf2300e1f..a5e3c196ae6 100644 --- a/arch/arm/src/nrf52/hardware/nrf52_saadc.h +++ b/arch/arm/src/nrf52/hardware/nrf52_saadc.h @@ -1,4 +1,4 @@ -/**************************************************************************** +/*************************************************************************** * arch/arm/src/nrf52/hardware/nrf52_saadc.h * * Copyright (C) 2019 Gregory Nutt. All rights reserved. @@ -43,50 +43,50 @@ #include #include "hardware/nrf52_memorymap.h" -/**************************************************************************** +/*************************************************************************** * Pre-processor Definitions ***************************************************************************/ /* Register offsets for SAADC **********************************************/ -#define NRF52_SAADC_TASKS_START_OFFSET 0x0000 /* Start the SAADCM */ -#define NRF52_SAADC_TASKS_SAMPLE_OFFSET 0x0004 /* Takes one SAADC sample */ -#define NRF52_SAADC_TASKS_STOP_OFFSET 0x0008 /* Stop the SAADC */ -#define NRF52_SAADC_TASKS_CALOFFSET_OFFSET 0x000c /* Starts offset auto-calibration */ -#define NRF52_SAADC_EVENTS_STARTED_OFFSET 0x0100 /* The SAADC has started */ -#define NRF52_SAADC_EVENTS_END_OFFSET 0x0104 /* The SAADC has filled up the result buffer */ -#define NRF52_SAADC_EVENTS_DONE_OFFSET 0x0108 /* A conversio ntask has been completed */ -#define NRF52_SAADC_EVENTS_RESDONE_OFFSET 0x010c /* Result ready for transfer to RAM */ -#define NRF52_SAADC_EVENTS_CALDONE_OFFSET 0x0110 /* Calibration is complete */ -#define NRF52_SAADC_EVENTS_STOPPED_OFFSET 0x0110 /* The SAADC has stopped */ -#define NRF52_SAADC_EVENTS_CHLIMH_OFFSET(x) (0x118 + (x + 0x8)) /* Limit high event for channel x */ -#define NRF52_SAADC_EVENTS_CHLIML_OFFSET(x) (0x11c + (x + 0x8)) /* Limit low event for channel x */ -#define NRF52_SAADC_INTEN_OFFSET 0x0300 /* Enable or disable interrupt */ -#define NRF52_SAADC_INTENSET_OFFSET 0x0304 /* Enable interrupt */ -#define NRF52_SAADC_INTENCLR_OFFSET 0x0308 /* Disable interrupt */ -#define NRF52_SAADC_STATUS_OFFSET 0x0400 /* Status */ -#define NRF52_SAADC_ENABLE_OFFSET 0x0500 /* Enable or disable SAADC */ -#define NRF52_SAADC_CHPSELP_OFFSET(x) (0x510 + (x + 0x10)) /* Input positive pin for CH[x] */ -#define NRF52_SAADC_CHPSELN_OFFSET(x) (0x514 + (x + 0x10)) /* Input negative pin for CH[x] */ -#define NRF52_SAADC_CHCONFIG_OFFSET(x) (0x518 + (x + 0x10)) /* Input configuration for CH[x] */ -#define NRF52_SAADC_CHLIMIT_OFFSET(x) (0x51c + (x + 0x10)) /* High/low limits for event monitoring of a CH[x] */ -#define NRF52_SAADC_RESOLUTION_OFFSET 0x05f0 /* Resolution configuration */ -#define NRF52_SAADC_OVERSAMPLE_OFFSET 0x05f4 /* Oversampling configuration */ -#define NRF52_SAADC_SAMPLERATE_OFFSET 0x05f8 /* Controls normal or continuous sample rate */ -#define NRF52_SAADC_PTR_OFFSET 0x062c /* Data pointer */ -#define NRF52_SAADC_MAXCNT_OFFSET 0x0630 /* Maximum number of 16-bit samples */ -#define NRF52_SAADC_AMOUNT_OFFSET 0x0634 /* Number of 16-bit samples written to buffer */ +#define NRF52_SAADC_TASKS_START_OFFSET 0x0000 /* Start the SAADCM */ +#define NRF52_SAADC_TASKS_SAMPLE_OFFSET 0x0004 /* Takes one SAADC sample */ +#define NRF52_SAADC_TASKS_STOP_OFFSET 0x0008 /* Stop the SAADC */ +#define NRF52_SAADC_TASKS_CALOFFSET_OFFSET 0x000c /* Starts offset auto-calibration */ +#define NRF52_SAADC_EVENTS_STARTED_OFFSET 0x0100 /* The SAADC has started */ +#define NRF52_SAADC_EVENTS_END_OFFSET 0x0104 /* The SAADC has filled up the result buffer */ +#define NRF52_SAADC_EVENTS_DONE_OFFSET 0x0108 /* A conversion task has been completed */ +#define NRF52_SAADC_EVENTS_RESDONE_OFFSET 0x010c /* Result ready for transfer to RAM */ +#define NRF52_SAADC_EVENTS_CALDONE_OFFSET 0x0110 /* Calibration is complete */ +#define NRF52_SAADC_EVENTS_STOPPED_OFFSET 0x0110 /* The SAADC has stopped */ +#define NRF52_SAADC_EVENTS_CHLIMH_OFFSET(x) (0x118 + ((x) * 0x8)) /* Limit high event for channel x */ +#define NRF52_SAADC_EVENTS_CHLIML_OFFSET(x) (0x11c + ((x) * 0x8)) /* Limit low event for channel x */ +#define NRF52_SAADC_INTEN_OFFSET 0x0300 /* Enable or disable interrupt */ +#define NRF52_SAADC_INTENSET_OFFSET 0x0304 /* Enable interrupt */ +#define NRF52_SAADC_INTENCLR_OFFSET 0x0308 /* Disable interrupt */ +#define NRF52_SAADC_STATUS_OFFSET 0x0400 /* Status */ +#define NRF52_SAADC_ENABLE_OFFSET 0x0500 /* Enable or disable SAADC */ +#define NRF52_SAADC_CHPSELP_OFFSET(x) (0x510 + ((x) * 0x10)) /* Input positive pin for CH[x] */ +#define NRF52_SAADC_CHPSELN_OFFSET(x) (0x514 + ((x) * 0x10)) /* Input negative pin for CH[x] */ +#define NRF52_SAADC_CHCONFIG_OFFSET(x) (0x518 + ((x) * 0x10)) /* Input configuration for CH[x] */ +#define NRF52_SAADC_CHLIMIT_OFFSET(x) (0x51c + ((x) * 0x10)) /* High/low limits for event monitoring of a CH[x] */ +#define NRF52_SAADC_RESOLUTION_OFFSET 0x05f0 /* Resolution configuration */ +#define NRF52_SAADC_OVERSAMPLE_OFFSET 0x05f4 /* Oversampling configuration */ +#define NRF52_SAADC_SAMPLERATE_OFFSET 0x05f8 /* Controls normal or continuous sample rate */ +#define NRF52_SAADC_PTR_OFFSET 0x062c /* Data pointer */ +#define NRF52_SAADC_MAXCNT_OFFSET 0x0630 /* Maximum number of 16-bit samples */ +#define NRF52_SAADC_AMOUNT_OFFSET 0x0634 /* Number of 16-bit samples written to buffer */ /* Register Bitfield Definitions for SAADC *********************************/ /* INTEN/INTENSET/INTENCLR Register */ -#define SAADC_INT_STARTED (1 << 0) /* Bit 0: Interrupt for event STARTED */ -#define SAADC_INT_END (1 << 1) /* Bit 1: Interrupt for event END */ -#define SAADC_INT_DONE (1 << 2) /* Bit 2: Interrupt for event DONE */ -#define SAADC_INT_RESDONE (1 << 3) /* Bit 3: Interrupt for event RESULTDONE */ -#define SAADC_INT_CALDONE (1 << 4) /* Bit 4: Interrupt for event CALIBRATEDONE */ -#define SAADC_INT_STOPPED (1 << 5) /* Bit 5: Interrupt for event STOPPED */ +#define SAADC_INT_STARTED (1 << 0) /* Bit 0: Interrupt for event STARTED */ +#define SAADC_INT_END (1 << 1) /* Bit 1: Interrupt for event END */ +#define SAADC_INT_DONE (1 << 2) /* Bit 2: Interrupt for event DONE */ +#define SAADC_INT_RESDONE (1 << 3) /* Bit 3: Interrupt for event RESULTDONE */ +#define SAADC_INT_CALDONE (1 << 4) /* Bit 4: Interrupt for event CALIBRATEDONE */ +#define SAADC_INT_STOPPED (1 << 5) /* Bit 5: Interrupt for event STOPPED */ #define SAADC_INT_CHXLIMH(x) (1 << (x + 0x6)) /* Bit (x+6): Interrupt for event CHxLIMITH */ #define SAADC_INT_CHXLIML(x) (1 << (x + 0x7)) /* Bit (x+7): Interrupt for event CHxLIMITL */ @@ -100,52 +100,36 @@ #define SAADC_ENABLE_DIS (0) /* Bit 0: Disable SAADC */ #define SAADC_ENABLE_EN (1 << 0) /* Bit 0: Enable SAADC */ -/* CH[n] PSELP Register */ +/* CH[n] PSELx Register */ -#define SAADC_CHPSELP_SHIFT (0) /* Bits 0-4: Input positive pin selection for CH[x] */ -#define SAADC_CHPSELP_MASK (0xf << SAADC_CHPSELP_SHIFT) -# define SAADC_CHPSELP_NC (0x0 << SAADC_CHPSELP_SHIFT) -# define SAADC_CHPSELP_IN0 (0x1 << SAADC_CHPSELP_SHIFT) -# define SAADC_CHPSELP_IN1 (0x2 << SAADC_CHPSELP_SHIFT) -# define SAADC_CHPSELP_IN2 (0x3 << SAADC_CHPSELP_SHIFT) -# define SAADC_CHPSELP_IN3 (0x4 << SAADC_CHPSELP_SHIFT) -# define SAADC_CHPSELP_IN4 (0x5 << SAADC_CHPSELP_SHIFT) -# define SAADC_CHPSELP_IN5 (0x6 << SAADC_CHPSELP_SHIFT) -# define SAADC_CHPSELP_IN6 (0x7 << SAADC_CHPSELP_SHIFT) -# define SAADC_CHPSELP_IN7 (0x8 << SAADC_CHPSELP_SHIFT) -# define SAADC_CHPSELP_VDD (0x9 << SAADC_CHPSELP_SHIFT) -# define SAADC_CHPSELP_VDDHDIV5 (0xd << SAADC_CHPSELP_SHIFT) - -/* CH[n] PSELN Register */ - -#define SAADC_CHPSELN_SHIFT (0) /* Bits 0-4: Input negative pin selection for CH[x] */ -#define SAADC_CHPSELN_MASK (0xf << SAADC_CHPSELN_SHIFT) -# define SAADC_CHPSELN_NC (0x0 << SAADC_CHPSELN_SHIFT) -# define SAADC_CHPSELN_IN0 (0x1 << SAADC_CHPSELN_SHIFT) -# define SAADC_CHPSELN_IN1 (0x2 << SAADC_CHPSELN_SHIFT) -# define SAADC_CHPSELN_IN2 (0x3 << SAADC_CHPSELN_SHIFT) -# define SAADC_CHPSELN_IN3 (0x4 << SAADC_CHPSELN_SHIFT) -# define SAADC_CHPSELN_IN4 (0x5 << SAADC_CHPSELN_SHIFT) -# define SAADC_CHPSELN_IN5 (0x6 << SAADC_CHPSELN_SHIFT) -# define SAADC_CHPSELN_IN6 (0x7 << SAADC_CHPSELN_SHIFT) -# define SAADC_CHPSELN_IN7 (0x8 << SAADC_CHPSELN_SHIFT) -# define SAADC_CHPSELN_VDD (0x9 << SAADC_CHPSELN_SHIFT) -# define SAADC_CHPSELN_VDDHDIV5 (0xd << SAADC_CHPSELN_SHIFT) +#define SAADC_CHPSEL_SHIFT (0) /* Bits 0-4: Input positive pin selection for CH[x] */ +#define SAADC_CHPSEL_MASK (0xf << SAADC_CHPSEL_SHIFT) +# define SAADC_CHPSEL_NC (0x0 << SAADC_CHPSEL_SHIFT) +# define SAADC_CHPSEL_IN0 (0x1 << SAADC_CHPSEL_SHIFT) +# define SAADC_CHPSEL_IN1 (0x2 << SAADC_CHPSEL_SHIFT) +# define SAADC_CHPSEL_IN2 (0x3 << SAADC_CHPSEL_SHIFT) +# define SAADC_CHPSEL_IN3 (0x4 << SAADC_CHPSEL_SHIFT) +# define SAADC_CHPSEL_IN4 (0x5 << SAADC_CHPSEL_SHIFT) +# define SAADC_CHPSEL_IN5 (0x6 << SAADC_CHPSEL_SHIFT) +# define SAADC_CHPSEL_IN6 (0x7 << SAADC_CHPSEL_SHIFT) +# define SAADC_CHPSEL_IN7 (0x8 << SAADC_CHPSEL_SHIFT) +# define SAADC_CHPSEL_VDD (0x9 << SAADC_CHPSEL_SHIFT) +# define SAADC_CHPSEL_VDDHDIV5 (0xd << SAADC_CHPSEL_SHIFT) /* CH[n] CONFIG Register */ #define SAADC_CONFIG_RESP_SHIFT (0) /* Bits 0-2: Positive channel resistor control */ #define SAADC_CONFIG_RESP_MASK (0x3 << SAADC_CONFIG_RESP_SHIFT) -# define SAADC_CONFIG_RESN_NONE (0x0 << SAADC_CONFIG_RESP_SHIFT) -# define SAADC_CONFIG_RESN_PD (0x1 << SAADC_CONFIG_RESP_SHIFT) -# define SAADC_CONFIG_RESN_PU (0x2 << SAADC_CONFIG_RESP_SHIFT) -# define SAADC_CONFIG_RESN_VDD1P2 (0x3 << SAADC_CONFIG_RESP_SHIFT) +# define SAADC_CONFIG_RESP_NONE (0x0 << SAADC_CONFIG_RESP_SHIFT) +# define SAADC_CONFIG_RESP_PD (0x1 << SAADC_CONFIG_RESP_SHIFT) +# define SAADC_CONFIG_RESP_PU (0x2 << SAADC_CONFIG_RESP_SHIFT) +# define SAADC_CONFIG_RESP_VDD1P2 (0x3 << SAADC_CONFIG_RESP_SHIFT) #define SAADC_CONFIG_RESN_SHIFT (4) /* Bits 4-5: Negative channel resistor control */ #define SAADC_CONFIG_RESN_MASK (0x3 << SAADC_CONFIG_RESN_SHIFT) # define SAADC_CONFIG_RESN_NONE (0x0 << SAADC_CONFIG_RESN_SHIFT) # define SAADC_CONFIG_RESN_PD (0x1 << SAADC_CONFIG_RESN_SHIFT) # define SAADC_CONFIG_RESN_PU (0x2 << SAADC_CONFIG_RESN_SHIFT) -# define SAADC_CONFIG_RESN_VDD1D2 (0x3 << SAADC_CONFIG_RESN_SHIFT) +# define SAADC_CONFIG_RESN_VDD1P2 (0x3 << SAADC_CONFIG_RESN_SHIFT) #define SAADC_CONFIG_GAIN_SHIFT (8) /* Bits 8-10: Gain control */ #define SAADC_CONFIG_GAIN_MASK (0x7 << SAADC_CONFIG_GAIN_SHIFT) # define SAADC_CONFIG_GAIN_1P6 (0x0 << SAADC_CONFIG_GAIN_SHIFT) @@ -156,8 +140,8 @@ # define SAADC_CONFIG_GAIN_1 (0x5 << SAADC_CONFIG_GAIN_SHIFT) # define SAADC_CONFIG_GAIN_2 (0x6 << SAADC_CONFIG_GAIN_SHIFT) # define SAADC_CONFIG_GAIN_4 (0x7 << SAADC_CONFIG_GAIN_SHIFT) -#define SAADC_REFSEL_INTERNAL (0 << 12) /* Bit 12: Internal reference (0.6V) */ -#define SAADC_REFSEL_VDD1P4 (1 << 12) /* Bit 12: VDD/4 as reference */ +#define SAADC_CONFIG_REFSEL_INTERNAL (0 << 12) /* Bit 12: Internal reference (0.6V) */ +#define SAADC_CONFIG_REFSEL_VDD1P4 (1 << 12) /* Bit 12: VDD/4 as reference */ #define SAADC_CONFIG_TACQ_SHIFT (16) /* Bits 16-18: Acquisition time */ #define SAADC_CONFIG_TACQ_MASK (0x7 << SAADC_CONFIG_TACQ_SHIFT) # define SAADC_CONFIG_TACQ_3US (0x0 << SAADC_CONFIG_TACQ_SHIFT) diff --git a/arch/arm/src/nrf52/nrf52_adc.c b/arch/arm/src/nrf52/nrf52_adc.c new file mode 100644 index 00000000000..621d296585b --- /dev/null +++ b/arch/arm/src/nrf52/nrf52_adc.c @@ -0,0 +1,950 @@ +/**************************************************************************** + * arch/arm/src/nrf52/nrf52_adc.c + * + * 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 + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "arm_arch.h" + +#include "nrf52_gpio.h" +#include "nrf52_adc.h" + +#include "hardware/nrf52_saadc.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct nrf52_adc_s +{ + /* Upper-half callback */ + + FAR const struct adc_callback_s *cb; + + /* Channels configuration */ + + struct nrf52_adc_channel_s channels[CONFIG_NRF52_SAADC_CHANNELS]; + + /* Samples buffer */ + + int16_t buffer[CONFIG_NRF52_SAADC_CHANNELS]; + + uint8_t chan_len; /* Configured channels */ + uint32_t base; /* Base address of ADC register */ + uint32_t irq; /* ADC interrupt */ + uint8_t resolution; /* ADC resolution */ +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/* ADC Register access */ + +static inline void nrf52_adc_putreg(FAR struct nrf52_adc_s *priv, + uint32_t offset, + uint32_t value); +static inline uint32_t nrf52_adc_getreg(FAR struct nrf52_adc_s *priv, + uint32_t offset); + +/* ADC helpers */ + +static int nrf52_adc_configure(FAR struct nrf52_adc_s *priv); +static int nrf52_adc_calibrate(FAR struct nrf52_adc_s *priv); +static uint32_t nrf52_adc_ch_config(FAR struct nrf52_adc_channel_s *cfg); +static uint32_t nrf52_adc_chanpsel(int psel); +static int nrf52_adc_chancfg(FAR struct nrf52_adc_s *priv, uint8_t chan, + FAR struct nrf52_adc_channel_s *cfg); +static int nrf52_adc_isr(int irq, void *context, FAR void *arg); + +/* ADC Driver Methods */ + +static int nrf52_adc_bind(FAR struct adc_dev_s *dev, + FAR const struct adc_callback_s *callback); +static void nrf52_adc_reset(FAR struct adc_dev_s *dev); +static int nrf52_adc_setup(FAR struct adc_dev_s *dev); +static void nrf52_adc_shutdown(FAR struct adc_dev_s *dev); +static void nrf52_adc_rxint(FAR struct adc_dev_s *dev, bool enable); +static int nrf52_adc_ioctl(FAR struct adc_dev_s *dev, int cmd, + unsigned long arg); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* ADC interface operations */ + +static const struct adc_ops_s g_nrf52_adcops = +{ + .ao_bind = nrf52_adc_bind, + .ao_reset = nrf52_adc_reset, + .ao_setup = nrf52_adc_setup, + .ao_shutdown = nrf52_adc_shutdown, + .ao_rxint = nrf52_adc_rxint, + .ao_ioctl = nrf52_adc_ioctl, +}; + +/* SAADC device */ + +struct nrf52_adc_s g_nrf52_adcpriv = +{ + .cb = NULL, + .base = NRF52_SAADC_BASE, + .irq = NRF52_IRQ_SAADC, + .resolution = CONFIG_NRF52_SAADC_RESOLUTION +}; + +/* Upper-half ADC device */ + +static struct adc_dev_s g_nrf52_adc = +{ + .ad_ops = &g_nrf52_adcops, + .ad_priv = &g_nrf52_adcpriv, +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: nrf52_adc_putreg + * + * Description: + * Put a 32-bit register value by offset + * + ****************************************************************************/ + +static inline void nrf52_adc_putreg(FAR struct nrf52_adc_s *priv, + uint32_t offset, + uint32_t value) +{ + DEBUGASSERT(priv); + + putreg32(value, priv->base + offset); +} + +/**************************************************************************** + * Name: nrf52_adc_getreg + * + * Description: + * Get a 32-bit register value by offset + * + ****************************************************************************/ + +static inline uint32_t nrf52_adc_getreg(FAR struct nrf52_adc_s *priv, + uint32_t offset) +{ + DEBUGASSERT(priv); + + return getreg32(priv->base + offset); +} + +/**************************************************************************** + * Name: nrf52_adc_isr + * + * Description: + * Common ADC interrupt service routine + * + ****************************************************************************/ + +static int nrf52_adc_isr(int irq, void *context, FAR void *arg) +{ + FAR struct adc_dev_s *dev = (FAR struct adc_dev_s *) arg; + FAR struct nrf52_adc_s *priv = NULL; + int ret = OK; + int i = 0; + + DEBUGASSERT(dev); + + priv = (FAR struct nrf52_adc_s *) dev->ad_priv; + DEBUGASSERT(priv); + + ainfo("nrf52_adc_isr\n"); + + /* END event */ + + if (nrf52_adc_getreg(priv, NRF52_SAADC_EVENTS_END_OFFSET) == 1) + { + DEBUGASSERT(priv->cb != NULL); + DEBUGASSERT(priv->cb->au_receive != NULL); + + /* Give the ADC data to the ADC driver */ + + for (i = 0; i < priv->chan_len; i += 1) + { + priv->cb->au_receive(dev, i, priv->buffer[i]); + } + + /* Clear event */ + + nrf52_adc_putreg(priv, NRF52_SAADC_EVENTS_END_OFFSET, 0); + } + + return ret; +} + +/**************************************************************************** + * Name: nrf52_adc_configure + * + * Description: + * Configure ADC + * + ****************************************************************************/ + +static int nrf52_adc_configure(FAR struct nrf52_adc_s *priv) +{ + int regval = 0; + + DEBUGASSERT(priv); + + /* Configure ADC resolution */ + + regval = CONFIG_NRF52_SAADC_RESOLUTION; + nrf52_adc_putreg(priv, NRF52_SAADC_RESOLUTION_OFFSET, regval); + + /* Configure oversampling */ + + regval = CONFIG_NRF52_SAADC_OVERSAMPLE; + nrf52_adc_putreg(priv, NRF52_SAADC_OVERSAMPLE_OFFSET, regval); + + /* Configure sample rate */ + +#if defined(CONFIG_NRF52_SAADC_TIMER) + /* Trigger from local timer */ + + regval = SAADC_SAMPLERATE_MODE_TIMERS; + regval |= ((CONFIG_NRF52_SAADC_TIMER_CC & SAADC_SAMPLERATE_CC_MASK) + << SAADC_SAMPLERATE_CC_SHIFT); +#elif defined(CONFIG_NRF52_SAADC_TASK) + /* Trigger on SAMPLE tas */ + + regval = SAADC_SAMPLERATE_MODE_TASK; +#else +# error SAADC trigger not selected +#endif + + nrf52_adc_putreg(priv, NRF52_SAADC_SAMPLERATE_OFFSET, regval); + + /* Configure ADC buffer */ + + regval = (uint32_t)&priv->buffer; + nrf52_adc_putreg(priv, NRF52_SAADC_PTR_OFFSET, regval); + + regval = priv->chan_len; + nrf52_adc_putreg(priv, NRF52_SAADC_MAXCNT_OFFSET, regval); + + return OK; +} + +/**************************************************************************** + * Name: nrf52_adc_calibrate + * + * Description: + * Calibrate ADC + * + ****************************************************************************/ + +static int nrf52_adc_calibrate(FAR struct nrf52_adc_s *priv) +{ + /* Start calibration */ + + nrf52_adc_putreg(priv, NRF52_SAADC_TASKS_CALOFFSET_OFFSET, 1); + + /* Wait for calibration done */ + + while (nrf52_adc_getreg(priv, NRF52_SAADC_EVENTS_CALDONE_OFFSET) != 1); + + return OK; +} + +/**************************************************************************** + * Name: nrf52_adc_ch_config + ****************************************************************************/ + +static uint32_t nrf52_adc_ch_config(FAR struct nrf52_adc_channel_s *cfg) +{ + uint32_t regval = 0; + + /* Positive channel resistor control */ + + switch (cfg->resp) + { + case NRF52_ADC_RES_BYPASS: + { + regval |= SAADC_CONFIG_RESP_NONE; + break; + } + + case NRF52_ADC_RES_PULLDOWN: + { + regval |= SAADC_CONFIG_RESP_PD; + break; + } + + case NRF52_ADC_RES_PULLUP: + { + regval |= SAADC_CONFIG_RESP_PU; + break; + } + + case NRF52_ADC_RES_VDD_2: + { + regval |= SAADC_CONFIG_RESP_VDD1P2; + break; + } + + default: + { + aerr("ERROR: invalid cfg->resp: %d\n", cfg->resp); + } + } + + /* Negative channel resistor control */ + + switch (cfg->resn) + { + case NRF52_ADC_RES_BYPASS: + { + regval |= SAADC_CONFIG_RESN_NONE; + break; + } + + case NRF52_ADC_RES_PULLDOWN: + { + regval |= SAADC_CONFIG_RESN_PD; + break; + } + + case NRF52_ADC_RES_PULLUP: + { + regval |= SAADC_CONFIG_RESN_PU; + break; + } + + case NRF52_ADC_RES_VDD_2: + { + regval |= SAADC_CONFIG_RESN_VDD1P2; + break; + } + + default: + { + aerr("ERROR: invalid cfg->resn: %d\n", cfg->resn); + } + } + + /* Gain control */ + + switch (cfg->gain) + { + case NRF52_ADC_GAIN_1_6: + { + regval |= SAADC_CONFIG_GAIN_1P6; + break; + } + + case NRF52_ADC_GAIN_1_5: + { + regval |= SAADC_CONFIG_GAIN_1P5; + break; + } + + case NRF52_ADC_GAIN_1_4: + { + regval |= SAADC_CONFIG_GAIN_1P4; + break; + } + + case NRF52_ADC_GAIN_1_3: + { + regval |= SAADC_CONFIG_GAIN_1P3; + break; + } + + case NRF52_ADC_GAIN_1_2: + { + regval |= SAADC_CONFIG_GAIN_1P2; + break; + } + + case NRF52_ADC_GAIN_1: + { + regval |= SAADC_CONFIG_GAIN_1; + break; + } + + case NRF52_ADC_GAIN_2: + { + regval |= SAADC_CONFIG_GAIN_2; + break; + } + + case NRF52_ADC_GAIN_4: + { + regval |= SAADC_CONFIG_GAIN_4; + break; + } + + default: + { + aerr("ERROR: invalid cfg->gain: %d\n", cfg->gain); + } + } + + /* Reference control */ + + switch (cfg->refsel) + { + case NRF52_ADC_REFSEL_INTERNAL: + { + regval |= SAADC_CONFIG_REFSEL_INTERNAL; + break; + } + + case NRF52_ADC_REFSEL_VDD_4: + { + regval |= SAADC_CONFIG_REFSEL_VDD1P4; + break; + } + + default: + { + aerr("ERROR: invalid cfg->refsel: %d\n", cfg->refsel); + } + } + + /* Acquisition time */ + + switch (cfg->tacq) + { + case NRF52_ADC_TACQ_3US: + { + regval |= SAADC_CONFIG_TACQ_3US; + break; + } + + case NRF52_ADC_TACQ_5US: + { + regval |= SAADC_CONFIG_TACQ_5US; + break; + } + + case NRF52_ADC_TACQ_10US: + { + regval |= SAADC_CONFIG_TACQ_10US; + break; + } + + case NRF52_ADC_TACQ_15US: + { + regval |= SAADC_CONFIG_TACQ_15US; + break; + } + + case NRF52_ADC_TACQ_20US: + { + regval |= SAADC_CONFIG_TACQ_20US; + break; + } + + case NRF52_ADC_TACQ_40US: + { + regval |= SAADC_CONFIG_TACQ_40US; + break; + } + + default: + { + aerr("ERROR: invalid cfg->tacq: %d\n", cfg->tacq); + } + } + + /* Singe-ended or differential mode */ + + switch (cfg->mode) + { + case NRF52_ADC_MODE_SE: + { + regval |= SAADC_CONFIG_MODE_SE; + break; + } + + case NRF52_ADC_MODE_DIFF: + { + regval |= SAADC_CONFIG_MODE_DIFF; + break; + } + + default: + { + aerr("ERROR: invalid cfg->mode: %d\n", cfg->mode); + } + } + + /* Burst mode */ + + switch (cfg->burst) + { + case NRF52_ADC_BURST_DISABLE: + { + regval |= SAADC_CONFIG_BURS_DIS; + break; + } + + case NRF52_ADC_BURST_ENABLE: + { + regval |= SAADC_CONFIG_BURS_EN; + break; + } + + default: + { + aerr("ERROR: invalid cfg->burst: %d\n", cfg->burst); + } + } + + return regval; +} + +/**************************************************************************** + * Name: nrf52_adc_chanpsel + ****************************************************************************/ + +static uint32_t nrf52_adc_chanpsel(int psel) +{ + uint32_t regval = 0; + + switch (psel) + { + case NRF52_ADC_IN_NC: + { + regval = SAADC_CHPSEL_NC; + break; + } + + case NRF52_ADC_IN_IN0: + { + regval = SAADC_CHPSEL_IN0; + break; + } + + case NRF52_ADC_IN_IN1: + { + regval = SAADC_CHPSEL_IN1; + break; + } + + case NRF52_ADC_IN_IN2: + { + regval = SAADC_CHPSEL_IN2; + break; + } + + case NRF52_ADC_IN_IN3: + { + regval = SAADC_CHPSEL_IN3; + break; + } + + case NRF52_ADC_IN_IN4: + { + regval = SAADC_CHPSEL_IN4; + break; + } + + case NRF52_ADC_IN_IN5: + { + regval = SAADC_CHPSEL_IN5; + break; + } + + case NRF52_ADC_IN_IN6: + { + regval = SAADC_CHPSEL_IN6; + break; + } + + case NRF52_ADC_IN_IN7: + { + regval = SAADC_CHPSEL_IN7; + break; + } + + case NRF52_ADC_IN_VDD: + { + regval = SAADC_CHPSEL_VDD; + break; + } + + case NRF52_ADC_IN_VDDHDIV5: + { + regval = SAADC_CHPSEL_VDDHDIV5; + break; + } + + default: + { + aerr("ERROR: invalid psel: %d\n", psel); + } + } + + return regval; +} + +/**************************************************************************** + * Name: nrf52_adc_chancfg + * + * Description: + * Configure ADC channel + * + ****************************************************************************/ + +static int nrf52_adc_chancfg(FAR struct nrf52_adc_s *priv, uint8_t chan, + FAR struct nrf52_adc_channel_s *cfg) +{ + uint32_t regval = 0; + int ret = OK; + + DEBUGASSERT(priv); + + /* Configure positive input */ + + regval = nrf52_adc_chanpsel(cfg->p_psel); + nrf52_adc_putreg(priv, NRF52_SAADC_CHPSELP_OFFSET(chan), regval); + + /* Configure negative input */ + + regval = nrf52_adc_chanpsel(cfg->p_psel); + nrf52_adc_putreg(priv, NRF52_SAADC_CHPSELN_OFFSET(chan), regval); + + /* Get channel configuration */ + + regval = nrf52_adc_ch_config(cfg); + + /* Write channel configuration */ + + nrf52_adc_putreg(priv, NRF52_SAADC_CHCONFIG_OFFSET(chan), regval); + +#ifdef CONFIG_NRF52_SAADC_LIMITS + /* Configure limits */ + + regval = (cfg->limith < 16) | (cfg->limith << 0); + nrf52_adc_putreg(priv, NRF52_SAADC_CHLIMIT_OFFSET(chan), regval); +#endif + + return ret; +} + +/**************************************************************************** + * Name: nrf52_adc_bind + * + * Description: + * Bind the upper-half driver callbacks to the lower-half implementation. + * This must be called early in order to receive ADC event notifications. + * + ****************************************************************************/ + +static int nrf52_adc_bind(FAR struct adc_dev_s *dev, + FAR const struct adc_callback_s *callback) +{ + FAR struct nrf52_adc_s *priv = (FAR struct nrf52_adc_s *) dev->ad_priv; + + DEBUGASSERT(dev); + DEBUGASSERT(priv); + + priv->cb = callback; + + return OK; +} + +/**************************************************************************** + * Name: nrf52_adc_reset + * + * Description: + * Reset the ADC device. Called early to initialize the hardware. + * This is called, before adc_setup() and on error conditions. + * + ****************************************************************************/ + +static void nrf52_adc_reset(FAR struct adc_dev_s *dev) +{ + FAR struct nrf52_adc_s *priv = (FAR struct nrf52_adc_s *) dev->ad_priv; + + DEBUGASSERT(dev); + DEBUGASSERT(priv); + + /* TODO */ + + UNUSED(priv); +} + +/**************************************************************************** + * Name: nrf52_adc_setup + * + * Description: + * Configure the ADC. This method is called the first time that the ADC + * device is opened. This will occur when the port is first opened. + * This setup includes configuring and attaching ADC interrupts. + * Interrupts are all disabled upon return. + * + ****************************************************************************/ + +static int nrf52_adc_setup(FAR struct adc_dev_s *dev) +{ + FAR struct nrf52_adc_s *priv = (FAR struct nrf52_adc_s *) dev->ad_priv; + int i = 0; + int ret = OK; + + DEBUGASSERT(dev); + DEBUGASSERT(priv); + + /* Enable ADC */ + + nrf52_adc_putreg(priv, NRF52_SAADC_ENABLE_OFFSET, 1); + + /* Calibrate ADC */ + + ret = nrf52_adc_calibrate(priv); + if (ret < 0) + { + aerr("ERROR: adc calibration failed: %d\n", ret); + goto errout; + } + + /* Configure ADC channels */ + + for (i = 0; i < priv->chan_len; i += 1) + { + ret = nrf52_adc_chancfg(priv, i, &priv->channels[i]); + if (ret < 0) + { + aerr("ERROR: chancfg failed: %d %d\n", i, ret); + goto errout; + } + } + + /* Confgiure ADC */ + + ret = nrf52_adc_configure(priv); + if (ret < 0) + { + aerr("ERROR: nrf52_adc_configure failed: %d\n", ret); + goto errout; + } + + /* Attach the ADC interrupt */ + + ret = irq_attach(priv->irq, nrf52_adc_isr, dev); + if (ret < 0) + { + aerr("ERROR: irq_attach failed: %d\n", ret); + goto errout; + } + + /* Enable the ADC interrupt */ + + up_enable_irq(priv->irq); + +errout: + return ret; +} + +/**************************************************************************** + * Name: nrf52_adc_shutdown + * + * Description: + * Disable the ADC. This method is called when the ADC device is closed. + * This method reverses the operation the setup method. + * + ****************************************************************************/ + +static void nrf52_adc_shutdown(FAR struct adc_dev_s *dev) +{ + FAR struct nrf52_adc_s *priv = (FAR struct nrf52_adc_s *) dev->ad_priv; + + DEBUGASSERT(dev); + DEBUGASSERT(priv); + + /* Stop SAADC */ + + nrf52_adc_putreg(priv, NRF52_SAADC_TASKS_STOP_OFFSET, 0); + + /* Wait for SAADC stopped */ + + while (nrf52_adc_getreg(priv, NRF52_SAADC_EVENTS_STOPPED_OFFSET) != 1); + + /* Disable SAADC */ + + nrf52_adc_putreg(priv, NRF52_SAADC_ENABLE_OFFSET, 0); +} + +/**************************************************************************** + * Name: nrf52_adc_rxint + * + * Description: + * Call to enable or disable RX interrupts. + * + ****************************************************************************/ + +static void nrf52_adc_rxint(FAR struct adc_dev_s *dev, bool enable) +{ + FAR struct nrf52_adc_s *priv = (FAR struct nrf52_adc_s *) dev->ad_priv; + uint32_t regval = 0; + + DEBUGASSERT(dev); + DEBUGASSERT(priv); + + ainfo("RXINT enable: %d\n", enable ? 1 : 0); + + regval = SAADC_INT_END; + + if (enable) + { + nrf52_adc_putreg(priv, NRF52_SAADC_INTENSET_OFFSET, regval); + } + else + { + nrf52_adc_putreg(priv, NRF52_SAADC_INTENCLR_OFFSET, regval); + } +} + +/**************************************************************************** + * Name: nrf52_adc_ioctl + * + * Description: + * All ioctl calls will be routed through this method. + * + ****************************************************************************/ + +static int nrf52_adc_ioctl(FAR struct adc_dev_s *dev, int cmd, + unsigned long arg) +{ + FAR struct nrf52_adc_s *priv = (FAR struct nrf52_adc_s *) dev->ad_priv; + int ret = OK; + + DEBUGASSERT(dev); + DEBUGASSERT(priv); + + switch (cmd) + { + case ANIOC_TRIGGER: + { + /* Start ADC */ + + nrf52_adc_putreg(priv, NRF52_SAADC_TASKS_START_OFFSET, 1); + + /* Trigger first sample */ + + nrf52_adc_putreg(priv, NRF52_SAADC_TASKS_SAMPLE_OFFSET, 1); + + break; + } + + default: + { + aerr("ERROR: Unknown cmd: %d\n", cmd); + ret = -ENOTTY; + break; + } + } + + return ret; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: nrf52_adcinitialize + * + * Description: + * Initialize the ADC. See nrf52_adc.c for more details. + * + * Input Parameters: + * chanlist - channels configuration + * nchannels - number of channels + * + * Returned Value: + * Valid ADC device structure reference on success; a NULL on failure + * + ****************************************************************************/ + +struct adc_dev_s *nrf52_adcinitialize(FAR struct nrf52_adc_channel_s *chan, + int channels) +{ + FAR struct adc_dev_s *dev = NULL; + FAR struct nrf52_adc_s *priv = NULL; + int i = 0; + + DEBUGASSERT(chan != NULL); + DEBUGASSERT(channels <= CONFIG_NRF52_SAADC_CHANNELS); + +#ifdef CONFIG_NRF52_SAADC_TIMER + if (channels > 1) + { + aerr("ERORR: timer trigger works only for 1 channel!\n"); + set_errno(-EINVAL); + goto errout; + } +#endif + + /* Get device */ + + dev = &g_nrf52_adc; + + /* Get private data */ + + priv = (FAR struct nrf52_adc_s *) dev->ad_priv; + + /* Copy channels configuration */ + + ainfo("channels: %d\n", channels); + + for (i = 0; i < channels; i += 1) + { + memcpy(&priv->channels[i], &chan[i], + sizeof(struct nrf52_adc_channel_s)); + } + + priv->chan_len = channels; + +#ifdef CONFIG_NRF52_SAADC_TIMER +errout: +#endif + return dev; +} diff --git a/arch/arm/src/nrf52/nrf52_adc.h b/arch/arm/src/nrf52/nrf52_adc.h new file mode 100644 index 00000000000..53bef1c43b3 --- /dev/null +++ b/arch/arm/src/nrf52/nrf52_adc.h @@ -0,0 +1,161 @@ +/**************************************************************************** + * arch/arm/src/nrf52/nrf52_adc.h + * + * 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_ADC_H +#define __ARCH_ARM_SRC_NRF52_NRF52_ADC_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include "chip.h" + +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* ADC input */ + +enum nrf52_adc_ain_e +{ + NRF52_ADC_IN_NC = 0, /* Not connected */ + NRF52_ADC_IN_IN0 = 1, /* Analog input 0 */ + NRF52_ADC_IN_IN1 = 2, /* Analog input 1 */ + NRF52_ADC_IN_IN2 = 3, /* Analog input 2 */ + NRF52_ADC_IN_IN3 = 4, /* Analog input 3 */ + NRF52_ADC_IN_IN4 = 5, /* Analog input 4 */ + NRF52_ADC_IN_IN5 = 6, /* Analog input 5 */ + NRF52_ADC_IN_IN6 = 7, /* Analog input 6 */ + NRF52_ADC_IN_IN7 = 8, /* Analog input 7 */ + NRF52_ADC_IN_VDD = 9, /* VDD */ + NRF52_ADC_IN_VDDHDIV5 = 10, /* VDDH/5 */ +}; + +/* Resistor control */ + +enum nrf52_adc_res_e +{ + NRF52_ADC_RES_BYPASS = 0, /* Bypass resistor ladder */ + NRF52_ADC_RES_PULLDOWN = 1, /* Pull-down to GND */ + NRF52_ADC_RES_PULLUP = 2, /* Pull-up to VDD */ + NRF52_ADC_RES_VDD_2 = 3 /* Set input at VDD/2 */ +}; + +/* Gain control */ + +enum nrf52_adc_gain_e +{ + NRF52_ADC_GAIN_1_6 = 0, /* 1/6 */ + NRF52_ADC_GAIN_1_5 = 1, /* 1/5 */ + NRF52_ADC_GAIN_1_4 = 2, /* 1/4 */ + NRF52_ADC_GAIN_1_3 = 3, /* 1/3 */ + NRF52_ADC_GAIN_1_2 = 4, /* 1/2 */ + NRF52_ADC_GAIN_1 = 5, /* 1 */ + NRF52_ADC_GAIN_2 = 6, /* 2 */ + NRF52_ADC_GAIN_4 = 7 /* 4 */ +}; + +/* Reference control */ + +enum nrf52_adc_refsel_e +{ + NRF52_ADC_REFSEL_INTERNAL = 0, /* Internal reference (0.6V) */ + NRF52_ADC_REFSEL_VDD_4 = 1 /* VDD/4 as reference */ +}; + +/* Acquisition time control */ + +enum nrf52_adc_tacq_e +{ + NRF52_ADC_TACQ_3US = 0, /* 3 us */ + NRF52_ADC_TACQ_5US = 1, /* 5 us */ + NRF52_ADC_TACQ_10US = 2, /* 10 us */ + NRF52_ADC_TACQ_15US = 3, /* 15 us */ + NRF52_ADC_TACQ_20US = 4, /* 20 us */ + NRF52_ADC_TACQ_40US = 5 /* 40 us */ +}; + +/* ADC mode control */ + +enum nrf52_adc_mode_e +{ + NRF52_ADC_MODE_SE = 0, /* Single-ended mode */ + NRF52_ADC_MODE_DIFF = 1 /* Differentail mode */ +}; + +/* ADC burst control */ + +enum nrf52_adc_burst_e +{ + NRF52_ADC_BURST_DISABLE = 0, /* Disable burst mode */ + NRF52_ADC_BURST_ENABLE = 1 /* Enable burst mode */ +}; + +/* NRF52 ADC channel configuration */ + +struct nrf52_adc_channel_s +{ + uint32_t p_psel; /* P pin */ + uint32_t n_psel; /* N pin */ +#ifdef CONFIG_NRF52_SAADC_LIMITS + uint16_t limith; /* High limit */ + uint16_t limitl; /* Low limit */ +#endif + uint8_t resp:2; /* Positive chan resistor */ + uint8_t resn:2; /* Negative chan resistor */ + uint8_t gain:3; /* Gain control */ + uint8_t refsel:1; /* Reference control */ + uint8_t tacq:3; /* Acquisition time */ + uint8_t mode:1; /* Singe-ended or differential mode */ + uint8_t burst:1; /* Burst mode */ + uint8_t _res:3; /* Reserved */ +}; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Name: nrf52_adcinitialize + * + * Description: + * Initialize the ADC. See nrf52_adc.c for more details. + * + * Input Parameters: + * chanlist - channels configuration + * nchannels - number of channels + * + * Returned Value: + * Valid ADC device structure reference on success; a NULL on failure + * + ****************************************************************************/ + +struct adc_dev_s *nrf52_adcinitialize(FAR struct nrf52_adc_channel_s *chan, + int channels); + +#endif /* __ARCH_ARM_SRC_NRF52_NRF52_ADC_H */