Skip to content

Receive by DMA on STM32F7 - #8

Merged
JohnAmadis merged 1 commit into
mainfrom
feature/rx-dma
Oct 11, 2026
Merged

JohnAmadis merged 1 commit into
mainfrom
feature/rx-dma

Conversation

@JaxCalder

Copy link
Copy Markdown
Contributor

Problem

The STM32F7 USART has no RX FIFO, so at 921600 baud a byte arrives every ~11 µs. The RX interrupt runs at dmosi's minimum priority, because it signals the ring's semaphore. When it ran late the byte was lost to an overrun: on the STM32F746G-DISCO console about 2 in 60 bytes sent back to back were lost.

Change

RX by DMA (src/port/stm32f7/port.c). A UART with an RX ring now receives into a circular 1 KiB DMA buffer:

  • The buffer comes from the dma heap (dmod-boot's DTCM, uncached) when there is one. Otherwise it is Dmod_AlignedMalloc with the D-cache invalidated before reading.
  • The USART's idle line and the DMA's half / end events drain it into the ring. The read position is taken from NDTR.
  • The stream is leased from dmdma (dmdma_lease_*), from the two the UART can use (table rx_dma_map, RM0385). If neither is free, the UART receives by interrupt as before, with a warning.
  • _set_interrupt_trigger keeps RXNE masked and IDLE on while DMA receives. _set_rx_ring(NULL) and _deinit stop the stream and free the buffer.
  • New dependencies of the F7 port: dmdma@>=0.4, dmheap.

Ring size (configs/board/* for F7). The ring now holds what arrives while the reader is not scheduled. 256 bytes are 2.8 ms at 921600, and a reader behind a busy display thread overran it: the oldest bytes were dropped. The F7 boards' consoles get 4096 (~44 ms). See docs/configuration.md, section "Receiving by DMA".

Tested on STM32F746G-DISCO

  • 20 × 200 bytes sent back to back to the console: 0 lost (2/60 lost before).
  • After a burst and a pause, NDTR always matches the drained position: no bytes left in the DMA buffer (40 bursts of 1–2000 bytes).
  • YMODEM (dmell's ymodem), 1024-byte blocks, no pacing: 8 × 300 KB with no block sent again, ~31 KiB/s.

Related

🤖 Generated with Claude Code

The USART has no RX FIFO: at 921600 baud a byte comes every ~11 us, and
an RX interrupt that runs late (at dmosi's minimum priority) loses bytes
to overrun. A UART with an RX ring now receives by DMA instead - a
circular 1 KiB buffer (from the "dma" heap - uncached DTCM - when there
is one, else with the D-cache invalidated) that the USART's idle line and
the DMA's half / end events drain into the ring. No free stream (dmdma
leases): by interrupt, as before.

The ring then holds what comes while the reader is not scheduled: the F7
boards' consoles get 4096 bytes (~44 ms at 921600) instead of 256
(2.8 ms), which a reader behind a busy display thread overran.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants