targets: correct pin mapping for Arduino UNO Q STM32 MCU

Signed-off-by: deadprogram <ron@hybridgroup.com>

machine/stm32u585: fix PWR peripheral clock was never enabled

On STM32U5, PWR is on AHB3 and requires RCC.AHB3ENR.PWREN -
unlike some other STM32 families where PWR is always clocked.
Without this, all writes to PWR registers (including IO2SV for
VDDIO2) were silently dropped, so GPIOG pins could never drive.

Signed-off-by: deadprogram <ron@hybridgroup.com>

target: correct I2C pin mapping for Arduino UNO Q

Signed-off-by: deadprogram <ron@hybridgroup.com>
This commit is contained in:
deadprogram
2026-03-17 20:16:03 +01:00
parent 323b62b405
commit 60fb4b95fa
4 changed files with 96 additions and 47 deletions
+60 -41
View File
@@ -7,54 +7,64 @@ package machine
import (
"device/stm32"
"runtime/interrupt"
"unsafe"
)
const (
// Arduino Pins
A0 = PA0
A1 = PA1
A2 = PA4
A3 = PB1
A4 = PB11
A5 = PB12
A0 = PA4
A1 = PA5
A2 = PA6
A3 = PA7
A4 = PC1
A5 = PC0
D0 = PB7
D1 = PB6
D2 = PA10
D3 = PB3
D4 = PB5
D5 = PB4
D6 = PB10
D7 = PA8
D8 = PA9
D9 = PC7
D10 = PB0
D11 = PA7
D12 = PA6
D13 = PA5
D14 = PB9
D15 = PB8
D2 = PB3
D3 = PB0
D4 = PA12
D5 = PA11
D6 = PB1
D7 = PB2
D8 = PB4
D9 = PB8
D10 = PB9
D11 = PB15
D12 = PB14
D13 = PB13
D18 = PC1
D19 = PC0
D20 = PB10
D21 = PB11
)
const (
LED = LED_BUILTIN
LED_BUILTIN = LED_GREEN
LED_GREEN = PA5
LED = LED3_R
LED3_R = PH10
LED3_G = PH11
LED3_B = PH12
LED4_R = PH13
LED4_G = PH14
LED4_B = PH15
)
const (
BUTTON = PC13
)
// Default UART pins (LPUART1 active via ST-LINK virtual COM port)
UART_TX_PIN = PG7
UART_RX_PIN = PG8
const (
// UART pins
// PA2 and PA3 are connected to the ST-Link Virtual Com Port (VCP)
UART_TX_PIN = PA2
UART_RX_PIN = PA3
// USART1 pins (Arduino header D1/D0)
UART1_TX_PIN = D1
UART1_RX_PIN = D0
// LPUART1 pins (active via ST-LINK virtual COM port)
UART2_TX_PIN = PG7
UART2_RX_PIN = PG8
// I2C pins
I2C0_SCL_PIN = PB8
I2C0_SDA_PIN = PB9
I2C0_SCL_PIN = D20
I2C0_SDA_PIN = D21
// SPI pins
SPI1_SCK_PIN = PA5
@@ -66,23 +76,31 @@ const (
)
var (
// USART2 is the hardware serial port connected to the onboard ST-LINK
// debugger to be exposed as virtual COM port over USB.
// USART1 on PB6/PB7 (Arduino header D1/D0).
UART1 = &_UART1
_UART1 = UART{
Buffer: NewRingBuffer(),
Bus: stm32.USART2,
Bus: stm32.USART1,
TxAltFuncSelector: AF7_USART1_2_3,
RxAltFuncSelector: AF7_USART1_2_3,
}
DefaultUART = UART1
// I2C1 is documented, alias to I2C0 as well
I2C1 = &I2C{
Bus: stm32.I2C1,
// LPUART1 on PG7/PG8 (active via ST-LINK virtual COM port).
UART2 = &_UART2
_UART2 = UART{
Buffer: NewRingBuffer(),
Bus: (*stm32.USART_Type)(unsafe.Pointer(stm32.LPUART1)),
TxAltFuncSelector: AF8_UART4_5_LPUART1_SDMMC1,
RxAltFuncSelector: AF8_UART4_5_LPUART1_SDMMC1,
}
DefaultUART = UART2
// I2C2 is documented, alias to I2C0 as well
I2C2 = &I2C{
Bus: stm32.I2C2,
AltFuncSelector: AF4_I2C1_2_3_4,
}
I2C0 = I2C1
I2C0 = I2C2
// SPI1 is documented, alias to SPI0 as well
SPI1 = &SPI{
@@ -93,5 +111,6 @@ var (
)
func init() {
UART1.Interrupt = interrupt.New(stm32.IRQ_USART2, _UART1.handleInterrupt)
UART1.Interrupt = interrupt.New(stm32.IRQ_USART1, _UART1.handleInterrupt)
UART2.Interrupt = interrupt.New(stm32.IRQ_LPUART1, _UART2.handleInterrupt)
}
+4 -1
View File
@@ -236,13 +236,16 @@ func (p Pin) enableClock() {
default:
panic("machine: unknown port")
}
// Dummy read-back to ensure the clock enable takes effect before
// accessing GPIO registers (2 AHB cycle delay per STM32 reference manual).
_ = stm32.RCC.AHB2ENR1.Get()
}
// Enable peripheral clock
func enableAltFuncClock(bus unsafe.Pointer) {
switch bus {
case unsafe.Pointer(stm32.PWR): // Power interface clock enable
// PWR clock is always enabled on U5 (no dedicated enable bit needed)
stm32.RCC.AHB3ENR.SetBits(stm32.RCC_AHB3ENR_PWREN)
case unsafe.Pointer(stm32.I2C1): // I2C1 clock enable
stm32.RCC.APB1ENR1.SetBits(stm32.RCC_APB1ENR1_I2C1EN)
case unsafe.Pointer(stm32.I2C2): // I2C2 clock enable
+26 -1
View File
@@ -4,6 +4,7 @@ package machine
import (
"device/stm32"
"unsafe"
)
func CPUFrequency() uint32 {
@@ -20,8 +21,23 @@ const APB2_TIM_FREQ = 4e6 // 4MHz (MSI default)
// Configure the UART.
func (uart *UART) configurePins(config UARTConfig) {
if uart.isLPUART1() {
// LPUART1 is on APB3. Explicitly enable its peripheral clock.
stm32.RCC.APB3ENR.SetBits(stm32.RCC_APB3ENR_LPUART1EN)
_ = stm32.RCC.APB3ENR.Get() // delay for clock stabilization
// Select PCLK3 as LPUART1 kernel clock source.
stm32.RCC.CCIPR3.ReplaceBits(
stm32.RCC_CCIPR3_LPUART1SEL_PCLK3<<stm32.RCC_CCIPR3_LPUART1SEL_Pos,
stm32.RCC_CCIPR3_LPUART1SEL_Msk, 0)
}
if config.RX.getPort() == stm32.GPIOG || config.TX.getPort() == stm32.GPIOG {
// Enable VDDIO2 power supply for PGx pins
// Enable VDDIO2 voltage monitoring and wait for ready before
// declaring VDDIO2 supply valid (matches HAL_PWREx_EnableVddIO2).
stm32.PWR.SetSVMCR_IO2VMEN(1)
for stm32.PWR.GetSVMSR_VDDIO2RDY() == 0 {
}
stm32.PWR.SetSVMCR_IO2SV(1)
}
@@ -30,9 +46,18 @@ func (uart *UART) configurePins(config UARTConfig) {
config.RX.ConfigureAltFunc(PinConfig{Mode: PinModeUARTRX}, uart.RxAltFuncSelector)
}
// isLPUART1 returns true if this UART is backed by the LPUART1 peripheral.
func (uart *UART) isLPUART1() bool {
return uintptr(unsafe.Pointer(uart.Bus)) == uintptr(unsafe.Pointer(stm32.LPUART1))
}
// UART baudrate calc based on the bus and clockspeed
// 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
}
return CPUFrequency() / baudRate
}
+6 -4
View File
@@ -3,6 +3,7 @@
package runtime
import (
"device/stm32"
"machine"
)
@@ -24,8 +25,9 @@ func buffered() int {
func initCLK() {
// Use MSI at 4MHz — the reset default clock configuration.
// This matches the known-working bare-metal C configuration for
// the Arduino Uno Q (STM32U585). The MCU boots with MSI at 4MHz,
// VOS Range 4, and 0 flash wait states. No additional configuration
// is needed.
// The MCU boots with MSI at 4MHz, VOS Range 4, and 0 flash wait states.
// 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
}