mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-06 20:13:40 +00:00
machine/stm32: fix UART transmission, baud rate, and interrupt handling
This fixes several issues in the STM32 UART implementation: - `writeByte` now waits for the transmit register to be empty before writing, preventing data loss by avoiding overwriting the shift register. - `flush` now correctly waits for the Transmission Complete (TC) flag. - The interrupt handler now clears all error flags (ORE, NE, FE, PE) to prevent interrupt storms, rather than just ORE. - Extracted `SetBaudRate` so it can be cleanly overridden by specific MCU families. It also introduces specific fixes for the STM32U5 family: - Enforces a minimum BRR divisor of 16 in `getBaudRateDivisor` to prevent undefined hardware behavior and CPU starvation. - Overrides `SetBaudRate` for STM32U585 to momentarily disable the USART (UE=0) before updating the BRR register, which is read-only when enabled. - Adds a readback after enabling the USART1 clock to ensure the clock is active before register access. Signed-off-by: deadprogram <ron@hybridgroup.com>
This commit is contained in:
@@ -76,13 +76,15 @@ func (uart *UART) handleInterrupt(interrupt.Interrupt) {
|
||||
uart.Receive(byte((uart.rxReg.Get() & 0xFF)))
|
||||
}
|
||||
|
||||
// Clear overrun error (ORE, bit 3) to prevent an interrupt storm.
|
||||
if s&0x8 != 0 {
|
||||
// Clear error flags (ORE=bit3, NE=bit2, FE=bit1, PE=bit0) to prevent
|
||||
// an interrupt storm and ensure the USART can continue receiving.
|
||||
if s&0xF != 0 {
|
||||
if uart.errClearReg != nil {
|
||||
// Newer USART peripherals: clear ORE via the ICR register.
|
||||
uart.errClearReg.Set(0x8) // ORECF
|
||||
// Newer USART peripherals (L0, L4, L5, G0, F7, U5, WL, etc.):
|
||||
// clear all error flags via ICR (ORECF|NECF|FECF|PECF = bits 3:0).
|
||||
uart.errClearReg.Set(s & 0xF)
|
||||
} else if s&0x20 == 0 {
|
||||
// Older USART (F1/F4): ORE is cleared by reading SR then DR.
|
||||
// Older USART (F1/F4): errors are 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.
|
||||
@@ -91,20 +93,20 @@ func (uart *UART) handleInterrupt(interrupt.Interrupt) {
|
||||
}
|
||||
}
|
||||
|
||||
// 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))
|
||||
|
||||
// Wait for the transmit data register to be empty before writing, so we
|
||||
// don't overwrite a byte that hasn't moved to the shift register yet.
|
||||
for !uart.statusReg.HasBits(uart.txEmptyFlag) {
|
||||
}
|
||||
uart.txReg.Set(uint32(c))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (uart *UART) flush() {}
|
||||
// flush waits until the USART shift register has finished transmitting the
|
||||
// last byte (TC = Transmission Complete, bit 6). Without this, Write() returns
|
||||
// while the final byte is still clocking out on the wire.
|
||||
func (uart *UART) flush() {
|
||||
for !uart.statusReg.HasBits(1 << 6) { // TC bit
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
//go:build stm32 && !stm32u585
|
||||
|
||||
package machine
|
||||
|
||||
// SetBaudRate sets the communication speed for the UART. Defers to
|
||||
// chip-specific getBaudRateDivisor for the divisor calculation.
|
||||
//
|
||||
// On STM32U585 this function is overridden in machine_stm32u585.go because
|
||||
// the U5 family requires UE=0 to write BRR.
|
||||
func (uart *UART) SetBaudRate(br uint32) {
|
||||
divider := uart.getBaudRateDivisor(br)
|
||||
uart.Bus.BRR.Set(divider)
|
||||
}
|
||||
@@ -256,6 +256,7 @@ func enableAltFuncClock(bus unsafe.Pointer) {
|
||||
stm32.RCC.APB1ENR2.SetBits(stm32.RCC_APB1ENR2_I2C4EN)
|
||||
case unsafe.Pointer(stm32.USART1): // USART1 clock enable
|
||||
stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_USART1EN)
|
||||
_ = stm32.RCC.APB2ENR.Get() // readback: ensure clock is active before accessing USART1 registers
|
||||
case unsafe.Pointer(stm32.USART2): // USART2 clock enable
|
||||
stm32.RCC.APB1ENR1.SetBits(stm32.RCC_APB1ENR1_USART2EN)
|
||||
case unsafe.Pointer(stm32.USART3): // USART3 clock enable
|
||||
|
||||
@@ -58,7 +58,16 @@ func (uart *UART) getBaudRateDivisor(baudRate uint32) uint32 {
|
||||
// LPUART uses BRR = 256 * fclk / baud
|
||||
return (256 * CPUFrequency()) / baudRate
|
||||
}
|
||||
return CPUFrequency() / baudRate
|
||||
// USART requires BRR >= 16 for 16x oversampling (OVER8=0).
|
||||
// A divisor below 16 is invalid per the STM32 reference manual and causes
|
||||
// undefined hardware behaviour — in practice the receiver fires ORE/RXNE
|
||||
// interrupts at an impossible rate, completely starving the CPU.
|
||||
const minBRR = 16
|
||||
divisor := CPUFrequency() / baudRate
|
||||
if divisor < minBRR {
|
||||
divisor = minBRR
|
||||
}
|
||||
return divisor
|
||||
}
|
||||
|
||||
// Register names vary by ST processor, these are for STM U5
|
||||
@@ -70,6 +79,24 @@ func (uart *UART) setRegisters() {
|
||||
uart.errClearReg = &uart.Bus.ICR
|
||||
}
|
||||
|
||||
// SetBaudRate overrides the shared implementation for STM32U5. On this
|
||||
// family the BRR register is read-only while UE=1 (USART enabled), so the
|
||||
// USART must be briefly disabled to change the baud rate. This matters when
|
||||
// the servo library (or any code) calls SetBaudRate after Configure has
|
||||
// already enabled the USART.
|
||||
func (uart *UART) SetBaudRate(br uint32) {
|
||||
cr1 := uart.Bus.CR1.Get()
|
||||
if cr1&stm32.USART_CR1_UE != 0 {
|
||||
// Disable the USART so BRR becomes writable.
|
||||
uart.Bus.CR1.Set(cr1 &^ stm32.USART_CR1_UE)
|
||||
}
|
||||
uart.Bus.BRR.Set(uart.getBaudRateDivisor(br))
|
||||
if cr1&stm32.USART_CR1_UE != 0 {
|
||||
// Restore CR1 exactly as it was (re-enables USART, TE, RE, etc.).
|
||||
uart.Bus.CR1.Set(cr1)
|
||||
}
|
||||
}
|
||||
|
||||
//---------- SPI related types and code
|
||||
|
||||
// SPI on the STM32U5 using the new SPIv2 peripheral
|
||||
|
||||
Reference in New Issue
Block a user