From d67bc0c3c82dd5fb4f7b2feaae1601207c1c84ab Mon Sep 17 00:00:00 2001 From: Thomas Axelsson Date: Tue, 15 Sep 2020 14:50:19 +0200 Subject: [PATCH] imxrt: ADC driver Based on LPC17xx_40xx and STM32 drivers. --- arch/arm/src/imxrt/Kconfig | 18 + arch/arm/src/imxrt/Make.defs | 4 + arch/arm/src/imxrt/hardware/imxrt_adc.h | 8 +- arch/arm/src/imxrt/hardware/imxrt_iomuxc.h | 2 + .../imxrt/hardware/rt106x/imxrt106x_pinmux.h | 34 + arch/arm/src/imxrt/imxrt_adc.c | 619 ++++++++++++++++++ arch/arm/src/imxrt/imxrt_adc.h | 100 +++ boards/arm/imxrt/imxrt1060-evk/src/Makefile | 4 + .../imxrt/imxrt1060-evk/src/imxrt1060-evk.h | 12 + .../arm/imxrt/imxrt1060-evk/src/imxrt_adc.c | 167 +++++ .../imxrt/imxrt1060-evk/src/imxrt_bringup.c | 10 + 11 files changed, 974 insertions(+), 4 deletions(-) create mode 100644 arch/arm/src/imxrt/imxrt_adc.c create mode 100644 arch/arm/src/imxrt/imxrt_adc.h create mode 100644 boards/arm/imxrt/imxrt1060-evk/src/imxrt_adc.c diff --git a/arch/arm/src/imxrt/Kconfig b/arch/arm/src/imxrt/Kconfig index 8290ff5481..0364e476db 100644 --- a/arch/arm/src/imxrt/Kconfig +++ b/arch/arm/src/imxrt/Kconfig @@ -163,6 +163,10 @@ config IMXRT_LPSPI bool default n +config IMXRT_ADC + bool + default n + config IMXRT_ENC bool default n @@ -518,6 +522,20 @@ menuconfig IMXRT_LPSPI4 endmenu # LPSPI Peripherals +menu "ADC Peripherals" + +menuconfig IMXRT_ADC1 + bool "ADC1" + default n + select IMXRT_ADC + +menuconfig IMXRT_ADC2 + bool "ADC2" + default n + select IMXRT_ADC + +endmenu + config IMXRT_SEMC bool "Smart External Memory Controller (SEMC)" default n diff --git a/arch/arm/src/imxrt/Make.defs b/arch/arm/src/imxrt/Make.defs index 3d3b8cf4ef..bfcefe5202 100644 --- a/arch/arm/src/imxrt/Make.defs +++ b/arch/arm/src/imxrt/Make.defs @@ -167,3 +167,7 @@ endif ifeq ($(CONFIG_IMXRT_USBDEV),y) CHIP_CSRCS += imxrt_usbdev.c endif + +ifeq ($(CONFIG_IMXRT_ADC),y) +CHIP_CSRCS += imxrt_adc.c +endif diff --git a/arch/arm/src/imxrt/hardware/imxrt_adc.h b/arch/arm/src/imxrt/hardware/imxrt_adc.h index de19fc85bf..78f04aef86 100644 --- a/arch/arm/src/imxrt/hardware/imxrt_adc.h +++ b/arch/arm/src/imxrt/hardware/imxrt_adc.h @@ -194,10 +194,10 @@ #define ADC_CFG_ADSTS_SHIFT (8) /* Bits: 8-9 Defines the sample time duration. */ #define ADC_CFG_ADSTS_MASK (3 << ADC_CFG_ADSTS_SHIFT) # define ADC_CFG_ADSTS(n) ((uint32_t)(n) << ADC_CFG_ADSTS_SHIFT) -# define ADC_CFG_ADSTS_2_12 (0 << ADC_CFG_ADSTS_SHIFT) /* Sample period (ADC clocks) = 2 if ADLSMP=0b, 12 if ADLSMP=1b */ -# define ADC_CFG_ADSTS_4_16 (1 << ADC_CFG_ADSTS_SHIFT) /* Sample period (ADC clocks) = 4 if ADLSMP=0b, 16 if ADLSMP=1b */ -# define ADC_CFG_ADSTS_6_20 (2 << ADC_CFG_ADSTS_SHIFT) /* Sample period (ADC clocks) = 6 if ADLSMP=0b, 20 if ADLSMP=1b */ -# define ADC_CFG_ADSTS_8_24 (3 << ADC_CFG_ADSTS_SHIFT) /* Sample period (ADC clocks) = 8 if ADLSMP=0b, 24 if ADLSMP=1b */ +# define ADC_CFG_ADSTS_3_13 (0 << ADC_CFG_ADSTS_SHIFT) /* Sample period (ADC clocks) = 3 if ADLSMP=0b, 13 if ADLSMP=1b */ +# define ADC_CFG_ADSTS_5_17 (1 << ADC_CFG_ADSTS_SHIFT) /* Sample period (ADC clocks) = 5 if ADLSMP=0b, 17 if ADLSMP=1b */ +# define ADC_CFG_ADSTS_7_21 (2 << ADC_CFG_ADSTS_SHIFT) /* Sample period (ADC clocks) = 7 if ADLSMP=0b, 21 if ADLSMP=1b */ +# define ADC_CFG_ADSTS_9_25 (3 << ADC_CFG_ADSTS_SHIFT) /* Sample period (ADC clocks) = 9 if ADLSMP=0b, 25 if ADLSMP=1b */ #define ADC_CFG_ADHSC (1 << 10) /* Bit: 10 High Speed Configuration*/ #define ADC_CFG_REFSEL_SHIFT (11) /* Bits: 11-12 Voltage Reference Selection */ #define ADC_CFG_REFSEL_MASK (3 << ADC_CFG_REFSEL_SHIFT) diff --git a/arch/arm/src/imxrt/hardware/imxrt_iomuxc.h b/arch/arm/src/imxrt/hardware/imxrt_iomuxc.h index 43041f4c63..493bf3b72f 100644 --- a/arch/arm/src/imxrt/hardware/imxrt_iomuxc.h +++ b/arch/arm/src/imxrt/hardware/imxrt_iomuxc.h @@ -181,4 +181,6 @@ IOMUX_SPEED_LOW ) #define IOMUX_USBOTG_OC_DEFAULT (IOMUX_PULL_UP_100K) +#define IOMUX_ADC_DEFAULT (0) + #endif /* __ARCH_ARM_SRC_IMXRT_HARDWARE_IMXRT_IOMUXC_H */ diff --git a/arch/arm/src/imxrt/hardware/rt106x/imxrt106x_pinmux.h b/arch/arm/src/imxrt/hardware/rt106x/imxrt106x_pinmux.h index 94608d0997..8b71af2335 100644 --- a/arch/arm/src/imxrt/hardware/rt106x/imxrt106x_pinmux.h +++ b/arch/arm/src/imxrt/hardware/rt106x/imxrt106x_pinmux.h @@ -1081,4 +1081,38 @@ #define GPIO_XBAR1_INOUT19_4 (GPIO_PERIPH | GPIO_ALT6 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_AD_B0_07_INDEX)) #define GPIO_XBAR1_XBAR_IN02_1 (GPIO_PERIPH | GPIO_ALT3 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_EMC_00_INDEX)) +/* ADC */ +#define GPIO_ADC1_CH0 (GPIO_PERIPH | GPIO_ALT5 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_AD_B1_11_INDEX)) +#define GPIO_ADC1_CH1 (GPIO_PERIPH | GPIO_ALT5 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_AD_B0_12_INDEX)) +#define GPIO_ADC1_CH2 (GPIO_PERIPH | GPIO_ALT5 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_AD_B0_13_INDEX)) +#define GPIO_ADC1_CH3 (GPIO_PERIPH | GPIO_ALT5 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_AD_B0_14_INDEX)) +#define GPIO_ADC1_CH4 (GPIO_PERIPH | GPIO_ALT5 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_AD_B0_15_INDEX)) +#define GPIO_ADC1_CH5 (GPIO_PERIPH | GPIO_ALT5 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_AD_B1_00_INDEX)) +#define GPIO_ADC1_CH6 (GPIO_PERIPH | GPIO_ALT5 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_AD_B1_01_INDEX)) +#define GPIO_ADC1_CH7 (GPIO_PERIPH | GPIO_ALT5 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_AD_B1_02_INDEX)) +#define GPIO_ADC1_CH8 (GPIO_PERIPH | GPIO_ALT5 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_AD_B1_03_INDEX)) +#define GPIO_ADC1_CH9 (GPIO_PERIPH | GPIO_ALT5 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_AD_B1_04_INDEX)) +#define GPIO_ADC1_CH10 (GPIO_PERIPH | GPIO_ALT5 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_AD_B1_05_INDEX)) +#define GPIO_ADC1_CH11 (GPIO_PERIPH | GPIO_ALT5 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_AD_B1_06_INDEX)) +#define GPIO_ADC1_CH12 (GPIO_PERIPH | GPIO_ALT5 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_AD_B1_07_INDEX)) +#define GPIO_ADC1_CH13 (GPIO_PERIPH | GPIO_ALT5 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_AD_B1_08_INDEX)) +#define GPIO_ADC1_CH14 (GPIO_PERIPH | GPIO_ALT5 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_AD_B1_09_INDEX)) +#define GPIO_ADC1_CH15 (GPIO_PERIPH | GPIO_ALT5 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_AD_B1_10_INDEX)) +#define GPIO_ADC2_CH0 (GPIO_PERIPH | GPIO_ALT5 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_AD_B1_11_INDEX)) +#define GPIO_ADC2_CH1 (GPIO_PERIPH | GPIO_ALT5 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_AD_B1_12_INDEX)) +#define GPIO_ADC2_CH2 (GPIO_PERIPH | GPIO_ALT5 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_AD_B1_13_INDEX)) +#define GPIO_ADC2_CH3 (GPIO_PERIPH | GPIO_ALT5 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_AD_B1_14_INDEX)) +#define GPIO_ADC2_CH4 (GPIO_PERIPH | GPIO_ALT5 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_AD_B1_15_INDEX)) +#define GPIO_ADC2_CH5 (GPIO_PERIPH | GPIO_ALT5 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_AD_B1_00_INDEX)) +#define GPIO_ADC2_CH6 (GPIO_PERIPH | GPIO_ALT5 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_AD_B1_01_INDEX)) +#define GPIO_ADC2_CH7 (GPIO_PERIPH | GPIO_ALT5 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_AD_B1_02_INDEX)) +#define GPIO_ADC2_CH8 (GPIO_PERIPH | GPIO_ALT5 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_AD_B1_03_INDEX)) +#define GPIO_ADC2_CH9 (GPIO_PERIPH | GPIO_ALT5 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_AD_B1_04_INDEX)) +#define GPIO_ADC2_CH10 (GPIO_PERIPH | GPIO_ALT5 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_AD_B1_05_INDEX)) +#define GPIO_ADC2_CH11 (GPIO_PERIPH | GPIO_ALT5 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_AD_B1_06_INDEX)) +#define GPIO_ADC2_CH12 (GPIO_PERIPH | GPIO_ALT5 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_AD_B1_07_INDEX)) +#define GPIO_ADC2_CH13 (GPIO_PERIPH | GPIO_ALT5 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_AD_B1_08_INDEX)) +#define GPIO_ADC2_CH14 (GPIO_PERIPH | GPIO_ALT5 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_AD_B1_09_INDEX)) +#define GPIO_ADC2_CH15 (GPIO_PERIPH | GPIO_ALT5 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_AD_B1_10_INDEX)) + #endif /* __ARCH_ARM_SRC_IMXRT_HARDWARE_IMXRT106X_PINMUX_H */ diff --git a/arch/arm/src/imxrt/imxrt_adc.c b/arch/arm/src/imxrt/imxrt_adc.c new file mode 100644 index 0000000000..585eb60a9f --- /dev/null +++ b/arch/arm/src/imxrt/imxrt_adc.c @@ -0,0 +1,619 @@ +/**************************************************************************** + * arch/arm/src/imxrt/imxrt_adc.c + * + * Copyright (C) 2020 Actia Nordic AB. All rights reserved. + * Author: Thomas Axelsson + * + * Based on arch/arm/src/lpc_17xx_40xx/imxrt_adc.c + * + * Copyright (C) 2011 Li Zhuoyi. All rights reserved. + * Copyright (C) 2016 Gregory Nutt. All rights reserved. + * Author: Li Zhuoyi + * Gregory Nutt + * + * and arch/arm/src/stm32/stm32_adc.c + * + * Copyright (C) 2018 Gregory Nutt. All rights reserved. + * Copyright (C) 2015 Omni Hoverboards Inc. All rights reserved. + * Authors: Gregory Nutt + * Diego Sanchez + * Paul Alexander Patience + * Mateusz Szafoni + * + * This file is a part of NuttX: + * + * Copyright (C) 2010, 2013, 2016 Gregory Nutt. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "arm_internal.h" +#include "arm_arch.h" + +#include "chip.h" +#include "hardware/imxrt_adc.h" +#include "hardware/imxrt_pinmux.h" +#include "imxrt_gpio.h" +#include "imxrt_periphclks.h" + +#ifdef CONFIG_IMXRT_ADC + +/* Some ADC peripheral must be enabled */ + +#if defined(CONFIG_IMXRT_ADC1) || defined(CONFIG_IMXRT_ADC2) + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define ADC_MAX_CHANNELS 16 + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct imxrt_dev_s +{ + FAR const struct adc_callback_s *cb; /* Upper driver callback */ + uint8_t intf; /* ADC number (i.e. ADC1, ADC2) */ + uint32_t base; /* ADC register base */ + uint8_t initialized; /* ADC initialization counter */ + int irq; /* ADC IRQ number */ + int nchannels; /* Number of configured ADC channels */ + uint8_t chanlist[ADC_MAX_CHANNELS]; /* ADC channel list */ + uint8_t current; /* Current channel being converted */ +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static void adc_putreg(FAR struct imxrt_dev_s *priv, uint32_t offset, + uint32_t value); +static uint32_t adc_getreg(FAR struct imxrt_dev_s *priv, uint32_t offset); +static void adc_modifyreg(FAR struct imxrt_dev_s *priv, uint32_t offset, + uint32_t clearbits, uint32_t setbits); + +/* ADC methods */ + +static int adc_bind(FAR struct adc_dev_s *dev, + FAR const struct adc_callback_s *callback); +static void adc_reset(FAR struct adc_dev_s *dev); +static int adc_setup(FAR struct adc_dev_s *dev); +static void adc_shutdown(FAR struct adc_dev_s *dev); +static void adc_rxint(FAR struct adc_dev_s *dev, bool enable); +static int adc_ioctl(FAR struct adc_dev_s *dev, int cmd, unsigned long arg); +static int adc_interrupt(int irq, void *context, FAR void *arg); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static const struct adc_ops_s g_adcops = +{ + .ao_bind = adc_bind, + .ao_reset = adc_reset, + .ao_setup = adc_setup, + .ao_shutdown = adc_shutdown, + .ao_rxint = adc_rxint, + .ao_ioctl = adc_ioctl, +}; + +#ifdef CONFIG_IMXRT_ADC1 +static struct imxrt_dev_s g_adcpriv1 = +{ + .irq = IMXRT_IRQ_ADC1, + .intf = 1, + .initialized = 0, + .base = IMXRT_ADC1_BASE, +}; + +static struct adc_dev_s g_adcdev1 = +{ + .ad_ops = &g_adcops, + .ad_priv = &g_adcpriv1, +}; + +gpio_pinset_t g_adcpinlist1[ADC_MAX_CHANNELS] = +{ + GPIO_ADC1_CH0, + GPIO_ADC1_CH1, + GPIO_ADC1_CH2, + GPIO_ADC1_CH3, + GPIO_ADC1_CH4, + GPIO_ADC1_CH5, + GPIO_ADC1_CH6, + GPIO_ADC1_CH7, + GPIO_ADC1_CH8, + GPIO_ADC1_CH9, + GPIO_ADC1_CH10, + GPIO_ADC1_CH11, + GPIO_ADC1_CH12, + GPIO_ADC1_CH13, + GPIO_ADC1_CH14, + GPIO_ADC1_CH15, +}; +#endif + +#ifdef CONFIG_IMXRT_ADC2 +static struct imxrt_dev_s g_adcpriv2 = +{ + .irq = IMXRT_IRQ_ADC2, + .intf = 2, + .initialized = 0, + .base = IMXRT_ADC2_BASE, +}; + +static struct adc_dev_s g_adcdev2 = +{ + .ad_ops = &g_adcops, + .ad_priv = &g_adcpriv2, +}; + +gpio_pinset_t g_adcpinlist2[ADC_MAX_CHANNELS] = +{ + GPIO_ADC2_CH0, + GPIO_ADC2_CH1, + GPIO_ADC2_CH2, + GPIO_ADC2_CH3, + GPIO_ADC2_CH4, + GPIO_ADC2_CH5, + GPIO_ADC2_CH6, + GPIO_ADC2_CH7, + GPIO_ADC2_CH8, + GPIO_ADC2_CH9, + GPIO_ADC2_CH10, + GPIO_ADC2_CH11, + GPIO_ADC2_CH12, + GPIO_ADC2_CH13, + GPIO_ADC2_CH14, + GPIO_ADC2_CH15, +}; +#endif + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static void adc_putreg(FAR struct imxrt_dev_s *priv, uint32_t offset, + uint32_t value) +{ + putreg32(value, priv->base + offset); +} + +static uint32_t adc_getreg(FAR struct imxrt_dev_s *priv, uint32_t offset) +{ + return getreg32(priv->base + offset); +} + +static void adc_modifyreg(FAR struct imxrt_dev_s *priv, uint32_t offset, + uint32_t clearbits, uint32_t setbits) +{ + modifyreg32(priv->base + offset, clearbits, setbits); +} + +/**************************************************************************** + * Name: 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 adc_bind(FAR struct adc_dev_s *dev, + FAR const struct adc_callback_s *callback) +{ + FAR struct imxrt_dev_s *priv = (FAR struct imxrt_dev_s *)dev->ad_priv; + + DEBUGASSERT(priv != NULL); + priv->cb = callback; + return OK; +} + +/**************************************************************************** + * Name: 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 adc_reset(FAR struct adc_dev_s *dev) +{ + FAR struct imxrt_dev_s *priv = (FAR struct imxrt_dev_s *)dev->ad_priv; + irqstate_t flags; + + flags = enter_critical_section(); + + /* Do nothing if ADC instance is currently in use */ + + if (priv->initialized > 0) + { + goto exit_leave_critical; + } + + /* Configure clock gating */ + + switch (priv->intf) + { +#ifdef CONFIG_IMXRT_ADC1 + case 1: + imxrt_clockall_adc1(); + break; +#endif +#ifdef CONFIG_IMXRT_ADC2 + case 2: + imxrt_clockall_adc2(); + break; +#endif + default: + aerr("ERROR: Tried to reset non-existing ADC: %d\n", priv->intf); + goto exit_leave_critical; + } + + leave_critical_section(flags); + + /* Configure ADC */ + + uint32_t adc_cfg = ADC_CFG_AVGS_4SMPL | ADC_CFG_ADTRG_SW | + ADC_CFG_REFSEL_VREF | ADC_CFG_ADSTS_7_21 | ADC_CFG_ADIV_DIV8 | \ + ADC_CFG_ADLSMP | ADC_CFG_MODE_10BIT | ADC_CFG_ADICLK_IPGDIV2; + adc_putreg(priv, IMXRT_ADC_CFG_OFFSET, adc_cfg); + + uint32_t adc_gc = 0; + adc_putreg(priv, IMXRT_ADC_GC_OFFSET, adc_gc); + + /* Calibration - After ADC has been configured as desired. + * ADTRG in ADC_CFG must be SW (0) during calibration + */ + + /* Clear calibration error */ + + adc_modifyreg(priv, IMXRT_ADC_GS_OFFSET, 0, ADC_GS_CALF); + + /* Start calibration */ + + adc_modifyreg(priv, IMXRT_ADC_GC_OFFSET, 0, ADC_GC_CAL); + + while ((adc_getreg(priv, IMXRT_ADC_GC_OFFSET) & ADC_GC_CAL) != 0 && + (adc_getreg(priv, IMXRT_ADC_GS_OFFSET) & ADC_GS_CALF) == 0); + + if ((adc_getreg(priv, IMXRT_ADC_GS_OFFSET) & ADC_GS_CALF) != 0 || + (adc_getreg(priv, IMXRT_ADC_HS_OFFSET) & ADC_HS_COCO0) == 0) + { + aerr("ERROR: ADC%d calibration failed\n", priv->intf); + return; + } + + /* Clear "conversion complete" */ + + uint32_t adc_r0 = adc_getreg(priv, IMXRT_ADC_R0_OFFSET); + UNUSED(adc_r0); + + /* Pad configuration */ + + gpio_pinset_t *pinlist = NULL; + switch (priv->intf) + { +#ifdef CONFIG_IMXRT_ADC1 + case 1: + pinlist = g_adcpinlist1; + break; +#endif +#ifdef CONFIG_IMXRT_ADC2 + case 2: + pinlist = g_adcpinlist2; + break; +#endif + default: + /* We have already checked the intf number earlier in this function, + * so we should never get here. + */ + + return; + } + + gpio_pinset_t pinset = 0; + for (int i = 0; i < priv->nchannels; i++) + { + DEBUGASSERT(priv->chanlist[i] < ADC_MAX_CHANNELS); + pinset = pinlist[priv->chanlist[i]] | IOMUX_ADC_DEFAULT; + imxrt_config_gpio(pinset); + } + + return; + +exit_leave_critical: + leave_critical_section(flags); +} + +/**************************************************************************** + * Name: 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 adc_setup(FAR struct adc_dev_s *dev) +{ + FAR struct imxrt_dev_s *priv = (FAR struct imxrt_dev_s *)dev->ad_priv; + + /* Do nothing when the ADC device is already set up */ + + if (priv->initialized > 0) + { + return OK; + } + + priv->initialized++; + + int ret = irq_attach(priv->irq, adc_interrupt, dev); + if (ret < 0) + { + ainfo("irq_attach failed: %d\n", ret); + return ret; + } + + up_enable_irq(priv->irq); + + /* Start the first conversion */ + + priv->current = 0; + adc_putreg(priv, IMXRT_ADC_HC0_OFFSET, + ADC_HC_ADCH(priv->chanlist[priv->current])); + + return ret; +} + +/**************************************************************************** + * Name: 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 adc_shutdown(FAR struct adc_dev_s *dev) +{ + FAR struct imxrt_dev_s *priv = (FAR struct imxrt_dev_s *)dev->ad_priv; + + /* Shutdown the ADC device only when not in use */ + + priv->initialized--; + + if (priv->initialized > 0) + { + return; + } + + /* Disable ADC interrupts, both at the level of the ADC device and at the + * level of the NVIC. + */ + + /* Disable interrupt and stop any on-going conversion */ + + adc_putreg(priv, IMXRT_ADC_HC0_OFFSET, ~ADC_HC_AIEN | ADC_HC_ADCH_DIS); + + up_disable_irq(priv->irq); + + /* Then detach the ADC interrupt handler. */ + + irq_detach(priv->irq); +} + +/**************************************************************************** + * Name: adc_rxint + * + * Description: + * Call to enable or disable RX interrupts + * + ****************************************************************************/ + +static void adc_rxint(FAR struct adc_dev_s *dev, bool enable) +{ + FAR struct imxrt_dev_s *priv = (FAR struct imxrt_dev_s *)dev->ad_priv; + + if (enable) + { + adc_modifyreg(priv, IMXRT_ADC_HC0_OFFSET, 0, ADC_HC_AIEN); + } + else + { + adc_modifyreg(priv, IMXRT_ADC_HC0_OFFSET, ADC_HC_AIEN, 0); + } +} + +/**************************************************************************** + * Name: adc_ioctl + * + * Description: + * All ioctl calls will be routed through this method. + * + * Input Parameters: + * dev - pointer to device structure used by the driver + * cmd - command + * arg - arguments passed with command + * + * Returned Value: + * + ****************************************************************************/ + +static int adc_ioctl(FAR struct adc_dev_s *dev, int cmd, unsigned long arg) +{ + /* No ioctl commands supported */ + + /* TODO: ANIOC_TRIGGER, for SW triggered conversion */ + + return -ENOTTY; +} + +/**************************************************************************** + * Name: adc_interrupt + * + * Description: + * ADC interrupt handler + * + ****************************************************************************/ + +static int adc_interrupt(int irq, void *context, FAR void *arg) +{ + FAR struct adc_dev_s *dev = (FAR struct adc_dev_s *)arg; + FAR struct imxrt_dev_s *priv = (FAR struct imxrt_dev_s *)dev->ad_priv; + int32_t data; + + if ((adc_getreg(priv, IMXRT_ADC_HS_OFFSET) & ADC_HS_COCO0) != 0) + { + /* Read data. This also clears the COCO bit. */ + + data = (int32_t)adc_getreg(priv, IMXRT_ADC_R0_OFFSET); + + if (priv->cb != NULL) + { + DEBUGASSERT(priv->cb->au_receive != NULL); + priv->cb->au_receive(dev, priv->chanlist[priv->current], data); + } + + /* Set the channel number of the next channel that will complete + * conversion. + */ + + priv->current++; + + if (priv->current >= priv->nchannels) + { + /* Restart the conversion sequence from the beginning */ + + priv->current = 0; + } + + /* Start the next conversion */ + + adc_modifyreg(priv, IMXRT_ADC_HC0_OFFSET, ADC_HC_ADCH_MASK, + ADC_HC_ADCH(priv->chanlist[priv->current])); + } + + /* There are no interrupt flags left to clear */ + + return OK; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: imxrt_adcinitialize + * + * Description: + * Initialize the adc + * + * Input Parameters: + * intf - ADC number (1 or 2) + * chanlist - The list of channels + * nchannels - Number of channels + * + * Returned Value: + * Valid can device structure reference on success; a NULL on failure + * + ****************************************************************************/ + +FAR struct adc_dev_s *imxrt_adcinitialize(int intf, + FAR const uint8_t *chanlist, + int nchannels) +{ + FAR struct adc_dev_s *dev; + FAR struct imxrt_dev_s *priv; + + DEBUGASSERT(nchannels > 0); + + switch (intf) + { +#ifdef CONFIG_IMXRT_ADC1 + case 1: + { + dev = &g_adcdev1; + break; + } +#endif /* CONFIG_IMXRT_ADC1 */ + +#ifdef CONFIG_IMXRT_ADC2 + case 2: + { + dev = &g_adcdev2; + break; + } +#endif /* CONFIG_IMXRT_ADC2 */ + + default: + { + aerr("ERROR: Tried to initialize invalid ADC: %d\n", intf); + return NULL; + } + } + + priv = (FAR struct imxrt_dev_s *)dev->ad_priv; + + priv->nchannels = nchannels; + memcpy(priv->chanlist, chanlist, nchannels); + + ainfo("intf: %d nchannels: %d\n", priv->intf, priv->nchannels); + + return dev; +} + +#endif /* CONFIG_IMXRT_ADC1 || CONFIG_IMXRT_ADC2 */ + +#endif /* CONFIG_IMXRT_ADC */ diff --git a/arch/arm/src/imxrt/imxrt_adc.h b/arch/arm/src/imxrt/imxrt_adc.h new file mode 100644 index 0000000000..1525f51e21 --- /dev/null +++ b/arch/arm/src/imxrt/imxrt_adc.h @@ -0,0 +1,100 @@ +/**************************************************************************** + * arch/arm/src/lpc17xx_40xx/lpc17_40_adc.h + * + * Copyright (C) 2020 Actia Nordic AB. All rights reserved. + * Author: Thomas Axelsson + * + * Based on arch/arm/src/lpc_17xx_40xx/imxrt_adc.h + * + * Copyright (C) 2010, 2012, 2013 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +#ifndef __ARCH_ARM_SRC_IMXRT_ADC_H +#define __ARCH_ARM_SRC_IMXRT_ADC_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Configuration ************************************************************/ + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +#undef EXTERN +#if defined(__cplusplus) +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +/**************************************************************************** + * Name: imxrt_adcinitialize + * + * Description: + * Initialize the adc + * + * Input Parameters: + * intf - ADC number (1 or 2) + * chanlist - The list of channels + * nchannels - Number of channels + * + * Returned Value: + * Valid can device structure reference on success; a NULL on failure + * + ****************************************************************************/ + +#ifdef CONFIG_IMXRT_ADC +FAR struct adc_dev_s *imxrt_adcinitialize(int intf, + FAR const uint8_t *chanlist, + int nchannels); +#endif + +#undef EXTERN +#ifdef __cplusplus +} +#endif + +#endif /* __ARCH_ARM_SRC_IMXRT_ADC_H */ diff --git a/boards/arm/imxrt/imxrt1060-evk/src/Makefile b/boards/arm/imxrt/imxrt1060-evk/src/Makefile index 7a073dce5f..9efc123ea2 100644 --- a/boards/arm/imxrt/imxrt1060-evk/src/Makefile +++ b/boards/arm/imxrt/imxrt1060-evk/src/Makefile @@ -73,6 +73,10 @@ ifeq ($(CONFIG_DEV_GPIO),y) CSRCS += imxrt_gpio.c endif +ifeq ($(CONFIG_IMXRT_ADC),y) +CSRCS += imxrt_adc.c +endif + ifeq ($(CONFIG_INPUT_FT5X06),y) CSRCS += imxrt_ft5x06.c endif diff --git a/boards/arm/imxrt/imxrt1060-evk/src/imxrt1060-evk.h b/boards/arm/imxrt/imxrt1060-evk/src/imxrt1060-evk.h index eb6e011389..216b916d6f 100644 --- a/boards/arm/imxrt/imxrt1060-evk/src/imxrt1060-evk.h +++ b/boards/arm/imxrt/imxrt1060-evk/src/imxrt1060-evk.h @@ -275,6 +275,18 @@ void imxrt_autoled_initialize(void); int imxrt_gpio_initialize(void); #endif +/**************************************************************************** + * Name: imxrt_adc_initialize + * + * Description: + * Initialize ADC drivers + * + ****************************************************************************/ + +#ifdef CONFIG_IMXRT_ADC +int imxrt_adc_initialize(void); +#endif + /**************************************************************************** * Name: imxrt_ft5x06_register * diff --git a/boards/arm/imxrt/imxrt1060-evk/src/imxrt_adc.c b/boards/arm/imxrt/imxrt1060-evk/src/imxrt_adc.c new file mode 100644 index 0000000000..119535f374 --- /dev/null +++ b/boards/arm/imxrt/imxrt1060-evk/src/imxrt_adc.c @@ -0,0 +1,167 @@ +/**************************************************************************** + * boards/arm/imxrt/imxrt1060-evk/src/imxrt_adc.c + * + * Copyright (C) 2020 Actia Nordic AB. All rights reserved. + * Author: Thomas Axelsson + * + * Based on boards/arm/lpc17xx_40xx/mbed/src/lpc17_40_adc.c + * + * Based on boards/zkit-arm-176/src/up-adc + * + * Copyright (C) 2013 Zilogic Systems. All rights reserved. + * Author: Kannan + * + * Based on boards/lpc1720g-eval/src/lpc17_40_adc.c + * + * Copyright (C) 2012, 2014, 2016 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include + +#include +#include +#include + +#include "chip.h" +#include "arm_arch.h" + +#include "imxrt_adc.h" + +#ifdef CONFIG_IMXRT_ADC + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Configuration ************************************************************/ + +#define ADC1_NCHANNELS 3 +#define ADC2_NCHANNELS 3 + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static const uint8_t g_chanlist1[ADC1_NCHANNELS] = + { + /* Arduino Interface pins A0 to A2 */ + + 15, + 0, + 9, + }; + +static const uint8_t g_chanlist2[ADC2_NCHANNELS] = + { + /* Arduino Interface pins A3 to A5 */ + + 10, + 6, + 5 + }; + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: imxrt_adc_setup + * + * Description: + * Initialize ADC and register the ADC driver. + * + ****************************************************************************/ + +int imxrt_adc_initialize(void) +{ + static bool initialized = false; + struct adc_dev_s *adc; + int ret; + + /* Check if we have already initialized */ + + if (!initialized) + { + /* Call imxrt_adcinitialize() to get an instance of the ADC interface */ + +#ifdef CONFIG_IMXRT_ADC1 + adc = imxrt_adcinitialize(1, g_chanlist1, ADC1_NCHANNELS); + if (adc == NULL) + { + aerr("ERROR: Failed to get ADC interface for ADC1\n"); + return -ENODEV; + } + + /* Register the ADC driver at "/dev/adc1" */ + + ret = adc_register("/dev/adc1", adc); + if (ret < 0) + { + aerr("ERROR: adc_register adc1 failed: %d\n", ret); + return ret; + } +#endif + +#ifdef CONFIG_IMXRT_ADC2 + adc = imxrt_adcinitialize(2, g_chanlist2, ADC2_NCHANNELS); + if (adc == NULL) + { + aerr("ERROR: Failed to get ADC interface for ADC2\n"); + return -ENODEV; + } + + /* Register the ADC driver at "/dev/adc2" */ + + ret = adc_register("/dev/adc2", adc); + if (ret < 0) + { + aerr("ERROR: adc_register adc2 failed: %d\n", ret); + return ret; + } +#endif + + /* Now we are initialized */ + + initialized = true; + } + + return OK; +} + +#endif /* CONFIG_ADC */ diff --git a/boards/arm/imxrt/imxrt1060-evk/src/imxrt_bringup.c b/boards/arm/imxrt/imxrt1060-evk/src/imxrt_bringup.c index 8584f9eef2..c0e07ba79d 100644 --- a/boards/arm/imxrt/imxrt1060-evk/src/imxrt_bringup.c +++ b/boards/arm/imxrt/imxrt1060-evk/src/imxrt_bringup.c @@ -223,6 +223,16 @@ int imxrt_bringup(void) } #endif +#ifdef CONFIG_IMXRT_ADC + /* Initialize ADC and register the ADC driver. */ + + ret = imxrt_adc_initialize(); + if (ret < 0) + { + syslog(LOG_ERR, "ERROR: imxrt_adc_initialize() failed: %d\n", ret); + } +#endif + #ifdef CONFIG_INPUT_FT5X06 /* Initialize the FT5X06 touchscreen driver */