Back to Insights & News

STM32H5 GPDMA: Why Only a Quarter of My PWM Values Were Transferred

2025-10-25 Semir Tursunović 2 min read
STM32H5 GPDMA: Why Only a Quarter of My PWM Values Were Transferred

I recently spent far too much time debugging what looked like a simple PWM sequence problem on an STM32H5. The idea was straightforward: an array of compare values, a timer, and DMA updating the compare register on every period.

The symptoms

Everything looked healthy. The DMA transfer completed successfully, all callbacks fired, no errors were flagged. But the timer's compare register was not getting the values from my array.

I went through every possible avenue: timing, interrupt priorities, preload configuration. I even rewrote the code using different DMA approaches. Nothing changed.

The clue: 22 × 4 = 88

Eventually, I noticed something in the debug output. Only 22 values from my 88-element array were actually being transferred. Then it clicked: 22 × 4 = 88.

My array held 32-bit values. The DMA was treating the size I passed in as a number of bytes, not a number of 32-bit words, so it stopped after a quarter of the data.

What the reference manual says

The STM32H5 uses the newer GPDMA peripheral. In the reference manual, the BNDT field of the GPDMA_CxBR1 register is the block size in bytes, regardless of the configured data width. A block of 88 words with a 32-bit source width therefore needs BNDT = 352.

The HAL follows the hardware here: on GPDMA-based parts, the length passed to HAL_DMA_Start_IT() is a byte count. That's different from what many of us are used to from the classic DMA on older STM32 families, where you pass the number of data items.

/* 88 x uint32_t compare values */
static uint32_t pwm_values[88];

/* Wrong on GPDMA: transfers only 88 bytes = 22 words */
HAL_DMA_Start_IT(hdma, (uint32_t)pwm_values, (uint32_t)&htim->Instance->CCR1, 88);

/* Right: size in bytes */
HAL_DMA_Start_IT(hdma, (uint32_t)pwm_values, (uint32_t)&htim->Instance->CCR1,
                 sizeof(pwm_values));

Using sizeof() on the array rather than an element count makes the intent clear and survives future changes to the data type.

Takeaways

  • When porting code between STM32 families, don't assume the DMA API has the same units. Check what the length parameter means on that specific peripheral.
  • If a DMA transfer "works" but moves exactly 1/2 or 1/4 of the data, suspect a bytes-versus-items mismatch first.
  • HAL libraries are great for speeding up development, but they can't replace knowing how to read a reference manual and trace behaviour down to the register level.

Sometimes the solution isn't in the application code at all. It's in understanding exactly what's happening at the silicon level, and sometimes it takes longer than it should to see a pattern that was right in front of you.