SAMA5: Add ADC-side of the logic to hook in timer/counter logic needed to drive periodic ADC sampling

This commit is contained in:
Gregory Nutt 2013-10-24 13:56:23 -06:00
parent aab5b6b2d9
commit da7a3d9c9a
3 changed files with 77 additions and 62 deletions

View File

@ -2141,7 +2141,7 @@ endchoice # TC0 channel
choice choice
prompt "TIOAx edge" prompt "TIOAx edge"
default SAMA5_ADC_TIOA_BOTH default SAMA5_ADC_TIOA_BOTH
depends on SAMA5_ADC_ADTRG depends on SAMA5_ADC_TIOATRIG
config SAMA5_ADC_TIOA_RISING config SAMA5_ADC_TIOA_RISING
bool "Rising edge" bool "Rising edge"

View File

@ -67,9 +67,14 @@
#include "up_arch.h" #include "up_arch.h"
#include "chip.h" #include "chip.h"
#include "cache.h"
#include "chip/sam_adc.h" #include "chip/sam_adc.h"
#include "chip/sam_pmc.h" #include "chip/sam_pmc.h"
#include "chip/sam_pinmap.h"
#include "sam_periphclks.h" #include "sam_periphclks.h"
#include "sam_memories.h"
#include "sam_pio.h"
#include "sam_dmac.h" #include "sam_dmac.h"
#include "sam_tc.h" #include "sam_tc.h"
#include "sam_tsd.h" #include "sam_tsd.h"
@ -374,7 +379,7 @@ struct sam_adc_s
volatile bool ready; /* Worker has completed the last set of samples */ volatile bool ready; /* Worker has completed the last set of samples */
volatile bool enabled; /* DMA data transfer is enabled */ volatile bool enabled; /* DMA data transfer is enabled */
#endif #endif
struct adc_dev_s dev; /* The external via of the ADC device */ struct adc_dev_s *dev; /* A reference to the outer, ADC device container */
uint32_t pending; /* Pending EOC events */ uint32_t pending; /* Pending EOC events */
struct work_s work; /* Supports the interrupt handling "bottom half" */ struct work_s work; /* Supports the interrupt handling "bottom half" */
#ifdef CONFIG_SAMA5_ADC_DMA #ifdef CONFIG_SAMA5_ADC_DMA
@ -419,7 +424,7 @@ static bool sam_adc_checkreg(struct sam_adc_s *priv, bool wr,
static void sam_adc_dmadone(void *arg); static void sam_adc_dmadone(void *arg);
static void sam_adc_dmacallback(DMA_HANDLE handle, void *arg, int result); static void sam_adc_dmacallback(DMA_HANDLE handle, void *arg, int result);
static int sam_adc_dmasetup(struct sam_adc_s *priv, FAR uint8_t *buffer, static int sam_adc_dmasetup(struct sam_adc_s *priv, FAR uint8_t *buffer,
size_t buflen) size_t buflen);
#endif #endif
/* ADC interrupt handling */ /* ADC interrupt handling */
@ -440,10 +445,11 @@ static int sam_adc_ioctl(struct adc_dev_s *dev, int cmd, unsigned long arg);
/* Initialization/Configuration */ /* Initialization/Configuration */
#ifdef CONFIG_SAMA5_ADC_TIOATRIG #ifdef CONFIG_SAMA5_ADC_TIOATRIG
static void sam_adc_settimer(struct sam_adc_s *priv, uint32_t frequency, static int sam_adc_settimer(struct sam_adc_s *priv, uint32_t frequency,
int channel, boot tioa); int channel);
static void sam_adc_freetimer(struct sam_adc_s *priv);
#endif #endif
static void sam_adc_trigger(struct sam_adc_s *priv); static int sam_adc_trigger(struct sam_adc_s *priv);
static void sam_adc_autocalibrate(struct sam_adc_s *priv); static void sam_adc_autocalibrate(struct sam_adc_s *priv);
static void sam_adc_offset(struct sam_adc_s *priv); static void sam_adc_offset(struct sam_adc_s *priv);
static void sam_adc_gain(struct sam_adc_s *priv); static void sam_adc_gain(struct sam_adc_s *priv);
@ -479,7 +485,7 @@ static struct sam_adc_s g_adcpriv;
static struct adc_dev_s g_adcdev = static struct adc_dev_s g_adcdev =
{ {
.ad_ops = &g_adcops, .ad_ops = &g_adcops,
.ad_priv = &g_adcpriv.dev, .ad_priv = &g_adcpriv,
}; };
#endif #endif
@ -581,7 +587,7 @@ static bool sam_adc_checkreg(struct sam_adc_s *priv, bool wr,
static void sam_adc_dmadone(void *arg) static void sam_adc_dmadone(void *arg)
{ {
struct sam_adc_s *priv = (struct sam_adc_s *)arg; struct sam_adc_s *priv = (struct sam_adc_s *)arg;
uint16_t *buffer; uint32_t *buffer;
uint16_t sample; uint16_t sample;
int chan; int chan;
int i; int i;
@ -612,9 +618,9 @@ static void sam_adc_dmadone(void *arg)
chan = (int)((*buffer & ADC_LCDR_CHANB_MASK) >> ADC_LCDR_CHANB_SHIFT); chan = (int)((*buffer & ADC_LCDR_CHANB_MASK) >> ADC_LCDR_CHANB_SHIFT);
sample = (uint16_t)((*buffer & ADC_LCDR_DATA_MASK) >> ADC_LCDR_DATA_SHIFT); sample = (uint16_t)((*buffer & ADC_LCDR_DATA_MASK) >> ADC_LCDR_DATA_SHIFT);
/* And give the sample data to the ADC upper half */ /* And give the sample data to the ADC upper half */
(void)adc_receive(&priv->dev, chan, sample); (void)adc_receive(priv->dev, chan, sample);
} }
} }
@ -637,7 +643,8 @@ static void sam_adc_dmadone(void *arg)
#ifdef CONFIG_SAMA5_ADC_DMA #ifdef CONFIG_SAMA5_ADC_DMA
static void sam_adc_dmacallback(DMA_HANDLE handle, void *arg, int result) static void sam_adc_dmacallback(DMA_HANDLE handle, void *arg, int result)
{ {
struct sam_dev_s *priv = (struct sam_dev_s *)arg; struct sam_adc_s *priv = (struct sam_adc_s *)arg;
int ret;
/* Check of the bottom half is keeping up with us */ /* Check of the bottom half is keeping up with us */
@ -664,7 +671,7 @@ static void sam_adc_dmacallback(DMA_HANDLE handle, void *arg, int result)
/* Restart the DMA conversion using the next buffer */ /* Restart the DMA conversion using the next buffer */
sam_adc_dmasetup(priv->dma, sam_adc_dmasetup(priv->dma,
priv->odd ? (void *)priv->oddbuf , (void *)priv->evenbuf priv->odd ? (void *)priv->oddbuf : (void *)priv->evenbuf,
SAMA5_NCHANNELS); SAMA5_NCHANNELS);
} }
#endif #endif
@ -752,7 +759,7 @@ static void sam_adc_endconversion(void *arg)
/* Get exclusive access to the driver data structure */ /* Get exclusive access to the driver data structure */
sam_adc_lock(priv->adc); sam_adc_lock(priv);
/* Check for the end of conversion event on each channel */ /* Check for the end of conversion event on each channel */
@ -763,23 +770,19 @@ static void sam_adc_endconversion(void *arg)
{ {
/* Read the ADC sample and pass it to the upper half */ /* Read the ADC sample and pass it to the upper half */
regval = sam_adc_getreg(priv, SAM_ADC_CDR(chan)); regval = sam_adc_getreg(priv, SAM_ADC_CDR(chan));
ret = adc_receive(&priv->dev, chan, regval & ADC_CDR_DATA_MASK); (void)adc_receive(priv->dev, chan, regval & ADC_CDR_DATA_MASK);
pending &= ~bit; pending &= ~bit;
} }
} }
/* Exit, re-enabling ADC interrupts */ /* Exit, re-enabling ADC interrupts */
ignored: sam_adc_putreg(priv, SAM_ADC_IER, SAMA5_CHAN_ENABLE);
/* Re-enable ADC interrupts. */
sam_adc_putreg32(priv->adc, SAM_ADC_IER, SAMA5_CHAN_ENABLE);
/* Release our lock on the ADC structure */ /* Release our lock on the ADC structure */
sem_adc_unlock(priv->adc); sam_adc_unlock(priv);
} }
#endif /* SAMA5_ADC_HAVE_CHANNELS */ #endif /* SAMA5_ADC_HAVE_CHANNELS */
@ -829,7 +832,7 @@ static int sam_adc_interrupt(int irq, void *context)
* interrupts will be re-enabled after the worker thread executes. * interrupts will be re-enabled after the worker thread executes.
*/ */
sam_adc_putreg32(priv->adc, SAM_ADC_IDR, ADC_INT_EOCALL); sam_adc_putreg(priv, SAM_ADC_IDR, ADC_INT_EOCALL);
/* Save the set of pending interrupts for the bottom half (in case any /* Save the set of pending interrupts for the bottom half (in case any
* were cleared by reading the ISR). * were cleared by reading the ISR).
@ -884,7 +887,7 @@ static void sam_adc_reset(struct adc_dev_s *dev)
/* Stop any DMA */ /* Stop any DMA */
dma_stop(priv->dma); sam_dmastop(priv->dma);
/* Stop an release any timer */ /* Stop an release any timer */
@ -908,15 +911,15 @@ static void sam_adc_reset(struct adc_dev_s *dev)
/* Reset gain, offset, differential modes */ /* Reset gain, offset, differential modes */
sam_adc_putreg(priv, SAM_CGR_MR, 0); sam_adc_putreg(priv, SAM_ADC_CGR, 0);
sam_adc_putreg(priv, SAM_COR_MR, 0); sam_adc_putreg(priv, SAM_ADC_COR, 0);
#ifndef CONFIG_SAMA5_ADC_SWTRIG #ifndef CONFIG_SAMA5_ADC_SWTRIG
/* Select software trigger (i.e., basically no trigger) */ /* Select software trigger (i.e., basically no trigger) */
regval = sam_adc_getreg(priv->dev, SAM_ADC_MR); regval = sam_adc_getreg(priv, SAM_ADC_MR);
regval &= ~ADC_MR_TRGSEL_MASK; regval &= ~ADC_MR_TRGSEL_MASK;
sam_adc_putreg(priv->dev, SAM_ADC_MR, regval); sam_adc_putreg(priv, SAM_ADC_MR, regval);
regval = sam_adc_getreg(priv, SAM_ADC_TRGR); regval = sam_adc_getreg(priv, SAM_ADC_TRGR);
regval &= ~ADC_TRGR_TRGMOD_MASK; regval &= ~ADC_TRGR_TRGMOD_MASK;
@ -939,7 +942,7 @@ static void sam_adc_reset(struct adc_dev_s *dev)
static int sam_adc_setup(struct adc_dev_s *dev) static int sam_adc_setup(struct adc_dev_s *dev)
{ {
struct sam_adc_s *priv = (struct sam_adc_s *)dev->ad_priv; struct sam_adc_s *priv = (struct sam_adc_s *)dev->ad_priv;
int ret; uint32_t regval;
/* Enable channel number tag. This bit will force the channel number (CHNB) /* Enable channel number tag. This bit will force the channel number (CHNB)
* to be included in the LDCR register content. * to be included in the LDCR register content.
@ -991,8 +994,7 @@ static int sam_adc_setup(struct adc_dev_s *dev)
/* Configure trigger mode and start conversion */ /* Configure trigger mode and start conversion */
sam_adc_trigger(priv); return sam_adc_trigger(priv);
return OK;
} }
/**************************************************************************** /****************************************************************************
@ -1009,10 +1011,10 @@ static void sam_adc_shutdown(struct adc_dev_s *dev)
struct sam_adc_s *priv = (struct sam_adc_s *)dev->ad_priv; struct sam_adc_s *priv = (struct sam_adc_s *)dev->ad_priv;
/* Disable ADC interrupts, both at the level of the ADC device and at the /* Disable ADC interrupts, both at the level of the ADC device and at the
* level of the NVIC. * level of the AIC.
*/ */
sam_adc_putreg32(priv, SAM_ADC_IDR, ADC_TSD_INTS); sam_adc_putreg(priv, SAM_ADC_IDR, ADC_INT_ALL);
up_disable_irq(SAM_IRQ_ADC); up_disable_irq(SAM_IRQ_ADC);
/* Then detach the ADC interrupt handler. */ /* Then detach the ADC interrupt handler. */
@ -1044,13 +1046,13 @@ static void sam_adc_rxint(struct adc_dev_s *dev, bool enable)
{ {
/* Enable channel interrupts */ /* Enable channel interrupts */
sam_adc_putreg32(priv, SAM_ADC_IER, SAMA5_CHAN_ENABLE); sam_adc_putreg(priv, SAM_ADC_IER, SAMA5_CHAN_ENABLE);
} }
else else
{ {
/* Disable channel interrupts */ /* Disable channel interrupts */
sam_adc_putreg32(priv, SAM_ADC_IDR, ADC_INT_EOCALL); sam_adc_putreg(priv, SAM_ADC_IDR, ADC_INT_EOCALL);
} }
#endif #endif
} }
@ -1090,11 +1092,13 @@ static int sam_adc_ioctl(struct adc_dev_s *dev, int cmd, unsigned long arg)
****************************************************************************/ ****************************************************************************/
#ifdef CONFIG_SAMA5_ADC_TIOATRIG #ifdef CONFIG_SAMA5_ADC_TIOATRIG
static void sam_adc_settimer(struct sam_adc_s *priv, uint32_t frequency, static int sam_adc_settimer(struct sam_adc_s *priv, uint32_t frequency,
int channel) int channel)
{ {
uint32_t div; uint32_t div;
uint32_t tcclks; uint32_t tcclks;
uint32_t mode;
int ret;
/* Configure TC for a 1Hz frequency and trigger on RC compare. */ /* Configure TC for a 1Hz frequency and trigger on RC compare. */
@ -1113,7 +1117,7 @@ static void sam_adc_settimer(struct sam_adc_s *priv, uint32_t frequency,
TC_CMR_WAVSEL_UPRC | /* UP mode w/ trigger on RC Compare */ TC_CMR_WAVSEL_UPRC | /* UP mode w/ trigger on RC Compare */
TC_CMR_WAVE | /* Wave mode */ TC_CMR_WAVE | /* Wave mode */
TC_CMR_ACPA_CLEAR | /* RA Compare Effect on TIOA: Clear */ TC_CMR_ACPA_CLEAR | /* RA Compare Effect on TIOA: Clear */
TC_CMR_ACPC_SET); /* RC effect on TIOA: Set TC_CMR_ACPC_SET); /* RC effect on TIOA: Set */
/* Now allocate and configure the channel */ /* Now allocate and configure the channel */
@ -1132,11 +1136,12 @@ static void sam_adc_settimer(struct sam_adc_s *priv, uint32_t frequency,
/* And start the timer */ /* And start the timer */
sam_tc_start(priv->tc); sam_tc_start(priv->tc);
return OK;
} }
#endif #endif
/**************************************************************************** /****************************************************************************
* Name: sam_adc_settimer * Name: sam_adc_freetimer
* *
* Description: * Description:
* Configure a timer to trigger the sampling periodically * Configure a timer to trigger the sampling periodically
@ -1144,8 +1149,7 @@ static void sam_adc_settimer(struct sam_adc_s *priv, uint32_t frequency,
****************************************************************************/ ****************************************************************************/
#ifdef CONFIG_SAMA5_ADC_TIOATRIG #ifdef CONFIG_SAMA5_ADC_TIOATRIG
static void sam_adc_settimer(struct sam_adc_s *priv, uint32_t frequency, static void sam_adc_freetimer(struct sam_adc_s *priv)
int channel)
{ {
/* Is a timer allocated? */ /* Is a timer allocated? */
@ -1168,16 +1172,17 @@ static void sam_adc_settimer(struct sam_adc_s *priv, uint32_t frequency,
* *
****************************************************************************/ ****************************************************************************/
static void sam_adc_trigger(struct sam_adc_s *priv) static int sam_adc_trigger(struct sam_adc_s *priv)
{ {
uint32_t reggal; uint32_t regval;
int ret = OK;
#if defined(CONFIG_SAMA5_ADC_SWTRIG) #if defined(CONFIG_SAMA5_ADC_SWTRIG)
/* Configure the software trigger */ /* Configure the software trigger */
regval = sam_adc_getreg(priv->dev, SAM_ADC_MR); regval = sam_adc_getreg(priv, SAM_ADC_MR);
regval &= ~ADC_MR_TRGSEL_MASK; regval &= ~ADC_MR_TRGSEL_MASK;
sam_adc_putreg(priv->dev, SAM_ADC_MR, regval); sam_adc_putreg(priv, SAM_ADC_MR, regval);
/* No trigger, only software trigger can start conversions */ /* No trigger, only software trigger can start conversions */
@ -1189,10 +1194,10 @@ static void sam_adc_trigger(struct sam_adc_s *priv)
#elif defined(CONFIG_SAMA5_ADC_ADTRG) #elif defined(CONFIG_SAMA5_ADC_ADTRG)
/* Configure the trigger via the external ADTRG signal */ /* Configure the trigger via the external ADTRG signal */
regval = sam_adc_getreg(priv->dev, SAM_ADC_MR); regval = sam_adc_getreg(priv, SAM_ADC_MR);
regval &= ~ADC_MR_TRGSEL_MASK; regval &= ~ADC_MR_TRGSEL_MASK;
regval |= ADC_MR_TRGSEL_ADC_ADTRIG; regval |= ADC_MR_TRGSEL_ADC_ADTRIG;
sam_adc_putreg(priv->dev, SAM_ADC_MR, regval); sam_adc_putreg(priv, SAM_ADC_MR, regval);
/* External trigger edge selection */ /* External trigger edge selection */
@ -1215,24 +1220,30 @@ static void sam_adc_trigger(struct sam_adc_s *priv)
/* Start the timer */ /* Start the timer */
#if defined(CONFIG_SAMA5_ADC_TIOA0TRIG) #if defined(CONFIG_SAMA5_ADC_TIOA0TRIG)
sam_adc_settimer(priv, CONFIG_SAMA5_ADC_TIOAFREQ, TC_CHAN0); ret = sam_adc_settimer(priv, CONFIG_SAMA5_ADC_TIOAFREQ, TC_CHAN0);
#elif defined(CONFIG_SAMA5_ADC_TIOA1TRIG) #elif defined(CONFIG_SAMA5_ADC_TIOA1TRIG)
sam_adc_settimer(priv, CONFIG_SAMA5_ADC_TIOAFREQ, TC_CHAN1); ret = sam_adc_settimer(priv, CONFIG_SAMA5_ADC_TIOAFREQ, TC_CHAN1);
#elif defined(CONFIG_SAMA5_ADC_TIOA2TRIG) #elif defined(CONFIG_SAMA5_ADC_TIOA2TRIG)
sam_adc_settimer(priv, CONFIG_SAMA5_ADC_TIOAFREQ, TC_CHAN2); ret = sam_adc_settimer(priv, CONFIG_SAMA5_ADC_TIOAFREQ, TC_CHAN2);
#else #else
# error Timer/counter for trigger not defined # error Timer/counter for trigger not defined
ret = -ENOSYS;
#endif #endif
if (ret < 0)
{
adbg("ERROR: sam_adc_settimer failed: %d\n", ret);
return ret;
}
/* Configure to trigger using Timer/counter 0, channel 1, 2, or 3. /* Configure to trigger using Timer/counter 0, channel 1, 2, or 3.
* NOTE: This trigger option depends on having properly configuer * NOTE: This trigger option depends on having properly configuer
* timer/counter 0 to provide this output. That is done independently * timer/counter 0 to provide this output. That is done independently
* the the timer/counter driver. * the the timer/counter driver.
*/ */
/* Set TIOAn trigger where n=0, 1, or 2 */ /* Set TIOAn trigger where n=0, 1, or 2 */
regval = sam_adc_getreg(priv->dev, SAM_ADC_MR); regval = sam_adc_getreg(priv, SAM_ADC_MR);
regval &= ~ADC_MR_TRGSEL_MASK; regval &= ~ADC_MR_TRGSEL_MASK;
#if defined(CONFIG_SAMA5_ADC_TIOA0TRIG) #if defined(CONFIG_SAMA5_ADC_TIOA0TRIG)
@ -1245,8 +1256,7 @@ static void sam_adc_trigger(struct sam_adc_s *priv)
# error Timer/counter for trigger not defined # error Timer/counter for trigger not defined
#endif #endif
regval |= ADC_MR_TRGSEL_ADC_TRIG1; sam_adc_putreg(priv, SAM_ADC_MR, regval);
sam_adc_putreg(priv->dev, SAM_ADC_MR, regval);
/* Timer trigger edge selection */ /* Timer trigger edge selection */
@ -1268,6 +1278,8 @@ static void sam_adc_trigger(struct sam_adc_s *priv)
#else #else
# error "Undefined ADC trigger" # error "Undefined ADC trigger"
#endif #endif
return ret;
} }
/**************************************************************************** /****************************************************************************
@ -1488,7 +1500,7 @@ static void sam_adc_analogchange(struct sam_adc_s *priv)
/* Enable/disable the analog change feature */ /* Enable/disable the analog change feature */
regval = sam_adc_getreg(priv->dev, SAM_ADC_MR); regval = sam_adc_getreg(priv, SAM_ADC_MR);
#ifdef CONFIG_SAMA5_ADC_ANARCH #ifdef CONFIG_SAMA5_ADC_ANARCH
/* Disable analog change: No analog change on channel switching: DIFF0, /* Disable analog change: No analog change on channel switching: DIFF0,
@ -1504,7 +1516,7 @@ static void sam_adc_analogchange(struct sam_adc_s *priv)
regval &= ~ADC_MR_ANACH; regval &= ~ADC_MR_ANACH;
#endif #endif
sam_adc_putreg(priv->dev, SAM_ADC_MR, regval); sam_adc_putreg(priv, SAM_ADC_MR, regval);
} }
/**************************************************************************** /****************************************************************************
@ -1532,6 +1544,7 @@ static void sam_adc_setseqr(int chan, uint32_t *seqr1, uint32_t *seqr2, int seq)
static void sam_adc_sequencer(struct sam_adc_s *priv) static void sam_adc_sequencer(struct sam_adc_s *priv)
{ {
#ifdef CONFIG_SAMA5_ADC_SEQUENCER #ifdef CONFIG_SAMA5_ADC_SEQUENCER
uint32_t regval;
uint32_t seqr1; uint32_t seqr1;
uint32_t seqr2; uint32_t seqr2;
int seq; int seq;
@ -1783,11 +1796,12 @@ struct sam_adc_s *sam_adc_initialize(void)
/* Initialize the ADC device data structure */ /* Initialize the ADC device data structure */
sem_init(&priv->exclsem, 0, 1); sem_init(&priv->exclsem, 0, 1);
priv->dev = &g_adcdev;
#ifdef CONFIG_SAMA5_ADC_DMA #ifdef CONFIG_SAMA5_ADC_DMA
/* Allocate a DMA channel */ /* Allocate a DMA channel from DMAC1 */
priv->dma = sam_dmachannel(dmac, DMA_FLAGS); priv->dma = sam_dmachannel(1, DMA_FLAGS);
DEBUGASSERT(priv->dma); DEBUGASSERT(priv->dma);
#endif #endif
@ -1858,7 +1872,7 @@ struct sam_adc_s *sam_adc_initialize(void)
/* Return a pointer to the device structure */ /* Return a pointer to the device structure */
return &g_adcpriv; return priv;
} }
/**************************************************************************** /****************************************************************************

View File

@ -61,6 +61,7 @@
#include "up_arch.h" #include "up_arch.h"
#include "sam_periphclks.h" #include "sam_periphclks.h"
#include "chip/sam_pinmap.h"
#include "chip/sam_pmc.h" #include "chip/sam_pmc.h"
#include "sam_pio.h" #include "sam_pio.h"
#include "sam_tc.h" #include "sam_tc.h"