This is an automated email from the ASF dual-hosted git repository.
xiaoxiang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git
The following commit(s) were added to refs/heads/master by this push:
new 94e12d0142d arch/arm/stm32h5: Use au_receive_batch in
adc_dmaconvcallback
94e12d0142d is described below
commit 94e12d0142dab39505697566900ac20c1cfbe43d
Author: kywwilson11 <[email protected]>
AuthorDate: Tue Oct 28 14:55:18 2025 -0500
arch/arm/stm32h5: Use au_receive_batch in adc_dmaconvcallback
The adc_dmaconvcallback routine was far too slow and inefficient. This was
not caught until recently when I was testing faster sample rates. When in
circular mode for the adc (dma and constant conversions), the
adc_dmaconvcallback function would overwhelm the OS and lock it up. I looked
into this and did a lot of testing. First I tried minimally improving the dma
callback by removing the % operator from the algorithm, this was slow and
inefficient. I also tried increasing the batch si [...]
Signed-off-by: kywwilson11 <[email protected]>
---
arch/arm/src/stm32h5/stm32_adc.c | 150 +++++++++++++++++++++++++++------------
arch/arm/src/stm32h5/stm32_adc.h | 8 +--
2 files changed, 109 insertions(+), 49 deletions(-)
diff --git a/arch/arm/src/stm32h5/stm32_adc.c b/arch/arm/src/stm32h5/stm32_adc.c
index 3e6553e2876..b44d778a474 100644
--- a/arch/arm/src/stm32h5/stm32_adc.c
+++ b/arch/arm/src/stm32h5/stm32_adc.c
@@ -145,7 +145,8 @@ struct stm32_dev_s
#ifdef ADC_HAVE_DMA
DMA_HANDLE dma; /* Allocated DMA channel */
- uint16_t *r_dmabuffer; /* DMA transfer buffer */
+ uint32_t *r_dmabuffer; /* DMA transfer buffer */
+ uint8_t *r_chanbuffer; /* DMA channel buffer */
#endif
bool wdg1_enable; /* True - Analog Watchdog 1 Enabled */
@@ -218,6 +219,7 @@ static void adc_dmaconvcallback(DMA_HANDLE handle, uint8_t
status,
void *arg);
static void adc_dmacfg(struct stm32_dev_s *priv,
struct stm32_gpdma_cfg_s *cfg);
+static void adc_dma_init_chanbuf(struct stm32_dev_s *priv);
static void adc_reset_dma(struct adc_dev_s *dev);
#endif
@@ -267,17 +269,20 @@ static const struct adc_ops_s g_adcops =
*/
#ifdef ADC1_HAVE_DMA
+# define ADC1_CHAN_BUFFER_SIZE (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)
+# define ADC1_DMA_BUFFER_SIZE (ADC1_CHAN_BUFFER_SIZE * 2)
# else
-# define ADC1_DMA_BUFFER_SIZE (CONFIG_STM32H5_ADC_MAX_SAMPLES *\
- CONFIG_STM32H5_ADC1_DMA_BATCH)
+# define ADC1_DMA_BUFFER_SIZE (ADC1_CHAN_BUFFER_SIZE)
# endif
-static uint16_t g_adc1_dmabuffer[ADC1_DMA_BUFFER_SIZE]
+static uint8_t g_adc1_chanbuffer[ADC1_CHAN_BUFFER_SIZE]
__attribute__((aligned(32)));
+static uint32_t g_adc1_dmabuffer[ADC1_DMA_BUFFER_SIZE]
+__attribute__((aligned(32)));
#endif
static struct stm32_dev_s g_adcpriv1 =
@@ -318,16 +323,17 @@ static struct stm32_dev_s g_adcpriv1 =
#endif
#ifdef ADC1_HAVE_DMA
- .hasdma = true,
- .r_dmabuffer = g_adc1_dmabuffer,
- .dmabatch = CONFIG_STM32H5_ADC1_DMA_BATCH,
+ .hasdma = true,
+ .r_chanbuffer = g_adc1_chanbuffer,
+ .r_dmabuffer = g_adc1_dmabuffer,
+ .dmabatch = CONFIG_STM32H5_ADC1_DMA_BATCH,
# ifdef CONFIG_STM32H5_ADC1_DMA_CFG
- .circular = true,
+ .circular = true,
# else
- .circular = false,
+ .circular = false,
# endif
#else
- .hasdma = false,
+ .hasdma = false,
#endif
#ifdef ADC1_HAVE_OVERSAMPLE
@@ -343,7 +349,7 @@ static struct stm32_dev_s g_adcpriv1 =
.oversample = false,
#endif
-#ifdef ADC1_HAVE_WDG1
+#ifdef CONFIG_STM32H5_ADC1_WDG1
.wdg1_enable = true,
.wdg1_flt = CONFIG_STM32H5_ADC1_WDG1_FLT,
.wdg1_low_thresh = CONFIG_STM32H5_ADC1_WDG1_LOWTHRESH,
@@ -372,15 +378,19 @@ static struct adc_dev_s g_adcdev1 =
#ifdef CONFIG_STM32H5_ADC2
#ifdef ADC2_HAVE_DMA
+# define ADC2_CHAN_BUFFER_SIZE (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)
+# define ADC2_DMA_BUFFER_SIZE (ADC2_CHAN_BUFFER_SIZE * 2)
# else
-# define ADC2_DMA_BUFFER_SIZE (CONFIG_STM32H5_ADC_MAX_SAMPLES *\
- CONFIG_STM32H5_ADC2_DMA_BATCH)
+# define ADC2_DMA_BUFFER_SIZE (ADC2_CHAN_BUFFER_SIZE)
# endif
-static uint16_t g_adc2_dmabuffer[ADC2_DMA_BUFFER_SIZE]
+static uint8_t g_adc2_chanbuffer[ADC2_CHAN_BUFFER_SIZE]
+__attribute__((aligned(32)));
+
+static uint32_t g_adc2_dmabuffer[ADC2_DMA_BUFFER_SIZE]
__attribute__((aligned(32)));
#endif
@@ -422,16 +432,17 @@ static struct stm32_dev_s g_adcpriv2 =
#endif
#ifdef ADC2_HAVE_DMA
- .hasdma = true,
- .r_dmabuffer = g_adc2_dmabuffer,
- .dmabatch = CONFIG_STM32H5_ADC2_DMA_BATCH,
+ .hasdma = true,
+ .r_chanbuffer = g_adc2_chanbuffer,
+ .r_dmabuffer = g_adc2_dmabuffer,
+ .dmabatch = CONFIG_STM32H5_ADC2_DMA_BATCH,
# ifdef CONFIG_STM32H5_ADC2_DMA_CFG
- .circular = true,
+ .circular = true,
# else
- .circular = false,
+ .circular = false,
# endif
#else
- .hasdma = false,
+ .hasdma = false,
#endif
#ifdef ADC2_HAVE_OVERSAMPLE
@@ -447,7 +458,7 @@ static struct stm32_dev_s g_adcpriv2 =
.oversample = false,
#endif
-#ifdef ADC2_HAVE_WDG1
+#ifdef CONFIG_STM32H5_ADC2_WDG1
.wdg1_enable = true,
.wdg1_flt = CONFIG_STM32H5_ADC2_WDG1_FLT,
.wdg1_low_thresh = CONFIG_STM32H5_ADC2_WDG1_LOWTHRESH,
@@ -1180,9 +1191,8 @@ 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;
- uint16_t conversion_count;
- uint16_t buffer_offset;
- int i;
+ uint32_t conversion_count;
+ uint32_t buffer_offset;
/* About Circular Mode
* The size of r_dmabuffer and transfer size is doubled
@@ -1195,7 +1205,7 @@ static void adc_dmaconvcallback(DMA_HANDLE handle,
uint8_t status, void *arg)
if (priv->cb != NULL)
{
- DEBUGASSERT(priv->cb->au_receive != NULL);
+ DEBUGASSERT(priv->cb->au_receive_batch != NULL);
if (status & DMA_STATUS_FATAL)
{
@@ -1237,22 +1247,18 @@ static void adc_dmaconvcallback(DMA_HANDLE handle,
uint8_t status, void *arg)
if (status & DMA_STATUS_HTF && priv->circular)
{
- for (i = 0; i < conversion_count; i++)
- {
- priv->cb->au_receive(dev,
- priv->chanlist[i % priv->rnchannels],
- priv->r_dmabuffer[i]);
- }
+ priv->cb->au_receive_batch(dev,
+ priv->r_chanbuffer,
+ priv->r_dmabuffer,
+ conversion_count);
}
if (status & DMA_STATUS_TCF)
{
- for (i = 0; i < conversion_count; i++)
- {
- priv->cb->au_receive(dev,
- priv->chanlist[i % priv->rnchannels],
- priv->r_dmabuffer[buffer_offset + i]);
- }
+ priv->cb->au_receive_batch(dev,
+ priv->r_chanbuffer,
+ &priv->r_dmabuffer[buffer_offset],
+ conversion_count);
}
}
@@ -1283,7 +1289,8 @@ static void adc_dmaconvcallback(DMA_HANDLE handle,
uint8_t status, void *arg)
static void adc_dmacfg(struct stm32_dev_s *priv,
struct stm32_gpdma_cfg_s *cfg)
{
- const uint32_t sdw_log2 = 1; /* Always 16-bit half-word for ADC_DR */
+ const uint32_t src_sdw_log2 = 1; /* Always 16-bit half-word for ADC_DR */
+ const uint32_t dst_sdw_log2 = 2; /* 32-bit word for dmabuffer */
cfg->src_addr = priv->base + STM32_ADC_DR_OFFSET;
cfg->dest_addr = (uintptr_t)priv->r_dmabuffer;
@@ -1296,13 +1303,64 @@ static void adc_dmacfg(struct stm32_dev_s *priv,
cfg->mode = priv->circular ? GPDMACFG_MODE_CIRC : 0;
- cfg->ntransfers = (priv->cchannels * priv->dmabatch) << sdw_log2;
+ cfg->ntransfers = (priv->cchannels * priv->dmabatch) << src_sdw_log2;
cfg->ntransfers <<= (priv->circular ? 1 : 0);
- cfg->tr1 = (sdw_log2 << GPDMA_CXTR1_SDW_LOG2_SHIFT)
- | (sdw_log2 << GPDMA_CXTR1_DDW_LOG2_SHIFT)
+ cfg->tr1 = (src_sdw_log2 << GPDMA_CXTR1_SDW_LOG2_SHIFT)
+ | (dst_sdw_log2 << GPDMA_CXTR1_DDW_LOG2_SHIFT)
| GPDMA_CXTR1_DINC; /* dest-inc, source fixed */
}
+
+/****************************************************************************
+ * Name: adc_dma_init_chanbuf
+ *
+ * Description:
+ * Initialize the channel buffer for use with au_receive_batch.
+ *
+ * Input Parameters:
+ * priv - ADC instance structure
+ *
+ * Returned Value:
+ * None
+ ****************************************************************************/
+
+static void adc_dma_init_chanbuf(struct stm32_dev_s *priv)
+{
+ const uint32_t channels = priv->cchannels;
+ const uint32_t conversions = channels * priv->dmabatch; /* total entries
in r_chanbuffer */
+ uint8_t *dst = priv->r_chanbuffer;
+ uint32_t filled = 0; /* number of valid bytes in dst */
+ uint32_t remain;
+ uint32_t chunk;
+
+ if (channels == 0 || conversions == 0)
+ {
+ return;
+ }
+
+ /* Fast path: single-channel scan ==> fill with one byte */
+
+ if (channels == 1)
+ {
+ memset(dst, priv->chanlist[0], conversions);
+ return;
+ }
+
+ /* Seed the first frame (one copy of chanlist) */
+
+ memcpy(dst, priv->chanlist, channels * sizeof(dst[0]));
+ filled = channels;
+
+ /* Exponentially replicate: copy the filled prefix onto the tail */
+
+ while (filled < conversions)
+ {
+ remain = conversions - filled;
+ chunk = (filled < remain) ? filled : remain;
+ memcpy(dst + filled, dst, chunk);
+ filled += chunk;
+ }
+}
#endif
/****************************************************************************
@@ -1457,6 +1515,8 @@ static int adc_setup(struct adc_dev_s *dev)
priv->dma = stm32_dmachannel(GPDMA_TTYPE_P2M);
+ adc_dma_init_chanbuf(priv);
+
adc_dmacfg(priv, &dmacfg);
stm32_dmasetup(priv->dma, &dmacfg);
diff --git a/arch/arm/src/stm32h5/stm32_adc.h b/arch/arm/src/stm32h5/stm32_adc.h
index 0a650b86e5b..7777d539376 100644
--- a/arch/arm/src/stm32h5/stm32_adc.h
+++ b/arch/arm/src/stm32h5/stm32_adc.h
@@ -115,22 +115,22 @@
#undef ADC_HAVE_WDG1
#if defined(CONFIG_STM32H5_ADC1_WDG1) || defined(CONFIG_STM32H5_ADC2_WDG1)
-# define ADC_HAVE_WDG1
+# define ADC_HAVE_WDG1 1
#endif
#undef ADC_HAVE_WDG2
#if defined(CONFIG_STM32H5_ADC1_WDG2) || defined(CONFIG_STM32H5_ADC2_WDG2)
-# define ADC_HAVE_WDG2
+# define ADC_HAVE_WDG2 1
#endif
#undef ADC_HAVE_WDG3
#if defined(CONFIG_STM32H5_ADC1_WDG3) || defined(CONFIG_STM32H5_ADC2_WDG3)
-# define ADC_HAVE_WDG3
+# define ADC_HAVE_WDG3 1
#endif
#if defined(ADC_HAVE_WDG1) || defined (ADC_HAVE_WDG2) || \
defined(ADC_HAVE_WDG3)
-# define ADC_HAVE_WDG
+# define ADC_HAVE_WDG 1
#endif
/* Timer configuration: If a timer trigger is specified, then get
* information about the timer.