summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGregory Nutt <gnutt@nuttx.org>2013-10-28 10:08:12 -0600
committerGregory Nutt <gnutt@nuttx.org>2013-10-28 10:08:12 -0600
commit022d45602666fd6966d721dde9635a62d8c56898 (patch)
tree210d028db3590d3069f1e2c268c1680a18f58b5c
parent252a9031e7e0c68d880273550358ab32008bbd12 (diff)
downloadnuttx-022d45602666fd6966d721dde9635a62d8c56898.tar.gz
nuttx-022d45602666fd6966d721dde9635a62d8c56898.tar.bz2
nuttx-022d45602666fd6966d721dde9635a62d8c56898.zip
SAMA5 ADC: Seems functional in all modes including DMA
-rw-r--r--nuttx/ChangeLog4
-rw-r--r--nuttx/arch/arm/src/sama5/Kconfig4
-rw-r--r--nuttx/arch/arm/src/sama5/sam_adc.c158
-rw-r--r--nuttx/configs/sama5d3x-ek/README.txt33
-rw-r--r--nuttx/drivers/analog/Kconfig36
-rw-r--r--nuttx/drivers/analog/adc.c2
-rw-r--r--nuttx/include/nuttx/analog/adc.h2
7 files changed, 179 insertions, 60 deletions
diff --git a/nuttx/ChangeLog b/nuttx/ChangeLog
index 614b9b89f..d63386c38 100644
--- a/nuttx/ChangeLog
+++ b/nuttx/ChangeLog
@@ -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).
diff --git a/nuttx/arch/arm/src/sama5/Kconfig b/nuttx/arch/arm/src/sama5/Kconfig
index e9f3eb914..7595f9387 100644
--- a/nuttx/arch/arm/src/sama5/Kconfig
+++ b/nuttx/arch/arm/src/sama5/Kconfig
@@ -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"
diff --git a/nuttx/arch/arm/src/sama5/sam_adc.c b/nuttx/arch/arm/src/sama5/sam_adc.c
index 512e1fe29..f2b7b2bb1 100644
--- a/nuttx/arch/arm/src/sama5/sam_adc.c
+++ b/nuttx/arch/arm/src/sama5/sam_adc.c
@@ -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 */
@@ -654,6 +694,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
*
* Description:
@@ -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);
-
- /* Check of the bottom half is keeping up with us */
+ DEBUGASSERT(priv->ready);
+
+ /* 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.
+ /* 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->odd = !priv->odd;
priv->ready = false;
- /* Transfer processing to the bottom half */
-
- DEBUGASSERT(priv->work.worker == NULL);
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->ready = true; /* Worker is avaiable */
+ priv->enabled = true; /* Transfers are enabled */
- priv->odd = false;
- priv->ready = true;
- priv->enabled = false;
+ 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 */
+
+ if (priv->enabled != enable)
+ {
+ /* Set a flag. If disabling, the DMA sequence will terminate at the
+ * completion of the next DMA.
+ */
+
+ priv->enabled = enable;
- 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
diff --git a/nuttx/configs/sama5d3x-ek/README.txt b/nuttx/configs/sama5d3x-ek/README.txt
index f30f35d5b..daa430f82 100644
--- a/nuttx/configs/sama5d3x-ek/README.txt
+++ b/nuttx/configs/sama5d3x-ek/README.txt
@@ -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
=================================
diff --git a/nuttx/drivers/analog/Kconfig b/nuttx/drivers/analog/Kconfig
index ebed79c78..5a4019571 100644
--- a/nuttx/drivers/analog/Kconfig
+++ b/nuttx/drivers/analog/Kconfig
@@ -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
diff --git a/nuttx/drivers/analog/adc.c b/nuttx/drivers/analog/adc.c
index 72f19452a..5b02efa96 100644
--- a/nuttx/drivers/analog/adc.c
+++ b/nuttx/drivers/analog/adc.c
@@ -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;
}
diff --git a/nuttx/include/nuttx/analog/adc.h b/nuttx/include/nuttx/analog/adc.h
index ac38d6820..44af05ef9 100644
--- a/nuttx/include/nuttx/analog/adc.h
+++ b/nuttx/include/nuttx/analog/adc.h
@@ -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.
*/