diff --git a/Makefile b/Makefile index 3ed2a895..5d400fff 100644 --- a/Makefile +++ b/Makefile @@ -435,6 +435,8 @@ ifneq ($(STM32), 0) @$(MD5SUM) test.hex $(TINYGO) build -size short -o test.hex -target=nucleo-l552ze examples/blinky1 @$(MD5SUM) test.hex + $(TINYGO) build -size short -o test.hex -target=nucleo-wl55jc examples/blinky1 + @$(MD5SUM) test.hex $(TINYGO) build -size short -o test.hex -target=stm32f4disco examples/blinky1 @$(MD5SUM) test.hex $(TINYGO) build -size short -o test.hex -target=stm32f4disco examples/blinky2 diff --git a/README.md b/README.md index 2252f6fd..74aed7f3 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,7 @@ See the [getting started instructions](https://tinygo.org/getting-started/) for You can compile TinyGo programs for microcontrollers, WebAssembly and Linux. -The following 72 microcontroller boards are currently supported: +The following 74 microcontroller boards are currently supported: * [Adafruit Circuit Playground Bluefruit](https://www.adafruit.com/product/4333) * [Adafruit Circuit Playground Express](https://www.adafruit.com/product/3333) @@ -115,6 +115,7 @@ The following 72 microcontroller boards are currently supported: * [ST Micro "Nucleo" L031K6](https://www.st.com/ja/evaluation-tools/nucleo-l031k6.html) * [ST Micro "Nucleo" L432KC](https://www.st.com/ja/evaluation-tools/nucleo-l432kc.html) * [ST Micro "Nucleo" L552ZE](https://www.st.com/en/evaluation-tools/nucleo-l552ze-q.html) +* [ST Micro "Nucleo" WL55JC](https://www.st.com/en/evaluation-tools/nucleo-wl55jc.html) * [ST Micro STM32F103XX "Bluepill"](https://stm32-base.org/boards/STM32F103C8T6-Blue-Pill) * [ST Micro STM32F407 "Discovery"](https://www.st.com/en/evaluation-tools/stm32f4discovery.html) * [X9 Pro smartwatch](https://github.com/curtpw/nRF5x-device-reverse-engineering/tree/master/X9-nrf52832-activity-tracker/) diff --git a/src/machine/board_lorae5.go b/src/machine/board_lorae5.go index d6634b4a..040e9c09 100644 --- a/src/machine/board_lorae5.go +++ b/src/machine/board_lorae5.go @@ -9,8 +9,8 @@ import ( ) const ( - LED1 = PB5 - LED = LED1 // Default LED + // We assume a LED is connected on PB5 + LED = PB5 // Default LED ) // SubGhz (SPI3) @@ -46,6 +46,11 @@ var ( RxAltFuncSelector: AF7_USART1_2, } DefaultUART = UART0 + + // SPI + SPI3 = SPI{ + Bus: stm32.SPI3, + } ) func init() { diff --git a/src/machine/board_nucleowl55jc.go b/src/machine/board_nucleowl55jc.go new file mode 100644 index 00000000..dbbc37b9 --- /dev/null +++ b/src/machine/board_nucleowl55jc.go @@ -0,0 +1,72 @@ +//go:build nucleowl55jc +// +build nucleowl55jc + +package machine + +import ( + "device/stm32" + "runtime/interrupt" +) + +const ( + LED_BLUE = PB15 + LED_GREEN = PB9 + LED_RED = PB11 + LED = LED_RED + + BTN1 = PA0 + BTN2 = PA1 + BTN3 = PC6 + BUTTON = BTN1 + + // SubGhz (SPI3) + SPI0_NSS_PIN = PA4 + SPI0_SCK_PIN = PA5 + SPI0_SDO_PIN = PA6 + SPI0_SDI_PIN = PA7 + + //MCU USART1 + UART1_TX_PIN = PB6 + UART1_RX_PIN = PB7 + + //MCU USART2 + UART2_RX_PIN = PA3 + UART2_TX_PIN = PA2 + + // DEFAULT USART + UART_RX_PIN = UART2_RX_PIN + UART_TX_PIN = UART2_TX_PIN +) + +var ( + // STM32 UART2 is connected to the embedded STLINKV3 Virtual Com Port + UART0 = &_UART0 + _UART0 = UART{ + Buffer: NewRingBuffer(), + Bus: stm32.USART2, + TxAltFuncSelector: 7, + RxAltFuncSelector: 7, + } + + // UART1 is free + UART1 = &_UART1 + _UART1 = UART{ + Buffer: NewRingBuffer(), + Bus: stm32.USART1, + TxAltFuncSelector: 7, + RxAltFuncSelector: 7, + } + + DefaultUART = UART0 + + // SPI + SPI3 = SPI{ + Bus: stm32.SPI3, + } +) + +func init() { + // Enable UARTs Interrupts + UART0.Interrupt = interrupt.New(stm32.IRQ_USART2, _UART0.handleInterrupt) + UART1.Interrupt = interrupt.New(stm32.IRQ_USART1, _UART1.handleInterrupt) +} diff --git a/src/machine/machine_stm32_spi.go b/src/machine/machine_stm32_spi.go index f9179a2f..481df8cd 100644 --- a/src/machine/machine_stm32_spi.go +++ b/src/machine/machine_stm32_spi.go @@ -1,5 +1,5 @@ -//go:build stm32 && !stm32f7x2 && !stm32l5x2 && !stm32wle5 -// +build stm32,!stm32f7x2,!stm32l5x2,!stm32wle5 +//go:build stm32 && !stm32f7x2 && !stm32l5x2 +// +build stm32,!stm32f7x2,!stm32l5x2 package machine diff --git a/src/machine/machine_stm32wle5.go b/src/machine/machine_stm32wle5.go index cd9653df..29c9c584 100644 --- a/src/machine/machine_stm32wle5.go +++ b/src/machine/machine_stm32wle5.go @@ -7,6 +7,7 @@ package machine import ( "device/stm32" + "math/bits" "runtime/interrupt" "runtime/volatile" "unsafe" @@ -28,12 +29,15 @@ const ( AF15_EVENTOUT = 15 ) -func CPUFrequency() uint32 { - return 4e6 -} +const ( + SYSCLK = 48e6 + APB1_TIM_FREQ = SYSCLK + APB2_TIM_FREQ = SYSCLK +) -const APB1_TIM_FREQ = 4e6 -const APB2_TIM_FREQ = 4e6 +func CPUFrequency() uint32 { + return SYSCLK +} const ( PA0 = portA + 0 @@ -221,6 +225,64 @@ func (p Pin) registerInterrupt() interrupt.Interrupt { return interrupt.Interrupt{} } +// -- SPI ---------------------------------------------------------------------- + +type SPI struct { + Bus *stm32.SPI_Type + AltFuncSelector uint8 +} + +func (spi SPI) config8Bits() { + // Set rx threshold to 8-bits, so RXNE flag is set for 1 byte + // (common STM32 SPI implementation does 8-bit transfers only) + spi.Bus.CR2.SetBits(stm32.SPI_CR2_FRXTH) +} + +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) +} + +func (spi SPI) getBaudRate(config SPIConfig) uint32 { + var clock uint32 + + // We keep this switch and separate management of SPI Clocks + // for future improvement of system/bus clocks and prescalers + switch spi.Bus { + case stm32.SPI1: + clock = CPUFrequency() + case stm32.SPI2, stm32.SPI3: + clock = CPUFrequency() + } + + // limit requested frequency to bus frequency and min frequency (DIV256) + freq := config.Frequency + if min := clock / 256; freq < min { + freq = min + } else if freq > clock { + freq = clock + } + + // calculate the exact clock divisor (freq=clock/div -> div=clock/freq). + // truncation is fine, since it produces a less-than-or-equal divisor, and + // thus a greater-than-or-equal frequency. + // divisors only come in consecutive powers of 2, so we can use log2 (or, + // equivalently, bits.Len - 1) to convert to respective enum value. + div := bits.Len32(clock/freq) - 1 + + // but DIV1 (2^0) is not permitted, as the least divisor is DIV2 (2^1), so + // subtract 1 from the log2 value, keeping a lower bound of 0 + if div < 0 { + div = 0 + } else if div > 0 { + div-- + } + + // finally, shift the enumerated value into position for SPI CR1 + return uint32(div) << stm32.SPI_CR1_BR_Pos +} + //---------- UART related code // Configure the UART. diff --git a/src/machine/spi.go b/src/machine/spi.go index 398c5c16..0541f5b5 100644 --- a/src/machine/spi.go +++ b/src/machine/spi.go @@ -1,5 +1,5 @@ -//go:build !baremetal || (stm32 && !stm32f7x2 && !stm32l5x2 && !stm32wle5) || fe310 || k210 || atmega -// +build !baremetal stm32,!stm32f7x2,!stm32l5x2,!stm32wle5 fe310 k210 atmega +//go:build !baremetal || (stm32 && !stm32f7x2 && !stm32l5x2) || fe310 || k210 || atmega +// +build !baremetal stm32,!stm32f7x2,!stm32l5x2 fe310 k210 atmega package machine diff --git a/src/runtime/runtime_stm32wle5.go b/src/runtime/runtime_stm32wle5.go index 598faff8..7a431f64 100644 --- a/src/runtime/runtime_stm32wle5.go +++ b/src/runtime/runtime_stm32wle5.go @@ -4,21 +4,100 @@ package runtime import ( + "device/stm32" "machine" ) type arrtype = uint32 +const ( + /* PLL Options RMN0461.p247 */ + PLL_M = 2 + PLL_N = 6 + PLL_R = 2 + PLL_P = 2 + PLL_Q = 2 +) + func init() { - // Currently the clock is not configured, which means - // the MCU runs at default reset clock speed (4Mhz). - // Code to initialize RCC and PLL can go here. + + // Configure 48Mhz clock + initCLK() // UART init machine.Serial.Configure(machine.UARTConfig{}) // Timers init initTickTimer(&machine.TIM1) + +} + +func initCLK() { + + // Enable HSE32 VDDTCXO output on package pin PB0-VDDTCXO + stm32.RCC.CR.ReplaceBits(stm32.RCC_CR_HSEBYPPWR_VDDTCXO, stm32.RCC_CR_HSEBYPPWR_Msk, 0) + + // SYSCLOCK from HSE32 clock division factor (SYSCLOCK=HSE32) + stm32.RCC.CR.ReplaceBits(stm32.RCC_CR_HSEPRE_Div1, stm32.RCC_CR_HSEPRE_Msk, 0) + + // enable external Clock HSE32 TXCO (RM0461p226) + stm32.RCC.CR.SetBits(stm32.RCC_CR_HSEBYPPWR) + stm32.RCC.CR.SetBits(stm32.RCC_CR_HSEON) + for !stm32.RCC.CR.HasBits(stm32.RCC_CR_HSERDY) { + } + + // Disable PLL + stm32.RCC.CR.ClearBits(stm32.RCC_CR_PLLON) + for stm32.RCC.CR.HasBits(stm32.RCC_CR_PLLRDY) { + } + + // Configure the PLL + stm32.RCC.PLLCFGR.Set((PLL_M-1)<