arch/arm/src/samv7: add DMA and TC trigger support to AFEC driver

This commit adds DMA and TC support to SAMV7 AFEC driver. The AFEC (ADC)
can now be triggered by Timer/counter at chosen frewuency and samples can
be transfered via DMA with configurable number of samples. Timer/counter
trigger is now set as a default option with the possibility to change it
to software generated trigger.

DMA is inspired by SAMA5 driver and also uses ping pong buffers.

Signed-off-by: Michal Lenc <michallenc@seznam.cz>
This commit is contained in:
Michal Lenc 2021-11-26 15:54:11 +01:00 committed by Xiang Xiao
parent 6f2e23ad0c
commit ce53ea5da6
2 changed files with 653 additions and 6 deletions

View File

@ -314,10 +314,16 @@ config SAMV7_ADC
bool "12-bit ADC Controller (ADC)"
default n
config SAMV7_AFEC0
config SAMV7_AFEC_TIOATRIG
bool
default n
menuconfig SAMV7_AFEC0
bool "Analog Front End 0 (AFEC0)"
default n
select SAMV7_AFEC
---help---
Support for AFEC0 (ADC) driver.
if SAMV7_AFEC0
@ -332,12 +338,65 @@ config SAMV7_AFEC0_RES
3 OSR16
4 OSR64
5 OSR256
endif
config SAMV7_AFEC1
config SAMV7_AFEC_DMA
bool "DMA Support"
default n
depends on SAMV7_XDMAC
---help---
Enables transfer of converted data over DMA.
config SAMV7_AFEC_DMASAMPLES
int "Number of DMA samples"
default 2
depends on SAMV7_AFEC_DMA
---help---
Number of DMA samples to be transfered. Number of samples less than
2 might be inneficient.
choice
prompt "AFEC Trigger mode"
default SAMV7_AFEC0_TIOATRIG
config SAMV7_AFEC0_TIOATRIG
bool "Timer/counter trigger"
select SAMV7_AFEC_TIOATRIG
---help---
Use output of Timer/Counter as a trigger.
config SAMV7_AFEC0_SWTRIG
bool "Software trigger"
---help---
Use software trigger.
endchoice
if SAMV7_AFEC0_TIOATRIG
config SAMV7_AFEC0_TIOAFREQ
int "AFEC sampling frequency"
default 1000
---help---
This setting provides the rate at which the timer will driver AFEC
sampling.
config SAMV7_AFEC0_TIOACHAN
int "Timer/counter channel number"
default 0
range 0 2
---help---
Number of TC channel to trigger the AFEC conversion.
endif #SAMV7_AFEC0_TIOATRIG
endif # SAMV7_AFEC0
menuconfig SAMV7_AFEC1
bool "Analog Front End 1 (AFEC1)"
default n
select SAMV7_AFEC
---help---
Support for AFEC1 (ADC) driver.
if SAMV7_AFEC1
@ -352,7 +411,58 @@ config SAMV7_AFEC1_RES
3 OSR16
4 OSR64
5 OSR256
endif
config SAMV7_AFEC_DMA
bool "DMA Support"
default n
depends on SAMV7_XDMAC
---help---
Enables transfer of converted data over DMA.
config SAMV7_AFEC_DMASAMPLES
int "Number of DMA samples"
default 2
depends on SAMV7_AFEC_DMA
---help---
Number of DMA samples to be transfered. Number of samples less than
2 might be inneficient.
choice
prompt "AFEC Trigger mode"
default SAMV7_AFEC1_TIOATRIG
config SAMV7_AFEC1_TIOATRIG
bool "Timer/counter trigger"
select SAMV7_AFEC_TIOATRIG
---help---
Use output of Timer/Counter as a trigger.
config SAMV7_AFEC1_SWTRIG
bool "Software trigger"
---help---
Use software trigger.
endchoice
if SAMV7_AFEC1_TIOATRIG
config SAMV7_AFEC1_TIOAFREQ
int "AFEC sampling frequency"
default 1000
---help---
This setting provides the rate at which the timer will driver ADC
sampling.
config SAMV7_AFEC1_TIOACHAN
int "Timer/counter channel number"
default 3
range 3 5
---help---
Number of TC channel to trigger the AFEC conversion.
endif #SAMV7_AFEC1_TIOATRIG
endif # SAMV7_AFEC1
config SAMV7_MCAN0
bool "CAN controller 0 (MCAN0)"

