Skip to content

Commit 94e12d0

Browse files
kywwilson11xiaoxiang781216
authored andcommitted
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 size considerably as well as the fifo size. This helped but the OS would still lockup with the fastest sample rate (5 Msps). au_receive_batch was the solution. In order to use this, I needed to change the dmabuffer to be of type uint32_t to match the data pointer in au_receive_batch. I also needed to create a channel buffer, which is initialized once in adc_setup. stm32_gpdma_cfg_s->tr1 needed to be updated for the uint32_t dmabuffer. Once I made changes to use au_receive_batch, I could reliably use the fastest sample rate without locking up the OS. Calling au_receive for every single conversion in a batch is far less efficient than calling au_receive_batch. I also snuck in some changes for the ADC watchdog defines and guards. These were fixes that were tested while testing these changes. Signed-off-by: kywwilson11 <kwilson@2g-eng.com>
1 parent 11e81d1 commit 94e12d0

File tree

2 files changed

+109
-49
lines changed

2 files changed

+109
-49
lines changed

arch/arm/src/stm32h5/stm32_adc.c

Lines changed: 105 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,8 @@ struct stm32_dev_s
145145

146146
#ifdef ADC_HAVE_DMA
147147
DMA_HANDLE dma; /* Allocated DMA channel */
148-
uint16_t *r_dmabuffer; /* DMA transfer buffer */
148+
uint32_t *r_dmabuffer; /* DMA transfer buffer */
149+
uint8_t *r_chanbuffer; /* DMA channel buffer */
149150
#endif
150151

151152
bool wdg1_enable; /* True - Analog Watchdog 1 Enabled */
@@ -218,6 +219,7 @@ static void adc_dmaconvcallback(DMA_HANDLE handle, uint8_t status,
218219
void *arg);
219220
static void adc_dmacfg(struct stm32_dev_s *priv,
220221
struct stm32_gpdma_cfg_s *cfg);
222+
static void adc_dma_init_chanbuf(struct stm32_dev_s *priv);
221223
static void adc_reset_dma(struct adc_dev_s *dev);
222224
#endif
223225

@@ -267,17 +269,20 @@ static const struct adc_ops_s g_adcops =
267269
*/
268270

269271
#ifdef ADC1_HAVE_DMA
272+
# define ADC1_CHAN_BUFFER_SIZE (CONFIG_STM32H5_ADC_MAX_SAMPLES *\
273+
CONFIG_STM32H5_ADC1_DMA_BATCH)
274+
270275
# ifdef CONFIG_STM32H5_ADC1_DMA_CFG
271-
# define ADC1_DMA_BUFFER_SIZE (CONFIG_STM32H5_ADC_MAX_SAMPLES *\
272-
CONFIG_STM32H5_ADC1_DMA_BATCH * 2)
276+
# define ADC1_DMA_BUFFER_SIZE (ADC1_CHAN_BUFFER_SIZE * 2)
273277
# else
274-
# define ADC1_DMA_BUFFER_SIZE (CONFIG_STM32H5_ADC_MAX_SAMPLES *\
275-
CONFIG_STM32H5_ADC1_DMA_BATCH)
278+
# define ADC1_DMA_BUFFER_SIZE (ADC1_CHAN_BUFFER_SIZE)
276279
# endif
277280

278-
static uint16_t g_adc1_dmabuffer[ADC1_DMA_BUFFER_SIZE]
281+
static uint8_t g_adc1_chanbuffer[ADC1_CHAN_BUFFER_SIZE]
279282
__attribute__((aligned(32)));
280283

284+
static uint32_t g_adc1_dmabuffer[ADC1_DMA_BUFFER_SIZE]
285+
__attribute__((aligned(32)));
281286
#endif
282287

283288
static struct stm32_dev_s g_adcpriv1 =
@@ -318,16 +323,17 @@ static struct stm32_dev_s g_adcpriv1 =
318323
#endif
319324

