diff --git a/arch/arm/src/stm32/Kconfig b/arch/arm/src/stm32/Kconfig index f69e0022fe0..83051fe50a9 100644 --- a/arch/arm/src/stm32/Kconfig +++ b/arch/arm/src/stm32/Kconfig @@ -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 diff --git a/arch/arm/src/stm32/stm32_adc.c b/arch/arm/src/stm32/stm32_adc.c index ef154b4027a..702c5b056e2 100644 --- a/arch/arm/src/stm32/stm32_adc.c +++ b/arch/arm/src/stm32/stm32_adc.c @@ -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); diff --git a/arch/arm/src/stm32/stm32_adc.h b/arch/arm/src/stm32/stm32_adc.h index e89d502c8cd..500df1ccec2 100644 --- a/arch/arm/src/stm32/stm32_adc.h +++ b/arch/arm/src/stm32/stm32_adc.h @@ -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