Fix sample time adc settings. Calibrate ADC when we init it.
[bertos.git] / bertos / cpu / cortex-m3 / drv / adc_stm32.c
index ef183865ac63eea58e469aab3e4850fc0f377924..ea07d88e717a76957b7b08acb7dbf33485d977a9 100644 (file)
 #include <drv/adc.h>
 #include <drv/clock_stm32.h>
 #include <drv/gpio_stm32.h>
-#include <drv/irq_cm3.h>
 
 #include <io/stm32.h>
 
-
 struct stm32_adc *adc = (struct stm32_adc *)ADC1_BASE;
 
+#if CONFIG_KERN
+       #include <cfg/module.h>
+
+       #include <kern/proc.h>
+       #include <kern/signal.h>
+
+       #include <drv/irq_cm3.h>
+
+
+       #if !CONFIG_KERN_SIGNALS
+               #error Signals must be active to use ADC with kernel
+       #endif
+
+       /* Signal adc convertion end */
+       #define SIG_ADC_COMPLETE SIG_USER0
+
+       /* ADC waiting process */
+       static struct Process *adc_process;
+
+       /**
+        * ADC ISR.
+        * Simply signal the adc process that convertion is complete.
+        */
+       static DECLARE_ISR(adc_conversion_end_irq)
+       {
+               sig_post(adc_process, SIG_ADC_COMPLETE);
+
+               /* Clear the status bit */
+               adc->SR &= ~BV(SR_EOC);
+       }
+
+       static void adc_enable_irq(void)
+       {
+               /* Register the IRQ handler */
+               sysirq_setHandler(ADC_IRQHANDLER, adc_conversion_end_irq);
+               adc->CR1 |= BV(CR1_EOCIE);
+       }
+
+#endif /* CONFIG_KERN */
+
 /**
  * Select mux channel \a ch.
+ * Generally the stm32 cpu family allow us to program the order
+ * of adc channel that we want to read.
+ * In this driver implementation we put as fist channel to read the
+ * select ones.
  */
 void adc_hw_select_ch(uint8_t ch)
 {
-       kprintf("Select[%d]\n", ch);
-       adc->SQR1 |= (0x1 << SQR1_SQ_LEN_SHIFT);
+       /* We sample only from one channel */
+       adc->SQR1 |= BV(SQR1_SQ_LEN_SHIFT);
        adc->SQR3 = (ch & SQR3_SQ_MASK);
 }
 