320325
#ifdef ADC1_HAVE_DMA
321-
.hasdma = true,
322-
.r_dmabuffer = g_adc1_dmabuffer,
323-
.dmabatch = CONFIG_STM32H5_ADC1_DMA_BATCH,
326+
.hasdma = true,
327+
.r_chanbuffer = g_adc1_chanbuffer,
328+
.r_dmabuffer = g_adc1_dmabuffer,
329+
.dmabatch = CONFIG_STM32H5_ADC1_DMA_BATCH,
324330
# ifdef CONFIG_STM32H5_ADC1_DMA_CFG
325-
.circular = true,
331+
.circular = true,
326332
# else
327-
.circular = false,
333+
.circular = false,
328334
# endif
329335
#else
330-
.hasdma = false,
336+
.hasdma = false,
331337
#endif
332338

333339
#ifdef ADC1_HAVE_OVERSAMPLE
@@ -343,7 +349,7 @@ static struct stm32_dev_s g_adcpriv1 =
343349
.oversample = false,
344350
#endif
345351

346-
#ifdef ADC1_HAVE_WDG1
352+
#ifdef CONFIG_STM32H5_ADC1_WDG1
347353
.wdg1_enable = true,
348354
.wdg1_flt = CONFIG_STM32H5_ADC1_WDG1_FLT,
349355
.wdg1_low_thresh = CONFIG_STM32H5_ADC1_WDG1_LOWTHRESH,
@@ -372,15 +378,19 @@ static struct adc_dev_s g_adcdev1 =
372378
#ifdef CONFIG_STM32H5_ADC2
373379

374380
#ifdef ADC2_HAVE_DMA
381+
# define ADC2_CHAN_BUFFER_SIZE (CONFIG_STM32H5_ADC_MAX_SAMPLES *\
382+
CONFIG_STM32H5_ADC2_DMA_BATCH)
383+
375384
# ifdef CONFIG_STM32H5_ADC2_DMA_CFG
376-
# define ADC2_DMA_BUFFER_SIZE (CONFIG_STM32H5_ADC_MAX_SAMPLES *\
377-
CONFIG_STM32H5_ADC2_DMA_BATCH * 2)
385+
# define ADC2_DMA_BUFFER_SIZE (ADC2_CHAN_BUFFER_SIZE * 2)
378386
# else
379-
# define ADC2_DMA_BUFFER_SIZE (CONFIG_STM32H5_ADC_MAX_SAMPLES *\
380-
CONFIG_STM32H5_ADC2_DMA_BATCH)
387+
# define ADC2_DMA_BUFFER_SIZE (ADC2_CHAN_BUFFER_SIZE)
381388
# endif
382389

383-
static uint16_t g_adc2_dmabuffer[ADC2_DMA_BUFFER_SIZE]
390+
static uint8_t g_adc2_chanbuffer[ADC2_CHAN_BUFFER_SIZE]
391+
__attribute__((aligned(32)));
392+
393+
static uint32_t g_adc2_dmabuffer[ADC2_DMA_BUFFER_SIZE]
384394
__attribute__((aligned(32)));
385395
#endif
386396

@@ -422,16 +432,17 @@ static struct stm32_dev_s g_adcpriv2 =
422432
#endif
423433

424434
#ifdef ADC2_HAVE_DMA
425-
.hasdma = true,
426-
.r_dmabuffer = g_adc2_dmabuffer,
427-
.dmabatch = CONFIG_STM32H5_ADC2_DMA_BATCH,
435+
.hasdma = true,
436+
.r_chanbuffer = g_adc2_chanbuffer,
437+
.r_dmabuffer = g_adc2_dmabuffer,
438+
.dmabatch = CONFIG_STM32H5_ADC2_DMA_BATCH,
428439
# ifdef CONFIG_STM32H5_ADC2_DMA_CFG
429-
.circular = true,
440+
.circular = true,
430441
# else
431-
.circular = false,
442+
.circular = false,
432443
# endif
433444
#else
434-
.hasdma = false,
445+
.hasdma = false,
435446
#endif
436447

437448
#ifdef ADC2_HAVE_OVERSAMPLE
@@ -447,7 +458,7 @@ static struct stm32_dev_s g_adcpriv2 =
447458
.oversample = false,
448459
#endif
449460

