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.

Reply via email to