mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-04 19:17:47 +00:00
machine/stm32: fix UART interrupt storm caused by uncleared overrun error
The UART handleInterrupt handler unconditionally read RDR on every interrupt without checking which flag triggered it. On newer STM32 USART peripherals (U5, L4, L5, L0, G0, F7, WL), RXNEIE enables interrupts for both RXFNE (data ready) and ORE (overrun error). Unlike older families (F1, F4), ORE is not cleared by reading the data register, it must be explicitly cleared via the ICR register. When an overrun occurred (e.g. serial data arriving while ADC busy-waits in Get()), ORE would trigger the interrupt, the handler would fire without clearing it, and the interrupt would re-trigger immediately, causing an infinite interrupt storm that locks up the CPU. Fix by: - Checking RXFNE/RXNE (bit 5) before reading data from RDR - Clearing ORE (bit 3) via ICR on newer peripherals when set - Adding errClearReg field to UART struct, set to &Bus.ICR in setRegisters() for all ICR-capable families - Preserving the SR+DR clearing sequence for older F1/F4 families Signed-off-by: deadprogram <ron@hybridgroup.com>
This commit is contained in:
@@ -24,6 +24,10 @@ type UART struct {
|
||||
txReg *volatile.Register32
|
||||
statusReg *volatile.Register32
|
||||
txEmptyFlag uint32
|
||||
// errClearReg points to the ICR register on newer STM32 USART peripherals
|
||||
// (L0, L4, L5, G0, F7, U5, WL, etc.) for clearing error flags. Nil for
|
||||
// older peripherals (F1, F4) where errors are cleared by reading SR+DR.
|
||||
errClearReg *volatile.Register32
|
||||
}
|
||||
|
||||
// Configure the UART.
|
||||
@@ -63,7 +67,28 @@ func (uart *UART) Configure(config UARTConfig) {
|
||||
// handleInterrupt should be called from the appropriate interrupt handler for
|
||||
// this UART instance.
|
||||
func (uart *UART) handleInterrupt(interrupt.Interrupt) {
|
||||
uart.Receive(byte((uart.rxReg.Get() & 0xFF)))
|
||||
s := uart.statusReg.Get()
|
||||
|
||||
// Only read data when RXNE/RXFNE (bit 5) is set. On all STM32 families,
|
||||
// RXNEIE enables both the RX-data-ready and overrun-error (ORE) interrupts.
|
||||
// Without this check, an ORE-only interrupt reads garbage from RDR.
|
||||
if s&0x20 != 0 { // RXNE / RXFNE
|
||||
uart.Receive(byte((uart.rxReg.Get() & 0xFF)))
|
||||
}
|
||||
|
||||
// Clear overrun error (ORE, bit 3) to prevent an interrupt storm.
|
||||
if s&0x8 != 0 {
|
||||
if uart.errClearReg != nil {
|
||||
// Newer USART peripherals: clear ORE via the ICR register.
|
||||
uart.errClearReg.Set(0x8) // ORECF
|
||||
} else if s&0x20 == 0 {
|
||||
// Older USART (F1/F4): ORE is cleared by reading SR then DR.
|
||||
// SR was already read above. If RXNE was set, DR was read in
|
||||
// the Receive path. Otherwise do a dummy DR read to complete
|
||||
// the clearing sequence.
|
||||
uart.rxReg.Get()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// SetBaudRate sets the communication speed for the UART. Defer to chip-specific
|
||||
|
||||
@@ -46,6 +46,7 @@ func (uart *UART) setRegisters() {
|
||||
uart.txReg = &uart.Bus.TDR
|
||||
uart.statusReg = &uart.Bus.ISR
|
||||
uart.txEmptyFlag = stm32.USART_ISR_TXE
|
||||
uart.errClearReg = &uart.Bus.ICR
|
||||
}
|
||||
|
||||
//---------- I2C related code
|
||||
|
||||
@@ -232,6 +232,7 @@ func (uart *UART) setRegisters() {
|
||||
uart.txReg = &uart.Bus.TDR
|
||||
uart.statusReg = &uart.Bus.ISR
|
||||
uart.txEmptyFlag = stm32.USART_ISR_TXE
|
||||
uart.errClearReg = &uart.Bus.ICR
|
||||
}
|
||||
|
||||
//---------- SPI related types and code
|
||||
|
||||
@@ -224,6 +224,7 @@ func (uart *UART) setRegisters() {
|
||||
uart.txReg = &uart.Bus.TDR
|
||||
uart.statusReg = &uart.Bus.ISR
|
||||
uart.txEmptyFlag = stm32.USART_ISR_TXE
|
||||
uart.errClearReg = &uart.Bus.ICR
|
||||
}
|
||||
|
||||
//---------- SPI related types and code
|
||||
|
||||
@@ -299,6 +299,7 @@ func (uart *UART) setRegisters() {
|
||||
uart.txReg = &uart.Bus.TDR
|
||||
uart.statusReg = &uart.Bus.ISR
|
||||
uart.txEmptyFlag = stm32.USART_ISR_TXE
|
||||
uart.errClearReg = &uart.Bus.ICR
|
||||
}
|
||||
|
||||
//---------- SPI related types and code
|
||||
|
||||
@@ -44,6 +44,7 @@ func (uart *UART) setRegisters() {
|
||||
uart.txReg = &uart.Bus.TDR
|
||||
uart.statusReg = &uart.Bus.ISR
|
||||
uart.txEmptyFlag = stm32.USART_ISR_TXE
|
||||
uart.errClearReg = &uart.Bus.ICR
|
||||
}
|
||||
|
||||
//---------- I2C related code
|
||||
|
||||
@@ -67,6 +67,7 @@ func (uart *UART) setRegisters() {
|
||||
uart.txReg = &uart.Bus.TDR
|
||||
uart.statusReg = &uart.Bus.ISR
|
||||
uart.txEmptyFlag = stm32.USART_ISR_TXE
|
||||
uart.errClearReg = &uart.Bus.ICR
|
||||
}
|
||||
|
||||
//---------- SPI related types and code
|
||||
|
||||
@@ -331,6 +331,7 @@ func (uart *UART) setRegisters() {
|
||||
uart.txReg = &uart.Bus.TDR
|
||||
uart.statusReg = &uart.Bus.ISR
|
||||
uart.txEmptyFlag = stm32.USART_ISR_TXFNF //(TXFNF == TXE == bit 7, but depends alternate RM0461/1094)
|
||||
uart.errClearReg = &uart.Bus.ICR
|
||||
}
|
||||
|
||||
//---------- Timer related code
|
||||
|
||||
Reference in New Issue
Block a user