450-
#ifdef ADC2_HAVE_WDG1
461+
#ifdef CONFIG_STM32H5_ADC2_WDG1
451462
.wdg1_enable = true,
452463
.wdg1_flt = CONFIG_STM32H5_ADC2_WDG1_FLT,
453464
.wdg1_low_thresh = CONFIG_STM32H5_ADC2_WDG1_LOWTHRESH,
@@ -1180,9 +1191,8 @@ static void adc_dmaconvcallback(DMA_HANDLE handle, uint8_t status, void *arg)
11801191
struct adc_dev_s *dev = (struct adc_dev_s *)arg;
11811192
struct stm32_dev_s *priv = (struct stm32_dev_s *)dev->ad_priv;
11821193

1183-
uint16_t conversion_count;
1184-
uint16_t buffer_offset;
1185-
int i;
1194+
uint32_t conversion_count;
1195+
uint32_t buffer_offset;
11861196

11871197
/* About Circular Mode
11881198
* 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)
11951205

11961206
if (priv->cb != NULL)
11971207
{
1198-
DEBUGASSERT(priv->cb->au_receive != NULL);
1208+
DEBUGASSERT(priv->cb->au_receive_batch != NULL);
11991209

12001210
if (status & DMA_STATUS_FATAL)
12011211
{
@@ -1237,22 +1247,18 @@ static void adc_dmaconvcallback(DMA_HANDLE handle, uint8_t status, void *arg)
12371247

12381248
if (status & DMA_STATUS_HTF && priv->circular)
12391249
{
1240-
for (i = 0; i < conversion_count; i++)
1241-
{
1242-
priv->cb->au_receive(dev,
1243-
priv->chanlist[i % priv->rnchannels],
1244-
priv->r_dmabuffer[i]);
1245-
}
1250+
priv->cb->au_receive_batch(dev,
1251+
priv->r_chanbuffer,
1252+
priv->r_dmabuffer,
1253+
conversion_count);
12461254
}
12471255

12481256
if (status & DMA_STATUS_TCF)
12491257
{
1250-
for (i = 0; i < conversion_count; i++)
1251-
{
1252-
priv->cb->au_receive(dev,
1253-
priv->chanlist[i % priv->rnchannels],
1254-
priv->r_dmabuffer[buffer_offset + i]);
1255-
}
1258+
priv->cb->au_receive_batch(dev,
1259+
priv->r_chanbuffer,
1260+
&priv->r_dmabuffer[buffer_offset],
1261+
conversion_count);
12561262
}
12571263
}
12581264

@@ -1283,7 +1289,8 @@ static void adc_dmaconvcallback(DMA_HANDLE handle, uint8_t status, void *arg)
12831289
static void adc_dmacfg(struct stm32_dev_s *priv,
12841290
struct stm32_gpdma_cfg_s *cfg)
12851291
{
1286-
const uint32_t sdw_log2 = 1; /* Always 16-bit half-word for ADC_DR */
1292+
const uint32_t src_sdw_log2 = 1; /* Always 16-bit half-word for ADC_DR */
1293+
const uint32_t dst_sdw_log2 = 2; /* 32-bit word for dmabuffer */
12871294

