diff --git a/src/machine/machine_stm32_uart.go b/src/machine/machine_stm32_uart.go index 10cf7d87bd..4c6a546586 100644 --- a/src/machine/machine_stm32_uart.go +++ b/src/machine/machine_stm32_uart.go @@ -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 + } +} diff --git a/src/machine/machine_stm32_uart_setbaudrate.go b/src/machine/machine_stm32_uart_setbaudrate.go new file mode 100644 index 0000000000..3a4c5f8cfc --- /dev/null +++ b/src/machine/machine_stm32_uart_setbaudrate.go @@ -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) +} diff --git a/src/machine/machine_stm32u5.go b/src/machine/machine_stm32u5.go index df13a3d66b..2c67d641f7 100644 --- a/src/machine/machine_stm32u5.go +++ b/src/machine/machine_stm32u5.go @@ -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 diff --git a/src/machine/machine_stm32u585.go b/src/machine/machine_stm32u585.go index fcb4c0152a..865310bcbd 100644 --- a/src/machine/machine_stm32u585.go +++ b/src/machine/machine_stm32u585.go @@ -8,14 +8,14 @@ import ( ) func CPUFrequency() uint32 { - return 4_000_000 + return 160_000_000 } // Internal use: configured speed of the APB1 and APB2 timers, this should be kept // in sync with any changes to runtime package which configures the oscillators // and clock frequencies -const APB1_TIM_FREQ = 4e6 // 4MHz (MSI default) -const APB2_TIM_FREQ = 4e6 // 4MHz (MSI default) +const APB1_TIM_FREQ = 160e6 // 160MHz (PLL1: MSIS 4MHz × 80 / 1 / 2) +const APB2_TIM_FREQ = 160e6 // 160MHz (PLL1: MSIS 4MHz × 80 / 1 / 2) //---------- UART related code @@ -55,10 +55,21 @@ func (uart *UART) isLPUART1() bool { // NOTE: keep this in sync with the runtime/runtime_stm32u5.go clock init code func (uart *UART) getBaudRateDivisor(baudRate uint32) uint32 { if uart.isLPUART1() { - // LPUART uses BRR = 256 * fclk / baud - return (256 * CPUFrequency()) / baudRate + // LPUART uses BRR = 256 * fclk / baud. + // Use 64-bit arithmetic to avoid overflow: at 160 MHz, + // 256 * 160_000_000 = 40_960_000_000 which exceeds uint32 max. + return uint32(uint64(256) * uint64(CPUFrequency()) / uint64(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 +81,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 diff --git a/src/runtime/runtime_stm32u5.go b/src/runtime/runtime_stm32u5.go index e54721f393..d66dbec2dc 100644 --- a/src/runtime/runtime_stm32u5.go +++ b/src/runtime/runtime_stm32u5.go @@ -24,18 +24,52 @@ func buffered() int { } func initCLK() { - // Use MSI at 4MHz — the reset default clock configuration. - // The MCU boots with MSI at 4MHz, VOS Range 4, and 0 flash wait states. + // Configure SYSCLK to 160 MHz via PLL1 using MSIS (4 MHz reset default) as the source. + // Formula: Fout = Fin × N / M / R = 4 × 80 / 1 / 2 = 160 MHz + // - M = 1 (Div1), N = 80 (stored as N-1 = 79), R = 2 (Div2) + // - VCO input = 4 MHz (PLL1RGE Range1: 4–8 MHz), VCO output = 320 MHz + // VOS Range 1 (1.2V) is required for SYSCLK > 100 MHz (RM0456 §6.3.6). // Enable PWR peripheral clock (required on STM32U5 before accessing PWR registers). stm32.RCC.AHB3ENR.SetBits(stm32.RCC_AHB3ENR_PWREN) _ = stm32.RCC.AHB3ENR.Get() // read-back for clock stabilization - // Switch from VOS Range 4 to Range 3. Range 4 doesn't support ADC (RM0456 §6.3.6). - // Range 3 supports up to 50 MHz HCLK and enables ADC. No flash wait-state change needed at 4 MHz. - // The EPOD booster must be enabled before changing VOS (RM0456 §10.5.4). + // Enable the EPOD booster before raising VOS (RM0456 §10.5.4). stm32.PWR.VOSR.SetBits(stm32.PWR_VOSR_BOOSTEN) - stm32.PWR.VOSR.ReplaceBits(stm32.PWR_VOSR_VOS_Range3<