Sitelet https://github.com/betaflight/betaflight/pull/15438
Skip to content

STM32H5/N6: drive WS2811 LED strip correctly on GPDMA - #15438

Merged
blckmn merged 2 commits into
betaflight:masterfrom
haslinghuis:fix/stm32h5-ws2811-gpdma
Jul 18, 2026
Merged

blckmn merged 2 commits into
betaflight:masterfrom
haslinghuis:fix/stm32h5-ws2811-gpdma

Conversation

@haslinghuis

@haslinghuis haslinghuis commented Jul 16, 2026 •

Copy link
Copy Markdown
Member

Split out of #15419 (which now carries only the hardware-verified ADC fix) so the LED strip change can be retested independently.

The WS2811 LED-strip DMA needed three GPDMA-specific corrections on the shared H5/N6 path; without them the strip shows nothing even though plain PWM on the same pin works.

  1. Request source — drive the channel from the timer UPDATE request (UDE via timerHardware->dmaTimUPChannel) instead of the per-channel compare request (CCxDE). Empirically a CCxDE-driven GPDMA channel re-fires continuously and races through the whole bit buffer in microseconds; RM0481 documents no semantic difference between the CC and UP requests, but UP gives exactly one CCRx write per timer period and matches the proven DShot GPDMA path (pwm_output_dshot_hal.c).

  2. Transfer length — HAL_DMA_Start_IT() programs CBR1.BNDT, which on GPDMA is the block size in bytes (RM0481 §16.8.12), not transfer items. Each item is a 32-bit word (one CCRx write per bit), so pass WS2811_DMA_BUFFER_SIZE * 4; the word count alone clocks out only a quarter of the frame and cuts the reset latch. Worst case (USE_LED_STRIP_64) is 8360 bytes, well within the 16-bit BNDT field. Classic DMA (F4/F7/H7/G4) counts items and is unchanged.

  3. Completion — TIM_DMACmd() was the only place TimHandle.State returned to HAL_TIM_STATE_READY, and the UDE path no longer calls it, so the handle state is now reset in the DMA IRQ handler. Without this, DMA_SetCurrDataCounter() returns HAL_BUSY from the second frame on and the strip freezes on the first frame — a static colour looks fine, but nothing ever updates.

The transfer is also split across GPDMA master ports (source buffer in SRAM on PORT1, timer CCRx on PORT0). On H5 both ports reach everything and the split just picks each port's zero-latency fast bus multiplexer path (RM0481 §2.1.5/§2.1.7, fig. 1); on N6 PORT1 (AXI) is genuinely required to reach AXISRAM.

The functional behaviour was checked against RM0481: with OCx preload enabled the DMA-written CCR values latch at the next update event, so the waveform shifts one bit period, which the 42-word trailing zero padding absorbs; the line idles low after the frame, keeping the >50 µs reset latch intact.

make STM32H563 builds clean with zero warnings.

Testing: needs an on-hardware retest with an animated pattern (e.g. larson scanner) — a static colour cannot distinguish a working strip from one frozen on frame 1, which is exactly what the pre-fix code did.

Summary by CodeRabbit

  • Bug Fixes
    • Improved WS2811 LED strip DMA handling on STM32H5/N6 devices.
    • Corrected LED strip transfer sizing and triggering for more reliable output.
    • Reduced the risk of LED update freezes during DMA operations.

@github-actions

Copy link
Copy Markdown

Do you want to test this code? You can flash it directly from the Betaflight App:

  • Simply put #15438 (this pull request number) in the Select commit field in the Firmware Flasher tab (you need to Enable expert mode, Show release candidates and Development).

WARNING: It may be unstable. Use only for testing!

@coderabbitai

coderabbitai Bot commented Jul 16, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: f9782373-ee26-4c9e-8b68-ce2d41f5c8de

📥 Commits

Reviewing files that changed from the base of the PR and between dd6e4c5 and b984661.

📒 Files selected for processing (1)
  • src/platform/STM32/light_ws2811strip_hal.c
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/platform/STM32/light_ws2811strip_hal.c

Walkthrough

Updates STM32H5/N6 WS2811 DMA initialization, timer UPDATE request handling, transfer sizing, and completion cleanup while preserving existing behavior on other STM32 platforms.

Changes

STM32 WS2811 GPDMA handling

Layer / File(s) Summary
H5/N6 DMA initialization
src/platform/STM32/light_ws2811strip_hal.c
H5/N6 DMA setup now uses the timer UPDATE request and assigns separate source and destination DMA ports.
Transfer start and completion lifecycle
src/platform/STM32/light_ws2811strip_hal.c
H5/N6 transfers use byte-sized lengths, enable and disable TIM_DMA_UPDATE, and reset the timer HAL state after completion.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Possibly related issues

  • betaflight/betaflight#15422: Covers the same STM32H5/N6 GPDMA UPDATE request, port allocation, byte-based transfer sizing, and HAL DMA handling patterns for another timer-driven peripheral.

Possibly related PRs

Suggested labels: STM32H5, STM32N6

Suggested reviewers: sugaark, nerdcopter

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main STM32H5/N6 WS2811 GPDMA fix.
Description check ✅ Passed The description explains the DMA fixes, rationale, and testing needs, and it is sufficiently detailed for this change.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

The WS2811 LED-strip DMA needed three GPDMA-specific corrections on the
shared H5/N6 path; without them the strip shows nothing even though
plain PWM on the same pin works.

1. Request source: drive the channel from the timer UPDATE request (UDE
   via timerHardware->dmaTimUPChannel) instead of the per-channel
   compare request (CCxDE). Empirically a CCxDE-driven GPDMA channel
   re-fires continuously and races through the whole bit buffer in
   microseconds; RM0481 documents no semantic difference between the CC
   and UP requests, but UP gives exactly one CCRx write per timer
   period and matches the proven DShot GPDMA path
   (pwm_output_dshot_hal.c).

2. Transfer length: HAL_DMA_Start_IT() programs CBR1.BNDT, which on
   GPDMA is the block size in BYTES (RM0481 section 16.8.12), not
   transfer items. Each item is a 32-bit word (one CCRx write per bit),
   so pass WS2811_DMA_BUFFER_SIZE * 4; the word count alone clocks out
   only a quarter of the frame and cuts the reset latch. Classic DMA
   (F4/F7/H7/G4) counts items and is unchanged.

3. Completion: TIM_DMACmd() was the only place TimHandle.State returned
   to HAL_TIM_STATE_READY, and the UDE path no longer calls it — reset
   the handle state in the DMA IRQ handler, otherwise
   DMA_SetCurrDataCounter() returns HAL_BUSY from the second frame on
   and the strip freezes on the first frame.

Also split the transfer across GPDMA master ports (source buffer in
SRAM on PORT1, timer CCRx on PORT0). On H5 both ports reach everything
and the split just picks each port's zero-latency fast path (RM0481
fig. 1); on N6 PORT1 (AXI) is required to reach AXISRAM.
@haslinghuis
haslinghuis force-pushed the fix/stm32h5-ws2811-gpdma branch from dd6e4c5 to b984661 Compare July 16, 2026 15:23
@blckmn
blckmn merged commit 6013abc into betaflight:master Jul 18, 2026
53 checks passed
@github-project-automation github-project-automation Bot moved this from Bugfix to Done in 2026.6.0 Jul 18, 2026
@haslinghuis
haslinghuis deleted the fix/stm32h5-ws2811-gpdma branch July 18, 2026 09:17
gwlim pushed a commit to gwlim/betaflight that referenced this pull request Jul 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

3 participants