12881295
cfg->src_addr = priv->base + STM32_ADC_DR_OFFSET;
12891296
cfg->dest_addr = (uintptr_t)priv->r_dmabuffer;
@@ -1296,13 +1303,64 @@ static void adc_dmacfg(struct stm32_dev_s *priv,
12961303

12971304
cfg->mode = priv->circular ? GPDMACFG_MODE_CIRC : 0;
12981305

1299-
cfg->ntransfers = (priv->cchannels * priv->dmabatch) << sdw_log2;
1306+
cfg->ntransfers = (priv->cchannels * priv->dmabatch) << src_sdw_log2;
13001307
cfg->ntransfers <<= (priv->circular ? 1 : 0);
13011308

1302-
cfg->tr1 = (sdw_log2 << GPDMA_CXTR1_SDW_LOG2_SHIFT)
1303-
| (sdw_log2 << GPDMA_CXTR1_DDW_LOG2_SHIFT)
1309+
cfg->tr1 = (src_sdw_log2 << GPDMA_CXTR1_SDW_LOG2_SHIFT)
1310+
| (dst_sdw_log2 << GPDMA_CXTR1_DDW_LOG2_SHIFT)
13041311
| GPDMA_CXTR1_DINC; /* dest-inc, source fixed */
13051312
}
1313+
1314+
/****************************************************************************
1315+
* Name: adc_dma_init_chanbuf
1316+
*
1317+
* Description:
1318+
* Initialize the channel buffer for use with au_receive_batch.
1319+
*
1320+
* Input Parameters:
1321+
* priv - ADC instance structure
1322+
*
1323+
* Returned Value:
1324+
* None
1325+
****************************************************************************/
1326+
1327+
static void adc_dma_init_chanbuf(struct stm32_dev_s *priv)
1328+
{
1329+
const uint32_t channels = priv->cchannels;
1330+
const uint32_t conversions = channels * priv->dmabatch; /* total entries in r_chanbuffer */
1331+
uint8_t *dst = priv->r_chanbuffer;
1332+
uint32_t filled = 0; /* number of valid bytes in dst */
1333+
uint32_t remain;
1334+
uint32_t chunk;
1335+
1336+
if (channels == 0 || conversions == 0)
1337+
{
1338+
return;
1339+
}
1340+
1341+
/* Fast path: single-channel scan ==> fill with one byte */
1342+
1343+
if (channels == 1)
1344+
{
1345+
memset(dst, priv->chanlist[0], conversions);
1346+
return;
1347+
}
1348+
1349+
/* Seed the first frame (one copy of chanlist) */
1350+
1351+
memcpy(dst, priv->chanlist, channels * sizeof(dst[0]));
1352+
filled = channels;
1353+
1354+
/* Exponentially replicate: copy the filled prefix onto the tail */
1355+
1356+
while (filled < conversions)
1357+
{
1358+
remain = conversions - filled;
1359+
chunk = (filled < remain) ? filled : remain;
1360+
memcpy(dst + filled, dst, chunk);
1361+
filled += chunk;
1362+
}
1363+
}
13061364
#endif
13071365

13081366
/****************************************************************************
@@ -1457,6 +1515,8 @@ static int adc_setup(struct adc_dev_s *dev)
14571515

14581516
priv->dma = stm32_dmachannel(GPDMA_TTYPE_P2M);
14591517

1518+
adc_dma_init_chanbuf(priv);
1519+
14601520
adc_dmacfg(priv, &dmacfg);
14611521

14621522
stm32_dmasetup(priv->dma, &dmacfg);

arch/arm/src/stm32h5/stm32_adc.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -115,22 +115,22 @@
115115

116116
#undef ADC_HAVE_WDG1
117117
#if defined(CONFIG_STM32H5_ADC1_WDG1) || defined(CONFIG_STM32H5_ADC2_WDG1)
118-
# define ADC_HAVE_WDG1
118+
# define ADC_HAVE_WDG1 1
119119
#endif
120120

121121
#undef ADC_HAVE_WDG2
122122
#if defined(CONFIG_STM32H5_ADC1_WDG2) || defined(CONFIG_STM32H5_ADC2_WDG2)
123-
# define ADC_HAVE_WDG2
123+
# define ADC_HAVE_WDG2 1
124124
#endif
125125

126126
#undef ADC_HAVE_WDG3
127127
#if defined(CONFIG_STM32H5_ADC1_WDG3) || defined(CONFIG_STM32H5_ADC2_WDG3)
128-
# define ADC_HAVE_WDG3
128+
# define ADC_HAVE_WDG3 1
129129
#endif
130130

131131
#if defined(ADC_HAVE_WDG1) || defined (ADC_HAVE_WDG2) || \
132132
defined(ADC_HAVE_WDG3)
133-
# define ADC_HAVE_WDG
133+
# define ADC_HAVE_WDG 1
134134
#endif
135135
/* Timer configuration: If a timer trigger is specified, then get
136136
* information about the timer.

0 commit comments

Comments
 (0)