From cc2768ef762caaafa83273a9924eea335c4967e0 Mon Sep 17 00:00:00 2001 From: akif999 Date: Sun, 16 Aug 2026 00:58:09 +0900 Subject: [PATCH] feat(machine/stm32): add STM32F401 and NUCLEO-F401RE support --- GNUmakefile | 2 + src/crypto/rand/rand_baremetal.go | 2 +- src/machine/board_nucleof401re.go | 119 +++++++++ src/machine/machine_stm32_adc_f4.go | 2 +- src/machine/machine_stm32_otgfs_usb.go | 2 +- src/machine/machine_stm32_rng.go | 2 +- src/machine/machine_stm32f4.go | 271 -------------------- src/machine/machine_stm32f401.go | 17 ++ src/machine/machine_stm32f401_adc.go | 87 +++++++ src/machine/machine_stm32f401_periph.go | 144 +++++++++++ src/machine/machine_stm32f4_extended.go | 286 ++++++++++++++++++++++ src/machine/machine_stm32f4_otgfs_vbus.go | 2 +- src/machine/machine_stm32f4_pll_84mhz.go | 19 ++ src/machine/usb.go | 2 +- src/runtime/rand_hwrng.go | 2 +- src/runtime/rand_norng.go | 2 +- src/runtime/runtime_stm32f401.go | 126 ++++++++++ targets/nucleo-f401re.json | 13 + targets/stm32f401.ld | 9 + 19 files changed, 830 insertions(+), 279 deletions(-) create mode 100644 src/machine/board_nucleof401re.go create mode 100644 src/machine/machine_stm32f401.go create mode 100644 src/machine/machine_stm32f401_adc.go create mode 100644 src/machine/machine_stm32f401_periph.go create mode 100644 src/machine/machine_stm32f4_extended.go create mode 100644 src/machine/machine_stm32f4_pll_84mhz.go create mode 100644 src/runtime/runtime_stm32f401.go create mode 100644 targets/nucleo-f401re.json create mode 100644 targets/stm32f401.ld diff --git a/GNUmakefile b/GNUmakefile index 627640a520..e1bd6c37d1 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -896,6 +896,8 @@ ifneq ($(STM32), 0) @$(MD5SUM) test.hex $(TINYGO) build -size short -o test.hex -target=nucleo-f103rb examples/blinky1 @$(MD5SUM) test.hex + $(TINYGO) build -size short -o test.hex -target=nucleo-f401re examples/blinky1 + @$(MD5SUM) test.hex $(TINYGO) build -size short -o test.hex -target=nucleo-f722ze examples/blinky1 @$(MD5SUM) test.hex $(TINYGO) build -size short -o test.hex -target=nucleo-h753zi examples/blinky1 diff --git a/src/crypto/rand/rand_baremetal.go b/src/crypto/rand/rand_baremetal.go index 6cf847ae64..82b43ad7ed 100644 --- a/src/crypto/rand/rand_baremetal.go +++ b/src/crypto/rand/rand_baremetal.go @@ -1,4 +1,4 @@ -//go:build nrf || (stm32 && !(stm32f103 || stm32l0x1 || stm32u0)) || (sam && atsamd51) || (sam && atsame5x) || esp32c3 || esp32s3 || tkey || (tinygo.riscv32 && virt) +//go:build nrf || (stm32 && !(stm32f103 || stm32l0x1 || stm32u0 || stm32f401)) || (sam && atsamd51) || (sam && atsame5x) || esp32c3 || esp32s3 || tkey || (tinygo.riscv32 && virt) // If you update the above build constraint, you'll probably also need to update // src/runtime/rand_hwrng.go. diff --git a/src/machine/board_nucleof401re.go b/src/machine/board_nucleof401re.go new file mode 100644 index 0000000000..28d26f87fa --- /dev/null +++ b/src/machine/board_nucleof401re.go @@ -0,0 +1,119 @@ +//go:build nucleof401re + +// Schematic: https://www.st.com/resource/en/user_manual/um1724-stm32-nucleo64-boards-mb1136-stmicroelectronics.pdf +// Datasheet: https://www.st.com/resource/en/datasheet/stm32f401re.pdf + +package machine + +import ( + "device/stm32" + "runtime/interrupt" +) + +const xtalHz = 8_000_000 + +const ( + // Arduino Pins + A0 = PA0 + A1 = PA1 + A2 = PA4 + A3 = PB0 + A4 = PC1 + A5 = PC0 + + D0 = PA3 + D1 = PA2 + D2 = PA10 + D3 = PB3 + D4 = PB5 + D5 = PB4 + D6 = PB10 + D7 = PA8 + D8 = PA9 + D9 = PC7 + D10 = PB6 + D11 = PA7 + D12 = PA6 + D13 = PA5 + D14 = PB9 + D15 = PB8 +) + +// User LD2: the green LED is a user LED connected to Arduino signal D13 +// corresponding to STM32 I/O PA5. +const ( + LED = LED_BUILTIN + LED_BUILTIN = LED_GREEN + LED_GREEN = PA5 +) + +// BUTTON is the user button B1 connected to PC13 (active low). +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 + UART1_TX_PIN = UART_TX_PIN + UART1_RX_PIN = UART_RX_PIN + + // USART1 on Arduino D8 (PA9 = TX) / D2 (PA10 = RX). + // Use for external UART communication (e.g. with another board). + UART2_TX_PIN = PA9 + UART2_RX_PIN = PA10 + + // I2C pins + // PB8 / Arduino D15 is SCL, PB9 / Arduino D14 is SDA. + 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, exposed as a 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 + + // USART1 on PA9 (TX=D8) / PA10 (RX=D2). Use for external communication. + UART2 = &_UART2 + _UART2 = UART{ + Buffer: NewRingBuffer(), + Bus: stm32.USART1, + TxAltFuncSelector: AF7_USART1_2_3, + RxAltFuncSelector: AF7_USART1_2_3, + } + + // I2C1 is documented; I2C0 is an alias. + I2C1 = &I2C{ + Bus: stm32.I2C1, + AltFuncSelector: AF4_I2C1_2_3, + } + I2C0 = I2C1 + + // SPI1 is documented; SPI0 is an alias. + SPI1 = &SPI{ + Bus: stm32.SPI1, + AltFuncSelector: AF5_SPI1_SPI2, + } + SPI0 = SPI1 +) + +func init() { + UART1.Interrupt = interrupt.New(stm32.IRQ_USART2, _UART1.handleInterrupt) + UART2.Interrupt = interrupt.New(stm32.IRQ_USART1, _UART2.handleInterrupt) +} diff --git a/src/machine/machine_stm32_adc_f4.go b/src/machine/machine_stm32_adc_f4.go index df4984c1b4..92bc1c7a8a 100644 --- a/src/machine/machine_stm32_adc_f4.go +++ b/src/machine/machine_stm32_adc_f4.go @@ -1,4 +1,4 @@ -//go:build stm32f4 +//go:build stm32f4 && !stm32f401 package machine diff --git a/src/machine/machine_stm32_otgfs_usb.go b/src/machine/machine_stm32_otgfs_usb.go index 47f70936d5..7107c72905 100644 --- a/src/machine/machine_stm32_otgfs_usb.go +++ b/src/machine/machine_stm32_otgfs_usb.go @@ -1,4 +1,4 @@ -//go:build stm32f4 || stm32f7 +//go:build (stm32f4 && !stm32f401) || stm32f7 package machine diff --git a/src/machine/machine_stm32_rng.go b/src/machine/machine_stm32_rng.go index 5b05d2daa7..318f5dc1ad 100644 --- a/src/machine/machine_stm32_rng.go +++ b/src/machine/machine_stm32_rng.go @@ -1,4 +1,4 @@ -//go:build stm32 && !(stm32f103 || stm32l0x1 || stm32g0 || stm32u0) +//go:build stm32 && !(stm32f103 || stm32l0x1 || stm32g0 || stm32u0 || stm32f401) package machine diff --git a/src/machine/machine_stm32f4.go b/src/machine/machine_stm32f4.go index 2a989b10a8..2ab4ae5ded 100644 --- a/src/machine/machine_stm32f4.go +++ b/src/machine/machine_stm32f4.go @@ -188,35 +188,6 @@ const ( PK15 = portK + 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 - case 4: - return stm32.GPIOE - case 5: - return stm32.GPIOF - case 6: - return stm32.GPIOG - case 7: - return stm32.GPIOH - case 8: - return stm32.GPIOI - case 9: - return stm32.GPIOJ - case 10: - return stm32.GPIOK - default: - panic("machine: unknown port") - } -} - // enableClock enables the clock for this desired GPIO port. func (p Pin) enableClock() { bit := p / 16 @@ -268,84 +239,6 @@ func (p Pin) registerInterrupt() interrupt.Interrupt { return interrupt.Interrupt{} } -// Enable peripheral clock -func enableAltFuncClock(bus unsafe.Pointer) { - switch bus { - case unsafe.Pointer(stm32.DAC): // DAC interface clock enable - stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_DACEN) - case unsafe.Pointer(stm32.PWR): // Power interface clock enable - stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_PWREN) - case unsafe.Pointer(stm32.CAN2): // CAN 2 clock enable - stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_CAN2EN) - case unsafe.Pointer(stm32.CAN1): // CAN 1 clock enable - stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_CAN1EN) - 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.UART5): // UART5 clock enable - stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_UART5EN) - case unsafe.Pointer(stm32.UART4): // UART4 clock enable - stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_UART4EN) - case unsafe.Pointer(stm32.USART3): // USART3 clock enable - stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_USART3EN) - 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.TIM14): // TIM14 clock enable - stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_TIM14EN) - case unsafe.Pointer(stm32.TIM13): // TIM13 clock enable - stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_TIM13EN) - case unsafe.Pointer(stm32.TIM12): // TIM12 clock enable - stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_TIM12EN) - case unsafe.Pointer(stm32.TIM7): // TIM7 clock enable - stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_TIM7EN) - case unsafe.Pointer(stm32.TIM6): // TIM6 clock enable - stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_TIM6EN) - 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.ADC3): // ADC3 clock enable - stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_ADC3EN) - case unsafe.Pointer(stm32.ADC2): // ADC2 clock enable - stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_ADC2EN) - 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.TIM8): // TIM8 clock enable - stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_TIM8EN) - case unsafe.Pointer(stm32.TIM1): // TIM1 clock enable - stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_TIM1EN) - } -} - //---------- Timer related code var ( @@ -414,45 +307,6 @@ var ( busFreq: APB1_TIM_FREQ, } - TIM6 = TIM{ - EnableRegister: &stm32.RCC.APB1ENR, - EnableFlag: stm32.RCC_APB1ENR_TIM6EN, - Device: stm32.TIM6, - Channels: [4]TimerChannel{ - TimerChannel{Pins: []PinFunction{}}, - TimerChannel{Pins: []PinFunction{}}, - TimerChannel{Pins: []PinFunction{}}, - TimerChannel{Pins: []PinFunction{}}, - }, - busFreq: APB1_TIM_FREQ, - } - - TIM7 = TIM{ - EnableRegister: &stm32.RCC.APB1ENR, - EnableFlag: stm32.RCC_APB1ENR_TIM7EN, - Device: stm32.TIM7, - Channels: [4]TimerChannel{ - TimerChannel{Pins: []PinFunction{}}, - TimerChannel{Pins: []PinFunction{}}, - TimerChannel{Pins: []PinFunction{}}, - TimerChannel{Pins: []PinFunction{}}, - }, - busFreq: APB1_TIM_FREQ, - } - - TIM8 = TIM{ - EnableRegister: &stm32.RCC.APB2ENR, - EnableFlag: stm32.RCC_APB2ENR_TIM8EN, - Device: stm32.TIM8, - Channels: [4]TimerChannel{ - TimerChannel{Pins: []PinFunction{{PC6, AF3_TIM8_9_10_11}, {PI5, AF3_TIM8_9_10_11}}}, - TimerChannel{Pins: []PinFunction{{PC7, AF3_TIM8_9_10_11}, {PI6, AF3_TIM8_9_10_11}}}, - TimerChannel{Pins: []PinFunction{{PC8, AF3_TIM8_9_10_11}, {PI7, AF3_TIM8_9_10_11}}}, - TimerChannel{Pins: []PinFunction{{PC9, AF3_TIM8_9_10_11}, {PI2, AF3_TIM8_9_10_11}}}, - }, - busFreq: APB2_TIM_FREQ, - } - TIM9 = TIM{ EnableRegister: &stm32.RCC.APB2ENR, EnableFlag: stm32.RCC_APB2ENR_TIM9EN, @@ -491,117 +345,8 @@ var ( }, busFreq: APB2_TIM_FREQ, } - - TIM12 = TIM{ - EnableRegister: &stm32.RCC.APB1ENR, - EnableFlag: stm32.RCC_APB1ENR_TIM12EN, - Device: stm32.TIM12, - Channels: [4]TimerChannel{ - TimerChannel{Pins: []PinFunction{{PB14, AF9_CAN1_CAN2_TIM12_13_14}, {PH6, AF9_CAN1_CAN2_TIM12_13_14}}}, - TimerChannel{Pins: []PinFunction{{PB15, AF9_CAN1_CAN2_TIM12_13_14}, {PH9, AF9_CAN1_CAN2_TIM12_13_14}}}, - TimerChannel{Pins: []PinFunction{}}, - TimerChannel{Pins: []PinFunction{}}, - }, - busFreq: APB1_TIM_FREQ, - } - - TIM13 = TIM{ - EnableRegister: &stm32.RCC.APB1ENR, - EnableFlag: stm32.RCC_APB1ENR_TIM13EN, - Device: stm32.TIM13, - Channels: [4]TimerChannel{ - TimerChannel{Pins: []PinFunction{{PA6, AF9_CAN1_CAN2_TIM12_13_14}, {PF8, AF9_CAN1_CAN2_TIM12_13_14}}}, - TimerChannel{Pins: []PinFunction{}}, - TimerChannel{Pins: []PinFunction{}}, - TimerChannel{Pins: []PinFunction{}}, - }, - busFreq: APB1_TIM_FREQ, - } - - TIM14 = TIM{ - EnableRegister: &stm32.RCC.APB1ENR, - EnableFlag: stm32.RCC_APB1ENR_TIM14EN, - Device: stm32.TIM14, - Channels: [4]TimerChannel{ - TimerChannel{Pins: []PinFunction{{PA7, AF9_CAN1_CAN2_TIM12_13_14}, {PF9, AF9_CAN1_CAN2_TIM12_13_14}}}, - TimerChannel{Pins: []PinFunction{}}, - TimerChannel{Pins: []PinFunction{}}, - TimerChannel{Pins: []PinFunction{}}, - }, - busFreq: APB1_TIM_FREQ, - } ) -func (t *TIM) registerUPInterrupt() interrupt.Interrupt { - switch t { - case &TIM1: - return interrupt.New(stm32.IRQ_TIM1_UP_TIM10, TIM1.handleUPInterrupt) - case &TIM2: - return interrupt.New(stm32.IRQ_TIM2, TIM2.handleUPInterrupt) - case &TIM3: - return interrupt.New(stm32.IRQ_TIM3, TIM3.handleUPInterrupt) - case &TIM4: - return interrupt.New(stm32.IRQ_TIM4, TIM4.handleUPInterrupt) - case &TIM5: - return interrupt.New(stm32.IRQ_TIM5, TIM5.handleUPInterrupt) - case &TIM6: - return interrupt.New(stm32.IRQ_TIM6_DAC, TIM6.handleUPInterrupt) - case &TIM7: - return interrupt.New(stm32.IRQ_TIM7, TIM7.handleUPInterrupt) - case &TIM8: - return interrupt.New(stm32.IRQ_TIM8_UP_TIM13, TIM8.handleUPInterrupt) - case &TIM9: - return interrupt.New(stm32.IRQ_TIM1_BRK_TIM9, TIM9.handleUPInterrupt) - case &TIM10: - return interrupt.New(stm32.IRQ_TIM1_UP_TIM10, TIM10.handleUPInterrupt) - case &TIM11: - return interrupt.New(stm32.IRQ_TIM1_TRG_COM_TIM11, TIM11.handleUPInterrupt) - case &TIM12: - return interrupt.New(stm32.IRQ_TIM8_BRK_TIM12, TIM12.handleUPInterrupt) - case &TIM13: - return interrupt.New(stm32.IRQ_TIM8_UP_TIM13, TIM13.handleUPInterrupt) - case &TIM14: - return interrupt.New(stm32.IRQ_TIM8_TRG_COM_TIM14, TIM14.handleUPInterrupt) - } - - return interrupt.Interrupt{} -} - -func (t *TIM) registerOCInterrupt() interrupt.Interrupt { - switch t { - case &TIM1: - return interrupt.New(stm32.IRQ_TIM1_CC, TIM1.handleOCInterrupt) - case &TIM2: - return interrupt.New(stm32.IRQ_TIM2, TIM2.handleOCInterrupt) - case &TIM3: - return interrupt.New(stm32.IRQ_TIM3, TIM3.handleOCInterrupt) - case &TIM4: - return interrupt.New(stm32.IRQ_TIM4, TIM4.handleOCInterrupt) - case &TIM5: - return interrupt.New(stm32.IRQ_TIM5, TIM5.handleOCInterrupt) - case &TIM6: - return interrupt.New(stm32.IRQ_TIM6_DAC, TIM6.handleOCInterrupt) - case &TIM7: - return interrupt.New(stm32.IRQ_TIM7, TIM7.handleOCInterrupt) - case &TIM8: - return interrupt.New(stm32.IRQ_TIM8_UP_TIM13, TIM8.handleOCInterrupt) - case &TIM9: - return interrupt.New(stm32.IRQ_TIM1_BRK_TIM9, TIM9.handleOCInterrupt) - case &TIM10: - return interrupt.New(stm32.IRQ_TIM1_UP_TIM10, TIM10.handleOCInterrupt) - case &TIM11: - return interrupt.New(stm32.IRQ_TIM1_TRG_COM_TIM11, TIM11.handleOCInterrupt) - case &TIM12: - return interrupt.New(stm32.IRQ_TIM8_BRK_TIM12, TIM12.handleOCInterrupt) - case &TIM13: - return interrupt.New(stm32.IRQ_TIM8_UP_TIM13, TIM13.handleOCInterrupt) - case &TIM14: - return interrupt.New(stm32.IRQ_TIM8_TRG_COM_TIM14, TIM14.handleOCInterrupt) - } - - return interrupt.Interrupt{} -} - func (t *TIM) enableMainOutput() { t.Device.BDTR.SetBits(stm32.TIM_BDTR_MOE) } @@ -615,11 +360,6 @@ const ( PSC_MAX = 0x10000 ) -func initRNG() { - stm32.RCC.AHB2ENR.SetBits(stm32.RCC_AHB2ENR_RNGEN) - stm32.RNG.CR.SetBits(stm32.RNG_CR_RNGEN) -} - // Alternative peripheral pin functions const ( AF0_SYSTEM = 0 @@ -648,17 +388,6 @@ func (uart *UART) configurePins(config UARTConfig) { config.RX.ConfigureAltFunc(PinConfig{Mode: PinModeUARTRX}, uart.RxAltFuncSelector) } -func (uart *UART) getBaudRateDivisor(baudRate uint32) uint32 { - var clock uint32 - switch uart.Bus { - case stm32.USART1, stm32.USART6: - clock = CPUFrequency() / 2 // APB2 Frequency - case stm32.USART2, stm32.USART3, stm32.UART4, stm32.UART5: - clock = CPUFrequency() / 4 // APB1 Frequency - } - return clock / baudRate -} - func (uart *UART) setRegisters() { uart.rxReg = &uart.Bus.DR uart.txReg = &uart.Bus.DR diff --git a/src/machine/machine_stm32f401.go b/src/machine/machine_stm32f401.go new file mode 100644 index 0000000000..0d2a0fdb2e --- /dev/null +++ b/src/machine/machine_stm32f401.go @@ -0,0 +1,17 @@ +//go:build stm32f4 && stm32f401 + +package machine + +// CPUFrequency returns the current CPU frequency of the STM32F401. +// The PLL is configured to 84MHz SYSCLK from an external crystal. +func CPUFrequency() uint32 { + pll := PLLParams84MHz() + return xtalHz / pll.M * pll.N / pll.P +} + +// Internal use: configured speed of the APB1 and APB2 timers. +// STM32F401 at 84MHz: SYSCLK=84MHz, AHB=84MHz, APB1=42MHz, APB2=84MHz. +// APB1 prescaler = 2, so APB1 timer clock = 42MHz × 2 = 84MHz. +// APB2 prescaler = 1, so APB2 timer clock = 84MHz × 1 = 84MHz. +const APB1_TIM_FREQ = 84_000_000 +const APB2_TIM_FREQ = 84_000_000 diff --git a/src/machine/machine_stm32f401_adc.go b/src/machine/machine_stm32f401_adc.go new file mode 100644 index 0000000000..b77bcd76ba --- /dev/null +++ b/src/machine/machine_stm32f401_adc.go @@ -0,0 +1,87 @@ +//go:build stm32f401 + +package machine + +import ( + "device/stm32" + "unsafe" +) + +// InitADC initializes the registers needed for ADC1 on STM32F401. +func InitADC() { + enableAltFuncClock(unsafe.Pointer(stm32.ADC1)) + + stm32.ADC1.CR1.ClearBits(stm32.ADC_CR1_SCAN | stm32.ADC_CR1_RES_Msk) + stm32.ADC1.CR1.SetBits(stm32.ADC_CR1_RES_TwelveBit) + stm32.ADC1.CR2.ClearBits(stm32.ADC_CR2_CONT | stm32.ADC_CR2_ALIGN | stm32.ADC_CR2_EXTEN_Msk | stm32.ADC_CR2_EXTSEL_Msk) + stm32.ADC1.CR2.SetBits(stm32.ADC_CR2_CONT_Single | stm32.ADC_CR2_ALIGN_Right) + stm32.ADC1.SQR1.ClearBits(stm32.ADC_SQR1_L_Msk) + stm32.ADC1.SQR1.SetBits(2 << stm32.ADC_SQR1_L_Pos) + stm32.ADC1.CR2.SetBits(stm32.ADC_CR2_ADON) +} + +// Configure configures an ADC pin on STM32F401. +// The F401 ADC SVD does not provide named Cycles84 constants per channel; +// the value 4 (binary 100) selects 84-cycle sample time in the 3-bit field. +func (a ADC) Configure(ADCConfig) { + a.Pin.ConfigureAltFunc(PinConfig{Mode: PinInputAnalog}, 0) + + const cycles84 = 4 + ch := a.getChannel() + if ch > 9 { + stm32.ADC1.SMPR1.SetBits(cycles84 << ((ch - 10) * 3)) + } else { + stm32.ADC1.SMPR2.SetBits(cycles84 << (ch * 3)) + } +} + +// Get returns the current value of a ADC pin in the range 0..0xffff. +func (a ADC) Get() uint16 { + ch := uint32(a.getChannel()) + stm32.ADC1.SQR3.SetBits(ch) + stm32.ADC1.CR2.SetBits(stm32.ADC_CR2_SWSTART) + for !stm32.ADC1.SR.HasBits(stm32.ADC_SR_EOC) { + } + result := uint16(stm32.ADC1.DR.Get()) << 4 + stm32.ADC1.SR.ClearBits(stm32.ADC_SR_EOC) + stm32.ADC1.SQR3.ClearBits(ch) + return result +} + +func (a ADC) getChannel() uint8 { + switch a.Pin { + case PA0: + return 0 + case PA1: + return 1 + case PA2: + return 2 + case PA3: + return 3 + case PA4: + return 4 + case PA5: + return 5 + case PA6: + return 6 + case PA7: + return 7 + case PB0: + return 8 + case PB1: + return 9 + case PC0: + return 10 + case PC1: + return 11 + case PC2: + return 12 + case PC3: + return 13 + case PC4: + return 14 + case PC5: + return 15 + } + return 0 +} diff --git a/src/machine/machine_stm32f401_periph.go b/src/machine/machine_stm32f401_periph.go new file mode 100644 index 0000000000..cd868e3a0a --- /dev/null +++ b/src/machine/machine_stm32f401_periph.go @@ -0,0 +1,144 @@ +//go:build stm32f401 + +package machine + +// STM32F401 peripheral implementations. The F401 has a reduced set of +// peripherals compared to F405/F407, so this file provides F401-specific +// versions of the functions in machine_stm32f4_extended.go. + +import ( + "device/stm32" + "runtime/interrupt" + "unsafe" +) + +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 + case 4: + return stm32.GPIOE + case 7: + return stm32.GPIOH + default: + panic("machine: unknown port") + } +} + +// Enable peripheral clock for STM32F401 peripherals. +func enableAltFuncClock(bus unsafe.Pointer) { + switch bus { + case unsafe.Pointer(stm32.PWR): + stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_PWREN) + case unsafe.Pointer(stm32.I2C3): + stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_I2C3EN) + case unsafe.Pointer(stm32.I2C2): + stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_I2C2EN) + case unsafe.Pointer(stm32.I2C1): + stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_I2C1EN) + case unsafe.Pointer(stm32.USART2): + stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_USART2EN) + case unsafe.Pointer(stm32.SPI3): + stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_SPI3EN) + case unsafe.Pointer(stm32.SPI2): + stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_SPI2EN) + case unsafe.Pointer(stm32.WWDG): + stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_WWDGEN) + case unsafe.Pointer(stm32.TIM5): + stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_TIM5EN) + case unsafe.Pointer(stm32.TIM4): + stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_TIM4EN) + case unsafe.Pointer(stm32.TIM3): + stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_TIM3EN) + case unsafe.Pointer(stm32.TIM2): + stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_TIM2EN) + case unsafe.Pointer(stm32.TIM11): + stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_TIM11EN) + case unsafe.Pointer(stm32.TIM10): + stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_TIM10EN) + case unsafe.Pointer(stm32.TIM9): + stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_TIM9EN) + case unsafe.Pointer(stm32.SYSCFG): + stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_SYSCFGEN) + case unsafe.Pointer(stm32.SPI4): + stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_SPI4EN) + case unsafe.Pointer(stm32.SPI1): + stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_SPI1EN) + case unsafe.Pointer(stm32.SDIO): + stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_SDIOEN) + case unsafe.Pointer(stm32.ADC1): + stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_ADC1EN) + case unsafe.Pointer(stm32.USART6): + stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_USART6EN) + case unsafe.Pointer(stm32.USART1): + stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_USART1EN) + case unsafe.Pointer(stm32.TIM1): + stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_TIM1EN) + } +} + +func (t *TIM) registerUPInterrupt() interrupt.Interrupt { + switch t { + case &TIM1: + return interrupt.New(stm32.IRQ_TIM1_UP_TIM10, TIM1.handleUPInterrupt) + case &TIM2: + return interrupt.New(stm32.IRQ_TIM2, TIM2.handleUPInterrupt) + case &TIM3: + return interrupt.New(stm32.IRQ_TIM3, TIM3.handleUPInterrupt) + case &TIM4: + return interrupt.New(stm32.IRQ_TIM4, TIM4.handleUPInterrupt) + case &TIM5: + return interrupt.New(stm32.IRQ_TIM5, TIM5.handleUPInterrupt) + case &TIM9: + return interrupt.New(stm32.IRQ_TIM1_BRK_TIM9, TIM9.handleUPInterrupt) + case &TIM10: + return interrupt.New(stm32.IRQ_TIM1_UP_TIM10, TIM10.handleUPInterrupt) + case &TIM11: + return interrupt.New(stm32.IRQ_TIM1_TRG_COM_TIM11, TIM11.handleUPInterrupt) + } + + return interrupt.Interrupt{} +} + +func (t *TIM) registerOCInterrupt() interrupt.Interrupt { + switch t { + case &TIM1: + return interrupt.New(stm32.IRQ_TIM1_CC, TIM1.handleOCInterrupt) + case &TIM2: + return interrupt.New(stm32.IRQ_TIM2, TIM2.handleOCInterrupt) + case &TIM3: + return interrupt.New(stm32.IRQ_TIM3, TIM3.handleOCInterrupt) + case &TIM4: + return interrupt.New(stm32.IRQ_TIM4, TIM4.handleOCInterrupt) + case &TIM5: + return interrupt.New(stm32.IRQ_TIM5, TIM5.handleOCInterrupt) + case &TIM9: + return interrupt.New(stm32.IRQ_TIM1_BRK_TIM9, TIM9.handleOCInterrupt) + case &TIM10: + return interrupt.New(stm32.IRQ_TIM1_UP_TIM10, TIM10.handleOCInterrupt) + case &TIM11: + return interrupt.New(stm32.IRQ_TIM1_TRG_COM_TIM11, TIM11.handleOCInterrupt) + } + + return interrupt.Interrupt{} +} + +// initRNG is a no-op on STM32F401 which has no hardware RNG peripheral. +func initRNG() {} + +func (uart *UART) getBaudRateDivisor(baudRate uint32) uint32 { + var clock uint32 + switch uart.Bus { + case stm32.USART1, stm32.USART6: + clock = CPUFrequency() // APB2 = HCLK (no prescaler) + case stm32.USART2: + clock = CPUFrequency() / 2 // APB1 = HCLK/2 + } + return clock / baudRate +} diff --git a/src/machine/machine_stm32f4_extended.go b/src/machine/machine_stm32f4_extended.go new file mode 100644 index 0000000000..4e7210da9d --- /dev/null +++ b/src/machine/machine_stm32f4_extended.go @@ -0,0 +1,286 @@ +//go:build stm32f4 && !stm32f401 + +package machine + +// This file contains stm32f4 peripheral implementations for chips that have the +// full set of F4 peripherals (F405, F407, F469, etc.). Chips with fewer +// peripherals (e.g. F401) use machine_stm32f401_periph.go instead. + +import ( + "device/stm32" + "runtime/interrupt" + "unsafe" +) + +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 + case 4: + return stm32.GPIOE + case 5: + return stm32.GPIOF + case 6: + return stm32.GPIOG + case 7: + return stm32.GPIOH + case 8: + return stm32.GPIOI + case 9: + return stm32.GPIOJ + case 10: + return stm32.GPIOK + default: + panic("machine: unknown port") + } +} + +// Enable peripheral clock +func enableAltFuncClock(bus unsafe.Pointer) { + switch bus { + case unsafe.Pointer(stm32.DAC): + stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_DACEN) + case unsafe.Pointer(stm32.PWR): + stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_PWREN) + case unsafe.Pointer(stm32.CAN2): + stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_CAN2EN) + case unsafe.Pointer(stm32.CAN1): + stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_CAN1EN) + case unsafe.Pointer(stm32.I2C3): + stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_I2C3EN) + case unsafe.Pointer(stm32.I2C2): + stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_I2C2EN) + case unsafe.Pointer(stm32.I2C1): + stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_I2C1EN) + case unsafe.Pointer(stm32.UART5): + stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_UART5EN) + case unsafe.Pointer(stm32.UART4): + stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_UART4EN) + case unsafe.Pointer(stm32.USART3): + stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_USART3EN) + case unsafe.Pointer(stm32.USART2): + stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_USART2EN) + case unsafe.Pointer(stm32.SPI3): + stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_SPI3EN) + case unsafe.Pointer(stm32.SPI2): + stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_SPI2EN) + case unsafe.Pointer(stm32.WWDG): + stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_WWDGEN) + case unsafe.Pointer(stm32.TIM14): + stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_TIM14EN) + case unsafe.Pointer(stm32.TIM13): + stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_TIM13EN) + case unsafe.Pointer(stm32.TIM12): + stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_TIM12EN) + case unsafe.Pointer(stm32.TIM7): + stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_TIM7EN) + case unsafe.Pointer(stm32.TIM6): + stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_TIM6EN) + case unsafe.Pointer(stm32.TIM5): + stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_TIM5EN) + case unsafe.Pointer(stm32.TIM4): + stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_TIM4EN) + case unsafe.Pointer(stm32.TIM3): + stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_TIM3EN) + case unsafe.Pointer(stm32.TIM2): + stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_TIM2EN) + case unsafe.Pointer(stm32.TIM11): + stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_TIM11EN) + case unsafe.Pointer(stm32.TIM10): + stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_TIM10EN) + case unsafe.Pointer(stm32.TIM9): + stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_TIM9EN) + case unsafe.Pointer(stm32.SYSCFG): + stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_SYSCFGEN) + case unsafe.Pointer(stm32.SPI1): + stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_SPI1EN) + case unsafe.Pointer(stm32.SDIO): + stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_SDIOEN) + case unsafe.Pointer(stm32.ADC3): + stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_ADC3EN) + case unsafe.Pointer(stm32.ADC2): + stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_ADC2EN) + case unsafe.Pointer(stm32.ADC1): + stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_ADC1EN) + case unsafe.Pointer(stm32.USART6): + stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_USART6EN) + case unsafe.Pointer(stm32.USART1): + stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_USART1EN) + case unsafe.Pointer(stm32.TIM8): + stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_TIM8EN) + case unsafe.Pointer(stm32.TIM1): + stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_TIM1EN) + } +} + +var ( + TIM6 = TIM{ + EnableRegister: &stm32.RCC.APB1ENR, + EnableFlag: stm32.RCC_APB1ENR_TIM6EN, + Device: stm32.TIM6, + Channels: [4]TimerChannel{ + TimerChannel{Pins: []PinFunction{}}, + TimerChannel{Pins: []PinFunction{}}, + TimerChannel{Pins: []PinFunction{}}, + TimerChannel{Pins: []PinFunction{}}, + }, + busFreq: APB1_TIM_FREQ, + } + + TIM7 = TIM{ + EnableRegister: &stm32.RCC.APB1ENR, + EnableFlag: stm32.RCC_APB1ENR_TIM7EN, + Device: stm32.TIM7, + Channels: [4]TimerChannel{ + TimerChannel{Pins: []PinFunction{}}, + TimerChannel{Pins: []PinFunction{}}, + TimerChannel{Pins: []PinFunction{}}, + TimerChannel{Pins: []PinFunction{}}, + }, + busFreq: APB1_TIM_FREQ, + } + + TIM8 = TIM{ + EnableRegister: &stm32.RCC.APB2ENR, + EnableFlag: stm32.RCC_APB2ENR_TIM8EN, + Device: stm32.TIM8, + Channels: [4]TimerChannel{ + TimerChannel{Pins: []PinFunction{{PC6, AF3_TIM8_9_10_11}, {PI5, AF3_TIM8_9_10_11}}}, + TimerChannel{Pins: []PinFunction{{PC7, AF3_TIM8_9_10_11}, {PI6, AF3_TIM8_9_10_11}}}, + TimerChannel{Pins: []PinFunction{{PC8, AF3_TIM8_9_10_11}, {PI7, AF3_TIM8_9_10_11}}}, + TimerChannel{Pins: []PinFunction{{PC9, AF3_TIM8_9_10_11}, {PI2, AF3_TIM8_9_10_11}}}, + }, + busFreq: APB2_TIM_FREQ, + } + + TIM12 = TIM{ + EnableRegister: &stm32.RCC.APB1ENR, + EnableFlag: stm32.RCC_APB1ENR_TIM12EN, + Device: stm32.TIM12, + Channels: [4]TimerChannel{ + TimerChannel{Pins: []PinFunction{{PB14, AF9_CAN1_CAN2_TIM12_13_14}, {PH6, AF9_CAN1_CAN2_TIM12_13_14}}}, + TimerChannel{Pins: []PinFunction{{PB15, AF9_CAN1_CAN2_TIM12_13_14}, {PH9, AF9_CAN1_CAN2_TIM12_13_14}}}, + TimerChannel{Pins: []PinFunction{}}, + TimerChannel{Pins: []PinFunction{}}, + }, + busFreq: APB1_TIM_FREQ, + } + + TIM13 = TIM{ + EnableRegister: &stm32.RCC.APB1ENR, + EnableFlag: stm32.RCC_APB1ENR_TIM13EN, + Device: stm32.TIM13, + Channels: [4]TimerChannel{ + TimerChannel{Pins: []PinFunction{{PA6, AF9_CAN1_CAN2_TIM12_13_14}, {PF8, AF9_CAN1_CAN2_TIM12_13_14}}}, + TimerChannel{Pins: []PinFunction{}}, + TimerChannel{Pins: []PinFunction{}}, + TimerChannel{Pins: []PinFunction{}}, + }, + busFreq: APB1_TIM_FREQ, + } + + TIM14 = TIM{ + EnableRegister: &stm32.RCC.APB1ENR, + EnableFlag: stm32.RCC_APB1ENR_TIM14EN, + Device: stm32.TIM14, + Channels: [4]TimerChannel{ + TimerChannel{Pins: []PinFunction{{PA7, AF9_CAN1_CAN2_TIM12_13_14}, {PF9, AF9_CAN1_CAN2_TIM12_13_14}}}, + TimerChannel{Pins: []PinFunction{}}, + TimerChannel{Pins: []PinFunction{}}, + TimerChannel{Pins: []PinFunction{}}, + }, + busFreq: APB1_TIM_FREQ, + } +) + +func (t *TIM) registerUPInterrupt() interrupt.Interrupt { + switch t { + case &TIM1: + return interrupt.New(stm32.IRQ_TIM1_UP_TIM10, TIM1.handleUPInterrupt) + case &TIM2: + return interrupt.New(stm32.IRQ_TIM2, TIM2.handleUPInterrupt) + case &TIM3: + return interrupt.New(stm32.IRQ_TIM3, TIM3.handleUPInterrupt) + case &TIM4: + return interrupt.New(stm32.IRQ_TIM4, TIM4.handleUPInterrupt) + case &TIM5: + return interrupt.New(stm32.IRQ_TIM5, TIM5.handleUPInterrupt) + case &TIM6: + return interrupt.New(stm32.IRQ_TIM6_DAC, TIM6.handleUPInterrupt) + case &TIM7: + return interrupt.New(stm32.IRQ_TIM7, TIM7.handleUPInterrupt) + case &TIM8: + return interrupt.New(stm32.IRQ_TIM8_UP_TIM13, TIM8.handleUPInterrupt) + case &TIM9: + return interrupt.New(stm32.IRQ_TIM1_BRK_TIM9, TIM9.handleUPInterrupt) + case &TIM10: + return interrupt.New(stm32.IRQ_TIM1_UP_TIM10, TIM10.handleUPInterrupt) + case &TIM11: + return interrupt.New(stm32.IRQ_TIM1_TRG_COM_TIM11, TIM11.handleUPInterrupt) + case &TIM12: + return interrupt.New(stm32.IRQ_TIM8_BRK_TIM12, TIM12.handleUPInterrupt) + case &TIM13: + return interrupt.New(stm32.IRQ_TIM8_UP_TIM13, TIM13.handleUPInterrupt) + case &TIM14: + return interrupt.New(stm32.IRQ_TIM8_TRG_COM_TIM14, TIM14.handleUPInterrupt) + } + + return interrupt.Interrupt{} +} + +func (t *TIM) registerOCInterrupt() interrupt.Interrupt { + switch t { + case &TIM1: + return interrupt.New(stm32.IRQ_TIM1_CC, TIM1.handleOCInterrupt) + case &TIM2: + return interrupt.New(stm32.IRQ_TIM2, TIM2.handleOCInterrupt) + case &TIM3: + return interrupt.New(stm32.IRQ_TIM3, TIM3.handleOCInterrupt) + case &TIM4: + return interrupt.New(stm32.IRQ_TIM4, TIM4.handleOCInterrupt) + case &TIM5: + return interrupt.New(stm32.IRQ_TIM5, TIM5.handleOCInterrupt) + case &TIM6: + return interrupt.New(stm32.IRQ_TIM6_DAC, TIM6.handleOCInterrupt) + case &TIM7: + return interrupt.New(stm32.IRQ_TIM7, TIM7.handleOCInterrupt) + case &TIM8: + return interrupt.New(stm32.IRQ_TIM8_UP_TIM13, TIM8.handleOCInterrupt) + case &TIM9: + return interrupt.New(stm32.IRQ_TIM1_BRK_TIM9, TIM9.handleOCInterrupt) + case &TIM10: + return interrupt.New(stm32.IRQ_TIM1_UP_TIM10, TIM10.handleOCInterrupt) + case &TIM11: + return interrupt.New(stm32.IRQ_TIM1_TRG_COM_TIM11, TIM11.handleOCInterrupt) + case &TIM12: + return interrupt.New(stm32.IRQ_TIM8_BRK_TIM12, TIM12.handleOCInterrupt) + case &TIM13: + return interrupt.New(stm32.IRQ_TIM8_UP_TIM13, TIM13.handleOCInterrupt) + case &TIM14: + return interrupt.New(stm32.IRQ_TIM8_TRG_COM_TIM14, TIM14.handleOCInterrupt) + } + + return interrupt.Interrupt{} +} + +func initRNG() { + stm32.RCC.AHB2ENR.SetBits(stm32.RCC_AHB2ENR_RNGEN) + stm32.RNG.CR.SetBits(stm32.RNG_CR_RNGEN) +} + +func (uart *UART) getBaudRateDivisor(baudRate uint32) uint32 { + var clock uint32 + switch uart.Bus { + case stm32.USART1, stm32.USART6: + clock = CPUFrequency() / 2 // APB2 Frequency + case stm32.USART2, stm32.USART3, stm32.UART4, stm32.UART5: + clock = CPUFrequency() / 4 // APB1 Frequency + } + return clock / baudRate +} diff --git a/src/machine/machine_stm32f4_otgfs_vbus.go b/src/machine/machine_stm32f4_otgfs_vbus.go index e9830d9969..7a2fbb5939 100644 --- a/src/machine/machine_stm32f4_otgfs_vbus.go +++ b/src/machine/machine_stm32f4_otgfs_vbus.go @@ -1,4 +1,4 @@ -//go:build stm32f4 && (stm32f429 || stm32f427 || stm32f411 || stm32f407 || stm32f405 || stm32f401) +//go:build stm32f4 && (stm32f429 || stm32f427 || stm32f411 || stm32f407 || stm32f405) package machine diff --git a/src/machine/machine_stm32f4_pll_84mhz.go b/src/machine/machine_stm32f4_pll_84mhz.go new file mode 100644 index 0000000000..5936e2645b --- /dev/null +++ b/src/machine/machine_stm32f4_pll_84mhz.go @@ -0,0 +1,19 @@ +//go:build stm32f4 && stm32f401 + +package machine + +// PLLParams84MHz returns the HSE PLL dividers needed to reach a 336MHz VCO +// (84MHz SYSCLK, P=4, Q=7) for the configured crystal frequency. M is chosen +// to bring the PLL input (HSE/M) to 2MHz, as recommended by the STM32F401 +// reference manual (RM0368). With VCO=336MHz and P=4, SYSCLK=84MHz. Q=7 +// produces the 48MHz USB clock (VCO/Q). +func PLLParams84MHz() PLLParams { + switch xtalHz { + case 8_000_000: + return PLLParams{M: 4, N: 168, P: 4, Q: 7} + case 16_000_000: + return PLLParams{M: 8, N: 168, P: 4, Q: 7} + default: + panic("unsupported xtal frequency") + } +} diff --git a/src/machine/usb.go b/src/machine/usb.go index 9fcc1997d7..ed27258ab8 100644 --- a/src/machine/usb.go +++ b/src/machine/usb.go @@ -1,4 +1,4 @@ -//go:build sam || nrf52840 || rp2040 || rp2350 || stm32f4 || stm32f7 || stm32h7 +//go:build sam || nrf52840 || rp2040 || rp2350 || (stm32f4 && !stm32f401) || stm32f7 || stm32h7 package machine diff --git a/src/runtime/rand_hwrng.go b/src/runtime/rand_hwrng.go index bc7b72d6c0..8a59e732c2 100644 --- a/src/runtime/rand_hwrng.go +++ b/src/runtime/rand_hwrng.go @@ -1,4 +1,4 @@ -//go:build baremetal && (nrf || (stm32 && !(stm32f103 || stm32l0x1 || stm32g0 || stm32u0)) || (sam && atsamd51) || (sam && atsame5x) || esp32c3 || esp32s3 || tkey || (tinygo.riscv32 && virt) || rp2040 || rp2350) +//go:build baremetal && (nrf || (stm32 && !(stm32f103 || stm32l0x1 || stm32g0 || stm32u0 || stm32f401)) || (sam && atsamd51) || (sam && atsame5x) || esp32c3 || esp32s3 || tkey || (tinygo.riscv32 && virt) || rp2040 || rp2350) // If you update the above build constraint, you'll probably also need to update // src/crypto/rand/rand_baremetal.go. diff --git a/src/runtime/rand_norng.go b/src/runtime/rand_norng.go index ee77df09fa..46de54f91a 100644 --- a/src/runtime/rand_norng.go +++ b/src/runtime/rand_norng.go @@ -1,4 +1,4 @@ -//go:build baremetal && !(nrf || (stm32 && !(stm32f103 || stm32l0x1 || stm32g0 || stm32u0)) || (sam && atsamd51) || (sam && atsame5x) || esp32c3 || esp32s3 || tkey || (tinygo.riscv32 && virt) || rp2040 || rp2350) +//go:build baremetal && !(nrf || (stm32 && !(stm32f103 || stm32l0x1 || stm32g0 || stm32u0 || stm32f401)) || (sam && atsamd51) || (sam && atsame5x) || esp32c3 || esp32s3 || tkey || (tinygo.riscv32 && virt) || rp2040 || rp2350) package runtime diff --git a/src/runtime/runtime_stm32f401.go b/src/runtime/runtime_stm32f401.go new file mode 100644 index 0000000000..9ca4ef5d1f --- /dev/null +++ b/src/runtime/runtime_stm32f401.go @@ -0,0 +1,126 @@ +//go:build stm32f401 + +package runtime + +import ( + "device/stm32" + "machine" +) + +const ( + // +---------------------------------------------+ + // | Clock Settings | + // +-------------+-------------------------------+ + // | HSE | selectable (xtal_8/16_mhz) | + // | SYSCLK | 84mhz | + // | HCLK | 84mhz | + // | APB1(PCLK1) | 42mhz | + // | APB2(PCLK2) | 84mhz | + // +-------------+-------------------------------+ + HCLK_FREQ_HZ = 84000000 + PCLK1_FREQ_HZ = HCLK_FREQ_HZ / 2 + PCLK2_FREQ_HZ = HCLK_FREQ_HZ / 1 +) + +const ( + // VOS Scale 1 (bits [15:14] = 0b11): max HCLK = 84 MHz + PWR_SCALE1 = 3 << stm32.PWR_CR_VOS_Pos + + PLL_SRC_HSE = 1 << stm32.RCC_PLLCFGR_PLLSRC_Pos + PLL_SRC_HSI = 0 + + SYSCLK_SRC_PLL = stm32.RCC_CFGR_SW_PLL << stm32.RCC_CFGR_SW_Pos + SYSCLK_STAT_PLL = stm32.RCC_CFGR_SWS_PLL << stm32.RCC_CFGR_SWS_Pos + + RCC_DIV_PCLK1 = stm32.RCC_CFGR_PPRE1_Div2 << stm32.RCC_CFGR_PPRE1_Pos // HCLK / 2 + RCC_DIV_PCLK2 = stm32.RCC_CFGR_PPRE2_Div1 << stm32.RCC_CFGR_PPRE2_Pos // HCLK / 1 + RCC_DIV_HCLK = stm32.RCC_CFGR_HPRE_Div1 << stm32.RCC_CFGR_HPRE_Pos // SYSCLK / 1 +) + +const ( + // +-----------------------------------+ + // | Voltage range = 2.7V - 3.6V | + // +----------------+------------------+ + // | Wait states | System Bus | + // | (WS, LATENCY) | HCLK (MHz) | + // +----------------+------------------+ + // | 0 WS, 1 cycle | 0 < HCLK ≤ 30 | + // | 1 WS, 2 cycles | 30 < HCLK ≤ 64 | + // | 2 WS, 3 cycles | 64 < HCLK ≤ 84 | + // +----------------+------------------+ + FLASH_LATENCY = 2 << stm32.FLASH_ACR_LATENCY_Pos // 2 WS (3 CPU cycles) + + // instruction cache, data cache, and prefetch + FLASH_OPTIONS = stm32.FLASH_ACR_ICEN | stm32.FLASH_ACR_DCEN | stm32.FLASH_ACR_PRFTEN +) + +func init() { + initOSC() + initCLK() + + machine.InitSerial() + + initTickTimer(&machine.TIM3) +} + +func initOSC() { + // enable voltage regulator + stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_PWREN) + stm32.PWR.CR.SetBits(PWR_SCALE1) + + // enable HSE + stm32.RCC.CR.Set(stm32.RCC_CR_HSEON) + for !stm32.RCC.CR.HasBits(stm32.RCC_CR_HSERDY) { + } + + // disable PLL before configuring it + stm32.RCC.CR.ClearBits(stm32.RCC_CR_PLLON) + for stm32.RCC.CR.HasBits(stm32.RCC_CR_PLLRDY) { + } + + // set HSE as PLL source and configure dividers for 84MHz SYSCLK + pll := machine.PLLParams84MHz() + stm32.RCC.PLLCFGR.Set(PLL_SRC_HSE | + pll.M<>1)-1)<