SAMA5 ADC: Seems functional in all modes including DMA

This commit is contained in:
Gregory Nutt 2013-10-28 10:08:12 -06:00
parent 77c0c6c2d6
commit 3fd4629bd7
7 changed files with 180 additions and 61 deletions

View File

@ -5900,4 +5900,8 @@
is called as part of a failed pthread startup before the flags
field in the TCB has been initialized, then a crash occurs.
Pointed out by David Sidrane (2013-10-27)
* arch/arm/src/sama5/sam_adc.c: ADC now works in all implemented
modes: single channel or multiple channel with sequencer support.
software trigger or timer trigger; ADC channel interrupts or
DMA (2013-10-28).

View File

@ -1697,12 +1697,12 @@ config SAMA5_ADC_DMASAMPLES
The DMA logic uses ping-pong buffers, so the total buffering
requirement will be
2 Buffers * Number_of_ADC_Channels * SAMA5_ADC_DMASAMPLES * sizeof(uint32_t)
2 Buffers * Number_of_ADC_Channels * SAMA5_ADC_DMASAMPLES * sizeof(uint16_t)
So, for example, if you had 8 ADC channels and 8 triggers per DMA
transfer, then the total DMA buffering requirment would be:
2 * 8 * 8 * 4 = 512 bytes.
2 * 8 * 8 * 2 = 256 bytes.
config SAMA5_ADC_AUTOCALIB
bool "ADC auto-calibration"

View File

@ -418,8 +418,8 @@ struct sam_adc_s
/* DMA sample data buffer */
#ifdef CONFIG_SAMA5_ADC_DMA
uint32_t evenbuf[SAMA5_ADC_SAMPLES];
uint32_t oddbuf[SAMA5_ADC_SAMPLES];
uint16_t evenbuf[SAMA5_ADC_SAMPLES];
uint16_t oddbuf[SAMA5_ADC_SAMPLES];
#endif
#endif /* SAMA5_ADC_HAVE_CHANNELS */
@ -451,6 +451,7 @@ static void sam_adc_dmadone(void *arg);
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,
size_t buflen);
static void sam_adc_dmastart(struct sam_adc_s *priv);
#endif
/* ADC interrupt handling */
@ -609,7 +610,8 @@ static bool sam_adc_checkreg(struct sam_adc_s *priv, bool wr,
static void sam_adc_dmadone(void *arg)
{
struct sam_adc_s *priv = (struct sam_adc_s *)arg;
uint32_t *buffer;
uint16_t *buffer;
uint16_t *next;
uint16_t sample;
int chan;
int i;
@ -617,20 +619,58 @@ static void sam_adc_dmadone(void *arg)
avdbg("ready=%d enabled=%d\n", priv->enabled, priv->ready);
ASSERT(priv != NULL && !priv->ready);
/* If the DMA is disabled, just ignore the data */
/* If the DMA transfer is not enabled, just ignore the data (and do not start
* the next DMA transfer).
*/
if (!priv->enabled)
if (priv->enabled)
{
/* Select the completed DMA buffer */
/* Toggle to the next buffer.
*
* buffer - The buffer on which the DMA has just completed
* next - The buffer in which to start the next DMA
*/
if (priv->odd)
{
buffer = priv->oddbuf;
next = priv->evenbuf;
priv->odd = false;
}
else
{
buffer = priv->evenbuf;
next = priv->oddbuf;
priv->odd = true;
}
/* Restart the DMA conversion as quickly as possible using the next
* buffer.
*
* REVISIT: In the original design, toggling the ping-pong buffers and
* restarting the DMA was done in the interrupt handler so that the
* next buffer could be filling while the current buffer is being
* processed here on the worker thread. But, unfortunately,
* sam_adcm_dmasetup() cannot be called from an interrupt handler.
*
* A consequence of this is that there is a small window from the time
* that the last set of samples was taken, the worker thread runs, and
* the follow logic restarts the DMA in which samples could be lost!
*
* Without the interrupt level DMA restart logic, there is not really
* any good reason to support the ping-poing buffers at all.
*/
sam_adc_dmasetup(priv, (FAR uint8_t *)next,
SAMA5_ADC_SAMPLES * sizeof(uint16_t));
buffer = priv->odd ? priv->evenbuf : priv->oddbuf;
/* Invalidate the DMA buffer so that we are guaranteed to reload the
* newly DMAed data from RAM.
*/
cp15_invalidate_dcache((uintptr_t)buffer,
(uintptr_t)buffer + SAMA5_ADC_SAMPLES * sizeof(uint32_t));
(uintptr_t)buffer + SAMA5_ADC_SAMPLES * sizeof(uint16_t));
/* Process each sample */
@ -639,7 +679,7 @@ static void sam_adc_dmadone(void *arg)
/* Get the sample and the channel number */
chan = (int)((*buffer & ADC_LCDR_CHANB_MASK) >> ADC_LCDR_CHANB_SHIFT);
sample = (uint16_t)((*buffer & ADC_LCDR_DATA_MASK) >> ADC_LCDR_DATA_SHIFT);
sample = ((*buffer & ADC_LCDR_DATA_MASK) >> ADC_LCDR_DATA_SHIFT);
/* And give the sample data to the ADC upper half */
@ -653,6 +693,26 @@ static void sam_adc_dmadone(void *arg)
}
#endif
/****************************************************************************
* Name: sam_adc_dmastart
*
* Description:
* Initiate DMA sampling.
*
****************************************************************************/
static void sam_adc_dmastart(struct sam_adc_s *priv)
{
/* Make sure that the worker is available and that DMA is not disabled */
if (priv->ready && priv->enabled)
{
priv->odd = false; /* Start with the even buffer */
sam_adc_dmasetup(priv, (FAR uint8_t *)priv->evenbuf,
SAMA5_ADC_SAMPLES * sizeof(uint16_t));
}
}
/****************************************************************************
* Name: sam_adc_dmacallback
*
@ -670,22 +730,28 @@ static void sam_adc_dmacallback(DMA_HANDLE handle, void *arg, int result)
int ret;
allvdbg("ready=%d enabled=%d\n", priv->enabled, priv->ready);
DEBUGASSERT(priv->ready);
/* Check of the bottom half is keeping up with us */
/* Check of the bottom half is keeping up with us.
*
* ready == false: Would mean that the worker thready has not ran since
* the the last DMA callback.
* enabled == false: Means that the upper half has asked us nicely to stop
* transferring DMA data.
*/
if (priv->ready && priv->enabled)
{
/* Toggle to the next buffer. Note that the toggle only occurs if
* the bottom half is ready to accept more data. Otherwise, we
* will get a data overrun and just re-use the last buffer.
*/
priv->odd = !priv->odd;
priv->ready = false;
/* Transfer processing to the bottom half */
/* Verify that the worker is available */
DEBUGASSERT(priv->work.worker == NULL);
/* Mark the work as busy and schedule the DMA done processing to
* occur on the worker thread.
*/
priv->ready = false;
ret = work_queue(HPWORK, &priv->work, sam_adc_dmadone, priv, 0);
if (ret != 0)
{
@ -693,11 +759,20 @@ static void sam_adc_dmacallback(DMA_HANDLE handle, void *arg, int result)
}
}
/* Restart the DMA conversion using the next buffer */
sam_adc_dmasetup(priv->dma,
priv->odd ? (void *)priv->oddbuf : (void *)priv->evenbuf,
SAMA5_ADC_SAMPLES * sizeof(uint32_t));
/* REVISIT: There used to be logic here to toggle the ping-pong buffers and
* to restart the DMA conversion. This would allow refilling one buffer
* while the worker processes the other buffer that was just filled. But,
* unfortunately, sam_adcm_dmasetup() and dma_rxsetup cannot be called
* from an interrupt handler.
*
* A consequence of this is that there is a small window from the time
* that the last set of samples was taken, the worker thread runs, and the
* logic on the worker thread restarts the DMA. Samples trigger during
* this window will be be lost!
*
* Without this logic, there is not really any strong reason to support
* the ping-poing buffers at all.
*/
}
#endif
@ -1015,17 +1090,18 @@ static int sam_adc_setup(struct adc_dev_s *dev)
sam_adc_autocalibrate(priv);
#ifdef CONFIG_SAMA5_ADC_DMA
/* Configure for DMA transfer */
/* Initiate DMA transfers */
priv->odd = false;
priv->ready = true;
priv->enabled = false;
priv->ready = true; /* Worker is avaiable */
priv->enabled = true; /* Transfers are enabled */
sam_adc_dmastart(priv);
sam_adc_dmasetup(priv, (void *)priv->evenbuf, SAMA5_ADC_SAMPLES);
#else
/* Enable end-of-conversion interrupts for all enabled channels. */
sam_adc_putreg(priv, SAM_ADC_IER, SAMA5_CHAN_ENABLE);
#endif
/* Configure trigger mode and start conversion */
@ -1048,11 +1124,12 @@ static void sam_adc_shutdown(struct adc_dev_s *dev)
avdbg("Shutdown\n");
/* Disable ADC interrupts, both at the level of the ADC device and at the
* level of the AIC.
*/
/* Reset the ADC peripheral */
sam_adc_reset(dev);
/* Disable ADC interrupts at the level of the AIC */
sam_adc_putreg(priv, SAM_ADC_IDR, ADC_INT_ALL);
up_disable_irq(SAM_IRQ_ADC);
/* Then detach the ADC interrupt handler. */
@ -1075,9 +1152,20 @@ static void sam_adc_rxint(struct adc_dev_s *dev, bool enable)
avdbg("enable=%d\n", enable);
#ifdef CONFIG_SAMA5_ADC_DMA
/* We don't stop the DMA when RX is disabled, we just stop the data transfer */
/* Ignore redundant requests */
priv->enabled = enable;
if (priv->enabled != enable)
{
/* Set a flag. If disabling, the DMA sequence will terminate at the
* completion of the next DMA.
*/
priv->enabled = enable;
/* If enabling, then we need to restart the DMA transfer */
sam_adc_dmastart(priv);
}
#else
/* Are we enabling or disabling? */
@ -1193,7 +1281,7 @@ static int sam_adc_settimer(struct sam_adc_s *priv, uint32_t frequency,
* do this without overflowing a 32-bit unsigned integer.
*/
fdiv = div * frequency;
fdiv = div * frequency;
DEBUGASSERT(div > 0 && div <= fdiv); /* Will check for integer overflow */
/* Set up TC_RA and TC_RC. The frequency is determined by RA and RC: TIOA is

View File

@ -746,28 +746,28 @@ SAMA5 ADC Support
Basic driver configuration:
System Type -> SAMA5 Peripheral Support
CONFIG_SAMA5_ADC=y : Enable ADC driver support
CONFIG_SAMA5_TC0=y : Enable the Timer/counter library need for periodic sampling
CONFIG_SAMA5_ADC=y : Enable ADC driver support
CONFIG_SAMA5_TC0=y : Enable the Timer/counter library need for periodic sampling
Drivers
CONFIG_ANALOG=y : Should be automatically selected
CONFIG_ADC=y : Should be automatically selected
CONFIG_ANALOG=y : Should be automatically selected
CONFIG_ADC=y : Should be automatically selected
System Type -> ADC Configuration
CONFIG_SAMA5_ADC_CHAN0=y : These settings enable the sequencer to collect
CONFIG_SAMA5_ADC_CHAN1=y : Samples from ADC channels 0-3 on each trigger
CONFIG_SAMA5_ADC_CHAN0=y : These settings enable the sequencer to collect
CONFIG_SAMA5_ADC_CHAN1=y : Samples from ADC channels 0-3 on each trigger
CONFIG_SAMA5_ADC_CHAN2=y
CONFIG_SAMA5_ADC_CHAN3=y
CONFIG_SAMA5_ADC_SEQUENCER=y
CONFIG_SAMA5_ADC_TIOA0TRIG=y : Trigger on the TC0, channel 0 output A
CONFIG_SAMA5_ADC_TIOAFREQ=2 : At a frequency of 2Hz
CONFIG_SAMA5_ADC_TIOA_RISING=y : Trigger on the rising edge
CONFIG_SAMA5_ADC_TIOA0TRIG=y : Trigger on the TC0, channel 0 output A
CONFIG_SAMA5_ADC_TIOAFREQ=2 : At a frequency of 2Hz
CONFIG_SAMA5_ADC_TIOA_RISING=y : Trigger on the rising edge
Default ADC settings (like gain and offset) may also be set if desired.
System Type -> Timer/counter Configuration
CONFIG_SAMA5_TC0_TIOA0=y : Should be automatically selected
CONFIG_SAMA5_TC0_TIOA0=y : Should be automatically selected
Work queue supported is also needed:
@ -779,7 +779,7 @@ SAMA5 ADC Support
enabled as follows:
Application Configuration -> Examples -> ADC eample
CONFIG_EXAMPLES_ADC=y : Enables the example code
CONFIG_EXAMPLES_ADC=y : Enables the example code
CONFIG_EXAMPLES_ADC_DEVPATH="/dev/adc0"
Other default settings for the ADC example should be okay.
@ -790,10 +790,17 @@ SAMA5 ADC Support
following in the configuration.
System Type -> SAMA5 Peripheral Support
CONFIG_SAMA5_DMAC1=y : Enable DMAC1 support
CONFIG_SAMA5_DMAC1=y : Enable DMAC1 support
System Type -> ADC Configuration
CONFIG_SAMA5_ADC_DMA=y : Enable ADC DMA transfers
CONFIG_SAMA5_ADC_DMA=y : Enable ADC DMA transfers
CONFIG_SAMA5_ADC_DMASAMPLES=2 : Collect two sets of samples per DMA
Drivers -> Analog device (ADC/DAC) support
CONFIG_ADC_FIFOSIZE=16 : Driver may need a large ring buffer
Application Configuration -> Examples -> ADC eample
CONFIG_EXAMPLES_ADC_GROUPSIZE=16 : Larger buffers in the test
SAMA5D3x-EK Configuration Options
=================================

View File

@ -11,37 +11,50 @@ config ADC
not only Analog-to-Digital Converters (ADC) but also amplifiers and
analog multiplexers.
if ADC
config ADC_FIFOSIZE
int "ADC buffer size"
default 8
---help---
This variable defines the size of the ADC ring buffer that is used
to queue received ADC data until they can be retrieved by the
application by reading from the ADC character device. NOTE: Since
this is a ring buffer, the actual number of bytes that can be
retained in buffer is (ADC_FIFOSIZE - 1).
config ADC_ADS125X
bool "TI ADS1255/ADS1256 support"
default n
depends on ADC
select SPI
if ADC_ADS125X
config ADS1255_FREQUENCY
int "ADS1255/ADS1256 SPI frequency"
default 1000000
depends on ADC_ADS125X
endif # ADC_ADS125X
config ADC_PGA11X
bool "TI PGA112/3/6/7 support"
default n
depends on ADC
select SPI
---help---
Enables support for the PGA112, PGA113, PGA116, PGA117 Zerø-Drift
PROGRAMMABLE GAIN AMPLIFIER with MUX
if ADC_PGA11X
config PGA11X_SPIFREQUENCY
int "TI PGA112/3/6/7 SPI frequency"
default 1000000
depends on ADC_PGA11X
---help---
PGA11x SPI frequency.
config PGA11X_SPIMODE
int "TI PGA112/3/6/7 SPI mode"
default 0
depends on ADC_PGA11X
---help---
PGA11x SPI mode. The specification says that the device operates in Mode 0 or
Mode 3. But sometimes you need to tinker with this to get things to work
@ -50,28 +63,33 @@ config PGA11X_SPIMODE
config PGA11X_DAISYCHAIN
bool "TI PGA112/3/6/7 daisy chain mode"
default n
depends on ADC_PGA11X
---help---
Enable support to use two PGA116/7's in Daisy Chain configuration.
config PGA11X_MULTIPLE
bool "Multiple TI PGA112/3/6/7 support"
default n
depends on ADC_PGA11X && !PGA11X_DAISYCHAIN
depends on !PGA11X_DAISYCHAIN
---help---
Can be defined to support multiple PGA11X devices on board with separate
chip selects (not daisy chained). Each device will require a customized
SPI interface to distinguish them when SPI_SELECT is called with
devid=SPIDEV_MUX.
endif # if ADC_PGA11X
endif # ADC
config DAC
bool "Digital-to-Analog Conversion"
default n
default n
---help---
Select to enable support for Digital-to-Analog Converters (DACs).
if DAC
config DAC_AD5410
bool "AD5410 support"
default n
depends on DAC
select SPI
endif # DAC

View File

@ -158,6 +158,7 @@ static int adc_open(FAR struct file *filep)
sem_post(&dev->ad_closesem);
}
return ret;
}
@ -207,6 +208,7 @@ static int adc_close(FAR struct file *filep)
sem_post(&dev->ad_closesem);
}
}
return ret;
}

View File

@ -61,7 +61,7 @@
* Pre-processor Definitions
************************************************************************************/
/* Default configuration settings that may be overridden in the board configuration.
/* Default configuration settings that may be overridden in the NuttX configuration
* file. The configured size is limited to 255 to fit into a uint8_t.
*/