diff options
Diffstat (limited to 'nuttx/arch/arm/src/stm32/stm32_adc.c')
-rw-r--r-- | nuttx/arch/arm/src/stm32/stm32_adc.c | 1780 |
1 files changed, 1780 insertions, 0 deletions
diff --git a/nuttx/arch/arm/src/stm32/stm32_adc.c b/nuttx/arch/arm/src/stm32/stm32_adc.c new file mode 100644 index 000000000..a45b732ae --- /dev/null +++ b/nuttx/arch/arm/src/stm32/stm32_adc.c @@ -0,0 +1,1780 @@ +/**************************************************************************** + * arch/arm/src/stm32/stm32_adc.c + * + * Copyright (C) 2011 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt <gnutt@nuttx.org> + * Diego Sanchez <dsanchez@nx-engineering.com> + * + * 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 <nuttx/config.h> + +#include <stdio.h> +#include <sys/types.h> +#include <stdint.h> +#include <stdbool.h> +#include <unistd.h> +#include <string.h> +#include <semaphore.h> +#include <errno.h> +#include <assert.h> +#include <debug.h> +#include <unistd.h> + +#include <arch/board/board.h> +#include <nuttx/arch.h> +#include <nuttx/analog/adc.h> + +#include "up_internal.h" +#include "up_arch.h" + +#include "chip.h" +#include "stm32_internal.h" +#include "stm32_adc.h" + +#ifdef CONFIG_ADC +#if defined(CONFIG_STM32_ADC1) || defined(CONFIG_STM32_ADC2) || defined(CONFIG_STM32_ADC3) + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ +/* ADC interrupts ***********************************************************/ + +#ifdef CONFIG_STM32_STM32F10XX +# define ADC_SR_ALLINTS (ADC_SR_AWD | ADC_SR_EOC | ADC_SR_JEOC) +#else +# define ADC_SR_ALLINTS (ADC_SR_AWD | ADC_SR_EOC | ADC_SR_JEOC | ADC_SR_OVR) +#endif + +#ifdef CONFIG_STM32_STM32F10XX +# define ADC_CR1_ALLINTS (ADC_CR1_AWDIE | ADC_CR1_EOCIE | ADC_CR1_JEOCIE) +#else +# define ADC_CR1_ALLINTS (ADC_CR1_AWDIE | ADC_CR1_EOCIE | ADC_CR1_JEOCIE | ADC_CR1_OVRIE) +#endif + +/* The maximum number of channels that can be sampled. If dma support is + * not enabled, then only a single channel can be sampled. Otherwise, + * data overruns would occur. + */ + +#ifdef CONFIG_ADC_DMA +# define ADC_MAX_SAMPLES 16 +#else +# define ADC_MAX_SAMPLES 1 +#endif + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/* This structure describes the state of one ADC block */ + +struct stm32_dev_s +{ + uint8_t irq; /* Interrupt generated by this ADC block */ + uint8_t nchannels; /* Number of channels */ + uint8_t intf; /* ADC interface number */ + uint8_t current; /* Current ADC channel being converted */ +#ifdef ADC_HAVE_TIMER + uint8_t trigger; /* Timer trigger channel: 0=CC1, 1=CC2, 2=CC3, 3=CC4, 4=TRGO */ +#endif + xcpt_t isr; /* Interrupt handler for this ADC block */ + uint32_t base; /* Base address of registers unique to this ADC block */ +#ifdef ADC_HAVE_TIMER + uint32_t tbase; /* Base address of timer used by this ADC block */ + uint32_t extsel; /* EXTSEL value used by this ADC block */ + uint32_t pclck; /* The PCLK frequency that drives this timer */ + uint32_t freq; /* The desired frequency of conversions */ +#endif + uint8_t chanlist[ADC_MAX_SAMPLES]; +#ifdef CONFIG_ADC_DMA + const unsigned int rxdma_channel; /* DMA channel assigned */ + DMA_HANDLE rxdma; /* currently-open receive DMA stream */ + bool rxenable; /* DMA-based reception en/disable */ + uint32_t rxdmanext; /* Next byte in the DMA buffer to be read */ + int32_t *const rxfifo; /* Receive DMA buffer */ +#endif +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +# ifdef CONFIG_ADC_DMA +static int16_t g_adc3dmarxfifo[ADC_MAX_SAMPLES]; +# endif + + + +/* ADC Register access */ + +static uint32_t adc_getreg(struct stm32_dev_s *priv, int offset); +static void adc_putreg(struct stm32_dev_s *priv, int offset, uint32_t value); +#ifdef ADC_HAVE_TIMER +static uint16_t tim_getreg(struct stm32_dev_s *priv, int offset); +static void tim_putreg(struct stm32_dev_s *priv, int offset, uint16_t value); +static void adc_tim_dumpregs(struct stm32_dev_s *priv, FAR const char *msg); +#endif +static void adc_rccreset(struct stm32_dev_s *priv, bool reset); + +/* ADC Interrupt Handler */ + +static int adc_interrupt(FAR struct adc_dev_s *dev); +#if defined(CONFIG_STM32_STM32F10XX) && (defined(CONFIG_STM32_ADC1) || defined(CONFIG_STM32_ADC2)) +static int adc12_interrupt(int irq, void *context); +#endif +#if defined(CONFIG_STM32_STM32F10XX) && defined (CONFIG_STM32_ADC3) +static int adc3_interrupt(int irq, void *context); +#endif +#if defined(CONFIG_STM32_STM32F20XX) || defined(CONFIG_STM32_STM32F40XX) +static int adc123_interrupt(int irq, void *context); +#endif + +/* ADC Driver Methods */ + +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 void adc_enable(FAR struct stm32_dev_s *priv, bool enable); + +#ifdef ADC_HAVE_TIMER +static void adc_timstart(FAR struct stm32_dev_s *priv, bool enable); +static int adc_timinit(FAR struct stm32_dev_s *priv); +#endif + +#if defined(CONFIG_STM32_STM32F20XX) || defined(CONFIG_STM32_STM32F40XX) +static void adc_startconv(FAR struct stm32_dev_s *priv, bool enable); +#endif + +#ifdef CONFIG_ADC_DMA +static int up_dma_setup(struct adc_dev_s *dev); +static void up_dma_shutdown(struct adc_dev_s *dev); +//static int up_dma_receive(struct uart_dev_s *dev, uint32_t *status); +//static void up_dma_rxint(struct uart_dev_s *dev, bool enable); +//static bool up_dma_rxavailable(struct uart_dev_s *dev); +static void up_dma_rxcallback(DMA_HANDLE handle, uint8_t status, FAR struct adc_dev_s *dev); +#endif + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* ADC interface operations */ + +static const struct adc_ops_s g_adcops = +{ + .ao_reset = adc_reset, + .ao_setup = adc_setup, + .ao_shutdown = adc_shutdown, + .ao_rxint = adc_rxint, + .ao_ioctl = adc_ioctl, +}; + +/* ADC1 state */ + +#ifdef CONFIG_STM32_ADC1 +static struct stm32_dev_s g_adcpriv1 = +{ +#ifdef CONFIG_STM32_STM32F10XX + .irq = STM32_IRQ_ADC12, + .isr = adc12_interrupt, +#else + .irq = STM32_IRQ_ADC, + .isr = adc123_interrupt, +#endif + .intf = 1, + .base = STM32_ADC1_BASE, +#ifdef ADC1_HAVE_TIMER + .trigger = CONFIG_STM32_ADC1_TIMTRIG, + .tbase = ADC1_TIMER_BASE, + .extsel = ADC1_EXTSEL_VALUE, + .pclck = ADC1_TIMER_PCLK_FREQUENCY, + .freq = CONFIG_STM32_ADC1_SAMPLE_FREQUENCY, +#endif +}; + +static struct adc_dev_s g_adcdev1 = +{ + .ad_ops = &g_adcops, + .ad_priv= &g_adcpriv1, +}; +#endif + +/* ADC2 state */ + +#ifdef CONFIG_STM32_ADC2 +static struct stm32_dev_s g_adcpriv2 = +{ +#ifdef CONFIG_STM32_STM32F10XX + .irq = STM32_IRQ_ADC12, + .isr = adc12_interrupt, +#else + .irq = STM32_IRQ_ADC, + .isr = adc123_interrupt, +#endif + .intf = 2, + .base = STM32_ADC2_BASE, +#ifdef ADC2_HAVE_TIMER + .trigger = CONFIG_STM32_ADC2_TIMTRIG, + .tbase = ADC2_TIMER_BASE, + .extsel = ADC2_EXTSEL_VALUE, + .pclck = ADC2_TIMER_PCLK_FREQUENCY, + .freq = CONFIG_STM32_ADC2_SAMPLE_FREQUENCY, +#endif +}; + +static struct adc_dev_s g_adcdev2 = +{ + .ad_ops = &g_adcops, + .ad_priv= &g_adcpriv2, +}; +#endif + +/* ADC3 state */ + +#ifdef CONFIG_STM32_ADC3 +static struct stm32_dev_s g_adcpriv3 = +{ +#ifdef CONFIG_STM32_STM32F10XX + .irq = STM32_IRQ_ADC3, + .isr = adc3_interrupt, +#else + .irq = STM32_IRQ_ADC, + .isr = adc123_interrupt, +#endif + .intf = 3, + .base = STM32_ADC3_BASE, +#ifdef ADC3_HAVE_TIMER + .trigger = CONFIG_STM32_ADC3_TIMTRIG, + .tbase = ADC3_TIMER_BASE, + .extsel = ADC3_EXTSEL_VALUE, + .pclck = ADC3_TIMER_PCLK_FREQUENCY, + .freq = CONFIG_STM32_ADC3_SAMPLE_FREQUENCY, +#endif +#ifdef CONFIG_ADC_DMA + .rxdma_channel = DMAMAP_ADC3_2, + .rxfifo = g_adc3dmarxfifo, +#endif +}; + +static struct adc_dev_s g_adcdev3 = +{ + .ad_ops = &g_adcops, + .ad_priv= &g_adcpriv3, +}; +#endif + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: adc_getreg + * + * Description: + * Read the value of an ADC register. + * + * Input Parameters: + * priv - A reference to the ADC block status + * offset - The offset to the register to read + * + * Returned Value: + * + ****************************************************************************/ + +static uint32_t adc_getreg(struct stm32_dev_s *priv, int offset) +{ + return getreg32(priv->base + offset); +} + +/**************************************************************************** + * Name: adc_getreg + * + * Description: + * Read the value of an ADC register. + * + * Input Parameters: + * priv - A reference to the ADC block status + * offset - The offset to the register to read + * + * Returned Value: + * + ****************************************************************************/ + +static void adc_putreg(struct stm32_dev_s *priv, int offset, uint32_t value) +{ + putreg32(value, priv->base + offset); +} + +/**************************************************************************** + * Name: tim_getreg + * + * Description: + * Read the value of an ADC timer register. + * + * Input Parameters: + * priv - A reference to the ADC block status + * offset - The offset to the register to read + * + * Returned Value: + * The current contents of the specified register + * + ****************************************************************************/ + +#ifdef ADC_HAVE_TIMER +static uint16_t tim_getreg(struct stm32_dev_s *priv, int offset) +{ + return getreg16(priv->tbase + offset); +} +#endif + +/**************************************************************************** + * Name: tim_putreg + * + * Description: + * Read the value of an ADC timer register. + * + * Input Parameters: + * priv - A reference to the ADC block status + * offset - The offset to the register to read + * + * Returned Value: + * None + * + ****************************************************************************/ + +#ifdef ADC_HAVE_TIMER +static void tim_putreg(struct stm32_dev_s *priv, int offset, uint16_t value) +{ + putreg16(value, priv->tbase + offset); +} +#endif + +/**************************************************************************** + * Name: adc_tim_dumpregs + * + * Description: + * Dump all timer registers. + * + * Input parameters: + * priv - A reference to the ADC block status + * + * Returned Value: + * None + * + ****************************************************************************/ + +#ifdef ADC_HAVE_TIMER +static void adc_tim_dumpregs(struct stm32_dev_s *priv, FAR const char *msg) +{ +#if defined(CONFIG_DEBUG_ANALOG) && defined(CONFIG_DEBUG_VERBOSE) + avdbg("%s:\n", msg); + avdbg(" CR1: %04x CR2: %04x SMCR: %04x DIER: %04x\n", + tim_getreg(priv, STM32_GTIM_CR1_OFFSET), + tim_getreg(priv, STM32_GTIM_CR2_OFFSET), + tim_getreg(priv, STM32_GTIM_SMCR_OFFSET), + tim_getreg(priv, STM32_GTIM_DIER_OFFSET)); + avdbg(" SR: %04x EGR: 0000 CCMR1: %04x CCMR2: %04x\n", + tim_getreg(priv, STM32_GTIM_SR_OFFSET), + tim_getreg(priv, STM32_GTIM_CCMR1_OFFSET), + tim_getreg(priv, STM32_GTIM_CCMR2_OFFSET)); + avdbg(" CCER: %04x CNT: %04x PSC: %04x ARR: %04x\n", + tim_getreg(priv, STM32_GTIM_CCER_OFFSET), + tim_getreg(priv, STM32_GTIM_CNT_OFFSET), + tim_getreg(priv, STM32_GTIM_PSC_OFFSET), + tim_getreg(priv, STM32_GTIM_ARR_OFFSET)); + avdbg(" CCR1: %04x CCR2: %04x CCR3: %04x CCR4: %04x\n", + tim_getreg(priv, STM32_GTIM_CCR1_OFFSET), + tim_getreg(priv, STM32_GTIM_CCR2_OFFSET), + tim_getreg(priv, STM32_GTIM_CCR3_OFFSET), + tim_getreg(priv, STM32_GTIM_CCR4_OFFSET)); + + if (priv->tbase == STM32_TIM1_BASE || priv->tbase == STM32_TIM8_BASE) + { + avdbg(" RCR: %04x BDTR: %04x DCR: %04x DMAR: %04x\n", + tim_getreg(priv, STM32_ATIM_RCR_OFFSET), + tim_getreg(priv, STM32_ATIM_BDTR_OFFSET), + tim_getreg(priv, STM32_ATIM_DCR_OFFSET), + tim_getreg(priv, STM32_ATIM_DMAR_OFFSET)); + } + else + { + avdbg(" DCR: %04x DMAR: %04x\n", + tim_getreg(priv, STM32_GTIM_DCR_OFFSET), + tim_getreg(priv, STM32_GTIM_DMAR_OFFSET)); + } +#endif +} +#endif + +/**************************************************************************** + * Name: adc_timstart + * + * Description: + * Start (or stop) the timer counter + * + * Input Parameters: + * priv - A reference to the ADC block status + * enable - True: Start conversion + * + * Returned Value: + * + ****************************************************************************/ + + #ifdef ADC_HAVE_TIMER +static void adc_timstart(struct stm32_dev_s *priv, bool enable) +{ + uint16_t regval; + + avdbg("enable: %d\n", enable); + regval = tim_getreg(priv, STM32_GTIM_CR1_OFFSET); + + if (enable) + { + /* Start the counter */ + + regval |= ATIM_CR1_CEN; + } + + else + { + /* Disable the counter */ + + regval &= ~ATIM_CR1_CEN; + } + tim_putreg(priv, STM32_GTIM_CR1_OFFSET, regval); +} +#endif + +/**************************************************************************** + * Name: adc_timinit + * + * Description: + * Initialize the timer that drivers the ADC sampling for this channel using + * the pre-calculated timer divider definitions. + * + * Input Parameters: + * priv - A reference to the ADC block status + * + * Returned Value: + * Zero on success; a negated errno value on failure. + * + ****************************************************************************/ + +#ifdef ADC_HAVE_TIMER +static int adc_timinit(FAR struct stm32_dev_s *priv) +{ + uint32_t prescaler; + uint32_t reload; + uint32_t regval; + uint32_t timclk; + + uint16_t cr1; + uint16_t cr2; + uint16_t ccmr1; + uint16_t ccmr2; + uint16_t ocmode1; + uint16_t ocmode2; + uint16_t ccenable; + uint16_t ccer; + uint16_t egr; + + avdbg("Initializing timers extsel = %d\n", priv->extsel); + + /* If the timer base address is zero, then this ADC was not configured to + * use a timer. + */ + + regval = adc_getreg(priv, STM32_ADC_CR2_OFFSET); + +#ifdef CONFIG_STM32_STM32F10XX + if (!priv->tbase) + { + /* Configure the ADC to use the selected timer and timer channel as the trigger + * EXTTRIG: External Trigger Conversion mode for regular channels DISABLE + */ + + regval &= ~ADC_CR2_EXTTRIG; + adc_putreg(priv, STM32_ADC_CR2_OFFSET, regval); + return OK; + } + else + { + regval |= ADC_CR2_EXTTRIG; + } +#endif + + /* EXTSEL selection: These bits select the external event used to trigger + * the start of conversion of a regular group. NOTE: + * + * - The position with with of the EXTSEL field varies from one STM32 MCU + * to another. + * - The width of the EXTSEL field varies from one STM3 MCU to another. + * - The value in priv->extsel is already shifted into the correct bit position. + */ + + regval &= ~ADC_CR2_EXTSEL_MASK; + regval |= priv->extsel; + adc_putreg(priv, STM32_ADC_CR2_OFFSET, regval); + + /* Configure the timer channel to drive the ADC */ + + /* Caculate optimal values for the timer prescaler and for the timer reload + * register. If freq is the desired frequency, then + * + * reload = timclk / freq + * reload = (pclck / prescaler) / freq + * + * There are many solutions to do this, but the best solution will be the + * one that has the largest reload value and the smallest prescaler value. + * That is the solution that should give us the most accuracy in the timer + * control. Subject to: + * + * 0 <= prescaler <= 65536 + * 1 <= reload <= 65535 + * + * So ( prescaler = pclck / 65535 / freq ) would be optimal. + */ + + prescaler = 128;//(priv->pclck / priv->freq + 65534) / 65535; + + /* We need to decrement the prescaler value by one, but only, the value does + * not underflow. + */ + + if (prescaler < 1) + { + adbg("WARNING: Prescaler underflowed.\n"); + prescaler = 1; + } + + /* Check for overflow */ + + else if (prescaler > 65536) + { + adbg("WARNING: Prescaler overflowed.\n"); + prescaler = 65536; + } + + timclk = priv->pclck / prescaler; + + reload = priv->freq;//timclk / priv->freq; + if (reload < 1) + { + adbg("WARNING: Reload value underflowed.\n"); + reload = 1; + } + else if (reload > 65535) + { + adbg("WARNING: Reload value overflowed.\n"); + reload = 65535; + } + + /* Set up the timer CR1 register */ + + cr1 = tim_getreg(priv, STM32_GTIM_CR1_OFFSET); + + /* Disable the timer until we get it configured */ + + adc_timstart(priv, false); + + /* Select the Counter Mode == count up: + * + * ATIM_CR1_EDGE: The counter counts up or down depending on the + * direction bit(DIR). + * ATIM_CR1_DIR: 0: count up, 1: count down + */ + + cr1 &= ~(ATIM_CR1_DIR | ATIM_CR1_CMS_MASK); + cr1 |= ATIM_CR1_EDGE; + + /* Set the clock division to zero for all */ + + cr1 &= ~GTIM_CR1_CKD_MASK; + tim_putreg(priv, STM32_GTIM_CR1_OFFSET, cr1); + + /* Set the reload and prescaler values */ + + tim_putreg(priv, STM32_GTIM_PSC_OFFSET, prescaler-1); + tim_putreg(priv, STM32_GTIM_ARR_OFFSET, reload); + + /* Clear the advanced timers repitition counter in TIM1 */ + + if (priv->tbase == STM32_TIM1_BASE || priv->tbase == STM32_TIM8_BASE) + { + tim_putreg(priv, STM32_ATIM_RCR_OFFSET, 0); + tim_putreg(priv, STM32_ATIM_BDTR_OFFSET, ATIM_BDTR_MOE); /* Check me */ + } + + /* TIMx event generation: Bit 0 UG: Update generation */ + + tim_putreg(priv, STM32_GTIM_EGR_OFFSET, ATIM_EGR_UG); + + /* Handle channel specific setup */ + + ocmode1 = 0; + ocmode2 = 0; + + switch (priv->trigger) + { + case 0: /* TimerX CC1 event */ + { + ccenable = ATIM_CCER_CC1E; + ocmode1 = (ATIM_CCMR_CCS_CCOUT << ATIM_CCMR1_CC1S_SHIFT) | + (ATIM_CCMR_MODE_PWM1 << ATIM_CCMR1_OC1M_SHIFT) | + ATIM_CCMR1_OC1PE; + + /* Set the event CC1 */ + + egr = ATIM_EGR_CC1G; + + /* Set the duty cycle by writing to the CCR register for this channel */ + + tim_putreg(priv, STM32_GTIM_CCR1_OFFSET, (uint16_t)(reload >> 1)); + } + break; + + case 1: /* TimerX CC2 event */ + { + ccenable = ATIM_CCER_CC2E; + ocmode1 = (ATIM_CCMR_CCS_CCOUT << ATIM_CCMR1_CC2S_SHIFT) | + (ATIM_CCMR_MODE_PWM1 << ATIM_CCMR1_OC2M_SHIFT) | + ATIM_CCMR1_OC2PE; + + /* Set the event CC2 */ + + egr = ATIM_EGR_CC2G; + + /* Set the duty cycle by writing to the CCR register for this channel */ + + tim_putreg(priv, STM32_GTIM_CCR2_OFFSET, (uint16_t)(reload >> 1)); + } + break; + + case 2: /* TimerX CC3 event */ + { + ccenable = ATIM_CCER_CC3E; + ocmode2 = (ATIM_CCMR_CCS_CCOUT << ATIM_CCMR2_CC3S_SHIFT) | + (ATIM_CCMR_MODE_PWM1 << ATIM_CCMR2_OC3M_SHIFT) | + ATIM_CCMR2_OC3PE; + + /* Set the event CC3 */ + + egr = ATIM_EGR_CC3G; + + /* Set the duty cycle by writing to the CCR register for this channel */ + + tim_putreg(priv, STM32_GTIM_CCR3_OFFSET, (uint16_t)(reload >> 1)); + } + break; + + case 3: /* TimerX CC4 event */ + { + ccenable = ATIM_CCER_CC4E; + ocmode2 = (ATIM_CCMR_CCS_CCOUT << ATIM_CCMR2_CC4S_SHIFT) | + (ATIM_CCMR_MODE_PWM1 << ATIM_CCMR2_OC4M_SHIFT) | + ATIM_CCMR2_OC4PE; + + /* Set the event CC4 */ + + egr = ATIM_EGR_CC4G; + + /* Set the duty cycle by writing to the CCR register for this channel */ + + tim_putreg(priv, STM32_GTIM_CCR4_OFFSET, (uint16_t)(reload >> 1)); + } + break; + + case 4: /* TimerX TRGO event */ + { +#warning "TRGO support not yet implemented" + + /* Set the event TRGO */ + + egr = GTIM_EGR_TG; + + /* Set the duty cycle by writing to the CCR register for this channel */ + + tim_putreg(priv, STM32_GTIM_CCR4_OFFSET, (uint16_t)(reload >> 1)); + } + break; + + default: + adbg("No such trigger: %d\n", priv->trigger); + return -EINVAL; + } + + /* Disable the Channel by resetting the CCxE Bit in the CCER register */ + + ccer = tim_getreg(priv, STM32_GTIM_CCER_OFFSET); + ccer &= ~ccenable; + tim_putreg(priv, STM32_GTIM_CCER_OFFSET, ccer); + + /* Fetch the CR2, CCMR1, and CCMR2 register (already have cr1 and ccer) */ + + cr2 = tim_getreg(priv, STM32_GTIM_CR2_OFFSET); + ccmr1 = tim_getreg(priv, STM32_GTIM_CCMR1_OFFSET); + ccmr2 = tim_getreg(priv, STM32_GTIM_CCMR2_OFFSET); + + /* Reset the Output Compare Mode Bits and set the select output compare mode */ + + ccmr1 &= ~(ATIM_CCMR1_CC1S_MASK | ATIM_CCMR1_OC1M_MASK | ATIM_CCMR1_OC1PE | + ATIM_CCMR1_CC2S_MASK | ATIM_CCMR1_OC2M_MASK | ATIM_CCMR1_OC2PE); + ccmr2 &= ~(ATIM_CCMR2_CC3S_MASK | ATIM_CCMR2_OC3M_MASK | ATIM_CCMR2_OC3PE | + ATIM_CCMR2_CC4S_MASK | ATIM_CCMR2_OC4M_MASK | ATIM_CCMR2_OC4PE); + ccmr1 |= ocmode1; + ccmr2 |= ocmode2; + + /* Reset the output polarity level of all channels (selects high polarity)*/ + + ccer &= ~(ATIM_CCER_CC1P | ATIM_CCER_CC2P | ATIM_CCER_CC3P | ATIM_CCER_CC4P); + + /* Enable the output state of the selected channel (only) */ + + ccer &= ~(ATIM_CCER_CC1E | ATIM_CCER_CC2E | ATIM_CCER_CC3E | ATIM_CCER_CC4E); + ccer |= ccenable; + + if (priv->tbase == STM32_TIM1_BASE || priv->tbase == STM32_TIM8_BASE) + { + /* Reset output N polarity level, output N state, output compre state, + * output compare N idle state. + */ +#if defined(CONFIG_STM32_STM32F20XX) || defined(CONFIG_STM32_STM32F40XX) + ccer &= ~(ATIM_CCER_CC1NE | ATIM_CCER_CC1NP | ATIM_CCER_CC2NE | ATIM_CCER_CC2NP | + ATIM_CCER_CC3NE | ATIM_CCER_CC3NP | ATIM_CCER_CC4NP); +#else + ccer &= ~(ATIM_CCER_CC1NE | ATIM_CCER_CC1NP | ATIM_CCER_CC2NE | ATIM_CCER_CC2NP | + ATIM_CCER_CC3NE | ATIM_CCER_CC3NP); +#endif + + /* Reset the output compare and output compare N IDLE State */ + + cr2 &= ~(ATIM_CR2_OIS1 | ATIM_CR2_OIS1N | ATIM_CR2_OIS2 | ATIM_CR2_OIS2N | + ATIM_CR2_OIS3 | ATIM_CR2_OIS3N | ATIM_CR2_OIS4); + } +#if defined(CONFIG_STM32_STM32F20XX) || defined(CONFIG_STM32_STM32F40XX) + else + { + ccer &= ~(GTIM_CCER_CC1NP | GTIM_CCER_CC2NP | GTIM_CCER_CC3NP | GTIM_CCER_CC4NP); + } +#endif + + /* Save the modified register values */ + + tim_putreg(priv, STM32_GTIM_CR2_OFFSET, cr2); + tim_putreg(priv, STM32_GTIM_CCMR1_OFFSET, ccmr1); + tim_putreg(priv, STM32_GTIM_CCMR2_OFFSET, ccmr2); + tim_putreg(priv, STM32_GTIM_CCER_OFFSET, ccer); + tim_putreg(priv, STM32_GTIM_EGR_OFFSET, egr); + + /* Set the ARR Preload Bit */ + + cr1 = tim_getreg(priv, STM32_GTIM_CR1_OFFSET); + cr1 |= GTIM_CR1_ARPE; + tim_putreg(priv, STM32_GTIM_CR1_OFFSET, cr1); + + /* Enable the timer counter + * All but the CEN bit with the default config in CR1 + */ + + adc_timstart(priv, true); + + adc_tim_dumpregs(priv, "After starting Timers"); + + return OK; +} +#endif + +/**************************************************************************** + * Name: adc_startconv + * + * Description: + * Start (or stop) the ADC conversion process in DMA mode + * + * Input Parameters: + * priv - A reference to the ADC block status + * enable - True: Start conversion + * + * Returned Value: + * + ****************************************************************************/ + +#if defined(CONFIG_STM32_STM32F20XX) || defined(CONFIG_STM32_STM32F40XX) +static void adc_startconv(struct stm32_dev_s *priv, bool enable) +{ + uint32_t regval; + + avdbg("enable: %d\n", enable); + + regval = adc_getreg(priv, STM32_ADC_CR2_OFFSET); + if (enable) + { + /* Start conversion of regular channles */ + + regval |= ADC_CR2_SWSTART; + } + else + { + /* Disable the conversion of regular channels */ + + regval &= ~ADC_CR2_SWSTART; + } + adc_putreg(priv, STM32_ADC_CR2_OFFSET,regval); +} +#endif + +/**************************************************************************** + * Name: adc_rccreset + * + * Description: + * Deinitializes the ADCx peripheral registers to their default + * reset values. It could set all the ADCs configured. + * + * Input Parameters: + * regaddr - The register to read + * reset - Condition, set or reset + * + * Returned Value: + * + ****************************************************************************/ + +static void adc_rccreset(struct stm32_dev_s *priv, bool reset) +{ + irqstate_t flags; + uint32_t regval; + uint32_t adcbit; + + /* Pick the appropriate bit in the APB2 reset register */ + +#ifdef CONFIG_STM32_STM32F10XX + /* For the STM32 F1, there is an individual bit to reset each ADC. */ + + switch (priv->intf) + { +#ifdef CONFIG_STM32_ADC1 + case 1: + adcbit = RCC_APB2RSTR_ADC1RST; + break; +#endif +#ifdef CONFIG_STM32_ADC2 + case 2: + adcbit = RCC_APB2RSTR_ADC2RST; + break; +#endif +#ifdef CONFIG_STM32_ADC3 + case 3: + adcbit = RCC_APB2RSTR_ADC3RST; + break; +#endif + default: + return; + } + +#else + /* For the STM32 F4, there is one common reset for all ADC block. + * THIS will probably cause some problems! + */ + + adcbit = RCC_APB2RSTR_ADCRST; +#endif + + /* Disable interrupts. This is necessary because the APB2RTSR register + * is used by several different drivers. + */ + + flags = irqsave(); + + /* Set or clear the selected bit in the APB2 reset register */ + + regval = getreg32(STM32_RCC_APB2RSTR); + if (reset) + { + /* Enable ADC reset state */ + + regval |= adcbit; + } + else + { + /* Release ADC from reset state */ + + regval &= ~adcbit; + } + putreg32(regval, STM32_RCC_APB2RSTR); + irqrestore(flags); +} + +/**************************************************************************** + * Name: up_dma_setup + * + * Description: + * + ****************************************************************************/ + +#ifdef CONFIG_ADC_DMA +static int up_dma_setup(FAR struct adc_dev_s *dev) +{ + + FAR struct stm32_dev_s *priv = (FAR struct stm32_dev_s *)dev->ad_priv; + int result; + uint32_t regval; + +// +// result = up_setup(dev); +// if (result != OK) +// { +// return result; +// } + + /* Acquire the DMA channel. This should always succeed. */ + + priv->rxdma = stm32_dmachannel(priv->rxdma_channel); + + /* Configure for circular DMA reception into the RX fifo */ + + stm32_dmasetup(priv->rxdma, + priv->base + STM32_ADC_DR_OFFSET, + (uint32_t)priv->rxfifo, + 4,//buffersize + DMA_SCR_DIR_P2M | + DMA_SCR_CIRC | + DMA_SCR_MINC | + DMA_SCR_PSIZE_16BITS | + DMA_SCR_MSIZE_16BITS | + DMA_SCR_PBURST_SINGLE | + DMA_SCR_MBURST_SINGLE); + + /* Reset our DMA shadow pointer to match the address just + * programmed above. + */ + + //priv->rxdmanext = 0; + + /* Enable DMA for the ADC */ + + /* Start the DMA channel, and arrange for callbacks at the half and + * full points in the FIFO. This ensures that we have half a FIFO + * worth of time to claim bytes before they are overwritten. + */ + + stm32_dmastart(priv->rxdma, up_dma_rxcallback, (void *)dev, false); + + return OK; +} +#endif +/**************************************************************************** + * Name: up_dma_rxcallback + * + * Description: + + * + ****************************************************************************/ + +#ifdef CONFIG_ADC_DMA +static void up_dma_rxcallback(DMA_HANDLE handle, uint8_t status, FAR struct adc_dev_s *dev) +{ + //struct up_dev_s *priv = (struct up_dev_s*)arg; + //uint32_t regval; + FAR struct stm32_dev_s *priv = (FAR struct stm32_dev_s *)dev->ad_priv; + //struct stm32_dma_s *dmast = (struct stm32_dma_s *)handle; + + //if (priv->rxenable && up_dma_rxavailable(&priv->dev)) + { + /* Give the ADC data to the ADC driver. adc_receive accepts 3 parameters: + * + * 1) The first is the ADC device instance for this ADC block. + * 2) The second is the channel number for the data, and + * 3) The third is the converted data for the channel. + */ + + + adc_receive(dev, 10, (int32_t)g_adc3dmarxfifo[1]); + adc_receive(dev, 11, (int32_t)g_adc3dmarxfifo[2]); + adc_receive(dev, 12, (int32_t)g_adc3dmarxfifo[3]); + adc_receive(dev, 13, (int32_t)g_adc3dmarxfifo[0]); + + + /* 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; + } + + + } +} +#endif + +/**************************************************************************** + * Name: up_dma_shutdown + * + * Description: + * Disable the DMA + * + ****************************************************************************/ + +#ifdef CONFIG_ADC_DMA +static void up_dma_shutdown(struct adc_dev_s *dev) +{ + + FAR struct stm32_dev_s *priv = (FAR struct stm32_dev_s *)dev->ad_priv; + + + /* Stop the DMA channel */ + + stm32_dmastop(priv->rxdma); + + /* Release the DMA channel */ + + stm32_dmafree(priv->rxdma); + priv->rxdma = NULL; +} +#endif + + +/******************************************************************************* + * Name: adc_enable + * + * Description : Enables or disables the specified ADC peripheral. + * Also, starts a conversion when the ADC is not + * triggered by timers + * + * Input Parameters: + * + * enable - true: enable ADC conversion + * false: disable ADC conversion + * + * Returned Value: + * + *******************************************************************************/ + +static void adc_enable(FAR struct stm32_dev_s *priv, bool enable) +{ + uint32_t regval; + + // avdbg("enable: %d\n", enable); + + regval = adc_getreg(priv, STM32_ADC_CR2_OFFSET); + if (enable) + { + regval |= ADC_CR2_ADON; + } + else + { + regval &= ~ADC_CR2_ADON; + } + adc_putreg(priv, STM32_ADC_CR2_OFFSET, regval); +} + +/**************************************************************************** + * Name: adc_reset + * + * Description: + * Reset the ADC device. Called early to initialize the hardware. This + * is called, before adc_setup() and on error conditions. + * + * Input Parameters: + * + * Returned Value: + * + ****************************************************************************/ + +static void adc_reset(FAR struct adc_dev_s *dev) +{ + FAR struct stm32_dev_s *priv = (FAR struct stm32_dev_s *)dev->ad_priv; + irqstate_t flags; + uint32_t regval; + int offset; + int i; + int ret; + + avdbg("intf: ADC%d\n", priv->intf); + flags = irqsave(); + + /* Enable ADC reset state */ + + adc_rccreset(priv, true); + + /* Release ADC from reset state */ + + adc_rccreset(priv, false); + + /* Initialize the ADC data structures */ + + /* Initialize the watchdog high threshold register */ + + adc_putreg(priv, STM32_ADC_HTR_OFFSET, 0x00000fff); + + /* Initialize the watchdog low threshold register */ + + adc_putreg(priv, STM32_ADC_LTR_OFFSET, 0x00000000); + + + /* Initialize the same sample time for each ADC 55.5 cycles + * + * During sample cycles channel selection bits must remain unchanged. + * + * 000: 1.5 cycles + * 001: 7.5 cycles + * 010: 13.5 cycles + * 011: 28.5 cycles + * 100: 41.5 cycles + * 101: 55.5 cycles + * 110: 71.5 cycles + * 111: 239.5 cycles + */ + + adc_putreg(priv, STM32_ADC_SMPR1_OFFSET, 0x00b6db6d); + adc_putreg(priv, STM32_ADC_SMPR2_OFFSET, 0x00b6db6d); + + /* ADC CR1 Configuration */ + + regval = adc_getreg(priv, STM32_ADC_CR1_OFFSET); + + /* Set mode configuration (Independent mode) */ + +#ifdef CONFIG_STM32_STM32F10XX + regval |= ADC_CR1_IND; +#endif + + /* Initialize the Analog watchdog enable */ + + regval |= ADC_CR1_AWDEN; + regval |= (priv->chanlist[0] << ADC_CR1_AWDCH_SHIFT); + +#if defined(CONFIG_STM32_STM32F20XX) || defined(CONFIG_STM32_STM32F40XX) + + /* Set the resolution of the conversion */ + + regval |= ACD_CR1_RES_12BIT; +#endif + + adc_putreg(priv, STM32_ADC_CR1_OFFSET, regval); + + /* ADC CR2 Configuration */ + + regval = adc_getreg(priv, STM32_ADC_CR2_OFFSET); + + /* Clear CONT, continuous mode disable */ + + regval &= ~ADC_CR2_CONT; + + /* Set ALIGN (Right = 0) */ + + regval &= ~ADC_CR2_ALIGN; + +#if defined(CONFIG_STM32_STM32F20XX) || defined(CONFIG_STM32_STM32F40XX) + /* External trigger enable for regular channels */ + + regval |= ACD_CR2_EXTEN_RISING; +#endif + + adc_putreg(priv, STM32_ADC_CR2_OFFSET, regval); + + /* Configuration of the channel conversions */ + + regval = adc_getreg(priv, STM32_ADC_SQR3_OFFSET) & ADC_SQR3_RESERVED; + for (i = 0, offset = 0; i < priv->nchannels && i < 6; i++, offset += 5) + { + regval |= (uint32_t)priv->chanlist[i] << offset; + } + adc_putreg(priv, STM32_ADC_SQR3_OFFSET, regval); + + regval = adc_getreg(priv, STM32_ADC_SQR2_OFFSET) & ADC_SQR2_RESERVED; + for (i = 6, offset = 0; i < priv->nchannels && i < 12; i++, offset += 5) + { + regval |= (uint32_t)priv->chanlist[i] << offset; + } + adc_putreg(priv, STM32_ADC_SQR2_OFFSET, regval); + + regval = adc_getreg(priv, STM32_ADC_SQR1_OFFSET) & ADC_SQR1_RESERVED; + for (i = 12, offset = 0; i < priv->nchannels && i < 16; i++, offset += 5) + { + regval |= (uint32_t)priv->chanlist[i] << offset; + } + /* Set the number of conversions */ + + DEBUGASSERT(priv->nchannels <= ADC_MAX_SAMPLES); + + regval |= (((uint32_t)priv->nchannels-1) << ADC_SQR1_L_SHIFT); + adc_putreg(priv, STM32_ADC_SQR1_OFFSET, regval); + + /* ADC CCR configuration */ + +#if defined(CONFIG_STM32_STM32F20XX) || defined(CONFIG_STM32_STM32F40XX) + regval = getreg32(STM32_ADC_CCR); + regval &= ~(ADC_CCR_MULTI_MASK | ADC_CCR_DELAY_MASK | ADC_CCR_DDS | ADC_CCR_DMA_MASK | + ADC_CCR_ADCPRE_MASK | ADC_CCR_VBATE | ADC_CCR_TSVREFE); + regval |= (ADC_CCR_MULTI_NONE | ADC_CCR_DMA_DISABLED | ADC_CCR_ADCPRE_DIV2); + putreg32(regval, STM32_ADC_CCR); +#endif + + + + /* Set the channel index of the first conversion */ + + priv->current = 0; + + /* Set ADON to wake up the ADC from Power Down state. */ + + adc_enable(priv, true); + +#ifdef ADC_HAVE_TIMER + ret = adc_timinit(priv); + if (ret!=OK) + { + adbg("Error initializing the timers\n"); + } +#else +#ifdef CONFIG_STM32_STM32F10XX + /* Set ADON (Again) to start the conversion. Only if Timers are not + * configured as triggers + */ + + adc_enable(priv, true); +#else +#ifdef CONFIG_ADC_DMA + //nothing +#else + adc_startconv(priv, true); +#endif +#endif /* CONFIG_STM32_STM32F10XX */ +#endif /* ADC_HAVE_TIMER */ + + + irqrestore(flags); + +// avdbg("SR: 0x%08x CR1: 0x%08x CR2: 0x%08x\n", +// adc_getreg(priv, STM32_ADC_SR_OFFSET), +// adc_getreg(priv, STM32_ADC_CR1_OFFSET), +// adc_getreg(priv, STM32_ADC_CR2_OFFSET)); +// avdbg("SQR1: 0x%08x SQR2: 0x%08x SQR3: 0x%08x\n", +// adc_getreg(priv, STM32_ADC_SQR1_OFFSET), +// adc_getreg(priv, STM32_ADC_SQR2_OFFSET), +// adc_getreg(priv, STM32_ADC_SQR3_OFFSET)); +//#if defined(CONFIG_STM32_STM32F20XX) || defined(CONFIG_STM32_STM32F40XX) +// avdbg("CCR: 0x%08x\n", +// getreg32(STM32_ADC_CCR)); +//#endif +} + +/**************************************************************************** + * 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. + * + * Input Parameters: + * + * Returned Value: + * + ****************************************************************************/ + +static int adc_setup(FAR struct adc_dev_s *dev) +{ + FAR struct stm32_dev_s *priv = (FAR struct stm32_dev_s *)dev->ad_priv; + int ret; + uint32_t regval; + + + /* the adc seems to need a full reset to be enabled correctly */ + adc_reset(dev); + +#ifndef CONFIG_ADC_DMA + /* Attach the ADC interrupt */ + + ret = irq_attach(priv->irq, priv->isr); + if (ret == OK) + { + /* Enable the ADC interrupt */ + + avdbg("Enable the ADC interrupt: irq=%d\n", priv->irq); + up_enable_irq(priv->irq); + } +#endif + +#ifdef CONFIG_ADC_DMA + up_dma_setup(dev); + + +//#ifndef ADC_HAVE_TIMER +// /*disable external trigger*/ +// regval = adc_getreg(priv, STM32_ADC_CR2_OFFSET); +// regval |= ACD_CR2_EXTEN_NONE; +// //regval |= ADC_CR1_DISCEN; +// adc_putreg(priv, STM32_ADC_CR2_OFFSET, regval); +//#else +// /*enable external trigger*/ +// regval = adc_getreg(priv, STM32_ADC_CR2_OFFSET); +// regval |=ADC_CR2_EXTSEL_T4CC4; +// regval |=ACD_CR2_EXTEN_BOTH; +// //regval |= ADC_CR2_CONT; +// adc_putreg(priv, STM32_ADC_CR2_OFFSET, regval); +//#endif + + + /*enable scan mode*/ + regval = adc_getreg(priv, STM32_ADC_CR1_OFFSET); + regval |= ADC_CR1_SCAN; + //regval |= ADC_CR1_DISCEN; + adc_putreg(priv, STM32_ADC_CR1_OFFSET, regval); + + /* Enable DMA request after last transfer (Single-ADC mode) */ + //ADC_DMARequestAfterLastTransferCmd(ADC3, ENABLE); + regval = adc_getreg(priv, STM32_ADC_CR2_OFFSET); + regval |= ADC_CR2_DDS; + adc_putreg(priv, STM32_ADC_CR2_OFFSET, regval); + + /* Enable ADC3 DMA */ + // ADC_DMACmd(ADC3, ENABLE); + regval = adc_getreg(priv, STM32_ADC_CR2_OFFSET); + regval |= ADC_CR2_DMA; + adc_putreg(priv, STM32_ADC_CR2_OFFSET, regval); +#endif + + +#ifndef ADC_HAVE_TIMER + /*set continuous conversion */ + regval = adc_getreg(priv, STM32_ADC_CR2_OFFSET); + regval |= ADC_CR2_CONT; + adc_putreg(priv, STM32_ADC_CR2_OFFSET, regval); +#endif + + /* Enable ADC3 */ + //ADC_Cmd(ADC3, ENABLE); + regval = adc_getreg(priv, STM32_ADC_CR2_OFFSET); + regval |= ADC_CR2_ADON; + adc_putreg(priv, STM32_ADC_CR2_OFFSET, regval); + +//// /*start conversion*/ +// regval = adc_getreg(priv, STM32_ADC_CR2_OFFSET); +// regval |= ADC_CR2_SWSTART; +// adc_putreg(priv, STM32_ADC_CR2_OFFSET, regval); + + + + + + 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. + * + * Input Parameters: + * + * Returned Value: + * + ****************************************************************************/ + +static void adc_shutdown(FAR struct adc_dev_s *dev) +{ + FAR struct stm32_dev_s *priv = (FAR struct stm32_dev_s *)dev->ad_priv; + +#ifdef CONFIG_ADC_DMA + up_dma_shutdown(dev); +#endif + +#ifdef ADC_HAVE_TIMER + /* stop adc timer */ + adc_timstart(priv, false); +#endif + + /* power down ADC */ + adc_enable(priv, false); + + adc_rxint(priv, false); + + /* Disable ADC interrupts and detach the ADC interrupt handler */ + up_disable_irq(priv->irq); +// irq_detach(priv->irq); + + + + +} + +/**************************************************************************** + * Name: adc_rxint + * + * Description: + * Call to enable or disable RX interrupts. + * + * Input Parameters: + * + * Returned Value: + * + ****************************************************************************/ + +static void adc_rxint(FAR struct adc_dev_s *dev, bool enable) +{ + FAR struct stm32_dev_s *priv = (FAR struct stm32_dev_s *)dev->ad_priv; + uint32_t regval; + + avdbg("intf: %d enable: %d\n", priv->intf, enable); + + regval = adc_getreg(priv, STM32_ADC_CR1_OFFSET); + if (enable) + { + /* Enable the end-of-conversion ADC and analog watchdog interrupts */ + + regval |= ADC_CR1_ALLINTS; + } + else + { + /* Disable all ADC interrupts */ + + regval &= ~ADC_CR1_ALLINTS; + } + adc_putreg(priv, STM32_ADC_CR1_OFFSET, regval); +} + +/**************************************************************************** + * Name: adc_ioctl + * + * Description: + * All ioctl calls will be routed through this method. + * + * Input Parameters: + * + * Returned Value: + * + ****************************************************************************/ + +static int adc_ioctl(FAR struct adc_dev_s *dev, int cmd, unsigned long arg) +{ + return -ENOTTY; +} + +/**************************************************************************** + * Name: adc_interrupt + * + * Description: + * Common ADC interrupt handler. + * + * Input Parameters: + * + * Returned Value: + * + ****************************************************************************/ + +static int adc_interrupt(FAR struct adc_dev_s *dev) +{ + FAR struct stm32_dev_s *priv = (FAR struct stm32_dev_s *)dev->ad_priv; + uint32_t adcsr; + int32_t value; + + /* Identifies the interruption AWD, OVR or EOC */ + + adcsr = adc_getreg(priv, STM32_ADC_SR_OFFSET); + if ((adcsr & ADC_SR_AWD) != 0) + { + alldbg("WARNING: Analog Watchdog, Value converted out of range!\n"); + } + +#if defined(CONFIG_STM32_STM32F20XX) || defined(CONFIG_STM32_STM32F40XX) + if ((adcsr & ADC_SR_OVR) != 0) + { + alldbg("WARNING: Overrun has ocurred!\n"); + } +#endif + + /* EOC: End of conversion */ + + if ((adcsr & ADC_SR_EOC) != 0) + { + /* Read the converted value and clear EOC bit + * (It is cleared by reading the ADC_DR) + */ + + value = adc_getreg(priv, STM32_ADC_DR_OFFSET); + value &= ADC_DR_DATA_MASK; + + /* Give the ADC data to the ADC driver. adc_receive accepts 3 parameters: + * + * 1) The first is the ADC device instance for this ADC block. + * 2) The second is the channel number for the data, and + * 3) The third is the converted data for the channel. + */ + + adc_receive(dev, priv->chanlist[priv->current], value); + + /* 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; + } + } + + return OK; +} + +/**************************************************************************** + * Name: adc12_interrupt + * + * Description: + * ADC12 interrupt handler for the STM32 F1 family. + * + * Input Parameters: + * + * Returned Value: + * + ****************************************************************************/ + +#if defined(CONFIG_STM32_STM32F10XX) && (defined(CONFIG_STM32_ADC1) || defined(CONFIG_STM32_ADC2)) +static int adc12_interrupt(int irq, void *context) +{ + uint32_t regval; + uint32_t pending; + + /* Check for pending ADC1 interrupts */ + +#ifdef CONFIG_STM32_ADC1 + regval = getreg32(STM32_ADC1_SR); + pending = regval & ADC_SR_ALLINTS; + if (pending != 0) + { + adc_interrupt(&g_adcdev1); + regval &= ~pending; + putreg32(regval, STM32_ADC1_SR); + } +#endif + + /* Check for pending ADC2 interrupts */ + +#ifdef CONFIG_STM32_ADC2 + regval = getreg32(STM32_ADC2_SR); + pending = regval & ADC_SR_ALLINTS; + if (pending != 0) + { + adc_interrupt(&g_adcdev2); + regval &= ~pending; + putreg32(regval, STM32_ADC2_SR); + } +#endif + return OK; +} +#endif + +/**************************************************************************** + * Name: adc3_interrupt + * + * Description: + * ADC1/2 interrupt handler for the STM32 F1 family. + * + * Input Parameters: + * + * Returned Value: + * + ****************************************************************************/ + +#if defined (CONFIG_STM32_STM32F10XX) && defined (CONFIG_STM32_ADC3) +static int adc3_interrupt(int irq, void *context) +{ + uint32_t regval; + uint32_t pending; + + /* Check for pending ADC3 interrupts */ + + regval = getreg32(STM32_ADC3_SR); + pending = regval & ADC_SR_ALLINTS; + if (pending != 0) + { + adc_interrupt(&g_adcdev3); + regval &= ~pending; + putreg32(regval, STM32_ADC3_SR); + } + + return OK; +} +#endif + +/**************************************************************************** + * Name: adc123_interrupt + * + * Description: + * ADC1/2/3 interrupt handler for the STM32 F4 family. + * + * Input Parameters: + * + * Returned Value: + * + ****************************************************************************/ + +#if defined(CONFIG_STM32_STM32F20XX) || defined(CONFIG_STM32_STM32F40XX) +static int adc123_interrupt(int irq, void *context) +{ + uint32_t regval; + uint32_t pending; + + /* Check for pending ADC1 interrupts */ + +#ifdef CONFIG_STM32_ADC1 + regval = getreg32(STM32_ADC1_SR); + pending = regval & ADC_SR_ALLINTS; + if (pending != 0) + { + adc_interrupt(&g_adcdev1); + regval &= ~pending; + putreg32(regval, STM32_ADC1_SR); + } +#endif + + /* Check for pending ADC2 interrupts */ + +#ifdef CONFIG_STM32_ADC2 + regval = getreg32(STM32_ADC2_SR); + pending = regval & ADC_SR_ALLINTS; + if (pending != 0) + { + adc_interrupt(&g_adcdev2); + regval &= ~pending; + putreg32(regval, STM32_ADC2_SR); + } +#endif + + /* Check for pending ADC3 interrupts */ + +#ifdef CONFIG_STM32_ADC3 + regval = getreg32(STM32_ADC3_SR); + pending = regval & ADC_SR_ALLINTS; + if (pending != 0) + { + adc_interrupt(&g_adcdev3); + regval &= ~pending; + putreg32(regval, STM32_ADC3_SR); + } +#endif + return OK; +} +#endif + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: stm32_adcinitialize + * + * Description: + * Initialize the ADC. + * + * The logic is, save nchannels : # of channels (conversions) in ADC_SQR1_L + * Then, take the chanlist array and store it in the SQR Regs, + * chanlist[0] -> ADC_SQR3_SQ1 + * chanlist[1] -> ADC_SQR3_SQ2 + * ... + * chanlist[15]-> ADC_SQR1_SQ16 + * + * up to + * chanlist[nchannels] + * + * Input Parameters: + * intf - Could be {1,2,3} for ADC1, ADC2, or ADC3 + * chanlist - The list of channels + * nchannels - Number of channels + * + * Returned Value: + * Valid ADC device structure reference on succcess; a NULL on failure + * + ****************************************************************************/ + +struct adc_dev_s *stm32_adcinitialize(int intf, const uint8_t *chanlist, int nchannels) +{ + FAR struct adc_dev_s *dev; + FAR struct stm32_dev_s *priv; + + avdbg("intf: %d nchannels: %d\n", intf, nchannels); + +#ifdef CONFIG_STM32_ADC1 + if (intf == 1) + { + avdbg("ADC1 Selected\n"); + dev = &g_adcdev1; + } + else +#endif +#ifdef CONFIG_STM32_ADC2 + if (intf == 2) + { + avdbg("ADC2 Selected\n"); + dev = &g_adcdev2; + } + else +#endif +#ifdef CONFIG_STM32_ADC3 + if (intf == 3) + { + avdbg("ADC3 Selected\n"); + dev = &g_adcdev3; + } + else +#endif + { + adbg("No ADC interface defined\n"); + return NULL; + } + + /* Configure the selected ADC */ + + priv = dev->ad_priv; + + DEBUGASSERT(nchannels <= ADC_MAX_SAMPLES); + priv->nchannels = nchannels; + + memcpy(priv->chanlist, chanlist, nchannels); + return dev; +} + +#endif /* CONFIG_STM32_ADC || CONFIG_STM32_ADC2 || CONFIG_STM32_ADC3 */ +#endif /* CONFIG_ADC */ + |