mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-04 19:17:47 +00:00
4c3680635e
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>
111 lines
3.0 KiB
Go
111 lines
3.0 KiB
Go
//go:build stm32
|
|
|
|
package machine
|
|
|
|
// Peripheral abstraction layer for UARTs on the stm32 family (except stm32g0).
|
|
|
|
import (
|
|
"device/stm32"
|
|
"runtime/interrupt"
|
|
"runtime/volatile"
|
|
"unsafe"
|
|
)
|
|
|
|
// UART representation
|
|
type UART struct {
|
|
Buffer *RingBuffer
|
|
Bus *stm32.USART_Type
|
|
Interrupt interrupt.Interrupt
|
|
TxAltFuncSelector uint8
|
|
RxAltFuncSelector uint8
|
|
|
|
// Registers specific to the chip
|
|
rxReg *volatile.Register32
|
|
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.
|
|
func (uart *UART) Configure(config UARTConfig) {
|
|
// Default baud rate to 115200.
|
|
if config.BaudRate == 0 {
|
|
config.BaudRate = 115200
|
|
}
|
|
|
|
// Set the GPIO pins to defaults if they're not set
|
|
if config.TX == 0 && config.RX == 0 {
|
|
config.TX = UART_TX_PIN
|
|
config.RX = UART_RX_PIN
|
|
}
|
|
|
|
// STM32 families have different, but compatible, registers for
|
|
// basic UART functions. For each family populate the registers
|
|
// into `uart`.
|
|
uart.setRegisters()
|
|
|
|
// Enable USART clock
|
|
enableAltFuncClock(unsafe.Pointer(uart.Bus))
|
|
|
|
uart.configurePins(config)
|
|
|
|
// Set baud rate
|
|
uart.SetBaudRate(config.BaudRate)
|
|
|
|
// Enable USART port, tx, rx and rx interrupts
|
|
uart.Bus.CR1.Set(stm32.USART_CR1_TE | stm32.USART_CR1_RE | stm32.USART_CR1_RXNEIE | stm32.USART_CR1_UE)
|
|
|
|
// Enable RX IRQ
|
|
uart.Interrupt.SetPriority(0xc0)
|
|
uart.Interrupt.Enable()
|
|
}
|
|
|
|
// handleInterrupt should be called from the appropriate interrupt handler for
|
|
// this UART instance.
|
|
func (uart *UART) handleInterrupt(interrupt.Interrupt) {
|
|
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
|
|
// routines for calculation
|
|
func (uart *UART) SetBaudRate(br uint32) {
|
|
divider := uart.getBaudRateDivisor(br)
|
|
uart.Bus.BRR.Set(divider)
|
|
}
|
|
|
|
// WriteByte writes a byte of data to the UART.
|
|
func (uart *UART) writeByte(c byte) error {
|
|
uart.txReg.Set(uint32(c))
|
|
|
|
for !uart.statusReg.HasBits(uart.txEmptyFlag) {
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (uart *UART) flush() {}
|