STM32H743 HAL_UART_Receive_DMA fires error callback immediately — TEIF1 set, RxCplt never fires
Asked by stale_biscuit_03 ·
Upgrading a project from STM32F4 to STM32H743. UART DMA receive worked on the F4 without any issues — standard CubeMX setup, call HAL_UART_Receive_DMA, RxCpltCallback fires on each frame. Same approach on the H743.
HAL_UART_Receive_DMA() returns HAL_OK, which I assume means the transfer started fine. But RxCpltCallback never fires. Added an error callback to see what's happening:
void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart) {
uint32_t err = HAL_DMA_GetError(huart->hdmarx);
// err = 1 (HAL_DMA_ERROR_TE)
}
Fires on every single call. I also opened the DMA registers in the debugger and can see TEIF1 set in DMA1_LISR. No idea what's causing a transfer error — the peripheral and DMA are configured exactly the same as the F4 version that worked.
CubeMX config: UART4 RX → DMA1 Stream1, Peripheral to Memory, circular mode, byte width, memory increment on. DMA1_Stream1 NVIC enabled at priority 5. Haven't touched any generated init code.
Buffer is a file-scope static:
static uint8_t rx_buf[256];
What triggers a DMA transfer error on STM32H7 when the same setup works on F4?
3 Replies
TEIF is a DMA bus error — the DMA controller attempted to write to your buffer address and got an error response from the bus interconnect. Nothing wrong with your DMA configuration or NVIC setup. The DMA physically cannot reach the memory region you pointed it at.
On STM32H7, DMA1 and DMA2 are on the AHB bus. They can access D1 SRAM (AXI SRAM / SRAM1–SRAM3, base address 0x24000000) and D2 SRAM. They cannot access DTCM or ITCM. Both TCM regions are tightly coupled to the Cortex-M7 core only — no other bus master has a path to them.
Why this didn't fail on F4:
STM32F4 doesn't have DTCM at 0x20000000. F4 has CCM (Core Coupled Memory) at
0x10000000, which linker scripts rarely use for general .bss. F4 SRAM starts
at 0x20000000 and is fully DMA-accessible. STM32H7 maps DTCM to
0x20000000–0x2001FFFF (128 KB). Any linker script that puts .bss at
"0x20000000 — the start of SRAM" puts everything in DTCM on H7. Same address,
completely different outcome.
Confirm the address first:
Halt in the debugger before the DMA call. Check &rx_buf:
0x200xxxxx→ DTCM. That's the problem.0x240xxxxx→ AXI SRAM D1. Buffer placement is fine, look elsewhere.
Fix:
Declare the buffer with an explicit section that maps into D1 SRAM:
__attribute__((section(".DMA_RAM"), aligned(32)))
static uint8_t rx_buf[256];
Add the section to your linker script if it's not already there:
.DMA_RAM (NOLOAD) :
{
*(.DMA_RAM)
} >RAM_D1
The aligned(32) matters too. Once the TEIF is gone you'll hit the next
class of problem: cache coherency. The Cortex-M7 D-cache is enabled by default
in CubeMX-generated startup code. DMA writes bypass the cache; the CPU reads
stale cached data unless you call SCB_InvalidateDCache_by_Addr() in the
RxCplt callback. That function requires 32-byte alignment to operate correctly.
Doing it now saves you the next debug session. The
STM32 HAL DMA guide covers both the
alignment requirement and the exact cache maintenance calls.
TEIF1 in DMA1_LISR is bit 9 (value 0x00000200) — confirms a transfer error on DMA1 Stream1 specifically. Each stream gets a 6-bit group in LISR (streams 0–3) and HISR (streams 4–7). Within each group the layout is: FEIFx at LSB, DMEIFx, TEIFx, HTIFx, TCIFx at MSB. Stream 0 starts at bit 0, stream 1 at bit 6. RM0433 Table 143 has the complete flag-to-stream mapping if you want to cross-reference; section 16.5.6 covers the error handling flow.
Also worth knowing: once TEIF fires, the DMA stream is disabled by hardware automatically. HAL_UART_Receive_DMA returning HAL_OK only means the HAL loaded the DMA registers and started the sequence — the bus error fires asynchronously a few cycles later from the DMA interrupt. The TEIF flag is latched in the status register until cleared; HAL clears it inside the error ISR handler before calling HAL_UART_ErrorCallback.
The err = 1 from HAL_DMA_GetError confirms HAL_DMA_ERROR_TE specifically.
FIFO error would be HAL_DMA_ERROR_FE (0x02), direct-mode error would be
HAL_DMA_ERROR_DME (0x04). Transfer error with a DTCM address is the classic
combination on H7.
The TEIF-vs-cache distinction is worth making explicit because they look superficially similar when you start reading about STM32H7 DMA issues:
TEIF (transfer error): DMA attempted the access and got a bus error. Callback never fires. HAL_DMA_GetError returns HAL_DMA_ERROR_TE. Buffer contents are unchanged from before the call — no data moved at all.
Cache coherency problem: DMA transfer completes normally. Callback fires. But the CPU reads stale D-cache data instead of what the DMA wrote to SRAM. No error reported, just wrong values in the buffer.
Your symptoms — callback never fires, HAL_DMA_ERROR_TE — are unambiguously the first case. You don't have a cache problem yet; you have a DTCM address problem. Fix the address first, then add the cache maintenance once the transfer is actually completing.
For the F4→H7 migration specifically: the 0x20000000 address space collision is a genuine footgun. F4 and H7 both start something at 0x20000000, but one is DMA-accessible SRAM and the other is DTCM that only the CPU can reach. Any linker script ported from F4 that doesn't explicitly account for this will put your DMA buffers in the wrong place. Worth auditing the MEMORY section of your .ld file while you're in there — confirm RAM_D1 (or whatever your script calls it) is pointing at 0x24000000 and not 0x20000000.
The MCU memory map guide is useful if you want the full H7 address space layout before editing the linker script — helps to know where ITCM, DTCM, AXI SRAM, and the D2/D3 regions all sit relative to each other.
Related Discussions
STM32 USB not detected by Windows after jumping to bootloader mode
Working on a custom STM32F411 board, trying to jump into the built-in USB DFU bootloader from application code instead of holding BOOT0 on p
SPI reads all returning 0xFF — logic analyser shows MISO activity, W25Q32 not responding to commands
Been staring at this one for a day and a half. I'm trying to read the JEDEC ID from a W25Q32JV SPI flash chip on a custom STM32L432 board. T
Global variable initialized to non-zero value but always reads as zero in main() — bare-metal STM32
Bit of a basic question maybe, but I'm genuinely stuck. Moving from ESP32/Arduino to bare-metal STM32 (STM32G031, Makefile project, arm-none
STM32F401 UART printing garbage after switching to 84 MHz PLL — same 115200 baud in CubeMX and PuTTY
Got a WeAct Black Pill (STM32F401CCU6) project that's been running happily on the default HSI clock at 16 MHz. Using USART1 on PA9/PA10 thro