summaryrefslogtreecommitdiff
path: root/nuttx/arch/arm/src/stm32/stm32_adc.c
diff options
context:
space:
mode:
authorpatacongo <patacongo@42af7a65-404d-4744-a932-0658087f49c3>2011-12-20 00:30:12 +0000
committerpatacongo <patacongo@42af7a65-404d-4744-a932-0658087f49c3>2011-12-20 00:30:12 +0000
commitc4080df06cc977a0801794b83a51f6739e347379 (patch)
tree69a06e4f9b24ba1b220778fc23922e7ed1bb5b05 /nuttx/arch/arm/src/stm32/stm32_adc.c
parent5fb7939d769fb3f1f84721cb0056d0cc621f05e2 (diff)
downloadpx4-nuttx-c4080df06cc977a0801794b83a51f6739e347379.tar.gz
px4-nuttx-c4080df06cc977a0801794b83a51f6739e347379.tar.bz2
px4-nuttx-c4080df06cc977a0801794b83a51f6739e347379.zip
ADC driver update
git-svn-id: svn://svn.code.sf.net/p/nuttx/code/trunk@4203 42af7a65-404d-4744-a932-0658087f49c3
Diffstat (limited to 'nuttx/arch/arm/src/stm32/stm32_adc.c')
-rw-r--r--nuttx/arch/arm/src/stm32/stm32_adc.c279
1 files changed, 237 insertions, 42 deletions
diff --git a/nuttx/arch/arm/src/stm32/stm32_adc.c b/nuttx/arch/arm/src/stm32/stm32_adc.c
index 34461349d..7f513b743 100644
--- a/nuttx/arch/arm/src/stm32/stm32_adc.c
+++ b/nuttx/arch/arm/src/stm32/stm32_adc.c
@@ -50,6 +50,7 @@
#include <errno.h>
#include <assert.h>
#include <debug.h>
+#include <unistd.h>
#include <arch/board/board.h>
#include <nuttx/arch.h>
@@ -98,14 +99,17 @@ struct stm32_dev_s
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 presc; /* Timer prescaler value */
+ 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];
};
@@ -117,6 +121,10 @@ struct stm32_dev_s
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);
+#endif
static void adc_rccreset(struct stm32_dev_s *priv, bool reset);
/* ADC Interrupt Handler */
@@ -142,6 +150,7 @@ 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
static void adc_startconv(FAR struct stm32_dev_s *priv, bool enable);
@@ -176,9 +185,11 @@ static struct stm32_dev_s g_adcpriv1 =
.intf = 1,
.base = STM32_ADC1_BASE,
#ifdef ADC1_HAVE_TIMER
+ .trigger = CONFIG_STM32_ADC1_TIMTRIG;
.tbase = ADC1_TIMER_BASE,
.extsel = ADC1_EXTSEL_VALUE,
- .presc = ADC1_TIMER_PCLK_FREQUENCY / CONFIG_STM32_ADC1_SAMPLE_FREQUENCY,
+ .pclck = ADC1_TIMER_PCLK_FREQUENCY,
+ .freq = CONFIG_STM32_ADC1_SAMPLE_FREQUENCY,
#endif
};
@@ -204,9 +215,11 @@ static struct stm32_dev_s g_adcpriv2 =
.intf = 2;
.base = STM32_ADC2_BASE,
#ifdef ADC2_HAVE_TIMER
+ .trigger = CONFIG_STM32_ADC2_TIMTRIG;
.tbase = ADC2_TIMER_BASE,
.extsel = ADC2_EXTSEL_VALUE,
- .presc = ADC2_TIMER_PCLK_FREQUENCY / CONFIG_STM32_ADC2_SAMPLE_FREQUENCY,
+ .pclck = ADC2_TIMER_PCLK_FREQUENCY,
+ .freq = CONFIG_STM32_ADC2_SAMPLE_FREQUENCY,
#endif
};
@@ -232,9 +245,11 @@ static struct stm32_dev_s g_adcpriv3 =
.intf = 3;
.base = STM32_ADC3_BASE,
#ifdef ADC3_HAVE_TIMER
+ .trigger = CONFIG_STM32_ADC3_TIMTRIG;
.tbase = ADC3_TIMER_BASE,
.extsel = ADC3_EXTSEL_VALUE,
- .presc = ADC3_TIMER_PCLK_FREQUENCY / CONFIG_STM32_ADC3_SAMPLE_FREQUENCY,
+ .pclck = ADC3_TIMER_PCLK_FREQUENCY,
+ .freq = CONFIG_STM32_ADC3_SAMPLE_FREQUENCY,
#endif
};
@@ -303,9 +318,9 @@ static void adc_putreg(struct stm32_dev_s *priv, int offset, uint32_t value)
****************************************************************************/
#ifdef ADC_HAVE_TIMER
-static uint32_t tim_getreg(struct stm32_dev_s *priv, int offset)
+static uint16_t tim_getreg(struct stm32_dev_s *priv, int offset)
{
- return getreg32(priv->tbase + offset);
+ return getreg16(priv->tbase + offset);
}
#endif
@@ -325,9 +340,48 @@ static uint32_t tim_getreg(struct stm32_dev_s *priv, int offset)
****************************************************************************/
#ifdef ADC_HAVE_TIMER
-static void tim_putreg(struct stm32_dev_s *priv, int offset, uint32_t value)
+static void tim_putreg(struct stm32_dev_s *priv, int offset, uint16_t value)
{
- putreg32(value, priv->tbase + offset);
+ putreg16(value, priv->tbase + offset);
+}
+#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_BTIM_CR1_OFFSET);
+
+ if (enable)
+ {
+ /* Start the counter */
+
+ regval |= ATIM_CR1_CEN;
+ }
+
+ else
+ {
+ /* Disable the counter */
+
+ regval &= ~ATIM_CR1_CEN;
+ }
+ tim_putreg(priv, STM32_BTIM_CR1_OFFSET, regval);
}
#endif
@@ -349,7 +403,13 @@ static void tim_putreg(struct stm32_dev_s *priv, int offset, uint32_t value)
#ifdef ADC_HAVE_TIMER
static int adc_timinit(FAR struct stm32_dev_s *priv)
{
+ uint32_t prescaler;
+ uint32_t reload;
uint32_t regval;
+ uint16_t cr1;
+ uint16_t cr2;
+ uint16_t ccmr1;
+ uint16_t ccmr2;
/* If the timer base address is zero, then this ADC was not configured to
* use a timer.
@@ -357,16 +417,20 @@ static int adc_timinit(FAR struct stm32_dev_s *priv)
if (!priv->tbase)
{
-#warning "Does anything need to be configured if there is no timer for this ADC?"
+ /* 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_getreg(priv, STM32_ADC_CR2_OFFSET);
+ regval &= ~ADC_CR2_EXTTRIG;
+ adc_putreg(priv, STM32_ADC_CR2_OFFSET, regval);
return OK;
}
-
- /* Configure the ADC to use the selected timer and timer channel as the trigger */
-
- /* EXTTRIG: External Trigger Conversion mode for regular channels */
-
- regval = adc_getreg(priv, STM32_ADC_CR2_OFFSET)
- regval |= ADC_CR2_EXTTRIG;
+ else
+ {
+ regval = adc_getreg(priv, STM32_ADC_CR2_OFFSET);
+ regval |= ADC_CR2_EXTTRIG;
+ }
/* EXTSEL selection: These bits select the external event used to trigger
* the start of conversion of a regular group. NOTE:
@@ -382,35 +446,153 @@ static int adc_timinit(FAR struct stm32_dev_s *priv)
adc_putreg(priv, STM32_ADC_CR2_OFFSET, regval);
/* Configure the timer channel to drive the ADC */
-#warning "LOTS of missing timer setup logic"
- regval = priv->presc;
+ /* 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 (regval > 0)
+ if (prescaler > 0)
{
- regval--;
+ prescaler--;
}
/* Check for overflow */
- if (regval > 0xffff)
+ else if (prescaler > 0xffff)
+ {
+ adbg("WARNING: Prescaler overflowed.\n");
+ prescaler = 0xffff;
+ }
+
+ reload = (priv->pclck / prescaler) / priv->freq;
+
+ if (reload < 1)
{
- regval = 0xffff;
+ adbg("WARNING: Reload value underflowed.\n");
+ reload = 1;
}
+ else if (reload > 65535)
+ {
+ adbg("WARNING: Reload value overflowed.\n");
+ reload = 65535;
+ }
+
+ avdbg("TIM%d PCLCK: %d frequency: %d TIMCLK: %d prescaler: %d reload: %d\n",
+ priv->intf, priv->pclck, priv->freq, (priv->pclck / (prescaler+1)), prescaler, reload);
+
+ /* Set up the timer CR1 register */
- /* Save the timer prescaler value */
+ cr1 = tim_getreg(priv, STM32_GTIM_CR1_OFFSET);
- tim_putreg(priv, STM32_BTIM_PSC_OFFSET, regval);
+ /* 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);
+
+ /* Save the timer prescaler value and autoreload value*/
+
+ tim_putreg(priv, STM32_BTIM_PSC_OFFSET, prescaler);
+ tim_putreg(priv, STM32_BTIM_ARR_OFFSET, reload);
+
+ /* Clear the advanced timers repitition counter in TIM1 */
+
+#if defined(CONFIG_STM32_TIM1_ADC3) || defined(CONFIG_STM32_TIM8_ADC3)
+ if (priv->tbase == STM32_TIM1_BASE || priv->tbase == STM32_TIM8_BASE)
+ {
+ tim_putreg(priv, STM32_ATIM_RCR_OFFSET, 0);
+ }
+#endif
+
+ /* TIMx event generation: Bit 0 UG: Update generation */
+
+ tim_putreg(priv, STM32_GTIM_EGR_OFFSET, ATIM_EGR_UG);
+
+ /* CCMR Configurations */
+
+ ccmr1 = 0;
+ ccmr2 = 0;
+
+ switch (priv->trigger)
+ {
+ case 0: /* Timer x CC1 event */
+ {
+ ccmr1 = (ATIM_CCMR_CCS_CCIN1 << ATIM_CCMR1_CC1S_SHIFT) |
+ (ATIM_CCMR_MODE_PWM1 << ATIM_CCMR1_OC1M_SHIFT) |
+ 0x01; /* CC1 channel is configured as input, IC1 is mapped on TI1 */
+
+#warning "* I will fix this numeric values with the definitions *"
+
+ ccmr2 = 0x68;
+ }
+ break;
+
+ case 1: /* Timer x CC2 event */
+ {
+#warning "missing logic, I want the Timer-x-CC1-event working first"
+ }
+ break;
+
+ case 2: /* Timer x CC3 event */
+ {
+#warning "missing logic, I want the Timer-x-CC1-event working first"
+ }
+ break;
+
+ case 3: /* Timer x CC4 event */
+ {
+#warning "missing logic, I want the Timer-x-CC1-event working first"
+ }
+ break;
+
+ case 4: /* Timer x TRGO event */
+ {
+#warning "missing logic, I want the Timer-x-CC1-event working first"
+ }
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
/* Enable the timer counter */
+ /* All but the CEN bit with the default config in CR1 */
- regval = stm32_tim_getreg(priv, STM32_BTIM_CR1_OFFSET);
- regval |= ATIM_CR1_CEN;
- tim_putreg(priv, STM32_BTIM_CR1_OFFSET, val);
+ adc_timstart(priv, true);
+
+ return OK;
}
#endif
@@ -447,7 +629,7 @@ static void adc_startconv(struct stm32_dev_s *priv, bool enable)
regval &= ~ADC_CR2_SWSTART;
}
- regval = adc_getreg(priv, STM32_ADC_CR2_OFFSET);
+ adc_putreg(priv, STM32_ADC_CR2_OFFSET,regval);
}
/****************************************************************************
@@ -573,6 +755,7 @@ static void adc_reset(FAR struct adc_dev_s *dev)
irqstate_t flags;
uint32_t regval;
int offset;
+ int ret;
int i;
avdbg("intf: %d\n", priv->intf);
@@ -625,14 +808,21 @@ static void adc_reset(FAR struct adc_dev_s *dev)
adc_putreg(priv, STM32_ADC_SMPR2_OFFSET, 0x00b6db6d);
#ifdef ADC_HAVE_TIMER
- adc_timinit(priv);
+ ret = adc_timinit(priv);
+ if (ret!=OK)
+ {
+ adbg("Error initializing the timers\n");
+ }
#endif
/* ADC CR1 Configuration */
regval = adc_getreg(priv, STM32_ADC_CR1_OFFSET);
- regval &= ~ADC_CR1_DUALMOD_MASK;
- regval &= ~ADC_CR1_SCAN; /* Clear DUALMODE and SCAN bits */
+
+ /* Clear DUALMODE and SCAN bits */
+
+ regval &= ~ADC_CR1_DUALMOD_MASK;
+ regval &= ~ADC_CR1_SCAN;
adc_putreg(priv, STM32_ADC_CR1_OFFSET, regval);
/* Initialize the ADC_Mode (ADC_Mode_Independent) */
@@ -703,16 +893,18 @@ static void adc_reset(FAR struct adc_dev_s *dev)
priv->current = 0;
+ usleep(10);
+
/* Set ADON to wake up the ADC from Power Down state. */
- usleep(10);
adc_enable(priv, true);
-
+
/* Set ADON (Again) to start the conversion. */
adc_enable(priv, true);
- irqrestore(flags);
+ irqrestore(flags);
+
avdbg("SR: %08x CR1: 0x%08x CR2: 0x%08x\n",
adc_getreg(priv, STM32_ADC_SR_OFFSET),
adc_getreg(priv, STM32_ADC_CR1_OFFSET),
@@ -752,9 +944,11 @@ static int adc_setup(FAR struct adc_dev_s *dev)
{
/* Enable the ADC interrupt */
+ avdbg("Enable the ADC interrupt: irq=%d\n", priv->irq);
up_enable_irq(priv->irq);
}
+ avdbg("Returning %d\n",ret);
return ret;
}
@@ -863,9 +1057,9 @@ static int adc_interrupt(FAR struct adc_dev_s *dev)
adcsr = adc_getreg(priv, STM32_ADC_SR_OFFSET);
if ((adcsr & ADC_SR_AWD) != 0)
{
- adbg(" Analog Watchdog, Value converted out of range!\n");
+ adbg("WARNING: Analog Watchdog, Value converted out of range!\n");
}
-
+
/* EOC: End of conversion */
if ((adcsr & ADC_SR_EOC) != 0)
@@ -878,7 +1072,7 @@ static int adc_interrupt(FAR struct adc_dev_s *dev)
#error "not yet implemented"
value &= ADC_DR_ADC2DATA_MASK;
#endif
-
+
/* Give the ADC data to the ADC dirver. adc_receive accepts 3 parameters:
*
* 1) The first is the ADC device instance for this ADC block.
@@ -886,8 +1080,9 @@ static int adc_interrupt(FAR struct adc_dev_s *dev)
* 3) The third is the converted data for the channel.
*/
+ avdbg("Calling adc_receive(dev, priv->chanlist[%d], value=%d)", priv->current, value);
adc_receive(dev, priv->chanlist[priv->current], value);
-
+
/* Set the channel number of the next channel that will complete conversion */
#if 0
#error "This logic force to read the following channels but never reads the real converted value"
@@ -1106,7 +1301,7 @@ struct adc_dev_s *stm32_adcinitialize(int intf, const uint8_t *chanlist, int nch
#ifdef CONFIG_STM32_ADC1
if (intf == 1)
{
- adbg("ADC1 Selected\n");
+ avdbg("ADC1 Selected\n");
dev = &g_adcdev1;
}
else
@@ -1114,7 +1309,7 @@ struct adc_dev_s *stm32_adcinitialize(int intf, const uint8_t *chanlist, int nch
#ifdef CONFIG_STM32_ADC2
if (intf == 2)
{
- adbg("ADC2 Selected\n");
+ avdbg("ADC2 Selected\n");
dev = &g_adcdev2;
}
else
@@ -1122,7 +1317,7 @@ struct adc_dev_s *stm32_adcinitialize(int intf, const uint8_t *chanlist, int nch
#ifdef CONFIG_STM32_ADC3
if (intf == 3)
{
- adbg("ADC3 Selected\n");
+ avdbg("ADC3 Selected\n");
dev = &g_adcdev3;
}
else