UART NVIC-enable gate checks only RX DMA, can leave TX IRQ permanently disabled
AI Generated issue-ticket
Summary
serialUART()'s post-init NVIC-enable gate checks only the RX-side DMA resource before
deciding whether to enable the USART/UART peripheral's NVIC interrupt line. The same
line services both RX and TX interrupt sources on these parts. When RX resolves to DMA
(so the RX-IRQ path is genuinely unneeded) but TX does not, this gate leaves the shared
NVIC line disabled, and any driver code that depends on IRQ-driven TX
(UART_IT_TXE/UART_IT_TC) can no longer run.
Where
Confirmed identical in shape across all four HAL/StdPeriph "modern" STM32 UART driver files (StdPeriph-only and pure-HAL variants don't gate NVIC enable at all, so they are unaffected):
src/main/drivers/stm32/serial_uart_stm32f4xx.c:363src/main/drivers/stm32/serial_uart_stm32f7xx.c:395src/main/drivers/stm32/serial_uart_stm32g4xx.c:328src/main/drivers/stm32/serial_uart_stm32h7xx.c:505
#ifdef USE_DMA
if (!s->rxDMAResource)
#endif
{
HAL_NVIC_SetPriority(hardware->rxIrq, NVIC_PRIORITY_BASE(hardware->rxPriority), NVIC_PRIORITY_SUB(hardware->rxPriority));
HAL_NVIC_EnableIRQ(hardware->rxIrq);
}Trigger condition
Any UART whose resolved DMA assignment is asymmetric between RX and TX — RX-DMA active
while TX-DMA is not (the common direction, since RX benefits most from DMA offload). In
that case !s->rxDMAResource is false, so HAL_NVIC_EnableIRQ() is skipped, and the
shared NVIC line stays disabled for the life of the port. TX bytes queued via the
non-DMA IRQ path (UART_IT_TXE) then never get serviced at the NVIC level, regardless of
what the peripheral's own CR1/CR3 interrupt-enable bits say.
Not confirmed reachable on any specific shipped target/config — that would require
auditing every target's per-UART rxDmaopt/txDmaopt resolution. Filed as a defect in
the shared gating pattern itself, present identically in all four files.
Suggested fix
Gate on both resources, e.g.:
if (!s->rxDMAResource || !s->txDMAResource) {
HAL_NVIC_SetPriority(...);
HAL_NVIC_EnableIRQ(hardware->rxIrq);
}so the shared NVIC line is enabled whenever either direction still needs IRQ service, not only when RX does.
Source: betaflight/betaflight