| /**************************************************************************** |
| * 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]; |
| }; |
| |
| /**************************************************************************** |
| * Private Function Prototypes |
| ****************************************************************************/ |
| |
| /* 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 |
| |
| /**************************************************************************** |
| * 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 |
| }; |
| |
| 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 = (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 = 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 */ |
| { |
| /* TODO: TRGO support not yet implemented */ |
| /* Set the event TRGO */ |
| |
| ccenable = 0; |
| 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: 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; |
| #ifdef ADC_HAVE_TIMER |
| int ret; |
| #endif |
| |
| 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); |
| |
| /* Enable interrupt flags */ |
| |
| regval |= ADC_CR1_ALLINTS; |
| |
| #if defined(CONFIG_STM32_STM32F20XX) || defined(CONFIG_STM32_STM32F40XX) |
| |
| /* Enable or disable Overrun interrupt */ |
| |
| regval &= ~ADC_CR1_OVRIE; |
| |
| /* 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; |
| } |
| |
| /* 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 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); |
| |
| /* 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 |
| adc_startconv(priv, true); |
| #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; |
| |
| /* Attach the ADC interrupt */ |
| |
| ret = irq_attach(priv->irq, priv->isr); |
| if (ret == OK) |
| { |
| /* Make sure that the ADC device is in the powered up, reset state */ |
| |
| adc_reset(dev); |
| |
| /* Enable the ADC interrupt */ |
| |
| avdbg("Enable the ADC interrupt: irq=%d\n", priv->irq); |
| up_enable_irq(priv->irq); |
| } |
| |
| 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; |
| |
| /* Disable ADC interrupts and detach the ADC interrupt handler */ |
| |
| up_disable_irq(priv->irq); |
| irq_detach(priv->irq); |
| |
| /* Disable and reset the ADC module */ |
| |
| adc_rccreset(priv, true); |
| } |
| |
| /**************************************************************************** |
| * 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 */ |
| |