View File

@ -36,6 +36,7 @@
#include <arch/board/board.h>
#include <nuttx/irq.h>
#include <nuttx/arch.h>
#include <nuttx/wqueue.h>
#include <nuttx/analog/adc.h>
#include <nuttx/analog/ioctl.h>
@ -47,7 +48,9 @@
#include "hardware/sam_pio.h"
#include "sam_periphclks.h"
#include "sam_gpio.h"
#include "sam_tc.h"
#include "sam_afec.h"
#include "sam_xdmac.h"
#ifdef CONFIG_ADC
@ -59,6 +62,27 @@
#define ADC_MAX_CHANNELS 11
#ifdef CONFIG_SAMV7_AFEC_DMA
#define DMA_FLAGS (DMACH_FLAG_FIFOCFG_LARGEST | \
DMACH_FLAG_PERIPHH2SEL | DMACH_FLAG_PERIPHISPERIPH | \
DMACH_FLAG_PERIPHWIDTH_32BITS | DMACH_FLAG_PERIPHCHUNKSIZE_1 | \
DMACH_FLAG_MEMPID_MAX | DMACH_FLAG_MEMAHB_AHB_IF0 | \
DMACH_FLAG_PERIPHAHB_AHB_IF1 | DMACH_FLAG_MEMWIDTH_32BITS | \
DMACH_FLAG_MEMINCREMENT | DMACH_FLAG_MEMCHUNKSIZE_1 | \
DMACH_FLAG_MEMBURST_1)
#endif
#if !defined(CONFIG_SAMV7_AFEC_DMA)
# undef CONFIG_SAMV7_AFEC_DMASAMPLES
# define CONFIG_SAMV7_AFEC_DMASAMPLES 1
#elif !defined(CONFIG_SAMV7_AFEC_DMASAMPLES)
# error CONFIG_SAMV7_AFEC_DMASAMPLES must be defined
#elif CONFIG_SAMV7_AFEC_DMASAMPLES < 2
# warning Values of CONFIG_SAMV7_AFEC_DMASAMPLES < 2 are inefficient
#endif
#define SAMV7_AFEC_SAMPLES (CONFIG_SAMV7_AFEC_DMASAMPLES * ADC_MAX_CHANNELS)
/****************************************************************************
* Private Types
****************************************************************************/
@ -70,10 +94,31 @@ struct samv7_dev_s
uint32_t base; /* ADC register base */
uint8_t initialized; /* ADC initialization counter */
uint8_t resolution; /* ADC resolution (SAMV7_AFECn_RES) */
uint8_t trigger; /* ADC trigger (software, timer...) */
uint8_t timer_channel; /* Timer channel to trigger ADC */
uint32_t frequency; /* Frequency of the timer */
int irq; /* ADC IRQ number */
int pid; /* ADC PID number */
int nchannels; /* Number of configured channels */
uint8_t chanlist[ADC_MAX_CHANNELS]; /* ADC channel list */
uint8_t current; /* Current channel being converted */
#ifdef CONFIG_SAMV7_AFEC_TIOATRIG
TC_HANDLE tc; /* Handle for the timer channel */
#endif
#ifdef CONFIG_SAMV7_AFEC_DMA
volatile bool odd; /* Odd buffer is in use */
volatile bool ready; /* Worker has completed the last set of samples */
volatile bool enabled; /* DMA data transfer is enabled */
int nsamples;
DMA_HANDLE dma; /* Handle for DMA channel */
struct work_s work; /* Supports the interrupt handling "bottom half" */
/* DMA sample data buffer */
uint32_t evenbuf[SAMV7_AFEC_SAMPLES];
uint32_t oddbuf[SAMV7_AFEC_SAMPLES];
#endif
};
/****************************************************************************
@ -84,6 +129,22 @@ static void afec_putreg(FAR struct samv7_dev_s *priv, uint32_t offset,
uint32_t value);
static uint32_t afec_getreg(FAR struct samv7_dev_s *priv, uint32_t offset);
#ifdef CONFIG_SAMV7_AFEC_DMA
static void sam_afec_dmadone(void *arg);
static void sam_afec_dmacallback(DMA_HANDLE handle, void *arg, int result);
static int sam_afec_dmasetup(struct adc_dev_s *dev, FAR uint8_t *buffer,
size_t buflen);
static void sam_afec_dmastart(struct adc_dev_s *dev);
#endif
#ifdef CONFIG_SAMV7_AFEC_TIOATRIG
static int sam_afec_settimer(struct samv7_dev_s *priv, uint32_t frequency,
int channel);
static void sam_afec_freetimer(struct samv7_dev_s *priv);
#endif
static int sam_afec_trigger(struct samv7_dev_s *priv);
/* ADC methods */
static int afec_bind(FAR struct adc_dev_s *dev,
@ -114,9 +175,17 @@ static const struct adc_ops_s g_adcops =
static struct samv7_dev_s g_adcpriv0 =
{
.irq = SAM_IRQ_AFEC0,
.pid = SAM_PID_AFEC0,
.intf = 0,
.initialized = 0,
.resolution = CONFIG_SAMV7_AFEC0_RES,
#ifdef CONFIG_SAMV7_AFEC0_SWTRIG
.trigger = 0,
#else
.trigger = 1,
.timer_channel = CONFIG_SAMV7_AFEC0_TIOACHAN,
.frequency = CONFIG_SAMV7_AFEC0_TIOAFREQ,
#endif
.base = SAM_AFEC0_BASE,
};
@ -146,16 +215,24 @@ gpio_pinset_t g_adcpinlist0[ADC_MAX_CHANNELS] =
static struct samv7_dev_s g_adcpriv1 =
{
.irq = SAM_IRQ_AFEC1,
.pid = SAM_PID_AFEC1,
.intf = 1,
.initialized = 0,
.resolution = CONFIG_SAMV7_AFEC1_RES,
#ifdef CONFIG_SAMV7_AFEC1_SWTRIG
.trigger = 0,
#else
.trigger = 1,
.timer_channel = CONFIG_SAMV7_AFEC1_TIOACHAN,
.frequency = CONFIG_SAMV7_AFEC1_TIOAFREQ,
#endif
.base = SAM_AFEC1_BASE,
};
static struct adc_dev_s g_adcdev1 =
{
.ad_ops = &g_adcops,
.ad_priv = &g_adcpriv0,
.ad_priv = &g_adcpriv1,
};
gpio_pinset_t g_adcpinlist1[ADC_MAX_CHANNELS] =
@ -189,6 +266,405 @@ static uint32_t afec_getreg(FAR struct samv7_dev_s *priv, uint32_t offset)
return getreg32(priv->base + offset);
}
/****************************************************************************
* DMA Helpers
****************************************************************************/
/****************************************************************************
* Name: sam_afec_dmadone
*
* Description:
* This function executes on the worker thread. It is scheduled by
* sam_adc_dmacallback at the complete of each DMA sequenece. There is
* and interlock using ping-pong buffers and boolean values to prevent
* overrunning the worker thread:
*
* oddbuf[]/evenbuf[] - Ping pong buffers are used. The DMA collects
* data in one buffer while the worker thread processes data in the
* other.
* odd - If true, then DMA is active in the oddbuf[]; evenbuf[] holds
* completed DMA data.
* ready - Ping ponging is halted while ready is false; If data overrun
* occurs, then sample data will be lost on one sequence. The worker
* thread sets ready when it has completed processing the last sample
* data.
*
* Input Parameters:
* arg - The ADC private data structure cast to (void *)
*
* Returned Value:
* None
*
****************************************************************************/
#ifdef CONFIG_SAMV7_AFEC_DMA
static void sam_afec_dmadone(void *arg)
{
FAR struct adc_dev_s *dev = (FAR struct adc_dev_s *)arg;
FAR struct samv7_dev_s *priv = (FAR struct samv7_dev_s *)dev->ad_priv;
uint32_t *buffer;
uint32_t *next;
uint32_t sample;
int chan;
int i;
ainfo("ready=%d enabled=%d\n", priv->enabled, priv->ready);
DEBUGASSERT(priv != NULL && !priv->ready);
/* If the DMA transfer is not enabled, just ignore the data (and do not
* start the next DMA transfer).
*/
if (priv->enabled)
{
/* 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.
*/
sam_afec_dmasetup(dev, (FAR uint8_t *)next,
priv->nsamples * sizeof(uint32_t));
/* Invalidate the DMA buffer so that we are guaranteed to reload the
* newly DMAed data from RAM.
*/
up_invalidate_dcache((uintptr_t)buffer,
(uintptr_t)buffer +
priv->nsamples * sizeof(uint32_t));
/* Process each sample */
for (i = 0; i < priv->nsamples; i++, buffer++)
{
/* Get the sample and the channel number */
chan = (int)((*buffer & AFEC_LCDR_CHANB_MASK) >>
AFEC_LCDR_CHANB_SHIFT);
sample = ((*buffer & AFEC_LCDR_LDATA_MASK) >>
AFEC_LCDR_LDATA_SHIFT);
/* Verify the upper-half driver has bound its callback functions */
if (priv->cb != NULL)
{
/* Give the sample data to the ADC upper half */
DEBUGASSERT(priv->cb->au_receive != NULL);
priv->cb->au_receive(dev, chan, sample);
}
}
}
/* We are ready to handle the next sample sequence */
priv->ready = true;
}
/****************************************************************************
* Name: sam_afec_dmacallback
*
* Description:
* Called when one ADC DMA sequence completes. This function defers
* processing of the samples to sam_adc_dmadone which runs on the worker
* thread.
*
****************************************************************************/
static void sam_afec_dmacallback(DMA_HANDLE handle, void *arg, int result)
{
FAR struct adc_dev_s *dev = (FAR struct adc_dev_s *)arg;
FAR struct samv7_dev_s *priv = (FAR struct samv7_dev_s *)dev->ad_priv;
int ret;
ainfo("ready=%d enabled=%d\n", priv->enabled, priv->ready);
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 last DMA callback.
* enabled == false: Means that the upper half has asked us nicely to stop
* transferring DMA data.
*/
if (priv->ready && priv->enabled)
{
/* 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_afec_dmadone, dev, 0);
if (ret != 0)
{
aerr("ERROR: Failed to queue work: %d\n", ret);
}
}
}
/****************************************************************************
* Name: sam_afec_dmasetup
*
* Description:
* Setup to perform a read DMA. If the processor supports a data cache,
* then this method will also make sure that the contents of the DMA memory
* and the data cache are coherent. For read transfers this may mean
* invalidating the data cache.
*
* Input Parameters:
* priv - An instance of the ADC device interface
* buffer - The memory to DMA from
* buflen - The size of the DMA transfer in bytes
*
* Returned Value:
* OK on success; a negated errno on failure
*
****************************************************************************/
static int sam_afec_dmasetup(FAR struct adc_dev_s *dev, FAR uint8_t *buffer,
size_t buflen)
{
FAR struct samv7_dev_s *priv = (FAR struct samv7_dev_s *)dev->ad_priv;
uint32_t paddr;
uint32_t maddr;
ainfo("buffer=%p buflen=%d\n", buffer, (int)buflen);
DEBUGASSERT(priv != NULL && buffer != NULL && buflen > 0);
DEBUGASSERT(((uint32_t)buffer & 3) == 0);
/* Physical address of the ADC LCDR register and of the buffer location in
* RAM.
*/
paddr = priv->base + SAM_AFEC_LCDR_OFFSET;
maddr = (uintptr_t)buffer;
/* Configure the RX DMA */
sam_dmarxsetup(priv->dma, paddr, maddr, buflen);
/* Start the DMA */
sam_dmastart(priv->dma, sam_afec_dmacallback, dev);
return OK;
}
/****************************************************************************
* Name: sam_afec_dmastart
*
* Description:
* Initiate DMA sampling.
*
****************************************************************************/
static void sam_afec_dmastart(struct adc_dev_s *dev)
{
FAR struct samv7_dev_s *priv = (FAR struct samv7_dev_s *)dev->ad_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_afec_dmasetup(dev, (FAR uint8_t *)priv->evenbuf,
priv->nsamples * sizeof(uint32_t));
}
}
#endif
/****************************************************************************
* Name: sam_adc_settimer
*
* Description:
* Configure a timer to trigger the sampling periodically
*
****************************************************************************/
#ifdef CONFIG_SAMV7_AFEC_TIOATRIG
static int sam_afec_settimer(struct samv7_dev_s *priv, uint32_t frequency,
int channel)
{
uint32_t div;
uint32_t tcclks;
uint32_t actual;
uint32_t mode;
uint32_t fdiv;
uint32_t regval;
int ret;
ainfo("frequency=%ld channel=%d\n", (long)frequency, channel);
DEBUGASSERT(priv && frequency > 0);
/* Configure TC for a 1Hz frequency and trigger on RC compare. */
ret = sam_tc_clockselect(frequency, &tcclks, &actual);
if (ret < 0)
{
aerr("ERROR: sam_tc_divisor failed: %d\n", ret);
return ret;
}
div = BOARD_MCK_FREQUENCY / actual;
/* Set the timer/counter waveform mode the clock input selected by
* sam_tc_clockselect()
*/
mode = ((tcclks << TC_CMR_TCCLKS_SHIFT) | /* Use selected TCCLKS value */
TC_CMR_WAVSEL_UPRC | /* UP mode w/ trigger on RC Compare */
TC_CMR_WAVE | /* Wave mode */
TC_CMR_ACPA_CLEAR | /* RA Compare Effect on TIOA: Clear */
TC_CMR_ACPC_SET); /* RC effect on TIOA: Set */
/* Now allocate and configure the channel */
priv->tc = sam_tc_allocate(channel, mode);
if (!priv->tc)
{
aerr("ERROR: Failed to allocate channel %d mode %08lx\n",
channel, mode);
return -EINVAL;
}
/* The divider returned by sam_tc_clockselect() is the reload value
* that will achieve a 1Hz rate. We need to multiply this to get the
* desired frequency. sam_tc_divisor() should have already assure
* that we can do this without overflowing a 32-bit unsigned integer.
*/
fdiv = div * frequency;
DEBUGASSERT(div > 0 && div <= fdiv); /* Will check for integer overflow */
/* Calculate the actual counter value from this divider and the tc input
* frequency.
*/
regval = BOARD_MCK_FREQUENCY / fdiv;
/* Set up TC_RA and TC_RC. The frequency is determined by RA and RC:
* TIOA is cleared on RA match; TIOA is set on RC match.
*/
sam_tc_setregister(priv->tc, TC_REGA, regval >> 1);
sam_tc_setregister(priv->tc, TC_REGC, regval);
/* And start the timer */
sam_tc_start(priv->tc);
return OK;
}
/****************************************************************************
* Name: sam_afec_freetimer
*
* Description:
* Configure a timer to trigger the sampling periodically
*
****************************************************************************/
static void sam_afec_freetimer(struct samv7_dev_s *priv)
{
/* Is a timer allocated? */
ainfo("tc=%p\n", priv->tc);
if (priv->tc)
{
/* Yes.. stop it and free it */
sam_tc_stop(priv->tc);
sam_tc_free(priv->tc);
priv->tc = NULL;
}
}
#endif
/****************************************************************************
* Name: sam_afec_trigger
*
* Description:
* Configure trigger mode and start conversion.
*
****************************************************************************/
static int sam_afec_trigger(struct samv7_dev_s *priv)
{
uint32_t regval;
int ret = OK;
#ifdef CONFIG_SAMV7_AFEC_SWTRIG
if (priv->trigger == 0)
{
ainfo("Setup software trigger\n");
/* Configure the software trigger */
regval = afec_getreg(priv, SAM_AFEC_MR_OFFSET);
regval &= ~AFEC_MR_TRGSEL_MASK;
afec_putreg(priv, SAM_AFEC_MR_OFFSET, regval);
}
#elif CONFIG_SAMV7_AFEC_TIOATRIG
if (priv->trigger == 1)
{
ainfo("Setup timer/counter trigger\n");
/* Start the timer */
ret = sam_afec_settimer(priv, priv->frequency, priv->timer_channel);
if (ret < 0)
{
aerr("ERROR: sam_afec_settimer failed: %d\n", ret);
return ret;
}
/* AFEC_MR registr still needs to select corresponding channels with
* 1, 2, 3 values (see TRGSEL bitfield description) even if channels
* 3, 4 and 5 are selected for AFEC1
*/
if (priv->intf == 1)
{
priv->timer_channel -= 3;
}
/* Set trigger for AFECn driver */
regval = afec_getreg(priv, SAM_AFEC_MR_OFFSET);
regval &= ~AFEC_MR_TRGSEL_MASK;
regval |= ((priv->timer_channel + 1) << AFEC_MR_TRGSEL_SHIFT) | \
AFEC_MR_TRGEN;
afec_putreg(priv, SAM_AFEC_MR_OFFSET, regval);
}
#endif
return ret;
}
/****************************************************************************
* Name: afec_bind
*
@ -231,6 +707,22 @@ static void afec_reset(FAR struct adc_dev_s *dev)
goto exit_leave_critical;
}
/* Stop any ongoing DMA */
#ifdef CONFIG_SAMV7_AFEC_DMA
if (priv->dma)
{
sam_dmastop(priv->dma);
}
#endif
#ifdef CONFIG_SAMV7_AFEC_TIOATRIG
if (priv->trigger == 1)
{
sam_afec_freetimer(priv);
}
#endif
/* Configure clock gating */
switch (priv->intf)
@ -368,6 +860,19 @@ static int afec_setup(FAR struct adc_dev_s *dev)
uint32_t afec_cselr = AFEC_CSELR_CSEL(priv->chanlist[priv->current]);
afec_putreg(priv, SAM_AFEC_CSELR_OFFSET, afec_cselr);
#ifdef CONFIG_SAMV7_AFEC_DMA
/* Initiate DMA transfers */
priv->ready = true; /* Worker is available */
priv->enabled = true; /* Transfers are enabled */
sam_afec_dmastart(dev);
#endif
/* Setup AFEC trigger */
ret = sam_afec_trigger(priv);
return ret;
}
@ -382,6 +887,25 @@ static int afec_setup(FAR struct adc_dev_s *dev)
static void afec_rxint(FAR struct adc_dev_s *dev, bool enable)
{
FAR struct samv7_dev_s *priv = (FAR struct samv7_dev_s *)dev->ad_priv;
#ifdef CONFIG_SAMV7_AFEC_DMA
/* 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;
/* If enabling, then we need to restart the DMA transfer */
sam_afec_dmastart(dev);
}
#else
uint32_t afec_ixr = 0;
for (int i = 0; i < priv->nchannels; i++)
@ -399,6 +923,7 @@ static void afec_rxint(FAR struct adc_dev_s *dev, bool enable)
{
afec_putreg(priv, SAM_AFEC_IDR_OFFSET, afec_ixr);
}
#endif
}
/****************************************************************************
@ -423,6 +948,10 @@ static void afec_shutdown(FAR struct adc_dev_s *dev)
return;
}
/* Reset ADC driver */
afec_reset(dev);
/* Disable ADC interrupts */
up_disable_irq(priv->irq);
@ -455,11 +984,13 @@ static int afec_ioctl(FAR struct adc_dev_s *dev, int cmd, unsigned long arg)
switch (cmd)
{
#ifndef CONFIG_SAMV7_AFEC_TIOATRIG
case ANIOC_TRIGGER:
{
afec_putreg(priv, SAM_AFEC_CR_OFFSET, AFEC_CR_START);
}
break;
#endif
case ANIOC_GET_NCHANNELS:
{
/* Return the number of configured channels */
@ -519,7 +1050,7 @@ static int afec_interrupt(int irq, void *context, FAR void *arg)
priv->current = 0;
}
/* Start the next conversion */
/* Setup the next conversion */
uint32_t afec_cselr = AFEC_CSELR_CSEL(priv->chanlist[priv->current]);
afec_putreg(priv, SAM_AFEC_CSELR_OFFSET, afec_cselr);
@ -589,6 +1120,12 @@ FAR struct adc_dev_s *sam_afec_initialize(int intf,
priv->nchannels = nchannels;
memcpy(priv->chanlist, chanlist, nchannels);
#ifdef CONFIG_SAMV7_AFEC_DMA
priv->nsamples = priv->nchannels * CONFIG_SAMV7_AFEC_DMASAMPLES;
priv->dma = sam_dmachannel(0, DMA_FLAGS | \
DMACH_FLAG_PERIPHPID(priv->pid));
#endif
ainfo("intf: %d nchannels: %d\n", priv->intf, priv->nchannels);
return dev;