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:
parent
6f2e23ad0c
commit
ce53ea5da6
@ -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)"
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user