Skip to content

Instantly share code, notes, and snippets.

@paulc
Created October 12, 2020 20:17
Show Gist options
  • Save paulc/10a9e618fbb5b185324f01e41afbf34a to your computer and use it in GitHub Desktop.
Save paulc/10a9e618fbb5b185324f01e41afbf34a to your computer and use it in GitHub Desktop.
WeAct stm32f411
diff --git a/src/machine/board_weact_stm32f411.go b/src/machine/board_weact_stm32f411.go
new file mode 100644
index 0000000..bdb1e97
--- /dev/null
+++ b/src/machine/board_weact_stm32f411.go
@@ -0,0 +1,49 @@
+// +build weact_stm32f411
+
+package machine
+
+import (
+ "device/stm32"
+)
+
+const (
+ LED = PC13
+)
+
+// UART pins
+const (
+ UART_TX_PIN = PA2
+ UART_RX_PIN = PA3
+)
+
+var (
+ UART0 = UART{
+ Buffer: NewRingBuffer(),
+ Bus: stm32.USART2,
+ }
+ UART1 = &UART0
+)
+
+// set up RX IRQ handler. Follow similar pattern for other UARTx instances
+func init() {
+ // UART0.Interrupt = interrupt.New(stm32.IRQ_USART2, UART0.handleInterrupt)
+}
+
+// SPI pins
+const (
+ 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
+)
+
+// Since the first interface is named SPI1, both SPI0 and SPI1 refer to SPI1.
+// TODO: implement SPI2 and SPI3.
+var (
+ SPI0 = SPI{
+ Bus: stm32.SPI1,
+ }
+ SPI1 = &SPI0
+)
diff --git a/src/machine/i2c.go b/src/machine/i2c.go
index b465060..3ab7b0d 100644
--- a/src/machine/i2c.go
+++ b/src/machine/i2c.go
@@ -1,4 +1,4 @@
-// +build avr nrf sam stm32,!stm32f4 fe310 k210
+// +build avr nrf sam stm32,!stm32f4,!stm32f411 fe310 k210
package machine
diff --git a/src/machine/machine_stm32f411.go b/src/machine/machine_stm32f411.go
new file mode 100644
index 0000000..8ba69b4
--- /dev/null
+++ b/src/machine/machine_stm32f411.go
@@ -0,0 +1,240 @@
+// +build stm32f411
+
+package machine
+
+// Peripheral abstraction layer for the stm32f411
+
+import (
+ "device/stm32"
+ "runtime/interrupt"
+ "unsafe"
+)
+
+func CPUFrequency() uint32 {
+ return 100000000
+}
+
+// --- From machine_stm32f4.go
+
+const (
+ PA0 = portA + 0
+ PA1 = portA + 1
+ PA2 = portA + 2
+ PA3 = portA + 3
+ PA4 = portA + 4
+ PA5 = portA + 5
+ PA6 = portA + 6
+ PA7 = portA + 7
+ PA8 = portA + 8
+ PA9 = portA + 9
+ PA10 = portA + 10
+ PA11 = portA + 11
+ PA12 = portA + 12
+ PA13 = portA + 13
+ PA14 = portA + 14
+ PA15 = portA + 15
+
+ PB0 = portB + 0
+ PB1 = portB + 1
+ PB2 = portB + 2
+ PB3 = portB + 3
+ PB4 = portB + 4
+ PB5 = portB + 5
+ PB6 = portB + 6
+ PB7 = portB + 7
+ PB8 = portB + 8
+ PB9 = portB + 9
+ PB10 = portB + 10
+ PB11 = portB + 11
+ PB12 = portB + 12
+ PB13 = portB + 13
+ PB14 = portB + 14
+ PB15 = portB + 15
+
+ PC0 = portC + 0
+ PC1 = portC + 1
+ PC2 = portC + 2
+ PC3 = portC + 3
+ PC4 = portC + 4
+ PC5 = portC + 5
+ PC6 = portC + 6
+ PC7 = portC + 7
+ PC8 = portC + 8
+ PC9 = portC + 9
+ PC10 = portC + 10
+ PC11 = portC + 11
+ PC12 = portC + 12
+ PC13 = portC + 13
+ PC14 = portC + 14
+ PC15 = portC + 15
+)
+
+func (p Pin) getPort() *stm32.GPIO_Type {
+ switch p / 16 {
+ case 0:
+ return stm32.GPIOA
+ case 1:
+ return stm32.GPIOB
+ case 2:
+ return stm32.GPIOC
+ case 3:
+ return stm32.GPIOD
+ default:
+ panic("machine: unknown port")
+ }
+}
+
+// enableClock enables the clock for this desired GPIO port.
+func (p Pin) enableClock() {
+ switch p / 16 {
+ case 0:
+ stm32.RCC.AHB1ENR.SetBits(stm32.RCC_AHB1ENR_GPIOAEN)
+ case 1:
+ stm32.RCC.AHB1ENR.SetBits(stm32.RCC_AHB1ENR_GPIOBEN)
+ case 2:
+ stm32.RCC.AHB1ENR.SetBits(stm32.RCC_AHB1ENR_GPIOCEN)
+ case 3:
+ stm32.RCC.AHB1ENR.SetBits(stm32.RCC_AHB1ENR_GPIODEN)
+ default:
+ panic("machine: unknown port")
+ }
+}
+
+// Enable peripheral clock
+func enableAltFuncClock(bus unsafe.Pointer) {
+ switch bus {
+ case unsafe.Pointer(stm32.PWR): // Power interface clock enable
+ stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_PWREN)
+ case unsafe.Pointer(stm32.I2C3): // I2C3 clock enable
+ stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_I2C3EN)
+ case unsafe.Pointer(stm32.I2C2): // I2C2 clock enable
+ stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_I2C2EN)
+ case unsafe.Pointer(stm32.I2C1): // I2C1 clock enable
+ stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_I2C1EN)
+ case unsafe.Pointer(stm32.USART2): // USART2 clock enable
+ stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_USART2EN)
+ case unsafe.Pointer(stm32.SPI3): // SPI3 clock enable
+ stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_SPI3EN)
+ case unsafe.Pointer(stm32.SPI2): // SPI2 clock enable
+ stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_SPI2EN)
+ case unsafe.Pointer(stm32.WWDG): // Window watchdog clock enable
+ stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_WWDGEN)
+ case unsafe.Pointer(stm32.TIM5): // TIM5 clock enable
+ stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_TIM5EN)
+ case unsafe.Pointer(stm32.TIM4): // TIM4 clock enable
+ stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_TIM4EN)
+ case unsafe.Pointer(stm32.TIM3): // TIM3 clock enable
+ stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_TIM3EN)
+ case unsafe.Pointer(stm32.TIM2): // TIM2 clock enable
+ stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_TIM2EN)
+ case unsafe.Pointer(stm32.TIM11): // TIM11 clock enable
+ stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_TIM11EN)
+ case unsafe.Pointer(stm32.TIM10): // TIM10 clock enable
+ stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_TIM10EN)
+ case unsafe.Pointer(stm32.TIM9): // TIM9 clock enable
+ stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_TIM9EN)
+ case unsafe.Pointer(stm32.SYSCFG): // System configuration controller clock enable
+ stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_SYSCFGEN)
+ case unsafe.Pointer(stm32.SPI1): // SPI1 clock enable
+ stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_SPI1EN)
+ case unsafe.Pointer(stm32.SDIO): // SDIO clock enable
+ stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_SDIOEN)
+ case unsafe.Pointer(stm32.ADC1): // ADC1 clock enable
+ stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_ADC1EN)
+ case unsafe.Pointer(stm32.USART6): // USART6 clock enable
+ stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_USART6EN)
+ case unsafe.Pointer(stm32.USART1): // USART1 clock enable
+ stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_USART1EN)
+ case unsafe.Pointer(stm32.TIM1): // TIM1 clock enable
+ stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_TIM1EN)
+ }
+}
+
+//---------- UART related types and code
+
+// UART representation
+type UART struct {
+ Buffer *RingBuffer
+ Bus *stm32.USART_Type
+ Interrupt interrupt.Interrupt
+ AltFuncSelector stm32.AltFunc
+}
+
+// Configure the UART.
+func (uart UART) configurePins(config UARTConfig) {
+ // enable the alternate functions on the TX and RX pins
+ config.TX.ConfigureAltFunc(PinConfig{Mode: PinModeUARTTX}, uart.AltFuncSelector)
+ config.RX.ConfigureAltFunc(PinConfig{Mode: PinModeUARTRX}, uart.AltFuncSelector)
+}
+
+// UART baudrate calc based on the bus and clockspeed
+// NOTE: keep this in sync with the runtime/runtime_stm32f407.go clock init code
+func (uart UART) getBaudRateDivisor(baudRate uint32) uint32 {
+ var clock uint32
+ switch uart.Bus {
+ case stm32.USART1:
+ clock = CPUFrequency() / 2 // APB2 Frequency
+ case stm32.USART2:
+ clock = CPUFrequency() / 4 // APB1 Frequency
+ }
+ return clock / baudRate
+}
+
+//---------- SPI related types and code
+
+// SPI on the STM32Fxxx using MODER / alternate function pins
+type SPI struct {
+ Bus *stm32.SPI_Type
+ AltFuncSelector stm32.AltFunc
+}
+
+// Set baud rate for SPI
+func (spi SPI) getBaudRate(config SPIConfig) uint32 {
+ var conf uint32
+
+ localFrequency := config.Frequency
+ if spi.Bus != stm32.SPI1 {
+ // Assume it's SPI2 or SPI3 on APB1 at 1/2 the clock frequency of APB2, so
+ // we want to pretend to request 2x the baudrate asked for
+ localFrequency = localFrequency * 2
+ }
+
+ // set frequency dependent on PCLK prescaler. Since these are rather weird
+ // speeds due to the CPU freqency, pick a range up to that frquency for
+ // clients to use more human-understandable numbers, e.g. nearest 100KHz
+
+ // These are based on APB2 clock frquency (84MHz on the discovery board)
+ // TODO: also include the MCU/APB clock setting in the equation
+ switch true {
+ case localFrequency < 328125:
+ conf = stm32.SPI_PCLK_256
+ case localFrequency < 656250:
+ conf = stm32.SPI_PCLK_128
+ case localFrequency < 1312500:
+ conf = stm32.SPI_PCLK_64
+ case localFrequency < 2625000:
+ conf = stm32.SPI_PCLK_32
+ case localFrequency < 5250000:
+ conf = stm32.SPI_PCLK_16
+ case localFrequency < 10500000:
+ conf = stm32.SPI_PCLK_8
+ // NOTE: many SPI components won't operate reliably (or at all) above 10MHz
+ // Check the datasheet of the part
+ case localFrequency < 21000000:
+ conf = stm32.SPI_PCLK_4
+ case localFrequency < 42000000:
+ conf = stm32.SPI_PCLK_2
+ default:
+ // None of the specific baudrates were selected; choose the lowest speed
+ conf = stm32.SPI_PCLK_256
+ }
+
+ return conf << stm32.SPI_CR1_BR_Pos
+}
+
+// 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)
+}
diff --git a/src/runtime/runtime_stm32f411.go b/src/runtime/runtime_stm32f411.go
new file mode 100644
index 0000000..073f13b
--- /dev/null
+++ b/src/runtime/runtime_stm32f411.go
@@ -0,0 +1,219 @@
+// +build stm32,stm32f411
+
+package runtime
+
+import (
+ "device/arm"
+ "device/stm32"
+ "machine"
+ "runtime/interrupt"
+ "runtime/volatile"
+)
+
+func init() {
+ initCLK()
+ initTIM3()
+ machine.UART0.Configure(machine.UARTConfig{})
+ // XXX Disable for stm32f411
+ // initTIM7()
+}
+
+func putchar(c byte) {
+ machine.UART0.WriteByte(c)
+}
+
+const (
+ HSE_STARTUP_TIMEOUT = 0x0500
+ /* PLL Options - See RM0090 Reference Manual pg. 95 */
+ PLL_M = 8 /* PLL_VCO = (HSE_VALUE or HSI_VLAUE / PLL_M) * PLL_N */
+ PLL_N = 336
+ PLL_P = 2 /* SYSCLK = PLL_VCO / PLL_P */
+ PLL_Q = 7 /* USB OTS FS, SDIO and RNG Clock = PLL_VCO / PLL_Q */
+)
+
+/*
+ clock settings
+ +-------------+--------+
+ | HSE | 8mhz |
+ | SYSCLK | 168mhz |
+ | HCLK | 168mhz |
+ | APB2(PCLK2) | 84mhz |
+ | APB1(PCLK1) | 42mhz |
+ +-------------+--------+
+*/
+func initCLK() {
+
+ // Reset clock registers
+ // Set HSION
+ stm32.RCC.CR.SetBits(stm32.RCC_CR_HSION)
+ for !stm32.RCC.CR.HasBits(stm32.RCC_CR_HSIRDY) {
+ }
+
+ // Reset CFGR
+ stm32.RCC.CFGR.Set(0x00000000)
+ // Reset HSEON, CSSON and PLLON
+ stm32.RCC.CR.ClearBits(stm32.RCC_CR_HSEON | stm32.RCC_CR_CSSON | stm32.RCC_CR_PLLON)
+ // Reset PLLCFGR
+ stm32.RCC.PLLCFGR.Set(0x24003010)
+ // Reset HSEBYP
+ stm32.RCC.CR.ClearBits(stm32.RCC_CR_HSEBYP)
+ // Disable all interrupts
+ stm32.RCC.CIR.Set(0x00000000)
+
+ // Set up the clock
+ var startupCounter uint32 = 0
+
+ // Enable HSE
+ stm32.RCC.CR.Set(stm32.RCC_CR_HSEON)
+
+ // Wait till HSE is ready and if timeout is reached exit
+ for {
+ startupCounter++
+ if stm32.RCC.CR.HasBits(stm32.RCC_CR_HSERDY) || (startupCounter == HSE_STARTUP_TIMEOUT) {
+ break
+ }
+ }
+ if stm32.RCC.CR.HasBits(stm32.RCC_CR_HSERDY) {
+ // Enable high performance mode, System frequency up to 168MHz
+ stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_PWREN)
+ stm32.PWR.CR.SetBits(0x4000) // PWR_CR_VOS
+ // HCLK = SYSCLK / 1
+ stm32.RCC.CFGR.SetBits(0x0 << stm32.RCC_CFGR_HPRE_Pos)
+ // PCLK2 = HCLK / 2
+ stm32.RCC.CFGR.SetBits(0x4 << stm32.RCC_CFGR_PPRE2_Pos)
+ // PCLK1 = HCLK / 4
+ stm32.RCC.CFGR.SetBits(0x5 << stm32.RCC_CFGR_PPRE1_Pos)
+ // Configure the main PLL
+ // PLL Options - See RM0090 Reference Manual pg. 95
+ stm32.RCC.PLLCFGR.Set(PLL_M | (PLL_N << 6) | (((PLL_P >> 1) - 1) << 16) |
+ (1 << stm32.RCC_PLLCFGR_PLLSRC_Pos) | (PLL_Q << 24))
+ // Enable main PLL
+ stm32.RCC.CR.SetBits(stm32.RCC_CR_PLLON)
+ // Wait till the main PLL is ready
+ for (stm32.RCC.CR.Get() & stm32.RCC_CR_PLLRDY) == 0 {
+ }
+ // Configure Flash prefetch, Instruction cache, Data cache and wait state
+ stm32.FLASH.ACR.Set(stm32.FLASH_ACR_ICEN | stm32.FLASH_ACR_DCEN | (5 << stm32.FLASH_ACR_LATENCY_Pos))
+ // Select the main PLL as system clock source
+ stm32.RCC.CFGR.ClearBits(stm32.RCC_CFGR_SW0 | stm32.RCC_CFGR_SW1)
+ stm32.RCC.CFGR.SetBits(0x2 << stm32.RCC_CFGR_SW0_Pos)
+ for (stm32.RCC.CFGR.Get() & (0x3 << stm32.RCC_CFGR_SWS0_Pos)) != (0x2 << stm32.RCC_CFGR_SWS0_Pos) {
+ }
+
+ } else {
+ // If HSE failed to start up, the application will have wrong clock configuration
+ for {
+ }
+ }
+ // Enable the CCM RAM clock
+ stm32.RCC.AHB1ENR.SetBits(1 << 20)
+
+}
+
+var (
+ // tick in milliseconds
+ tickCount timeUnit
+)
+
+var timerWakeup volatile.Register8
+
+func ticksToNanoseconds(ticks timeUnit) int64 {
+ return int64(ticks) * 1000
+}
+
+func nanosecondsToTicks(ns int64) timeUnit {
+ return timeUnit(ns / 1000)
+}
+
+// Enable the TIM3 clock.(sleep count)
+func initTIM3() {
+ stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_TIM3EN)
+
+ intr := interrupt.New(stm32.IRQ_TIM3, handleTIM3)
+ intr.SetPriority(0xc3)
+ intr.Enable()
+}
+
+// XXX Disable for smt32f411 (no TIM7)
+//
+// // Enable the TIM7 clock.(tick count)
+// func initTIM7() {
+// stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_TIM7EN)
+//
+// // CK_INT = APB1 x2 = 84mhz
+// stm32.TIM7.PSC.Set(84000000/10000 - 1) // 84mhz to 10khz(0.1ms)
+// stm32.TIM7.ARR.Set(10 - 1) // interrupt per 1ms
+//
+// // Enable the hardware interrupt.
+// stm32.TIM7.DIER.SetBits(stm32.TIM_DIER_UIE)
+//
+// // Enable the timer.
+// stm32.TIM7.CR1.SetBits(stm32.TIM_CR1_CEN)
+//
+// intr := interrupt.New(stm32.IRQ_TIM7, handleTIM7)
+// intr.SetPriority(0xc1)
+// intr.Enable()
+// }
+
+const asyncScheduler = false
+
+// sleepTicks should sleep for specific number of microseconds.
+func sleepTicks(d timeUnit) {
+ timerSleep(uint32(d))
+}
+
+// number of ticks (microseconds) since start.
+func ticks() timeUnit {
+ // milliseconds to microseconds
+ return tickCount * 1000
+}
+
+// ticks are in microseconds
+func timerSleep(ticks uint32) {
+ timerWakeup.Set(0)
+
+ // CK_INT = APB1 x2 = 84mhz
+ // prescale counter down from 84mhz to 10khz aka 0.1 ms frequency.
+ stm32.TIM3.PSC.Set(84000000/10000 - 1) // 8399
+
+ // set duty aka duration
+ arr := (ticks / 100) - 1 // convert from microseconds to 0.1 ms
+ if arr == 0 {
+ arr = 1 // avoid blocking
+ }
+ stm32.TIM3.ARR.Set(arr)
+
+ // Enable the hardware interrupt.
+ stm32.TIM3.DIER.SetBits(stm32.TIM_DIER_UIE)
+
+ // Enable the timer.
+ stm32.TIM3.CR1.SetBits(stm32.TIM_CR1_CEN)
+
+ // wait till timer wakes up
+ for timerWakeup.Get() == 0 {
+ arm.Asm("wfi")
+ }
+}
+
+func handleTIM3(interrupt.Interrupt) {
+ if stm32.TIM3.SR.HasBits(stm32.TIM_SR_UIF) {
+ // Disable the timer.
+ stm32.TIM3.CR1.ClearBits(stm32.TIM_CR1_CEN)
+
+ // clear the update flag
+ stm32.TIM3.SR.ClearBits(stm32.TIM_SR_UIF)
+
+ // timer was triggered
+ timerWakeup.Set(1)
+ }
+}
+
+// XXX Disable for stm32f411
+//
+// func handleTIM7(interrupt.Interrupt) {
+// if stm32.TIM7.SR.HasBits(stm32.TIM_SR_UIF) {
+// // clear the update flag
+// stm32.TIM7.SR.ClearBits(stm32.TIM_SR_UIF)
+// tickCount++
+// }
+// }
diff --git a/targets/stm32f411.ld b/targets/stm32f411.ld
new file mode 100644
index 0000000..664cc8b
--- /dev/null
+++ b/targets/stm32f411.ld
@@ -0,0 +1,10 @@
+
+MEMORY
+{
+ FLASH_TEXT (rw) : ORIGIN = 0x08000000, LENGTH = 512K
+ RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 128K
+}
+
+_stack_size = 4K;
+
+INCLUDE "targets/arm.ld"
diff --git a/targets/weact_stm32f411.json b/targets/weact_stm32f411.json
new file mode 100644
index 0000000..cc7215c
--- /dev/null
+++ b/targets/weact_stm32f411.json
@@ -0,0 +1,13 @@
+{
+ "inherits": ["cortex-m4"],
+ "build-tags": ["weact_stm32f411", "stm32f411", "stm32"],
+ "cflags": [
+ "-Qunused-arguments"
+ ],
+ "linkerscript": "targets/stm32f411.ld",
+ "extra-files": [
+ "src/device/stm32/stm32f411.s"
+ ],
+ "flash-method": "command",
+ "flash-command": "dfu-util --alt 0 --dfuse-address 0x08000000 --download {bin}"
+}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment