feat(machine/stm32): make HSE crystal frequency selectable.

Define the board's external crystal (HSE) frequency `xtalHz` in the
respective `board_*.go` files. Per-topology PLL tables (F1, F4, F7)
will compute the register dividers from it.

Moving this parameter to the board definition level cleanly isolates
board hardware characteristics from general MCU chip configurations,
eliminating the need for custom target build tags. Targets using
HSE-clocked STM32 chips must define `xtalHz` or fail to build.
This commit is contained in:
Konstantin Sharlaimov
2026-07-12 16:30:16 +02:00
committed by Ron Evans
parent b8adb803c4
commit 5f02d6b659
22 changed files with 193 additions and 79 deletions
+7 -4
View File
@@ -31,8 +31,10 @@ func buffered() int {
return machine.Serial.Buffered()
}
// initCLK sets clock to 72MHz using HSE 8MHz crystal w/ PLL X 9 (8MHz x 9 = 72MHz).
// initCLK sets clock to 72MHz using the board's HSE crystal (defined by the xtalHz) w/ PLL.
func initCLK() {
pll := machine.PLLParams72MHz()
stm32.FLASH.ACR.SetBits(stm32.FLASH_ACR_LATENCY_WS2) // Two wait states, per datasheet
stm32.RCC.CFGR.SetBits(stm32.RCC_CFGR_PPRE1_Div2 << stm32.RCC_CFGR_PPRE1_Pos) // prescale PCLK1 = HCLK/2
stm32.RCC.CFGR.SetBits(stm32.RCC_CFGR_PPRE2_Div1 << stm32.RCC_CFGR_PPRE2_Pos) // prescale PCLK2 = HCLK/1
@@ -49,9 +51,10 @@ func initCLK() {
for !stm32.RCC.CR.HasBits(stm32.RCC_CR_HSIRDY) {
}
stm32.RCC.CFGR.SetBits(stm32.RCC_CFGR_PLLSRC) // set PLL source to HSE
stm32.RCC.CFGR.SetBits(stm32.RCC_CFGR_PLLMUL_Mul9 << stm32.RCC_CFGR_PLLMUL_Pos) // multiply by 9
stm32.RCC.CR.SetBits(stm32.RCC_CR_PLLON) // enable the PLL
stm32.RCC.CFGR.SetBits(stm32.RCC_CFGR_PLLSRC) // set PLL source to HSE
stm32.RCC.CFGR.SetBits(pll.Prediv << stm32.RCC_CFGR_PLLXTPRE_Pos) // optional HSE /2 prescaler
stm32.RCC.CFGR.SetBits(pll.Mul << stm32.RCC_CFGR_PLLMUL_Pos) // PLL multiplier
stm32.RCC.CR.SetBits(stm32.RCC_CR_PLLON) // enable the PLL
// wait for the PLLRDY flag
for !stm32.RCC.CR.HasBits(stm32.RCC_CR_PLLRDY) {
+1 -1
View File
@@ -73,7 +73,7 @@ func initCLK() {
// PCLK1 = HCLK / 4
stm32.RCC.CFGR.SetBits(stm32.RCC_CFGR_PPRE1_Div4 << stm32.RCC_CFGR_PPRE1_Pos)
// Configure the main PLL
stm32.RCC.PLLCFGR.Set(PLL_CFGR)
stm32.RCC.PLLCFGR.Set(pllCFGR())
// Enable main PLL
stm32.RCC.CR.SetBits(stm32.RCC_CR_PLLON)
// Wait till the main PLL is ready
+15 -15
View File
@@ -8,15 +8,15 @@ import (
)
const (
// +----------------------+
// | Clock Settings |
// +-------------+--------+
// | HSE | 12mhz |
// | SYSCLK | 168mhz |
// | HCLK | 168mhz |
// | APB1(PCLK1) | 42mhz |
// | APB2(PCLK2) | 84mhz |
// +-------------+--------+
// +---------------------------------------------+
// | Clock Settings |
// +-------------+-------------------------------+
// | HSE | selectable (xtal_8/12/16_mhz) |
// | SYSCLK | 168mhz |
// | HCLK | 168mhz |
// | APB1(PCLK1) | 42mhz |
// | APB2(PCLK2) | 84mhz |
// +-------------+-------------------------------+
HCLK_FREQ_HZ = 168000000
PCLK1_FREQ_HZ = HCLK_FREQ_HZ / 4
PCLK2_FREQ_HZ = HCLK_FREQ_HZ / 2
@@ -29,11 +29,6 @@ const (
PLL_SRC_HSE = 1 << stm32.RCC_PLLCFGR_PLLSRC_Pos // use HSE for PLL and PLLI2S
PLL_SRC_HSI = 0 // use HSI for PLL and PLLI2S
PLL_DIV_M = 6 << stm32.RCC_PLLCFGR_PLLM_Pos
PLL_MLT_N = 168 << stm32.RCC_PLLCFGR_PLLN_Pos
PLL_DIV_P = ((2 >> 1) - 1) << stm32.RCC_PLLCFGR_PLLP_Pos
PLL_DIV_Q = 7 << stm32.RCC_PLLCFGR_PLLQ_Pos
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
@@ -94,7 +89,12 @@ func initOSC() {
}
// set HSE as PLL source and configure clock divisors
stm32.RCC.PLLCFGR.Set(PLL_SRC_HSE | PLL_DIV_M | PLL_MLT_N | PLL_DIV_P | PLL_DIV_Q)
pll := machine.PLLParams168MHz()
stm32.RCC.PLLCFGR.Set(PLL_SRC_HSE |
pll.M<<stm32.RCC_PLLCFGR_PLLM_Pos |
pll.N<<stm32.RCC_PLLCFGR_PLLN_Pos |
((pll.P>>1)-1)<<stm32.RCC_PLLCFGR_PLLP_Pos |
pll.Q<<stm32.RCC_PLLCFGR_PLLQ_Pos)
// enable PLL and wait for it to sync
stm32.RCC.CR.SetBits(stm32.RCC_CR_PLLON)
+20 -18
View File
@@ -2,26 +2,28 @@
package runtime
import "device/stm32"
import (
"device/stm32"
"machine"
)
/*
clock settings
+-------------+--------+
| HSE | 8mhz |
| SYSCLK | 168mhz |
| HCLK | 168mhz |
| APB2(PCLK2) | 84mhz |
| APB1(PCLK1) | 42mhz |
+-------------+--------+
+-------------+--------------------------------+
| HSE | selectable (xtal_8/12/16_mhz) |
| SYSCLK | 168mhz |
| HCLK | 168mhz |
| APB2(PCLK2) | 84mhz |
| APB1(PCLK1) | 42mhz |
+-------------+--------------------------------+
*/
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
PLL_CFGR = PLL_M | (PLL_N << stm32.RCC_PLLCFGR_PLLN_Pos) | (((PLL_P >> 1) - 1) << stm32.RCC_PLLCFGR_PLLP_Pos) |
(1 << stm32.RCC_PLLCFGR_PLLSRC_Pos) | (PLL_Q << stm32.RCC_PLLCFGR_PLLQ_Pos)
)
const HSE_STARTUP_TIMEOUT = 0x0500
// pllCFGR builds the RCC_PLLCFGR value for the current xtal - see RM0090
// Reference Manual pg. 95.
func pllCFGR() uint32 {
pll := machine.PLLParams168MHz()
return pll.M | (pll.N << stm32.RCC_PLLCFGR_PLLN_Pos) | (((pll.P >> 1) - 1) << stm32.RCC_PLLCFGR_PLLP_Pos) |
(1 << stm32.RCC_PLLCFGR_PLLSRC_Pos) | (pll.Q << stm32.RCC_PLLCFGR_PLLQ_Pos)
}
+20 -19
View File
@@ -2,27 +2,28 @@
package runtime
import "device/stm32"
import (
"device/stm32"
"machine"
)
/*
clock settings
+-------------+--------+
| HSE | 8mhz |
| SYSCLK | 180mhz |
| HCLK | 180mhz |
| APB2(PCLK2) | 90mhz |
| APB1(PCLK1) | 45mhz |
+-------------+--------+
+-------------+--------------------------------+
| HSE | selectable (xtal_8/12/16_mhz) |
| SYSCLK | 180mhz |
| HCLK | 180mhz |
| APB2(PCLK2) | 90mhz |
| APB1(PCLK1) | 45mhz |
+-------------+--------------------------------+
*/
const (
HSE_STARTUP_TIMEOUT = 0x0500
// PLL Options - See RM0386 Reference Manual pg. 148
PLL_M = 8 // PLL_VCO = (HSE_VALUE or HSI_VALUE / PLL_M) * PLL_N
PLL_N = 360
PLL_P = 2 // SYSCLK = PLL_VCO / PLL_P
PLL_Q = 7 // USB OTS FS, SDIO and RNG Clock = PLL_VCO / PLL_Q
PLL_R = 6 // DSI
PLL_CFGR = PLL_M | (PLL_N << stm32.RCC_PLLCFGR_PLLN_Pos) | (((PLL_P >> 1) - 1) << stm32.RCC_PLLCFGR_PLLP_Pos) |
(1 << stm32.RCC_PLLCFGR_PLLSRC_Pos) | (PLL_Q << stm32.RCC_PLLCFGR_PLLQ_Pos) | (PLL_R << stm32.RCC_PLLCFGR_PLLR_Pos)
)
const HSE_STARTUP_TIMEOUT = 0x0500
// pllCFGR builds the RCC_PLLCFGR value for the current xtal - see RM0386
// Reference Manual pg. 148.
func pllCFGR() uint32 {
pll := machine.PLLParams180MHz()
return pll.M | (pll.N << stm32.RCC_PLLCFGR_PLLN_Pos) | (((pll.P >> 1) - 1) << stm32.RCC_PLLCFGR_PLLP_Pos) |
(1 << stm32.RCC_PLLCFGR_PLLSRC_Pos) | (pll.Q << stm32.RCC_PLLCFGR_PLLQ_Pos) | (pll.R << stm32.RCC_PLLCFGR_PLLR_Pos)
}
+13 -18
View File
@@ -10,21 +10,15 @@ import (
/*
clock settings
+-------------+--------+
| HSE | 8mhz |
| SYSCLK | 216mhz |
| HCLK | 216mhz |
| APB1(PCLK1) | 27mhz |
| APB2(PCLK2) | 108mhz |
+-------------+--------+
+-------------+--------------------------------+
| HSE | selectable (xtal_8/12/16_mhz) |
| SYSCLK | 216mhz |
| HCLK | 216mhz |
| APB1(PCLK1) | 27mhz |
| APB2(PCLK2) | 108mhz |
+-------------+--------------------------------+
*/
const (
HSE_STARTUP_TIMEOUT = 0x0500
PLL_M = 4
PLL_N = 216
PLL_P = 2
PLL_Q = 9
)
const HSE_STARTUP_TIMEOUT = 0x0500
func init() {
initCLK()
@@ -107,12 +101,13 @@ func initOsc() {
}
// Configure the PLL: HSE as source, use SVD constants for positions.
pll := machine.PLLParams216MHz()
stm32.RCC.PLLCFGR.Set(
(stm32.RCC_PLLCFGR_PLLSRC_HSE << stm32.RCC_PLLCFGR_PLLSRC_Pos) |
(PLL_M << stm32.RCC_PLLCFGR_PLLM_Pos) |
(PLL_N << stm32.RCC_PLLCFGR_PLLN_Pos) |
(((PLL_P >> 1) - 1) << stm32.RCC_PLLCFGR_PLLP_Pos) |
(PLL_Q << stm32.RCC_PLLCFGR_PLLQ_Pos))
(pll.M << stm32.RCC_PLLCFGR_PLLM_Pos) |
(pll.N << stm32.RCC_PLLCFGR_PLLN_Pos) |
(((pll.P >> 1) - 1) << stm32.RCC_PLLCFGR_PLLP_Pos) |
(pll.Q << stm32.RCC_PLLCFGR_PLLQ_Pos))
// Enable the PLL, wait until ready
stm32.RCC.CR.SetBits(stm32.RCC_CR_PLLON)