-static DECLARE_ISR(adc_redyRead)
-{
-       kputs("end\n");
-}
 /**
  * Start an ADC convertion.
  * If a kernel is present, preempt until convertion is complete, otherwise
@@ -92,13 +130,32 @@ static DECLARE_ISR(adc_redyRead)
  */
 uint16_t adc_hw_read(void)
 {
-       // Start convertion
-    adc->CR2 |= CR2_EXTTRIG_SWSTRT_SET;
+       #if CONFIG_KERN
+               /* Ensure ADC is not already in use by another process */
+               ASSERT(adc_process == NULL);
+               adc_process = proc_current();
+       #endif
 
-       while (!(adc->SR & BV(SR_EOC)));
+       /* Start convertion */
+    adc->CR2 |= CR2_EXTTRIG_SWSTRT_SET;
 
-       //Return the last converted data
-       return (adc->DR);
+       #if CONFIG_KERN
+               /* Ensure IRQs enabled. */
+               IRQ_ASSERT_ENABLED();
+               sig_wait(SIG_ADC_COMPLETE);
+
+               /* Prevent race condition in case of preemptive kernel */
+               uint16_t ret = adc->DR;
+               MEMORY_BARRIER;
+               adc_process = NULL;
+               return ret;
+       #else
+               /* Wait in polling until conversion is done */
+               while (!(adc->SR & BV(SR_EOC)));
+
+               /* Return the last converted data */
+               return (adc->DR);
+       #endif
 }
 
 /**
@@ -106,36 +163,53 @@ uint16_t adc_hw_read(void)
  */
 void adc_hw_init(void)
 {
-       /* Enable clocking on AFIO */
-       RCC->APB2ENR |= RCC_APB2_AFIO;
-       RCC->APB2ENR |= RCC_APB2_GPIOC;
+       RCC->APB2ENR |= (RCC_APB2_GPIOA | RCC_APB2_GPIOB | RCC_APB2_GPIOC);
        RCC->APB2ENR |= RCC_APB2_ADC1;
 
-       /* Reset cr1 registry */
+       /* Reset registry */
        adc->CR1 = 0;
        adc->CR2 = 0;
+       adc->SQR1 = 0;
+       adc->SQR2 = 0;
+       adc->SQR3 = 0;
+
+       /* Calibrate ADC */
+       adc->CR2 |= BV(CR2_RTSCAL);
+       adc->CR2 |= BV(CR2_CAL);
+
+       /* Wait in polling until calibration is done */
+       while (adc->CR2 & BV(CR2_CAL));
 
        /*
         * Configure ADC
         *  - Regular mode
-        *  - scan mode
+        *  - Wake up adc
+        *  - Wake up temperature and Vrefint
         */
        adc->CR2 |= (BV(CR2_ADON) | ADC_EXTERNALTRIGCONV_NONE | BV(CR2_TSVREFE));
-       /*
-        * Configure ADC settings
-        *  - align rigth
-        *  - enable adc
-        */
-       adc->SQR1 = 0;
-       adc->SQR2 = 0;
-       adc->SQR3 = 0;
-
-       /* Set 17.1usec sampling time on channel 16 and 17 */
-       adc->SMPR1 |= ((ADC_SAMPLETIME_239CYCLES5 << ADC_CHANNEL_16) |
-               (ADC_SAMPLETIME_239CYCLES5 << ADC_CHANNEL_17));
-
-       /* Register the IRQ handler */
-       sysirq_setHandler(ADC_IRQHANDLER, adc_redyRead);
-       //adc->CR1 |= BV(CR1_EOCIE);
 
+       /* Set 17.1usec sampling time*/
+       adc->SMPR1 |= ((ADC_SAMPLETIME_239CYCLES5 << SMPR1_CH17) |
+               (ADC_SAMPLETIME_239CYCLES5 << SMPR1_CH16) |
+               (ADC_SAMPLETIME_239CYCLES5 << SMPR1_CH15) |
+               (ADC_SAMPLETIME_239CYCLES5 << SMPR1_CH14) |
+               (ADC_SAMPLETIME_239CYCLES5 << SMPR1_CH13) |
+               (ADC_SAMPLETIME_239CYCLES5 << SMPR1_CH12) |
+               (ADC_SAMPLETIME_239CYCLES5 << SMPR1_CH11) |
+               (ADC_SAMPLETIME_239CYCLES5 << SMPR1_CH10));
+
+       adc->SMPR2 |= ((ADC_SAMPLETIME_239CYCLES5 << SMPR2_CH9) |
+               (ADC_SAMPLETIME_239CYCLES5 << SMPR2_CH8) |
+               (ADC_SAMPLETIME_239CYCLES5 << SMPR2_CH7) |
+               (ADC_SAMPLETIME_239CYCLES5 << SMPR2_CH6) |
+               (ADC_SAMPLETIME_239CYCLES5 << SMPR2_CH5) |
+               (ADC_SAMPLETIME_239CYCLES5 << SMPR2_CH4) |
+               (ADC_SAMPLETIME_239CYCLES5 << SMPR2_CH3) |
+               (ADC_SAMPLETIME_239CYCLES5 << SMPR2_CH2) |
+               (ADC_SAMPLETIME_239CYCLES5 << SMPR2_CH1) |
+               (ADC_SAMPLETIME_239CYCLES5 << SMPR2_CH0));
+
+       #if CONFIG_KERN
+               adc_enable_irq();
+       #endif
 }