Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 17 additions & 15 deletions src/machine/machine_stm32_uart.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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
}
}
13 changes: 13 additions & 0 deletions src/machine/machine_stm32_uart_setbaudrate.go
Original file line number Diff line number Diff line change
@@ -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)
}
1 change: 1 addition & 0 deletions src/machine/machine_stm32u5.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
29 changes: 28 additions & 1 deletion src/machine/machine_stm32u585.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
Loading