/**************************************************************************** * arch/arm/src/stm32/stm32_adc.c * * Copyright (C) 2011 Gregory Nutt. All rights reserved. * Author: Gregory Nutt * Diego Sanchez * * 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 "up_internal.h" #include "up_arch.h" #include "chip.h" #include "stm32_internal.h" #include "stm32_adc.h" #ifdef CONFIG_ADC /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ /* Configuration ************************************************************/ /* Up to 3 ADC interfaces are supported */ #if STM32_NADC < 3 # undef CONFIG_STM32_ADC3 #endif #if STM32_NADC < 2 # undef CONFIG_STM32_ADC2 #endif #if STM32_NADC < 1 # undef CONFIG_STM32_ADC1 #endif #if defined(CONFIG_STM32_ADC1) || defined(CONFIG_STM32_ADC2) || defined(CONFIG_STM32_ADC3) /* 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 /**************************************************************************** * Private Types ****************************************************************************/ /* This structure describes the state of one ADC block */ struct stm32_dev_s { int irq; /* Interrupt generated by this ADC block */ xcpt_t isr; /* Interrupt handler for this ADC block */ uint32_t base; /* Base address of registers unique to this ADC block */ int32_t buf[8]; uint8_t count[8]; }; /**************************************************************************** * 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); /* ADC Interrupt Handler */ static void adc_interrupt(FAR struct stm32_dev_s *priv); #if defined(CONFIG_STM32_STM32F10XX) && (defined(CONFIG_STM32_ADC1) || defined(CONFIG_STM32_ADC2)) static int adc12_interrupt(int irq, void *context) #endif #ifdef CONFIG_STM32_STM32F10XX && defined (CONFIG_STM32_ADC3) static int adc3_interrupt(int irq, void *context) #endif #ifdef 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); /**************************************************************************** * 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 .base = STM32_ADC1_BASE, }; 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 .base = STM32_ADC2_BASE, }; 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 .base = STM32_ADC3_BASE, }; 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: 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; flags = irqsave(); /* Initialize the ADC data structures */ /* ADC1 CR Configuration */ regval = adc_getreg(priv, STM32_ADC_CR1_OFFSET); regval &= ~ADC_CR1_DUALMOD_MASK; regval &= ~ADC_CR1_SCAN; /* Clear DUALMODE and SCAN bits */ adc_putreg(priv, STM32_ADC_CR1_OFFSET, regval); /* Initialize the ADC_Mode (ADC_Mode_Independent) */ regval = adc_getreg(priv, STM32_ADC_CR1_OFFSET); regval |= ADC_CR1_IND; /* Initialize the ADC_CR1_SCAN member DISABLE */ regval &= ~ADC_CR1_SCAN; adc_putreg(priv, STM32_ADC_CR1_OFFSET, regval); /* ADC1 CR2 Configuration */ /* Clear CONT, ALIGN and EXTTRIG bits */ regval = adc_getreg(priv, STM32_ADC_CR2_OFFSET); regval &= ~ADC_CR2_CONT; regval &= ~ADC_CR2_ALIGN; regval &= ~ADC_CR2_EXTSEL_MASK; adc_putreg(priv, STM32_ADC_CR2_OFFSET, regval); /* Set CONT, ALIGN and EXTTRIG bits */ /* Initialize the ALIGN: Data alignment Right */ regval = adc_getreg(priv, STM32_ADC_CR2_OFFSET); regval &= ~ADC_CR2_ALIGN; /* Initialize the External event select "Timer CC1 event" */ regval &= ~ADC_CR2_EXTSEL_MASK; /* Initialize the ADC_ContinuousConvMode "Single conversion mode" */ regval &= ~ADC_CR2_CONT; adc_putreg(priv, STM32_ADC_CR2_OFFSET, regval); /* ADC1 SQR1 Configuration */ regval = adc_getreg(priv, STM32_ADC_SQR1_OFFSET); regval &= ~ADC_SQR1_L_MASK; /* L = 0000: 1 conversion */ adc_putreg(priv, STM32_ADC_SQR_OFFSET1, regval); irqrestore(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. * * 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 = 0; int i; /* Attach the ADC interrupt */ ret = irq_attach(priv->irq, priv->isr); if (ret == OK) { for (i = 0; i < 8; i++) { priv->buf[i] = 0; priv->count[i] = 0; } /* Enable the ADC interrupt */ 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 */ } /**************************************************************************** * 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; uint32_t adc_getreg(priv, STM32_ADC_CR1_OFFSET); if (enable) { /* Enable the end-of-conversion ADC interrupt */ regval |= ADC_CR1_EOCIE; } else { /* Enable 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 void adc_interrupt(FAR struct stm32_dev_s *priv) { uint32_t regval; unsigned char ch; /* channel */ int32_t value; regval = adc_getreg(priv, STM32_ADC_CR1_OFFSET); regval &= ADC_CR1_AWDCH_MASK; ch = regval; /* Handle the ADC interrupt */ # warning "still missing logic, value computation" adc_receive(priv, ch, value); priv->buf[ch] = 0; priv->count[ch] = 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_regval; uint32_t pending; /* Check for pending ADC1 interrupts */ #ifdef CONFIG_STM32_ADC1 regval = getreg32(priv, STM32_ADC1_SR); pending = regval & ADC_SR_ALLINTS; if (pending != 0) { adc_interrupt(&g_adcpriv1); regval &= ~pending; putreg32(regval, STM32_ADC1_SR); } #endif /* Check for pending ADC2 interrupts */ #ifdef CONFIG_STM32_ADC2 regval = getreg32(priv, STM32_ADC2_SR); pending = regval & ADC_SR_ALLINTS; if (pending != 0) { adc_interrupt(&g_adcpriv2); 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: * ****************************************************************************/ #ifdef 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(priv, STM32_ADC3_SR); pending = regval & ADC_SR_ALLINTS; if (pending != 0) { adc_interrupt(&g_adcpriv3); 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: * ****************************************************************************/ #ifdef 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(priv, STM32_ADC1_SR); pending = regval & ADC_SR_ALLINTS; if (pending != 0) { adc_interrupt(&g_adcpriv1); regval &= ~pending; putreg32(regval, STM32_ADC1_SR); } #endif /* Check for pending ADC2 interrupts */ #ifdef CONFIG_STM32_ADC2 regval = getreg32(priv, STM32_ADC2_SR); pending = regval & ADC_SR_ALLINTS; if (pending != 0) { adc_interrupt(&g_adcpriv2); regval &= ~pending; putreg32(regval, STM32_ADC2_SR); } #endif /* Check for pending ADC3 interrupts */ #ifdef CONFIG_STM32_ADC3 regval = getreg32(priv, STM32_ADC3_SR); pending = regval & ADC_SR_ALLINTS; if (pending != 0) { adc_interrupt(&g_adcpriv3); regval &= ~pending; putreg32(regval, STM32_ADC3_SR); } #endif return OK; } #endif /**************************************************************************** * Public Functions ****************************************************************************/ /**************************************************************************** * Name: up_adcinitialize * * Description: * Initialize the adc * * Returned Value: * Valid can device structure reference on succcess; a NULL on failure * ****************************************************************************/ struct adc_dev_s *up_adcinitialize(int intf) { # warning "Question: How do you plan to handle the ADC channels? Can we do" # " 16 or 18 individual channels? or one group?" #ifdef CONFIG_STM32_ADC1 if (intf == 1) { return &g_adcdev1; } else #endif #ifdef CONFIG_STM32_ADC2 if (intf == 2) { return &g_adcdev2; } else #endif #ifdef CONFIG_STM32_ADC3 if (intf == 3) { return &g_adcdev3; } else #endif { return NULL; } } #endif /* CONFIG_STM32_ADC || CONFIG_STM32_ADC2 || CONFIG_STM32_ADC3 */ #endif /* CONFIG_ADC */