diff --git a/arch/arm/src/stm32h5/stm32_adc.c b/arch/arm/src/stm32h5/stm32_adc.c index 1f74265c184..993c5531033 100644 --- a/arch/arm/src/stm32h5/stm32_adc.c +++ b/arch/arm/src/stm32h5/stm32_adc.c @@ -241,9 +241,24 @@ static const struct adc_ops_s g_adcops = #ifdef CONFIG_STM32H5_ADC1 +/* Double the size of the buffer in circular mode + * Circular mode utilizes half-transfer DMA interrupts and a 2x buffer + * to implement "double buffer" operation. While the CPU is servicing + * one half, DMA is filling the other half. + */ + #ifdef ADC1_HAVE_DMA -static uint16_t g_adc1_dmabuffer[CONFIG_STM32H5_ADC_MAX_SAMPLES * - CONFIG_STM32H5_ADC1_DMA_BATCH]; +# ifdef CONFIG_STM32H5_ADC1_DMA_CFG +# define ADC1_DMA_BUFFER_SIZE (CONFIG_STM32H5_ADC_MAX_SAMPLES *\ + CONFIG_STM32H5_ADC1_DMA_BATCH * 2) +# else +# define ADC1_DMA_BUFFER_SIZE (CONFIG_STM32H5_ADC_MAX_SAMPLES *\ + CONFIG_STM32H5_ADC1_DMA_BATCH) +# endif + +static uint16_t g_adc1_dmabuffer[ADC1_DMA_BUFFER_SIZE] +__attribute__((aligned(32))); + #endif static struct stm32_dev_s g_adcpriv1 = @@ -303,8 +318,16 @@ static struct adc_dev_s g_adcdev1 = #ifdef CONFIG_STM32H5_ADC2 #ifdef ADC2_HAVE_DMA -static uint16_t g_adc2_dmabuffer[CONFIG_STM32H5_ADC_MAX_SAMPLES * - CONFIG_STM32H5_ADC2_DMA_BATCH]; +# ifdef CONFIG_STM32H5_ADC2_DMA_CFG +# define ADC2_DMA_BUFFER_SIZE (CONFIG_STM32H5_ADC_MAX_SAMPLES *\ + CONFIG_STM32H5_ADC2_DMA_BATCH * 2) +# else +# define ADC2_DMA_BUFFER_SIZE (CONFIG_STM32H5_ADC_MAX_SAMPLES *\ + CONFIG_STM32H5_ADC2_DMA_BATCH) +# endif + +static uint16_t g_adc2_dmabuffer[ADC2_DMA_BUFFER_SIZE] +__attribute__((aligned(32))); #endif static struct stm32_dev_s g_adcpriv2 = @@ -870,6 +893,42 @@ static void adc_reset(struct adc_dev_s *dev) adc_rccreset(priv, false); } +/**************************************************************************** + * Name: adc_restart_dma + * + * Description: + * Restarts DMA for the configured ADC DMA channel. This is used in + * one-shot mode, where DMA and ADC must be reconfigured and restarted + * after each completed transfer. + * + * Input Parameters: + * dev - adc device + * + * Returned Value: + * None + * + ****************************************************************************/ + +#ifdef ADC_HAVE_DMA +static void adc_restart_dma(struct adc_dev_s *dev) +{ + struct stm32_dev_s *priv = (struct stm32_dev_s *)dev->ad_priv; + struct stm32_gpdma_cfg_s dmacfg; + + DEBUGASSERT(!priv->circular); + +#ifdef ADC_HAVE_TIMER + bool software_trigger = (priv->tbase == 0); +#else + bool software_trigger = true; +#endif + + adc_dmacfg(priv, &dmacfg); + stm32_dmasetup(priv->dma, &dmacfg); + stm32_dmastart(priv->dma, adc_dmaconvcallback, dev, false); + adc_startconv(priv, software_trigger); +} + /**************************************************************************** * Name: adc_dmaconvcallback * @@ -888,43 +947,103 @@ static void adc_reset(struct adc_dev_s *dev) * ****************************************************************************/ - #ifdef ADC_HAVE_DMA static void adc_dmaconvcallback(DMA_HANDLE handle, uint8_t status, void *arg) { struct adc_dev_s *dev = (struct adc_dev_s *)arg; struct stm32_dev_s *priv = (struct stm32_dev_s *)dev->ad_priv; - struct stm32_gpdma_cfg_s dmacfg; + + uint16_t conversion_count; + uint16_t buffer_offset; int i; + /* About Circular Mode + * The size of r_dmabuffer and transfer size is doubled + * half-transfer interrupts are enabled. Code should do this: + * half-transfer int: read r_dmabuffer[0] through r_dmabuffer[conv_count-1] + * transfer complete int: read r_dmabuffer[conv_count] to end of buffer. + */ + /* Verify that the upper-half has bound its callback */ if (priv->cb != NULL) { DEBUGASSERT(priv->cb->au_receive != NULL); - /* Deliver one sample per configured channel */ - - for (i = 0; i < priv->rnchannels * priv->dmabatch; i++) + if (status & DMA_STATUS_FATAL) { - priv->cb->au_receive(dev, - priv->chanlist[priv->current], - priv->r_dmabuffer[i]); - priv->current++; - if (priv->current >= priv->rnchannels) + aerr("ADC DMA fatal error(s) — stopping DMA\n"); + + if (status & DMA_STATUS_DTEF) { - priv->current = 0; + aerr("ADC DMA Error: DTEF (Data Transfer Error) occurred\n"); + } + + if (status & DMA_STATUS_ULEF) + { + aerr("ADC DMA Error: ULEF (Linked-list update error)\n"); + } + + if (status & DMA_STATUS_USEF) + { + aerr("ADC DMA Config Error: USEF (User Setting Error)\n"); + } + + stm32_dmastop(priv->dma); + + if (!priv->circular) + { + /* In non-circular mode, restart DMA and conversion */ + + adc_restart_dma(dev); + } + + return; + } + + /* Circular Mode - Use second half of double size buffer on TCF */ + + conversion_count = priv->rnchannels * priv->dmabatch; + buffer_offset = (priv->circular) ? conversion_count : 0; + + /* Half-Transfer Interrupt enabled for circular mode only */ + + if (status & DMA_STATUS_HTF && priv->circular) + { + for (i = 0; i < conversion_count; i++) + { + priv->cb->au_receive(dev, + priv->chanlist[priv->current], + priv->r_dmabuffer[i]); + + priv->current++; + if (priv->current >= priv->rnchannels) + { + priv->current = 0; + } + } + } + + if (status & DMA_STATUS_TCF) + { + for (i = 0; i < conversion_count; i++) + { + priv->cb->au_receive(dev, + priv->chanlist[priv->current], + priv->r_dmabuffer[buffer_offset + i]); + priv->current++; + if (priv->current >= priv->rnchannels) + { + priv->current = 0; + } } } } - /* Restart DMA for the next conversion series */ + /* Restart DMA for the next conversion series if in one-shot mode */ - if (priv->circular == 0) + if (!priv->circular) { - adc_dmacfg(priv, &dmacfg); - stm32_dmasetup(priv->dma, &dmacfg); - stm32_dmastart(priv->dma, adc_dmaconvcallback, dev, false); - adc_startconv(priv, true); + adc_restart_dma(dev); } } @@ -960,7 +1079,8 @@ static void adc_dmacfg(struct stm32_dev_s *priv, cfg->mode = priv->circular ? GPDMACFG_MODE_CIRC : 0; - cfg->ntransfers = priv->cchannels * priv->dmabatch * (1u << sdw_log2); + cfg->ntransfers = (priv->cchannels * priv->dmabatch) << sdw_log2; + cfg->ntransfers <<= (priv->circular ? 1 : 0); cfg->tr1 = (sdw_log2 << GPDMA_CXTR1_SDW_LOG2_SHIFT) | (sdw_log2 << GPDMA_CXTR1_DDW_LOG2_SHIFT) @@ -1124,7 +1244,7 @@ static int adc_setup(struct adc_dev_s *dev) stm32_dmasetup(priv->dma, &dmacfg); - stm32_dmastart(priv->dma, adc_dmaconvcallback, dev, false); + stm32_dmastart(priv->dma, adc_dmaconvcallback, dev, priv->circular); } #endif @@ -1145,6 +1265,13 @@ static int adc_setup(struct adc_dev_s *dev) } #endif +#ifdef ADC_HAVE_DMA + if (priv->circular) + { + adc_startconv(priv, true); + } +#endif + leave_critical_section(flags); ainfo("ISR: 0x%08" PRIx32 " CR: 0x%08" PRIx32 " " diff --git a/arch/arm/src/stm32h5/stm32_dma.c b/arch/arm/src/stm32h5/stm32_dma.c index 3d10262eb4c..b952072a19f 100644 --- a/arch/arm/src/stm32h5/stm32_dma.c +++ b/arch/arm/src/stm32h5/stm32_dma.c @@ -182,10 +182,6 @@ static struct gpdma_ch_s g_chan[] = #endif }; -static uint32_t circ_addr_1; - -static uint32_t circ_addr_1; - /**************************************************************************** * Private Functions ****************************************************************************/ @@ -359,18 +355,6 @@ static int gpdma_setup(struct gpdma_ch_s *chan, reg = cfg->ntransfers; - if (cfg->mode & GPDMACFG_MODE_CIRC) - { - /* This only targets peripheral to memory, with memory increment */ - - circ_addr_1 = cfg->dest_addr; - gpdmach_putreg(chan, CH_CXLBAR_OFFSET, - (uint32_t)&circ_addr_1 & (0xffff << 16)); - - reg = GPDMA_CXLLR_UDA | ((uint32_t)&circ_addr_1 & GPDMA_CXLLR_LA_MASK); - gpdmach_putreg(chan, CH_CXLLR_OFFSET, reg); - } - gpdmach_putreg(chan, CH_CXBR1_OFFSET, reg); return 0; @@ -595,7 +579,7 @@ void stm32_dmasetup(DMA_HANDLE handle, struct stm32_gpdma_cfg_s *cfg) /* Clear any unhandled flags from previous transactions */ - /* gpdmach_putreg(chan, CH_CXFCR_OFFSET, 0x7f << 8); */ + gpdmach_putreg(chan, CH_CXFCR_OFFSET, ~0); if (cfg->mode & GPDMACFG_MODE_CIRC) { diff --git a/arch/arm/src/stm32h5/stm32_dma.h b/arch/arm/src/stm32h5/stm32_dma.h index 63e554dca2b..6c941507457 100644 --- a/arch/arm/src/stm32h5/stm32_dma.h +++ b/arch/arm/src/stm32h5/stm32_dma.h @@ -60,8 +60,8 @@ #define DMA_STATUS_SUSPF (1 << 5) /* Completed suspension flag */ #define DMA_STATUS_TOF (1 << 6) /* Trigger overrun flag */ -#define DMA_STATUS_ERROR (DMA_STATUS_DTEF | DMA_STATUS_ULEF | DMA_STATUS_USEF | DMA_STATUS_TOF) -#define DMA_STATUS_SUCCESS (DMA_STATUS_TCF | DMA_STATUS_HTEF) +#define DMA_STATUS_FATAL (DMA_STATUS_DTEF | DMA_STATUS_ULEF | DMA_STATUS_USEF) +#define DMA_STATUS_SUCCESS (DMA_STATUS_TCF | DMA_STATUS_HTF) /* GPDMA Mode Flags: WARNING!! NOT YET IMPLEMENTED! */