diff --git a/src/machine/board_arduino_uno_q.go b/src/machine/board_arduino_uno_q.go index 364529e58..bf26eadb5 100644 --- a/src/machine/board_arduino_uno_q.go +++ b/src/machine/board_arduino_uno_q.go @@ -1,84 +1,97 @@ -package tmp //go:build arduino_uno_q - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -} UART1.Interrupt = interrupt.New(stm32.IRQ_USART2, _UART1.handleInterrupt)func init() {) I2C0 = I2C1 } AltFuncSelector: AF4_I2C1_2_3_4, Bus: stm32.I2C1, I2C1 = &I2C{ // I2C1 is documented, alias to I2C0 as well DefaultUART = UART1 } RxAltFuncSelector: AF7_USART1_2_3, TxAltFuncSelector: AF7_USART1_2_3, Bus: stm32.USART2, Buffer: NewRingBuffer(), _UART1 = UART{ UART1 = &_UART1 // debugger to be exposed as virtual COM port over USB. // USART2 is the hardware serial port connected to the onboard ST-LINKvar () I2C0_SDA_PIN = PB9 I2C0_SCL_PIN = PB8 // I2C pins UART_RX_PIN = PA3 UART_TX_PIN = PA2 // PA2 and PA3 are connected to the ST-Link Virtual Com Port (VCP) // UART pinsconst () BUTTON = PC13const () LED_GREEN = PA5 LED_BUILTIN = LED_GREEN LED = LED_BUILTINconst () D15 = PB8 D14 = PB9 D13 = PA5 D12 = PA6 D11 = PA7 D10 = PB0 D9 = PC7 D8 = PA9 D7 = PA8 D6 = PB10 D5 = PB4 D4 = PB5 D3 = PB3 D2 = PA10 D1 = PB6 D0 = PB7 A5 = PB12 A4 = PB11 A3 = PB1 A2 = PA4 A1 = PA1 A0 = PA0 // Arduino Pinsconst () "runtime/interrupt" "device/stm32"import (package machine// Arduino UNO Q board with STM32U585 processor. \ No newline at end of file +// Arduino UNO Q board with STM32U585 processor. + +package machine + +import ( + "device/stm32" + "runtime/interrupt" +) + +const ( + // Arduino Pins + A0 = PA0 + A1 = PA1 + A2 = PA4 + A3 = PB1 + A4 = PB11 + A5 = PB12 + + 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 +) + +const ( + LED = LED_BUILTIN + LED_BUILTIN = LED_GREEN + LED_GREEN = PA5 +) + +const ( + BUTTON = PC13 +) + +const ( + // UART pins + // PA2 and PA3 are connected to the ST-Link Virtual Com Port (VCP) + UART_TX_PIN = PA2 + UART_RX_PIN = PA3 + + // I2C pins + I2C0_SCL_PIN = PB8 + I2C0_SDA_PIN = PB9 + + // SPI pins + SPI1_SCK_PIN = PA5 + SPI1_SDI_PIN = PA6 + SPI1_SDO_PIN = PA7 + SPI0_SCK_PIN = SPI1_SCK_PIN + SPI0_SDI_PIN = SPI1_SDI_PIN + SPI0_SDO_PIN = SPI1_SDO_PIN +) + +var ( + // USART2 is the hardware serial port connected to the onboard ST-LINK + // debugger to be exposed as virtual COM port over USB. + UART1 = &_UART1 + _UART1 = UART{ + Buffer: NewRingBuffer(), + Bus: stm32.USART2, + 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, + AltFuncSelector: AF4_I2C1_2_3_4, + } + I2C0 = I2C1 + + // SPI1 is documented, alias to SPI0 as well + SPI1 = &SPI{ + Bus: stm32.SPI1, + AltFuncSelector: AF5_SPI1_2_3_OCTOSPI1_OCTOSPI2, + } + SPI0 = SPI1 +) + +func init() { + UART1.Interrupt = interrupt.New(stm32.IRQ_USART2, _UART1.handleInterrupt) +} diff --git a/src/machine/machine_stm32_spi.go b/src/machine/machine_stm32_spi.go index 3fb9af7fb..d246391be 100644 --- a/src/machine/machine_stm32_spi.go +++ b/src/machine/machine_stm32_spi.go @@ -1,4 +1,4 @@ -//go:build stm32 && !stm32f7x2 && !stm32l5x2 && !stm32g0 +//go:build stm32 && !stm32f7x2 && !stm32l5x2 && !stm32g0 && !stm32u5 package machine diff --git a/src/machine/machine_stm32u585.go b/src/machine/machine_stm32u585.go index d8d971940..901bec5e8 100644 --- a/src/machine/machine_stm32u585.go +++ b/src/machine/machine_stm32u585.go @@ -1,68 +1,123 @@ -package tmp //go:build stm32u585 +package machine +import ( + "device/stm32" +) +func CPUFrequency() uint32 { + return 160000000 +} +// 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 = 160e6 // 160MHz +const APB2_TIM_FREQ = 160e6 // 160MHz +//---------- UART related code +// Configure the UART. +func (uart *UART) configurePins(config UARTConfig) { + if config.RX.getPort() == stm32.GPIOG || config.TX.getPort() == stm32.GPIOG { + // Enable VDDIO2 power supply for PGx pins + stm32.PWR.SetSVMCR_IO2SV(1) + } + // enable the alternate functions on the TX and RX pins + config.TX.ConfigureAltFunc(PinConfig{Mode: PinModeUARTTX}, uart.TxAltFuncSelector) + config.RX.ConfigureAltFunc(PinConfig{Mode: PinModeUARTRX}, uart.RxAltFuncSelector) +} +// 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 { + return CPUFrequency() / baudRate +} +// Register names vary by ST processor, these are for STM U5 +func (uart *UART) setRegisters() { + uart.rxReg = &uart.Bus.RDR + uart.txReg = &uart.Bus.TDR + uart.statusReg = &uart.Bus.ISR + uart.txEmptyFlag = stm32.USART_ISR_TXE +} +//---------- SPI related types and code +// SPI on the STM32U5 using the new SPIv2 peripheral +type SPI struct { + Bus *stm32.SPI_Type + AltFuncSelector uint8 +} +func (spi *SPI) config8Bits() { + // U5 SPI has DSIZE field in CFG1, set to 7 for 8-bit frames (DSIZE = bits-1) + spi.Bus.CFG1.ReplaceBits(7, 0x1f, 0) // DSIZE[4:0] = 0x7 = 8 bits +} +// Set baud rate for SPI +func (spi *SPI) getBaudRate(config SPIConfig) uint32 { + var conf uint32 + localFrequency := config.Frequency + // Default + if localFrequency == 0 { + localFrequency = 4e6 + } + // Set frequency dependent on PCLK prescaler + // MBR field in CFG1 register, bits [30:28] + switch { + case localFrequency < 625000: + conf = 7 // Div256 + case localFrequency < 1250000: + conf = 6 // Div128 + case localFrequency < 2500000: + conf = 5 // Div64 + case localFrequency < 5000000: + conf = 4 // Div32 + case localFrequency < 10000000: + conf = 3 // Div16 + case localFrequency < 20000000: + conf = 2 // Div8 + case localFrequency < 40000000: + conf = 1 // Div4 + case localFrequency < 80000000: + conf = 0 // Div2 + default: + conf = 7 // Div256 (safest) + } + return conf << 28 // MBR position in CFG1 +} +// Configure SPI pins for input output and clock +func (spi *SPI) configurePins(config SPIConfig) { + config.SCK.ConfigureAltFunc(PinConfig{Mode: PinModeSPICLK}, spi.AltFuncSelector) + config.SDO.ConfigureAltFunc(PinConfig{Mode: PinModeSPISDO}, spi.AltFuncSelector) + config.SDI.ConfigureAltFunc(PinConfig{Mode: PinModeSPISDI}, spi.AltFuncSelector) +} +//---------- I2C related code - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -} } return 0 default: return 0x00802172 case 500 * KHz: return 0x10802D9B case 400 * KHz: return 0x30A0A7FB case 100 * KHz: return 0xF010F3FE case 10 * KHz: switch br { // TODO: Do calculations based on PCLK1 // for 160MHz PCLK1. // These are 'magic' values calculated by STM32CubeMXfunc (i2c *I2C) getFreqRange(br uint32) uint32 {// Gets the value for TIMINGR register//---------- I2C related code} uart.txEmptyFlag = stm32.USART_ISR_TXE uart.statusReg = &uart.Bus.ISR uart.txReg = &uart.Bus.TDR uart.rxReg = &uart.Bus.RDRfunc (uart *UART) setRegisters() {// Register names vary by ST processor, these are for STM U5} return CPUFrequency() / baudRatefunc (uart *UART) getBaudRateDivisor(baudRate uint32) uint32 {// NOTE: keep this in sync with the runtime/runtime_stm32u5.go clock init code// UART baudrate calc based on the bus and clockspeed} config.RX.ConfigureAltFunc(PinConfig{Mode: PinModeUARTRX}, uart.RxAltFuncSelector) config.TX.ConfigureAltFunc(PinConfig{Mode: PinModeUARTTX}, uart.TxAltFuncSelector) // enable the alternate functions on the TX and RX pins } stm32.PWR.SetSVMCR_IO2SV(1) // Enable VDDIO2 power supply for PGx pins if config.RX.getPort() == stm32.GPIOG || config.TX.getPort() == stm32.GPIOG {func (uart *UART) configurePins(config UARTConfig) {// Configure the UART.//---------- UART related codeconst APB2_TIM_FREQ = 160e6 // 160MHzconst APB1_TIM_FREQ = 160e6 // 160MHz// and clock frequencies// in sync with any changes to runtime package which configures the oscillators// Internal use: configured speed of the APB1 and APB2 timers, this should be kept} return 160000000func CPUFrequency() uint32 {) "device/stm32"import (package machine \ No newline at end of file +// Gets the value for TIMINGR register +func (i2c *I2C) getFreqRange(br uint32) uint32 { + // These are 'magic' values calculated by STM32CubeMX + // for 160MHz PCLK1. + // TODO: Do calculations based on PCLK1 + switch br { + case 10 * KHz: + return 0xF010F3FE + case 100 * KHz: + return 0x30A0A7FB + case 400 * KHz: + return 0x10802D9B + case 500 * KHz: + return 0x00802172 + default: + return 0 + } +} diff --git a/src/machine/machine_stm32u5_spi.go b/src/machine/machine_stm32u5_spi.go new file mode 100644 index 000000000..2b8ca474a --- /dev/null +++ b/src/machine/machine_stm32u5_spi.go @@ -0,0 +1,119 @@ +//go:build stm32u5 + +package machine + +// SPI on STM32U5 uses the new SPIv2 peripheral with separate TXDR/RXDR registers. + +import ( + "unsafe" +) + +// SPIConfig is used to store config info for SPI. +type SPIConfig struct { + Frequency uint32 + SCK Pin + SDO Pin + SDI Pin + LSBFirst bool + Mode uint8 +} + +// Configure is intended to setup the STM32U5 SPI peripheral. +func (spi *SPI) Configure(config SPIConfig) error { + // Disable SPI interface before any configuration changes + spi.Bus.CR1.ClearBits(1) // Clear SPE (bit 0) + + // Enable clock for SPI + enableAltFuncClock(unsafe.Pointer(spi.Bus)) + + // Init pins - use defaults if not specified + if config.SCK == 0 && config.SDO == 0 && config.SDI == 0 { + config.SCK = SPI0_SCK_PIN + config.SDO = SPI0_SDO_PIN + config.SDI = SPI0_SDI_PIN + } + spi.configurePins(config) + + // Configure CFG1: baud rate and data size + var cfg1 uint32 + + // Set baud rate (MBR bits [30:28]) + cfg1 |= spi.getBaudRate(config) + + // Set data size to 8 bits (DSIZE[4:0] = 7 = 8-1) + cfg1 |= 7 + + spi.Bus.CFG1.Set(cfg1) + + // Configure CFG2: master mode, SS output, polarity, phase + var cfg2 uint32 + const ( + cfg2_MASTER = 1 << 22 // MASTER bit + cfg2_SSM = 1 << 26 // Software SS management + cfg2_SSOE = 1 << 29 // SS output enable + cfg2_AFCNTR = 1 << 31 // Alternate function GPIO control always active + cfg2_CPOL = 1 << 25 + cfg2_CPHA = 1 << 24 + cfg2_LSBFRST = 1 << 23 + ) + + cfg2 |= cfg2_MASTER | cfg2_SSM | cfg2_AFCNTR + + // Set polarity and phase + switch config.Mode { + case Mode1: + cfg2 |= cfg2_CPHA + case Mode2: + cfg2 |= cfg2_CPOL + case Mode3: + cfg2 |= cfg2_CPOL | cfg2_CPHA + } + + // Set bit transfer order + if config.LSBFirst { + cfg2 |= cfg2_LSBFRST + } + + spi.Bus.CFG2.Set(cfg2) + + // Enable SPI + spi.Bus.CR1.SetBits(1) // Set SPE (bit 0) + + return nil +} + +// Transfer writes/reads a single byte using the SPI interface. +func (spi *SPI) Transfer(w byte) (byte, error) { + // Set transfer size to 1 frame + spi.Bus.CR2.Set(1) + + // Start the transfer + spi.Bus.CR1.SetBits(1 << 9) // CSTART bit + + // Wait until TX FIFO is ready (TXP bit in SR) + const sr_TXP = 1 << 1 + const sr_RXP = 1 << 0 + const sr_EOT = 1 << 3 + + for !spi.Bus.SR.HasBits(sr_TXP) { + } + + // Write byte to TXDR + spi.Bus.TXDR.Set(uint32(w)) + + // Wait for RX data available (RXP bit in SR) + for !spi.Bus.SR.HasBits(sr_RXP) { + } + + // Read received byte + data := byte(spi.Bus.RXDR.Get()) + + // Wait for end of transfer + for !spi.Bus.SR.HasBits(sr_EOT) { + } + + // Clear EOT flag + spi.Bus.IFCR.Set(sr_EOT) + + return data, nil +}