STM32 ADC: Add DMA support for the STM32 F4 family. From Max Kriegler

This commit is contained in:
Max Kriegler
2015-07-30 08:47:45 -06:00
committed by Gregory Nutt
parent 7407e41569
commit 9ed14b0924
3 changed files with 325 additions and 87 deletions
+62
View File
@@ -1294,6 +1294,22 @@ config STM32_HAVE_ADC4
bool
default n
config STM32_HAVE_ADC1_DMA
bool
default n
config STM32_HAVE_ADC2_DMA
bool
default n
config STM32_HAVE_ADC3_DMA
bool
default n
config STM32_HAVE_ADC4_DMA
bool
default n
config STM32_HAVE_CAN1
bool
default n
@@ -1336,24 +1352,29 @@ config STM32_ADC1
bool "ADC1"
default n
select STM32_ADC
select STM32_HAVE_ADC1_DMA if STM32_STM32F10XX && STM32_DMA1
select STM32_HAVE_ADC1_DMA if !STM32_STM32F10XX && STM32_DMA2
config STM32_ADC2
bool "ADC2"
default n
select STM32_ADC
depends on STM32_HAVE_ADC2
select STM32_HAVE_ADC2_DMA if STM32_DMA2
config STM32_ADC3
bool "ADC3"
default n
select STM32_ADC
depends on STM32_HAVE_ADC3
select STM32_HAVE_ADC3_DMA if STM32_DMA2
config STM32_ADC4
bool "ADC4"
default n
select STM32_ADC
depends on STM32_HAVE_ADC4
select STM32_HAVE_ADC4_DMA if STM32_DMA2
config STM32_COMP
bool "COMP"
@@ -3092,6 +3113,47 @@ config STM32_TIM14_DAC2
endchoice
menu "ADC Configuration"
depends on STM32_ADC1
config STM32_ADC1_DMA
bool "ADC1 DMA"
depends on STM32_ADC1 && STM32_HAVE_ADC1_DMA
default n
---help---
If DMA is selected, then the ADC may be configured to support
DMA transfer, which is necessary if multiple channels are read
or if very high trigger frequencies are used.
config STM32_ADC2_DMA
bool "ADC2 DMA"
depends on STM32_ADC2 && STM32_HAVE_ADC2_DMA
default n
---help---
If DMA is selected, then the ADC may be configured to support
DMA transfer, which is necessary if multiple channels are read
or if very high trigger frequencies are used.
config STM32_ADC3_DMA
bool "ADC3 DMA"
depends on STM32_ADC3 && STM32_HAVE_ADC3_DMA
default n
---help---
If DMA is selected, then the ADC may be configured to support
DMA transfer, which is necessary if multiple channels are read
or if very high trigger frequencies are used.
config STM32_ADC4_DMA
bool "ADC4 DMA"
depends on STM32_ADC4 && STM32_HAVE_ADC4_DMA
default n
---help---
If DMA is selected, then the ADC may be configured to support
DMA transfer, which is necessary if multiple channels are read
or if very high trigger frequencies are used.
endmenu
menu "DAC Configuration"
depends on STM32_DAC1 || STM32_DAC2
+230 -84
View File
@@ -61,6 +61,7 @@
#include "chip.h"
#include "stm32.h"
#include "stm32_dma.h"
#include "stm32_adc.h"
/* ADC "upper half" support must be enabled */
@@ -104,20 +105,20 @@
ADC_CR1_OVRIE)
#endif
/* The maximum number of channels that can be sampled. If dma support is
/* ADC Channels/DMA ********************************************************/
/* The maximum number of channels that can be sampled. If DMA support is
* not enabled, then only a single channel can be sampled. Otherwise,
* data overruns would occur.
*/
#ifdef CONFIG_ADC_DMA
# define ADC_MAX_SAMPLES 16
#else
# define ADC_MAX_SAMPLES 1
#endif
#define ADC_MAX_CHANNELS_DMA 16
#define ADC_MAX_CHANNELS_NODMA 1
/* DMA channels and interface values differ for the F1 and F4 families */
#if defined(CONFIG_STM32_STM32L15XX)
# define ADC_CHANNELS_NUMBER 32
# define ADC_DEFAULT_SAMPLE 0x7
# define ADC_CHANNELS_NUMBER 32
# define ADC_DEFAULT_SAMPLE 0x7
#endif
/* This can be refined or defined in Kconfig */
@@ -136,37 +137,48 @@
struct stm32_dev_s
{
uint8_t irq; /* Interrupt generated by this
* ADC block */
uint8_t nchannels; /* Number of channels */
uint8_t cchannels; /* Number of configured channels */
uint8_t intf; /* ADC interface number */
uint8_t current; /* Current ADC channel being
* converted */
uint8_t irq; /* Interrupt generated by this ADC block */
uint8_t nchannels; /* Number of channels */
uint8_t cchannels; /* Number of configured channels */
uint8_t intf; /* ADC interface number */
uint8_t current; /* Current ADC channel being converted */
#ifdef ADC_HAVE_DMA
uint8_t dmachan; /* DMA channel needed by this ADC */
bool hasdma; /* True: This channel supports DMA */
#endif
#if defined(CONFIG_STM32_STM32L15XX)
uint8_t sample_rate[ADC_CHANNELS_NUMBER]; /* Sample time selection. These
* bits must be written only
* when ADON=0 */
/* Sample time selection. These bits must be written only when ADON=0 */
uint8_t sample_rate[ADC_CHANNELS_NUMBER];
#endif
#ifdef ADC_HAVE_TIMER
uint8_t trigger; /* Timer trigger channel: 0=CC1,
* 1=CC2, 2=CC3, 3=CC4, 4=TRGO */
uint8_t trigger; /* Timer trigger channel: 0=CC1, 1=CC2, 2=CC3,
* 3=CC4, 4=TRGO */
#endif
xcpt_t isr; /* Interrupt handler for this
* ADC block */
uint32_t base; /* Base address of registers
* unique to this ADC block */
xcpt_t isr; /* Interrupt handler for this ADC block */
uint32_t base; /* Base address of registers unique to this ADC
* block */
#ifdef ADC_HAVE_TIMER
uint32_t tbase; /* Base address of timer used by
* this ADC block */
uint32_t extsel; /* EXTSEL value used by this ADC
* block */
uint32_t pclck; /* The PCLK frequency that
* drives this timer */
uint32_t freq; /* The desired frequency of
* conversions */
uint32_t tbase; /* Base address of timer used by this ADC block */
uint32_t extsel; /* EXTSEL value used by this ADC block */
uint32_t pclck; /* The PCLK frequency that drives this timer */
uint32_t freq; /* The desired frequency of conversions */
#endif
#ifdef ADC_HAVE_DMA
DMA_HANDLE dma; /* Allocated DMA channel */
/* List of selected DMA channels to sample */
uint8_t chanlist[ADC_MAX_CHANNELS_DMA];
/* DMA transfer buffer */
uint16_t dmabuffer[ADC_MAX_CHANNELS_DMA];
#else
/* List of selected DMA channels to sample */
uint8_t chanlist[ADC_MAX_CHANNELS_NODMA];
#endif
uint8_t chanlist[ADC_MAX_CHANNELS];
};
/****************************************************************************
@@ -289,6 +301,10 @@ static struct stm32_dev_s g_adcpriv1 =
.pclck = ADC1_TIMER_PCLK_FREQUENCY,
.freq = CONFIG_STM32_ADC1_SAMPLE_FREQUENCY,
#endif
#ifdef ADC1_HAVE_DMA
.dmachan = ADC1_DMA_CHAN,
.hasdma = true,
#endif
};
static struct adc_dev_s g_adcdev1 =
@@ -319,6 +335,10 @@ static struct stm32_dev_s g_adcpriv2 =
.pclck = ADC2_TIMER_PCLK_FREQUENCY,
.freq = CONFIG_STM32_ADC2_SAMPLE_FREQUENCY,
#endif
#ifdef ADC2_HAVE_DMA
.dmachan = ADC2_DMA_CHAN,
.hasdma = true,
#endif
};
static struct adc_dev_s g_adcdev2 =
@@ -349,6 +369,10 @@ static struct stm32_dev_s g_adcpriv3 =
.pclck = ADC3_TIMER_PCLK_FREQUENCY,
.freq = CONFIG_STM32_ADC3_SAMPLE_FREQUENCY,
#endif
#ifdef ADC3_HAVE_DMA
.dmachan = ADC3_DMA_CHAN,
.hasdma = true,
#endif
};
static struct adc_dev_s g_adcdev3 =
@@ -358,6 +382,12 @@ static struct adc_dev_s g_adcdev3 =
};
#endif
/* ADC4 state */
#ifdef CONFIG_STM32_ADC4
# error Missing ADC4 implementation
#endif
/****************************************************************************
* Private Functions
****************************************************************************/
@@ -956,7 +986,8 @@ static void adc_startconv(struct stm32_dev_s *priv, bool enable)
regval &= ~ADC_CR2_SWSTART;
}
adc_putreg(priv, STM32_ADC_CR2_OFFSET,regval);
adc_putreg(priv, STM32_ADC_CR2_OFFSET, regval);
}
#endif
@@ -1316,6 +1347,54 @@ static void adc_write_sample_time_registers(FAR struct adc_dev_s *dev)
}
#endif
/****************************************************************************
* Name: adc_dmacovcallback
*
* Description:
* Callback for DMA. Called from the DMA transfer complete interrupt after
* all channels have been converted and transferred with DMA.
*
* Input Parameters:
*
* handle - handle to DMA
* isr -
* arg - adc device
*
* Returned Value:
*
****************************************************************************/
#ifdef ADC_HAVE_DMA
static void adc_dmaconvcallback(DMA_HANDLE handle, uint8_t isr, void *arg)
{
FAR struct adc_dev_s *dev = (FAR struct adc_dev_s*) arg;
FAR struct stm32_dev_s *priv = dev->ad_priv;
uint32_t regval;
int i;
for (i = 0; i < priv->nchannels; i++)
{
adc_receive(dev, priv->current, priv->dmabuffer[priv->current]);
priv->current++;
if (priv->current >= priv->nchannels)
{
/* Restart the conversion sequence from the beginning */
priv->current = 0;
}
}
/* Restart DMA for the next conversion series */
regval = adc_getreg(priv, STM32_ADC_CR2_OFFSET);
regval &= ~ADC_CR2_DMA;
adc_putreg(priv, STM32_ADC_CR2_OFFSET, regval);
regval |= ADC_CR2_DMA;
adc_putreg(priv, STM32_ADC_CR2_OFFSET, regval);
}
#endif
/****************************************************************************
* Name: adc_reset
*
@@ -1409,6 +1488,13 @@ static void adc_reset(FAR struct adc_dev_s *dev)
regval |= ADC_CR1_ALLINTS;
#ifdef ADC_HAVE_DMA
if (priv->hasdma)
{
regval |= ADC_CR1_SCAN;
}
#endif
#if defined(CONFIG_STM32_STM32F20XX) || defined(CONFIG_STM32_STM32F40XX) || \
defined(CONFIG_STM32_STM32L15XX)
@@ -1461,6 +1547,13 @@ static void adc_reset(FAR struct adc_dev_s *dev)
regval |= ADC_CR2_EXTEN_NONE;
#endif
#ifdef ADC_HAVE_DMA
if (priv->hasdma)
{
regval |= ADC_CR2_DMA;
}
#endif
adc_putreg(priv, STM32_ADC_CR2_OFFSET, regval);
/* Configuration of the channel conversions */
@@ -1492,7 +1585,16 @@ static void adc_reset(FAR struct adc_dev_s *dev)
/* Set the number of conversions */
DEBUGASSERT(priv->nchannels <= ADC_MAX_SAMPLES);
#ifdef ADC_HAVE_DMA
if (priv->hasdma)
{
DEBUGASSERT(priv->nchannels <= ADC_MAX_CHANNELS_DMA);
}
else
#endif
{
DEBUGASSERT(priv->nchannels <= ADC_MAX_CHANNELS_NODMA);
}
regval |= (((uint32_t)priv->nchannels-1) << ADC_SQR1_L_SHIFT);
adc_putreg(priv, STM32_ADC_SQR1_OFFSET, regval);
@@ -1501,6 +1603,38 @@ static void adc_reset(FAR struct adc_dev_s *dev)
priv->current = 0;
#ifdef ADC_HAVE_DMA
/* Enable DMA */
if (priv->hasdma)
{
uint32_t ccr;
/* Stop and free DMA if it was started before */
if (priv->dma != NULL)
{
stm32_dmastop(priv->dma);
stm32_dmafree(priv->dma);
}
priv->dma = stm32_dmachannel(priv->dmachan);
ccr = DMA_SCR_MSIZE_16BITS | /* Memory size */
DMA_SCR_PSIZE_16BITS | /* Peripheral size */
DMA_SCR_MINC | /* Memory increment mode */
DMA_SCR_CIRC | /* Circular buffer */
DMA_SCR_DIR_P2M; /* Read from peripheral */
stm32_dmasetup(priv->dma,
priv->base + STM32_ADC_DR_OFFSET,
(uint32_t)priv->dmabuffer,
priv->nchannels,
ccr);
stm32_dmastart(priv->dma, adc_dmaconvcallback, dev, false);
}
#endif
/* Set ADON to wake up the ADC from Power Down state. */
adc_enable(priv, true);
@@ -2218,83 +2352,85 @@ static int adc_set_ch(FAR struct adc_dev_s *dev, uint8_t ch)
static int adc_ioctl(FAR struct adc_dev_s *dev, int cmd, unsigned long arg)
{
#ifdef CONFIG_STM32_STM32L15XX
int ret = OK;
FAR struct stm32_dev_s * priv = (FAR struct stm32_dev_s *)dev->ad_priv;
int ret = OK;
switch (cmd)
{
case IO_ENABLE_TEMPER_VOLT_CH:
{
#ifdef ADC_HAVE_DMA
case ANIOC_TRIGGER:
adc_startconv(priv, true);
break;
#endif
#ifdef CONFIG_STM32_STM32L15XX
case IO_ENABLE_TEMPER_VOLT_CH:
adc_ioc_enable_tvref_register(dev, *(bool *)arg);
break;
case IO_ENABLE_DISABLE_PDI:
case IO_ENABLE_DISABLE_PDD:
case IO_ENABLE_DISABLE_PDD_PDI:
case IO_ENABLE_DISABLE_PDI:
case IO_ENABLE_DISABLE_PDD:
case IO_ENABLE_DISABLE_PDD_PDI:
adc_ioc_change_sleep_between_opers(dev, cmd, *(bool *)arg);
break;
case IO_ENABLE_DISABLE_AWDIE:
case IO_ENABLE_DISABLE_EOCIE:
case IO_ENABLE_DISABLE_JEOCIE:
case IO_ENABLE_DISABLE_OVRIE:
case IO_ENABLE_DISABLE_ALL_INTS:
case IO_ENABLE_DISABLE_AWDIE:
case IO_ENABLE_DISABLE_EOCIE:
case IO_ENABLE_DISABLE_JEOCIE:
case IO_ENABLE_DISABLE_OVRIE:
case IO_ENABLE_DISABLE_ALL_INTS:
adc_ioc_change_ints(dev, cmd, *(bool*)arg);
break;
case IO_START_CONV:
{
uint8_t ch = ((uint8_t)arg);
case IO_START_CONV:
{
uint8_t ch = ((uint8_t)arg);
ret = adc_ioc_wait_rcnr_zeroed(priv);
if (ret < 0)
{
set_errno(-ret);
return ret;
}
ret = adc_ioc_wait_rcnr_zeroed(priv);
if (ret < 0)
{
set_errno(-ret);
return ret;
}
ret = adc_set_ch(dev,ch);
if (ret < 0)
{
set_errno(-ret);
return ret;
}
ret = adc_set_ch(dev,ch);
if (ret < 0)
{
set_errno(-ret);
return ret;
}
if (ch)
{
/* Clear fifo */
if (ch)
{
/* Clear fifo */
dev->ad_recv.af_head = 0;
dev->ad_recv.af_tail = 0;
}
dev->ad_recv.af_head = 0;
dev->ad_recv.af_tail = 0;
}
adc_startconv(priv, true);
break;
}
adc_startconv(priv, true);
break;
}
#if defined(CONFIG_STM32_STM32L15XX) && ((STM32_CFGR_PLLSRC != 0) || \
(STM32_SYSCLK_SW != RCC_CFGR_SW_HSI))
case IO_STOP_ADC:
#if (STM32_CFGR_PLLSRC != 0 || STM32_SYSCLK_SW != RCC_CFGR_SW_HSI)
case IO_STOP_ADC:
adc_enable(priv, false);
adc_enable_hsi(false);
break;
case IO_START_ADC:
case IO_START_ADC:
adc_enable_hsi(true);
adc_enable(priv, true);
break;
#endif
#endif /* CONFIG_STM32_STM32L15XX */
default:
alldbg("unknown cmd: %d\n", cmd);
break;
default:
adbg("ERROR: Unknown cmd: %d\n", cmd);
ret = -ENOTTY;
}
return OK;
#else
return -ENOTTY;
#endif
return ret;
}
/****************************************************************************
@@ -2681,7 +2817,17 @@ struct adc_dev_s *stm32_adcinitialize(int intf, const uint8_t *chanlist,
#endif
DEBUGASSERT(cchannels <= ADC_MAX_CHANNELS);
#ifdef ADC_HAVE_DMA
if (priv->hasdma)
{
DEBUGASSERT(priv->nchannels <= ADC_MAX_CHANNELS_DMA);
}
else
#endif
{
DEBUGASSERT(priv->nchannels <= ADC_MAX_CHANNELS_NODMA);
}
priv->cchannels = cchannels;
memcpy(priv->chanlist, chanlist, cchannels);
+33 -3
View File
@@ -204,10 +204,40 @@
#if defined(CONFIG_STM32_ADC1) || defined(CONFIG_STM32_ADC2) || \
defined(CONFIG_STM32_ADC3)
/* DMA support is not yet implemented for this driver */
/* DMA support */
#ifdef CONFIG_ADC_DMA
# warning "DMA is not supported by the current driver"
#undef ADC_HAVE_DMA
#if defined(CONFIG_STM32_ADC1_DMA) || defined(CONFIG_STM32_ADC2_DMA) || \
defined(CONFIG_STM32_ADC3_DMA) || defined(CONFIG_STM32_ADC4_DMA)
# if defined(CONFIG_STM32_STM32F40XX)
# define ADC_HAVE_DMA 1
#else
# warning DMA is only supported for the stm32f40xx family
# endif
#endif
#ifdef CONFIG_STM32_ADC1_DMA
# define ADC1_HAVE_DMA 1
#else
# undef ADC1_HAVE_DMA
#endif
#ifdef CONFIG_STM32_ADC2_DMA
# define ADC2_HAVE_DMA 1
#else
# undef ADC2_HAVE_DMA
#endif
#ifdef CONFIG_STM32_ADC3_DMA
# define ADC3_HAVE_DMA 1
#else
# undef ADC3_HAVE_DMA
#endif
#ifdef CONFIG_STM32_ADC4_DMA
# define ADC4_HAVE_DMA 1
#else
# undef ADC4_HAVE_DMA
#endif
/* Timer configuration: If a timer trigger is specified, then get