feat(machine/stm32): add STM32H7 and NUCLEO-H753ZI support

This commit is contained in:
Konstantin Sharlaimov
2026-07-23 11:06:52 +02:00
committed by Ron Evans
parent 40ed956d6c
commit 3797e89600
27 changed files with 2576 additions and 11 deletions
+11
View File
@@ -0,0 +1,11 @@
//go:build stm32h7
package main
import "machine"
var (
pwm = &machine.TIM1
pinA = machine.PA8
pinB = machine.PA9
)
+31
View File
@@ -0,0 +1,31 @@
package main
import (
"machine"
"time"
)
func main() {
time.Sleep(2 * time.Second)
println("configuring window watchdog")
config := machine.WindowWatchdogConfig{
TimeoutMicros: 100000, // 100ms
WindowPercent: 50, // 50ms to 100ms refresh window
}
machine.WindowWatchdog.Configure(config)
machine.WindowWatchdog.Start()
println("updating wwdg for 1 second")
for i := 0; i < 10; i++ {
time.Sleep(75 * time.Millisecond) // middle of the window
machine.WindowWatchdog.Update()
println("alive")
}
println("entering tight loop (will reset)")
for {
time.Sleep(10 * time.Millisecond)
}
}
+105
View File
@@ -0,0 +1,105 @@
//go:build nucleoh753zi
package machine
import (
"device/stm32"
"runtime/interrupt"
)
const xtalHz = 8_000_000
const hseBypass = true
const (
// Arduino Pins
A0 = PA3
A1 = PC0
A2 = PC3
A3 = PB1
A4 = PC2
A5 = PF10
D0 = PG9
D1 = PG14
D2 = PF15
D3 = PE13
D4 = PF14
D5 = PE11
D6 = PE9
D7 = PF13
D8 = PF12
D9 = PD15
D10 = PD14
D11 = PA7
D12 = PA6
D13 = PA5
D14 = PB9
D15 = PB8
)
const (
LED = LED_BUILTIN
LED_BUILTIN = LED_GREEN
LED_GREEN = PB0
LED_YELLOW = PE1
LED_RED = PB14
)
const (
BUTTON = BUTTON_USER
BUTTON_USER = PC13
)
// UART pins
const (
// PD8 and PD9 are connected to the ST-Link Virtual Com Port (VCP)
UART_TX_PIN = PD8
UART_RX_PIN = PD9
UART_ALT_FN = AF7_SPI2_3_USART1_2_3_UART5_SPDIFRX
)
var (
// USART3 is the hardware serial port connected to the onboard ST-LINK
// debugger to be exposed as virtual COM port over USB on Nucleo boards.
UART1 = &_UART1
_UART1 = UART{
Buffer: NewRingBuffer(),
Bus: stm32.USART3,
TxAltFuncSelector: UART_ALT_FN,
RxAltFuncSelector: UART_ALT_FN,
}
DefaultUART = UART1
)
func init() {
UART1.Interrupt = interrupt.New(stm32.IRQ_USART3, _UART1.handleInterrupt)
}
// SPI pins
const (
SPI0_SCK_PIN = PA5
SPI0_SDI_PIN = PA6
SPI0_SDO_PIN = PA7
)
var (
SPI1 = &SPI{
Bus: stm32.SPI1,
AltFuncSelector: AF5_SPI1_2_3_4_5_6_I2S,
}
SPI0 = SPI1
)
// I2C pins
const (
I2C0_SCL_PIN = PB8
I2C0_SDA_PIN = PB9
)
var (
I2C1 = &I2C{
Bus: stm32.I2C1,
AltFuncSelector: AF4_I2C1_2_3_4_USART1,
}
I2C0 = I2C1
)
-6
View File
@@ -86,12 +86,6 @@ func (p Pin) PortMaskClear() (*uint32, uint32) {
return &port.BSRR.Reg, 1 << (pin + 16)
}
// EnterBootloader resets the chip into the bootloader.
// This is currently a stub for STM32, required to satisfy machine.EnterBootloader
// called by machine/usb/cdc.
func EnterBootloader() {
}
var deviceID [12]byte
// DeviceID returns an identifier that is unique within
+184
View File
@@ -0,0 +1,184 @@
//go:build stm32h7
package machine
import (
"device/arm"
"device/stm32"
)
// InitADC initializes the registers needed for ADC1 and ADC3.
func InitADC() {
// 1. Enable ADC bus clocks
stm32.RCC.AHB1ENR.SetBits(stm32.RCC_AHB1ENR_ADC12EN)
stm32.RCC.AHB4ENR.SetBits(stm32.RCC_AHB4ENR_ADC3EN)
// 2. Configure ADC clock mode (Async from kernel clock)
// CCR is at offset 0x308 from ADC base.
stm32.ADC12_Common.CR.ReplaceBits(0x0, 0x3, 16) // CKMODE = 00
stm32.ADC3_Common.CR.ReplaceBits(0x0, 0x3, 16) // CKMODE = 00
// 3. Exit deep power-down mode
stm32.ADC1.CR.ClearBits(stm32.ADC_CR_DEEPPWD)
stm32.ADC3.CR.ClearBits(stm32.ADC_CR_DEEPPWD)
// 4. Enable voltage regulators
stm32.ADC1.CR.SetBits(stm32.ADC_CR_ADVREGEN)
stm32.ADC3.CR.SetBits(stm32.ADC_CR_ADVREGEN)
// Wait for T_ADCVREG_STUP (min 10us). The nop keeps the compiler from
// eliminating the loop as free of side effects.
for i := 0; i < 10000; i++ {
arm.Asm("nop")
}
// Set BOOST[1:0]=0b11 for ADC kernel clock >25MHz (RM0433 §25.4.3 Table 121).
// The kernel clock is 80MHz (PLL2_P), so BOOST must be enabled.
stm32.ADC1.CR.ReplaceBits(0b11, 0x3, stm32.ADC_CR_BOOST_Pos)
stm32.ADC3.CR.ReplaceBits(0b11, 0x3, stm32.ADC_CR_BOOST_Pos)
// 5. Calibration
// ADC1
stm32.ADC1.CR.SetBits(stm32.ADC_CR_ADCAL | stm32.ADC_CR_ADCALLIN)
for stm32.ADC1.CR.HasBits(stm32.ADC_CR_ADCAL) {
}
// ADC3
stm32.ADC3.CR.SetBits(stm32.ADC_CR_ADCAL | stm32.ADC_CR_ADCALLIN)
for stm32.ADC3.CR.HasBits(stm32.ADC_CR_ADCAL) {
}
// 6. Enable ADCs
// ADC1
stm32.ADC1.ISR.SetBits(stm32.ADC_ISR_ADRDY) // Clear ADRDY by writing 1
stm32.ADC1.CR.SetBits(stm32.ADC_CR_ADEN)
for !stm32.ADC1.ISR.HasBits(stm32.ADC_ISR_ADRDY) {
}
// ADC3
stm32.ADC3.ISR.SetBits(stm32.ADC_ISR_ADRDY) // Clear ADRDY by writing 1
stm32.ADC3.CR.SetBits(stm32.ADC_CR_ADEN)
for !stm32.ADC3.ISR.HasBits(stm32.ADC_ISR_ADRDY) {
}
// 7. Configure resolution (16-bit)
// RES[2:0] is at bits 4:2 in CFGR. 000: 16-bit.
stm32.ADC1.CFGR.ReplaceBits(0x0, 0x7, 2)
stm32.ADC3.CFGR.ReplaceBits(0x0, 0x7, 2)
}
// Configure configures an ADC pin to be able to read analog data.
func (a ADC) Configure(config ADCConfig) {
a.Pin.Configure(PinConfig{Mode: PinInputAnalog})
// Set sampling time.
// H7 has SMPR1 (channels 0-9) and SMPR2 (channels 10-19).
// Each channel has 3 bits.
ch := a.getChannel()
adc, _ := a.getPeripheral()
const smpVal = 0x2 // 8.5 cycles
if ch <= 9 {
adc.SMPR1.ReplaceBits(uint32(smpVal), 0x7, uint8(ch)*3)
} else {
adc.SMPR2.ReplaceBits(uint32(smpVal), 0x7, uint8(ch-10)*3)
}
}
// Get returns the current value of an ADC pin in the range 0..0xffff.
func (a ADC) Get() uint16 {
ch := uint32(a.getChannel())
adc, ok := a.getPeripheral()
if !ok {
return 0
}
// Select channel (PCSEL register)
// Refer to RM0433 §25.4.12: Only one PCSELx bit must be set at a time.
adc.PCSEL.Set(1 << ch)
// Set rank 1 to channel
// SQ1[4:0] at bits 10:6. L[3:0] at bits 3:0.
adc.SQR1.ReplaceBits(ch, 0x1F, 6)
adc.SQR1.ReplaceBits(0x0, 0xF, 0) // L=0 (1 conversion)
// Start conversion
adc.CR.SetBits(stm32.ADC_CR_ADSTART)
// Wait for end of conversion
for !adc.ISR.HasBits(stm32.ADC_ISR_EOC) {
}
// Read 16-bit result
result := uint16(adc.DR.Get())
// Clear EOC
adc.ISR.SetBits(stm32.ADC_ISR_EOC)
// Deselect channel
adc.PCSEL.Set(0)
return result
}
func (a ADC) getPeripheral() (*stm32.ADC_Type, bool) {
switch a.Pin {
case PF3, PF4, PF5, PF6, PF7, PF8, PF9, PF10:
return stm32.ADC3, true
default:
// Assume ADC1 for PA/PB/PC pins
return stm32.ADC1, true
}
}
// getChannel returns the ADC channel number for a given GPIO pin.
// Mapping for STM32H743 per RM0433 and DS12110.
func (a ADC) getChannel() uint8 {
switch a.Pin {
case PA0:
return 16
case PA1:
return 17
case PA2:
return 14
case PA3:
return 15
case PA4:
return 18
case PA5:
return 19
case PA6:
return 3
case PA7:
return 7
case PB0:
return 9
case PB1:
return 5
case PC0:
return 10
case PC1:
return 11
case PC2:
return 12
case PC3:
return 13
case PC4:
return 4
case PC5:
return 8
case PF3:
return 5
case PF4:
return 9
case PF5:
return 4
case PF6:
return 8
case PF7:
return 3
case PF8:
return 7
case PF9:
return 2
case PF10:
return 6
}
return 0
}
+9
View File
@@ -0,0 +1,9 @@
//go:build stm32 && !stm32h7
package machine
// EnterBootloader resets the chip into the bootloader.
// This is currently a stub for STM32, required to satisfy machine.EnterBootloader
// called by machine/usb/cdc.
func EnterBootloader() {
}
+1 -1
View File
@@ -1,4 +1,4 @@
//go:build stm32 && !stm32f1 && !stm32l5 && !stm32wlx && !stm32g0 && !stm32u5 && !stm32u0
//go:build stm32 && !stm32f1 && !stm32l5 && !stm32wlx && !stm32g0 && !stm32u5 && !stm32u0 && !stm32h7
package machine
+1 -1
View File
@@ -1,4 +1,4 @@
//go:build stm32 && !stm32l4 && !stm32l5 && !stm32wlx && !stm32g0 && !stm32u5 && !stm32u0
//go:build stm32 && !stm32l4 && !stm32l5 && !stm32wlx && !stm32g0 && !stm32u5 && !stm32u0 && !stm32h7
package machine
+1 -1
View File
@@ -1,4 +1,4 @@
//go:build stm32l5 || stm32f7 || stm32l4 || stm32l0 || stm32wlx || stm32g0 || stm32u0 || stm32u5
//go:build stm32l5 || stm32f7 || stm32l4 || stm32l0 || stm32wlx || stm32g0 || stm32u0 || stm32u5 || stm32h7
package machine
+10
View File
@@ -36,6 +36,9 @@ const (
// for PWM
PinModePWMOutput PinMode = 12
// for USB (DP/DM lines)
PinModeUSB PinMode = 13
)
// Define several bitfields that have different names across chip families but
@@ -144,6 +147,13 @@ func (p Pin) ConfigureAltFunc(config PinConfig, altFunc uint8) {
port.PUPDR.ReplaceBits(gpioPullFloating, gpioPullMask, pos)
p.SetAltFunc(altFunc)
// USB
case PinModeUSB:
port.MODER.ReplaceBits(gpioModeAlternate, gpioModeMask, pos)
port.OSPEEDR.ReplaceBits(gpioOutputSpeedVeryHigh, gpioOutputSpeedMask, pos)
port.PUPDR.ReplaceBits(gpioPullFloating, gpioPullMask, pos)
p.SetAltFunc(altFunc)
// ADC
case PinInputAnalog:
port.MODER.ReplaceBits(gpioModeAnalog, gpioModeMask, pos)
+1 -1
View File
@@ -1,4 +1,4 @@
//go:build stm32 && !stm32f7x2 && !stm32l5x2 && !stm32g0 && !stm32u5 && !stm32u0
//go:build stm32 && !stm32f7x2 && !stm32l5x2 && !stm32g0 && !stm32u5 && !stm32u0 && !stm32h7
package machine
+19
View File
@@ -79,6 +79,25 @@ func (t *TIM) Count() uint32 {
return uint32(t.Device.CNT.Get())
}
// SetOnePulseMode enables or disables the one-pulse mode.
// When enabled, the timer will automatically stop at the next update event.
func (t *TIM) SetOnePulseMode(enable bool) {
if enable {
t.Device.CR1.SetBits(stm32.TIM_CR1_OPM)
} else {
t.Device.CR1.ClearBits(stm32.TIM_CR1_OPM)
}
}
// SetEnabled enables or disables the timer.
func (t *TIM) SetEnabled(enable bool) {
if enable {
t.Device.CR1.SetBits(stm32.TIM_CR1_CEN)
} else {
t.Device.CR1.ClearBits(stm32.TIM_CR1_CEN)
}
}
// SetWraparoundInterrupt configures a callback to be called each
// time the timer 'wraps-around'.
//
+558
View File
@@ -0,0 +1,558 @@
//go:build stm32 && stm32h7
package machine
import (
"device/arm"
"device/stm32"
"runtime/interrupt"
"runtime/volatile"
"unsafe"
)
var deviceIDAddr = []uintptr{0x1FF1E800, 0x1FF1E804, 0x1FF1E808}
// Default USB identifiers; board files with USB support should override these.
const (
usb_STRING_PRODUCT = "STM32H7"
usb_STRING_MANUFACTURER = "TinyGo"
usb_VID uint16 = 0x239A
usb_PID uint16 = 0x0001
)
// EnterBootloader resets the chip. Jumping to the H7 system bootloader
// (0x1FF09800) is not implemented; a plain reset restarts the application.
func EnterBootloader() {
arm.SystemReset()
}
// HSI_KER_FREQ is the fixed-frequency internal RC oscillator (RM0433 §8.2),
// unaffected by the board's HSE crystal.
const HSI_KER_FREQ = 64_000_000
// sysClockFreq returns SYSCLK (PLL1P output) for the board's configured
// xtalHz, per the M/N/P dividers initCLK() programs into PLL1.
func sysClockFreq() uint32 {
pll := PLLParams400MHz()
return xtalHz / pll.M * pll.N / pll.P
}
// pll1QFreq returns the PLL1Q output (SPI1/2/3 kernel clock source).
func pll1QFreq() uint32 {
pll := PLLParams400MHz()
return xtalHz / pll.M * pll.N / pll.Q
}
// hclkFreq, pclk1Freq..pclk4Freq return the AHB/APBx bus clocks. initCLK()
// hardcodes HPRE/D2PPRE1/D2PPRE2/D1PPRE/D3PPRE to Div2, so these are fixed
// ratios of SYSCLK regardless of xtal.
func hclkFreq() uint32 { return sysClockFreq() / 2 }
func pclk1Freq() uint32 { return hclkFreq() / 2 }
func pclk2Freq() uint32 { return hclkFreq() / 2 }
func pclk3Freq() uint32 { return hclkFreq() / 2 }
func pclk4Freq() uint32 { return hclkFreq() / 2 }
// Peripheral kernel clocks as configured by initCLK().
func spi123KerFreq() uint32 { return pll1QFreq() } // D2CCIP1R.SPI123SEL = PLL1_Q
func spi45KerFreq() uint32 { return pclk2Freq() } // D2CCIP1R.SPI45SEL = APB
func spi6KerFreq() uint32 { return pclk4Freq() } // D3CCIPR.SPI6SEL = PCLK4
const I2C_KER_FREQ = HSI_KER_FREQ // D2CCIP2R.I2C123SEL = HSI_KER
func CPUFrequency() uint32 {
return sysClockFreq()
}
// initRNG gates the AHB2 bus clock for the RNG and enables the peripheral.
// HSI48 is started and selected as the RNG kernel clock in initCLK().
func initRNG() {
stm32.RCC.AHB2ENR.SetBits(stm32.RCC_AHB2ENR_RNGEN)
stm32.RNG.CR.SetBits(stm32.RNG_CR_RNGEN)
}
// Alternate function pin selection.
const (
AF0_SYSTEM = 0
AF1_TIM1_2_16_17_HRTIM = 1
AF2_TIM3_4_5_HRTIM = 2
AF3_TIM8_LPTIM1_DFSDM_HRTIM = 3
AF4_I2C1_2_3_4_USART1 = 4
AF5_SPI1_2_3_4_5_6_I2S = 5
AF6_SPI2_3_SAI1_I2S_UART4_DFSDM = 6
AF7_SPI2_3_USART1_2_3_UART5_SPDIFRX = 7
AF8_SAI2_UART4_5_8_SPDIFRX_LPUART = 8
AF9_FDCAN1_2_TIM13_14_QUADSPI_FMC = 9
AF10_OTG_HS_FS_SAI2_QUADSPI_SDMMC2 = 10
AF11_SDMMC2_ETH_MDIO_UART7_SWPMI = 11
AF12_FMC_SDMMC1_MDIOS_OTG_FS_UART7 = 12
AF13_DCMI_DSI_COMP_LTDC = 13
AF14_LTDC = 14
AF15_EVENTOUT = 15
)
// Timer clock = 2×PCLK when both HPRE and PPREx prescalers are active (RM0433 §8.5.5).
func apb1TimFreq() uint64 { return 2 * uint64(pclk1Freq()) }
func apb2TimFreq() uint64 { return 2 * uint64(pclk2Freq()) }
//---------- Timer related code
var (
TIM1 = TIM{
EnableRegister: &stm32.RCC.APB2ENR,
EnableFlag: stm32.RCC_APB2ENR_TIM1EN,
Device: stm32.TIM1,
Channels: [4]TimerChannel{
TimerChannel{Pins: []PinFunction{{PA8, AF1_TIM1_2_16_17_HRTIM}, {PE9, AF1_TIM1_2_16_17_HRTIM}}},
TimerChannel{Pins: []PinFunction{{PA9, AF1_TIM1_2_16_17_HRTIM}, {PE11, AF1_TIM1_2_16_17_HRTIM}}},
TimerChannel{Pins: []PinFunction{{PA10, AF1_TIM1_2_16_17_HRTIM}, {PE13, AF1_TIM1_2_16_17_HRTIM}}},
TimerChannel{Pins: []PinFunction{{PA11, AF1_TIM1_2_16_17_HRTIM}, {PE14, AF1_TIM1_2_16_17_HRTIM}}},
},
busFreq: apb2TimFreq(),
}
TIM2 = TIM{
EnableRegister: &stm32.RCC.APB1LENR,
EnableFlag: stm32.RCC_APB1LENR_TIM2EN,
Device: stm32.TIM2,
Channels: [4]TimerChannel{
TimerChannel{Pins: []PinFunction{{PA0, AF1_TIM1_2_16_17_HRTIM}, {PA5, AF1_TIM1_2_16_17_HRTIM}, {PA15, AF1_TIM1_2_16_17_HRTIM}}},
TimerChannel{Pins: []PinFunction{{PA1, AF1_TIM1_2_16_17_HRTIM}, {PB3, AF1_TIM1_2_16_17_HRTIM}}},
TimerChannel{Pins: []PinFunction{{PA2, AF1_TIM1_2_16_17_HRTIM}, {PB10, AF1_TIM1_2_16_17_HRTIM}}},
TimerChannel{Pins: []PinFunction{{PA3, AF1_TIM1_2_16_17_HRTIM}, {PB11, AF1_TIM1_2_16_17_HRTIM}}},
},
busFreq: apb1TimFreq(),
}
TIM3 = TIM{
EnableRegister: &stm32.RCC.APB1LENR,
EnableFlag: stm32.RCC_APB1LENR_TIM3EN,
Device: stm32.TIM3,
Channels: [4]TimerChannel{
TimerChannel{Pins: []PinFunction{}},
TimerChannel{Pins: []PinFunction{}},
TimerChannel{Pins: []PinFunction{}},
TimerChannel{Pins: []PinFunction{}},
},
busFreq: apb1TimFreq(),
}
TIM4 = TIM{
EnableRegister: &stm32.RCC.APB1LENR,
EnableFlag: stm32.RCC_APB1LENR_TIM4EN,
Device: stm32.TIM4,
Channels: [4]TimerChannel{
TimerChannel{Pins: []PinFunction{{PB6, AF2_TIM3_4_5_HRTIM}, {PD12, AF2_TIM3_4_5_HRTIM}}},
TimerChannel{Pins: []PinFunction{{PB7, AF2_TIM3_4_5_HRTIM}, {PD13, AF2_TIM3_4_5_HRTIM}}},
TimerChannel{Pins: []PinFunction{{PB8, AF2_TIM3_4_5_HRTIM}, {PD14, AF2_TIM3_4_5_HRTIM}}},
TimerChannel{Pins: []PinFunction{{PB9, AF2_TIM3_4_5_HRTIM}, {PD15, AF2_TIM3_4_5_HRTIM}}},
},
busFreq: apb1TimFreq(),
}
TIM5 = TIM{
EnableRegister: &stm32.RCC.APB1LENR,
EnableFlag: stm32.RCC_APB1LENR_TIM5EN,
Device: stm32.TIM5,
Channels: [4]TimerChannel{
TimerChannel{Pins: []PinFunction{{PA0, AF2_TIM3_4_5_HRTIM}, {PH10, AF2_TIM3_4_5_HRTIM}}},
TimerChannel{Pins: []PinFunction{{PA1, AF2_TIM3_4_5_HRTIM}, {PH11, AF2_TIM3_4_5_HRTIM}}},
TimerChannel{Pins: []PinFunction{{PA2, AF2_TIM3_4_5_HRTIM}, {PH12, AF2_TIM3_4_5_HRTIM}}},
TimerChannel{Pins: []PinFunction{{PA3, AF2_TIM3_4_5_HRTIM}, {PI0, AF2_TIM3_4_5_HRTIM}}},
},
busFreq: apb1TimFreq(),
}
TIM8 = TIM{
EnableRegister: &stm32.RCC.APB2ENR,
EnableFlag: stm32.RCC_APB2ENR_TIM8EN,
Device: stm32.TIM8,
Channels: [4]TimerChannel{
TimerChannel{Pins: []PinFunction{{PC6, AF3_TIM8_LPTIM1_DFSDM_HRTIM}, {PI5, AF3_TIM8_LPTIM1_DFSDM_HRTIM}}},
TimerChannel{Pins: []PinFunction{{PC7, AF3_TIM8_LPTIM1_DFSDM_HRTIM}, {PI6, AF3_TIM8_LPTIM1_DFSDM_HRTIM}}},
TimerChannel{Pins: []PinFunction{{PC8, AF3_TIM8_LPTIM1_DFSDM_HRTIM}, {PI7, AF3_TIM8_LPTIM1_DFSDM_HRTIM}}},
TimerChannel{Pins: []PinFunction{{PC9, AF3_TIM8_LPTIM1_DFSDM_HRTIM}, {PI2, AF3_TIM8_LPTIM1_DFSDM_HRTIM}}},
},
busFreq: apb2TimFreq(),
}
)
func (t *TIM) registerUPInterrupt() interrupt.Interrupt {
switch t {
case &TIM1:
return interrupt.New(stm32.IRQ_TIM1_UP, 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 &TIM8:
return interrupt.New(stm32.IRQ_TIM8_UP_TIM13, TIM8.handleUPInterrupt)
}
return interrupt.Interrupt{}
}
func (t *TIM) registerOCInterrupt() interrupt.Interrupt {
switch t {
case &TIM1:
return interrupt.New(stm32.IRQ_TIM_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 &TIM8:
return interrupt.New(stm32.IRQ_TIM8_CC, TIM8.handleOCInterrupt)
}
return interrupt.Interrupt{}
}
func (t *TIM) enableMainOutput() {
if t.Device == stm32.TIM1 || t.Device == stm32.TIM8 {
t.Device.BDTR.SetBits(stm32.TIM_BDTR_MOE)
}
}
type psctype = uint32
type arrtype = uint32
type arrRegType = volatile.Register32
const ARR_MAX = 0x10000
const PSC_MAX = 0x10000
//---------- UART related code
// Configure the UART.
func (uart *UART) configurePins(config UARTConfig) {
config.TX.ConfigureAltFunc(PinConfig{Mode: PinModeUARTTX}, uart.TxAltFuncSelector)
config.RX.ConfigureAltFunc(PinConfig{Mode: PinModeUARTRX}, uart.RxAltFuncSelector)
}
func (uart *UART) getBaudRateDivisor(baudRate uint32) uint32 {
// Default USART kernel clock is the peripheral's own APB clock:
// USART1/6 sit on APB2, the rest on APB1.
clock := pclk1Freq()
if uart.Bus == stm32.USART1 || uart.Bus == stm32.USART6 {
clock = pclk2Freq()
}
return clock / baudRate
}
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
uart.errClearReg = &uart.Bus.ICR
}
func enableAltFuncClock(bus unsafe.Pointer) {
switch bus {
case unsafe.Pointer(stm32.USART1):
stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_USART1EN)
case unsafe.Pointer(stm32.USART2):
stm32.RCC.APB1LENR.SetBits(stm32.RCC_APB1LENR_USART2EN)
case unsafe.Pointer(stm32.USART3):
stm32.RCC.APB1LENR.SetBits(stm32.RCC_APB1LENR_USART3EN)
case unsafe.Pointer(stm32.UART4):
stm32.RCC.APB1LENR.SetBits(stm32.RCC_APB1LENR_UART4EN)
case unsafe.Pointer(stm32.UART5):
stm32.RCC.APB1LENR.SetBits(stm32.RCC_APB1LENR_UART5EN)
case unsafe.Pointer(stm32.USART6):
stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_USART6EN)
case unsafe.Pointer(stm32.UART7):
stm32.RCC.APB1LENR.SetBits(stm32.RCC_APB1LENR_UART7EN)
case unsafe.Pointer(stm32.UART8):
stm32.RCC.APB1LENR.SetBits(stm32.RCC_APB1LENR_UART8EN)
case unsafe.Pointer(stm32.LPUART1):
stm32.RCC.APB4ENR.SetBits(stm32.RCC_APB4ENR_LPUART1EN)
case unsafe.Pointer(stm32.I2C1):
stm32.RCC.APB1LENR.SetBits(stm32.RCC_APB1LENR_I2C1EN)
case unsafe.Pointer(stm32.I2C2):
stm32.RCC.APB1LENR.SetBits(stm32.RCC_APB1LENR_I2C2EN)
case unsafe.Pointer(stm32.I2C3):
stm32.RCC.APB1LENR.SetBits(stm32.RCC_APB1LENR_I2C3EN)
case unsafe.Pointer(stm32.I2C4):
stm32.RCC.APB4ENR.SetBits(stm32.RCC_APB4ENR_I2C4EN)
case unsafe.Pointer(stm32.SPI1):
stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_SPI1EN)
case unsafe.Pointer(stm32.SPI2):
stm32.RCC.APB1LENR.SetBits(stm32.RCC_APB1LENR_SPI2EN)
case unsafe.Pointer(stm32.SPI3):
stm32.RCC.APB1LENR.SetBits(stm32.RCC_APB1LENR_SPI3EN)
case unsafe.Pointer(stm32.SPI4):
stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_SPI4EN)
case unsafe.Pointer(stm32.SPI5):
stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_SPI5EN)
case unsafe.Pointer(stm32.SPI6):
stm32.RCC.APB4ENR.SetBits(stm32.RCC_APB4ENR_SPI6EN)
case unsafe.Pointer(stm32.WWDG):
stm32.RCC.APB3ENR.SetBits(stm32.RCC_APB3ENR_WWDG1EN)
}
}
//---------- GPIO related code
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")
}
}
func (p Pin) enableClock() {
switch p.getPort() {
case stm32.GPIOA:
stm32.RCC.AHB4ENR.SetBits(stm32.RCC_AHB4ENR_GPIOAEN)
case stm32.GPIOB:
stm32.RCC.AHB4ENR.SetBits(stm32.RCC_AHB4ENR_GPIOBEN)
case stm32.GPIOC:
stm32.RCC.AHB4ENR.SetBits(stm32.RCC_AHB4ENR_GPIOCEN)
case stm32.GPIOD:
stm32.RCC.AHB4ENR.SetBits(stm32.RCC_AHB4ENR_GPIODEN)
case stm32.GPIOE:
stm32.RCC.AHB4ENR.SetBits(stm32.RCC_AHB4ENR_GPIOEEN)
case stm32.GPIOF:
stm32.RCC.AHB4ENR.SetBits(stm32.RCC_AHB4ENR_GPIOFEN)
case stm32.GPIOG:
stm32.RCC.AHB4ENR.SetBits(stm32.RCC_AHB4ENR_GPIOGEN)
case stm32.GPIOH:
stm32.RCC.AHB4ENR.SetBits(stm32.RCC_AHB4ENR_GPIOHEN)
case stm32.GPIOI:
stm32.RCC.AHB4ENR.SetBits(stm32.RCC_AHB4ENR_GPIOIEN)
case stm32.GPIOJ:
stm32.RCC.AHB4ENR.SetBits(stm32.RCC_AHB4ENR_GPIOJEN)
case stm32.GPIOK:
stm32.RCC.AHB4ENR.SetBits(stm32.RCC_AHB4ENR_GPIOKEN)
}
}
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
PD0 = portD + 0
PD1 = portD + 1
PD2 = portD + 2
PD3 = portD + 3
PD4 = portD + 4
PD5 = portD + 5
PD6 = portD + 6
PD7 = portD + 7
PD8 = portD + 8
PD9 = portD + 9
PD10 = portD + 10
PD11 = portD + 11
PD12 = portD + 12
PD13 = portD + 13
PD14 = portD + 14
PD15 = portD + 15
PE0 = portE + 0
PE1 = portE + 1
PE2 = portE + 2
PE3 = portE + 3
PE4 = portE + 4
PE5 = portE + 5
PE6 = portE + 6
PE7 = portE + 7
PE8 = portE + 8
PE9 = portE + 9
PE10 = portE + 10
PE11 = portE + 11
PE12 = portE + 12
PE13 = portE + 13
PE14 = portE + 14
PE15 = portE + 15
PF0 = portF + 0
PF1 = portF + 1
PF2 = portF + 2
PF3 = portF + 3
PF4 = portF + 4
PF5 = portF + 5
PF6 = portF + 6
PF7 = portF + 7
PF8 = portF + 8
PF9 = portF + 9
PF10 = portF + 10
PF11 = portF + 11
PF12 = portF + 12
PF13 = portF + 13
PF14 = portF + 14
PF15 = portF + 15
PG0 = portG + 0
PG1 = portG + 1
PG2 = portG + 2
PG3 = portG + 3
PG4 = portG + 4
PG5 = portG + 5
PG6 = portG + 6
PG7 = portG + 7
PG8 = portG + 8
PG9 = portG + 9
PG10 = portG + 10
PG11 = portG + 11
PG12 = portG + 12
PG13 = portG + 13
PG14 = portG + 14
PG15 = portG + 15
PH0 = portH + 0
PH1 = portH + 1
PH2 = portH + 2
PH3 = portH + 3
PH4 = portH + 4
PH5 = portH + 5
PH6 = portH + 6
PH7 = portH + 7
PH8 = portH + 8
PH9 = portH + 9
PH10 = portH + 10
PH11 = portH + 11
PH12 = portH + 12
PH13 = portH + 13
PH14 = portH + 14
PH15 = portH + 15
PI0 = portI + 0
PI1 = portI + 1
PI2 = portI + 2
PI3 = portI + 3
PI4 = portI + 4
PI5 = portI + 5
PI6 = portI + 6
PI7 = portI + 7
PI8 = portI + 8
PI9 = portI + 9
PI10 = portI + 10
PI11 = portI + 11
PI12 = portI + 12
PI13 = portI + 13
PI14 = portI + 14
PI15 = portI + 15
PJ0 = portJ + 0
PJ1 = portJ + 1
PJ2 = portJ + 2
PJ3 = portJ + 3
PJ4 = portJ + 4
PJ5 = portJ + 5
PJ6 = portJ + 6
PJ7 = portJ + 7
PJ8 = portJ + 8
PJ9 = portJ + 9
PJ10 = portJ + 10
PJ11 = portJ + 11
PJ12 = portJ + 12
PJ13 = portJ + 13
PJ14 = portJ + 14
PJ15 = portJ + 15
PK0 = portK + 0
PK1 = portK + 1
PK2 = portK + 2
PK3 = portK + 3
PK4 = portK + 4
PK5 = portK + 5
PK6 = portK + 6
PK7 = portK + 7
)
//---------- I2C related code
// getFreqRange returns the TIMINGR value for the given I2C frequency.
// Values are for HSI_KER=64MHz (configured in initCLK).
// Derived from ST I2C timing calculator.
func (i2c *I2C) getFreqRange(br uint32) uint32 {
switch br {
case 10 * KHz:
return 0x30E0E7CF
case 100 * KHz:
return 0x10B0BFCF
case 400 * KHz:
return 0x00901E74
case 800 * KHz:
return 0x00401137
case 1_000 * KHz:
return 0x00401028
default:
return 0x10B0BFCF
}
}
+57
View File
@@ -0,0 +1,57 @@
//go:build stm32 && stm32h7
package machine
import (
"device/arm"
"runtime/volatile"
"unsafe"
)
// Cortex-M7 cache maintenance by-address registers (ARMv7-M ARM Table B3-7).
var (
scbDCIMVAC = (*volatile.Register32)(unsafe.Pointer(uintptr(0xE000EF5C))) // Invalidate D-cache line by address (W)
scbDCCMVAC = (*volatile.Register32)(unsafe.Pointer(uintptr(0xE000EF68))) // Clean D-cache line by address (W)
scbDCCIMVAC = (*volatile.Register32)(unsafe.Pointer(uintptr(0xE000EF70))) // Clean+Invalidate D-cache line by address (W)
)
const dCacheLineSize = 32 // bytes; fixed on Cortex-M7
// DCacheClean writes dirty cache lines covering [addr, addr+size) back to
// memory without invalidating them. Call before the CPU hands a buffer to a
// DMA controller that only reads the buffer.
func DCacheClean(addr uintptr, size uintptr) {
arm.Asm("dsb 0xF")
end := addr + size
for a := addr &^ (dCacheLineSize - 1); a < end; a += dCacheLineSize {
scbDCCMVAC.Set(uint32(a))
}
arm.Asm("dsb 0xF")
arm.Asm("isb 0xF")
}
// DCacheInvalidate marks cache lines covering [addr, addr+size) as invalid so
// the next access re-fetches from memory. Call after a DMA write completes
// before the CPU reads the buffer.
func DCacheInvalidate(addr uintptr, size uintptr) {
arm.Asm("dsb 0xF")
end := addr + size
for a := addr &^ (dCacheLineSize - 1); a < end; a += dCacheLineSize {
scbDCIMVAC.Set(uint32(a))
}
arm.Asm("dsb 0xF")
arm.Asm("isb 0xF")
}
// DCacheFlush cleans and invalidates cache lines covering [addr, addr+size).
// Use when the region is both written by the CPU and read by DMA (or vice
// versa) and you want to synchronize in a single pass.
func DCacheFlush(addr uintptr, size uintptr) {
arm.Asm("dsb 0xF")
end := addr + size
for a := addr &^ (dCacheLineSize - 1); a < end; a += dCacheLineSize {
scbDCCIMVAC.Set(uint32(a))
}
arm.Asm("dsb 0xF")
arm.Asm("isb 0xF")
}
+27
View File
@@ -0,0 +1,27 @@
//go:build stm32 && stm32h7
package machine
import (
"device/stm32"
"runtime/volatile"
)
func getEXTIConfigRegister(pin uint8) *volatile.Register32 {
switch (pin & 0xf) / 4 {
case 0:
return &stm32.SYSCFG.EXTICR1
case 1:
return &stm32.SYSCFG.EXTICR2
case 2:
return &stm32.SYSCFG.EXTICR3
case 3:
return &stm32.SYSCFG.EXTICR4
}
return nil
}
func enableEXTIConfigRegisters() {
// Enable SYSCFG in APB4ENR
stm32.RCC.APB4ENR.SetBits(stm32.RCC_APB4ENR_SYSCFGEN)
}
+111
View File
@@ -0,0 +1,111 @@
//go:build stm32 && stm32h7
package machine
import (
"device/stm32"
"runtime/interrupt"
)
// Callbacks for pin interrupt events
var pinCallbacks [16]func(Pin)
// The pin currently associated with interrupt callback
// for a given slot.
var interruptPins [16]Pin
// SetInterrupt sets an interrupt to be executed when a particular pin changes
// state. The pin should already be configured as an input, including a pull up
// or down if no external pull is provided.
func (p Pin) SetInterrupt(change PinChange, callback func(Pin)) error {
port := uint32(uint8(p) / 16)
pin := uint8(p) % 16
enableEXTIConfigRegisters()
if callback == nil {
stm32.EXTI.CPUIMR1.ClearBits(1 << pin)
pinCallbacks[pin] = nil
return nil
}
if pinCallbacks[pin] != nil {
return ErrNoPinChangeChannel
}
pinCallbacks[pin] = callback
interruptPins[pin] = p
crReg := getEXTIConfigRegister(pin)
shift := (pin & 0x3) * 4
crReg.ReplaceBits(port, 0xf, shift)
if (change & PinRising) != 0 {
stm32.EXTI.RTSR1.SetBits(1 << pin)
}
if (change & PinFalling) != 0 {
stm32.EXTI.FTSR1.SetBits(1 << pin)
}
stm32.EXTI.CPUIMR1.SetBits(1 << pin)
intr := p.registerInterrupt()
intr.SetPriority(0)
intr.Enable()
return nil
}
func (p Pin) registerInterrupt() interrupt.Interrupt {
pin := uint8(p) % 16
switch pin {
case 0:
return interrupt.New(stm32.IRQ_EXTI0, handlePinInterrupt0)
case 1:
return interrupt.New(stm32.IRQ_EXTI1, handlePinInterrupt1)
case 2:
return interrupt.New(stm32.IRQ_EXTI2, handlePinInterrupt2)
case 3:
return interrupt.New(stm32.IRQ_EXTI3, handlePinInterrupt3)
case 4:
return interrupt.New(stm32.IRQ_EXTI4, handlePinInterrupt4)
case 5, 6, 7, 8, 9:
return interrupt.New(stm32.IRQ_EXTI9_5, handlePinInterrupt9_5)
case 10, 11, 12, 13, 14, 15:
return interrupt.New(stm32.IRQ_EXTI15_10, handlePinInterrupt15_10)
}
return interrupt.Interrupt{}
}
func handlePinInterrupt0(interrupt.Interrupt) { handlePinInterrupt(0) }
func handlePinInterrupt1(interrupt.Interrupt) { handlePinInterrupt(1) }
func handlePinInterrupt2(interrupt.Interrupt) { handlePinInterrupt(2) }
func handlePinInterrupt3(interrupt.Interrupt) { handlePinInterrupt(3) }
func handlePinInterrupt4(interrupt.Interrupt) { handlePinInterrupt(4) }
func handlePinInterrupt9_5(interrupt.Interrupt) {
handlePinInterrupt(5)
handlePinInterrupt(6)
handlePinInterrupt(7)
handlePinInterrupt(8)
handlePinInterrupt(9)
}
func handlePinInterrupt15_10(interrupt.Interrupt) {
handlePinInterrupt(10)
handlePinInterrupt(11)
handlePinInterrupt(12)
handlePinInterrupt(13)
handlePinInterrupt(14)
handlePinInterrupt(15)
}
func handlePinInterrupt(pin uint8) {
if stm32.EXTI.CPUPR1.HasBits(1 << pin) {
// Writing 1 to the pending register clears the
// pending flag for that bit
stm32.EXTI.CPUPR1.Set(1 << pin)
callback := pinCallbacks[pin]
if callback != nil {
callback(interruptPins[pin])
}
}
}
+173
View File
@@ -0,0 +1,173 @@
//go:build stm32 && stm32h753
package machine
import (
"device/stm32"
"runtime/volatile"
"unsafe"
)
var (
HSEM_CORE1 = (*HSEM_CORE_Type)(unsafe.Pointer((uintptr(unsafe.Pointer(stm32.HSEM)) + 0x100)))
HSEM_CORE2 = (*HSEM_CORE_Type)(unsafe.Pointer((uintptr(unsafe.Pointer(stm32.HSEM)) + 0x110)))
)
// HSEM_CORE represents the registers for a core's hardware semaphore interrupts.
type HSEM_CORE_Type struct {
IER volatile.Register32 // HSEM Interrupt enable register Address offset: HSEM + 0x100 + n*0x10 (Interrupt N (0 or 1))
ICR volatile.Register32 // HSEM Interrupt clear register Address offset: HSEM + 0x104 + n*0x10 (Interrupt N (0 or 1))
ISR volatile.Register32 // HSEM Interrupt Status register Address offset: HSEM + 0x108 + n*0x10 (Interrupt N (0 or 1))
MISR volatile.Register32 // HSEM Interrupt Masked Status register Address offset: HSEM + 0x10C + n*0x10 (Interrupt N (0 or 1))
}
type HSEM_ID_Type uint8
// Lock performs a 1-step (read) lock on the receiver semaphore ID.
// Semaphores can be used to ensure synchronization between processes running on
// different cores. Lock provides a non-blocking mechanism to lock semaphores
// in an atomic way.
// Returns true if and only if the semaphore lock is acquired or the given core
// already has the semaphore locked.
func (id HSEM_ID_Type) Lock(core uint32) bool {
switch id {
case 0:
return stm32.HSEM.RLR0.Get() == (stm32.HSEM_RLR_LOCK | core)
case 1:
return stm32.HSEM.RLR1.Get() == (stm32.HSEM_RLR_LOCK | core)
case 2:
return stm32.HSEM.RLR2.Get() == (stm32.HSEM_RLR_LOCK | core)
case 3:
return stm32.HSEM.RLR3.Get() == (stm32.HSEM_RLR_LOCK | core)
case 4:
return stm32.HSEM.RLR4.Get() == (stm32.HSEM_RLR_LOCK | core)
case 5:
return stm32.HSEM.RLR5.Get() == (stm32.HSEM_RLR_LOCK | core)
case 6:
return stm32.HSEM.RLR6.Get() == (stm32.HSEM_RLR_LOCK | core)
case 7:
return stm32.HSEM.RLR7.Get() == (stm32.HSEM_RLR_LOCK | core)
case 8:
return stm32.HSEM.RLR8.Get() == (stm32.HSEM_RLR_LOCK | core)
case 9:
return stm32.HSEM.RLR9.Get() == (stm32.HSEM_RLR_LOCK | core)
case 10:
return stm32.HSEM.RLR10.Get() == (stm32.HSEM_RLR_LOCK | core)
case 11:
return stm32.HSEM.RLR11.Get() == (stm32.HSEM_RLR_LOCK | core)
case 12:
return stm32.HSEM.RLR12.Get() == (stm32.HSEM_RLR_LOCK | core)
case 13:
return stm32.HSEM.RLR13.Get() == (stm32.HSEM_RLR_LOCK | core)
case 14:
return stm32.HSEM.RLR14.Get() == (stm32.HSEM_RLR_LOCK | core)
case 15:
return stm32.HSEM.RLR15.Get() == (stm32.HSEM_RLR_LOCK | core)
case 16:
return stm32.HSEM.RLR16.Get() == (stm32.HSEM_RLR_LOCK | core)
case 17:
return stm32.HSEM.RLR17.Get() == (stm32.HSEM_RLR_LOCK | core)
case 18:
return stm32.HSEM.RLR18.Get() == (stm32.HSEM_RLR_LOCK | core)
case 19:
return stm32.HSEM.RLR19.Get() == (stm32.HSEM_RLR_LOCK | core)
case 20:
return stm32.HSEM.RLR20.Get() == (stm32.HSEM_RLR_LOCK | core)
case 21:
return stm32.HSEM.RLR21.Get() == (stm32.HSEM_RLR_LOCK | core)
case 22:
return stm32.HSEM.RLR22.Get() == (stm32.HSEM_RLR_LOCK | core)
case 23:
return stm32.HSEM.RLR23.Get() == (stm32.HSEM_RLR_LOCK | core)
case 24:
return stm32.HSEM.RLR24.Get() == (stm32.HSEM_RLR_LOCK | core)
case 25:
return stm32.HSEM.RLR25.Get() == (stm32.HSEM_RLR_LOCK | core)
case 26:
return stm32.HSEM.RLR26.Get() == (stm32.HSEM_RLR_LOCK | core)
case 27:
return stm32.HSEM.RLR27.Get() == (stm32.HSEM_RLR_LOCK | core)
case 28:
return stm32.HSEM.RLR28.Get() == (stm32.HSEM_RLR_LOCK | core)
case 29:
return stm32.HSEM.RLR29.Get() == (stm32.HSEM_RLR_LOCK | core)
case 30:
return stm32.HSEM.RLR30.Get() == (stm32.HSEM_RLR_LOCK | core)
case 31:
return stm32.HSEM.RLR31.Get() == (stm32.HSEM_RLR_LOCK | core)
}
return false
}
// Unlock releases the lock on the receiver semaphore ID.
// Semaphores can be used to ensure synchronization between processes running on
// different cores. Unlock provides a non-blocking mechanism to unlock
// semaphores in an atomic way.
func (id HSEM_ID_Type) Unlock(core uint32) {
switch id {
case 0:
stm32.HSEM.R0.Set(core)
case 1:
stm32.HSEM.R1.Set(core)
case 2:
stm32.HSEM.R2.Set(core)
case 3:
stm32.HSEM.R3.Set(core)
case 4:
stm32.HSEM.R4.Set(core)
case 5:
stm32.HSEM.R5.Set(core)
case 6:
stm32.HSEM.R6.Set(core)
case 7:
stm32.HSEM.R7.Set(core)
case 8:
stm32.HSEM.R8.Set(core)
case 9:
stm32.HSEM.R9.Set(core)
case 10:
stm32.HSEM.R10.Set(core)
case 11:
stm32.HSEM.R11.Set(core)
case 12:
stm32.HSEM.R12.Set(core)
case 13:
stm32.HSEM.R13.Set(core)
case 14:
stm32.HSEM.R14.Set(core)
case 15:
stm32.HSEM.R15.Set(core)
case 16:
stm32.HSEM.R16.Set(core)
case 17:
stm32.HSEM.R17.Set(core)
case 18:
stm32.HSEM.R18.Set(core)
case 19:
stm32.HSEM.R19.Set(core)
case 20:
stm32.HSEM.R20.Set(core)
case 21:
stm32.HSEM.R21.Set(core)
case 22:
stm32.HSEM.R22.Set(core)
case 23:
stm32.HSEM.R23.Set(core)
case 24:
stm32.HSEM.R24.Set(core)
case 25:
stm32.HSEM.R25.Set(core)
case 26:
stm32.HSEM.R26.Set(core)
case 27:
stm32.HSEM.R27.Set(core)
case 28:
stm32.HSEM.R28.Set(core)
case 29:
stm32.HSEM.R29.Set(core)
case 30:
stm32.HSEM.R30.Set(core)
case 31:
stm32.HSEM.R31.Set(core)
}
}
+46
View File
@@ -0,0 +1,46 @@
//go:build stm32 && stm32h7
package machine
import "device/stm32"
// PLLParams400MHz returns the HSE PLL dividers needed to reach a 800MHz VCO
// (400MHz SYSCLK, P=2, Q=4) for the configured crystal frequency.
// It returns the appropriate PLL1RGE range value in the R field.
func PLLParams400MHz() PLLParams {
var m, n, rge uint32
switch xtalHz {
case 8_000_000:
m = 1
n = 100
case 16_000_000:
m = 2
n = 100
case 24_000_000:
m = 3
n = 100
case 25_000_000:
m = 5
n = 160
default:
panic("unsupported xtal frequency")
}
vcoIn := xtalHz / m
if vcoIn < 2_000_000 {
rge = stm32.RCC_PLLCFGR_PLL1RGE_Range1
} else if vcoIn < 4_000_000 {
rge = stm32.RCC_PLLCFGR_PLL1RGE_Range2
} else if vcoIn < 8_000_000 {
rge = stm32.RCC_PLLCFGR_PLL1RGE_Range4
} else {
rge = stm32.RCC_PLLCFGR_PLL1RGE_Range8
}
return PLLParams{M: m, N: n, P: 2, Q: 4, R: rge}
}
// HSEBypass returns whether the HSE clock is configured in bypass mode (external MCO clock).
func HSEBypass() bool {
return hseBypass
}
+148
View File
@@ -0,0 +1,148 @@
//go:build stm32 && stm32h7
package machine
// Peripheral abstraction layer for SPI on the stm32h7 family
import (
"device/stm32"
"errors"
"math/bits"
"runtime/volatile"
"unsafe"
)
var errSPIOverrun = errors.New("SPI overrun or mode fault")
type SPI struct {
Bus *stm32.SPI_Type
AltFuncSelector uint8
}
// 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 STM32 SPI interface.
func (spi *SPI) Configure(config SPIConfig) error {
// disable SPI interface before any configuration changes
spi.Bus.CR1.ClearBits(stm32.SPI_CR1_SPE)
// enable clock for SPI
enableAltFuncClock(unsafe.Pointer(spi.Bus))
// init pins
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)
// CFG1 configuration: MBR and DSIZE (8-bit)
cfg1 := spi.getBaudRate(config)
cfg1 |= (8 - 1) << stm32.SPI_CFG1_DSIZE_Pos // 8-bit data size
spi.Bus.CFG1.Set(cfg1)
// CFG2 configuration: CPOL, CPHA, MASTER, SSM, COMM
var cfg2 uint32 = stm32.SPI_CFG2_MASTER // bit mask, not field value
cfg2 |= stm32.SPI_CFG2_SSM // software NSS; bit mask, not field value
if config.LSBFirst {
cfg2 |= 1 << 23 // LSBFRST is bit 23 in CFG2
}
// set polarity and phase
switch config.Mode {
case Mode1:
cfg2 |= stm32.SPI_CFG2_CPHA_SecondEdge << stm32.SPI_CFG2_CPHA_Pos
case Mode2:
cfg2 |= stm32.SPI_CFG2_CPOL_IdleHigh << stm32.SPI_CFG2_CPOL_Pos
case Mode3:
cfg2 |= stm32.SPI_CFG2_CPOL_IdleHigh << stm32.SPI_CFG2_CPOL_Pos
cfg2 |= stm32.SPI_CFG2_CPHA_SecondEdge << stm32.SPI_CFG2_CPHA_Pos
}
spi.Bus.CFG2.Set(cfg2)
// CR2: TSIZE = 0 (Endless mode)
spi.Bus.CR2.Set(0)
// CR1: SPE and SSI (use bit masks, not field values)
spi.Bus.CR1.Set(stm32.SPI_CR1_SSI | stm32.SPI_CR1_SPE)
return nil
}
func (spi *SPI) config8Bits() {
// Already handled in Configure via DSIZE
}
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 {
clock := spi45KerFreq()
if spi.Bus == stm32.SPI1 || spi.Bus == stm32.SPI2 || spi.Bus == stm32.SPI3 {
clock = spi123KerFreq()
} else if spi.Bus == stm32.SPI6 {
clock = spi6KerFreq()
}
if config.Frequency == 0 {
config.Frequency = clock / 2
}
// 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/2 {
freq = clock / 2
}
// Round up to the next power-of-two divisor so output never exceeds freq.
// MBR encodes actual divider as 2^(MBR+1), so MBR = ceil_log2(ratio) - 1.
div := bits.Len32(clock/freq-1) - 1
if div > 7 {
div = 7
}
return uint32(div) << stm32.SPI_CFG1_MBR_Pos
}
// Transfer writes/reads a single byte using the SPI interface.
func (spi *SPI) Transfer(w byte) (byte, error) {
// RM0433 §50.4.9: set CSTART before writing TXDR.
spi.Bus.CR1.SetBits(stm32.SPI_CR1_CSTART)
// Wait for TXP (Transmit packet space available)
for !spi.Bus.SR.HasBits(stm32.SPI_SR_TXP) {
}
// Write to TXDR as 8-bit access to push exactly one byte into the FIFO.
(*volatile.Register8)(unsafe.Pointer(&spi.Bus.TXDR.Reg)).Set(w)
// Wait for RXP (Receive packet available)
for !spi.Bus.SR.HasBits(stm32.SPI_SR_RXP) {
}
// Check for overrun or mode fault before reading, to avoid returning stale data.
if sr := spi.Bus.SR.Get(); sr&(stm32.SPI_SR_OVR|stm32.SPI_SR_MODF) != 0 {
spi.Bus.IFCR.SetBits(stm32.SPI_IFCR_OVRC | stm32.SPI_IFCR_MODFC)
return 0, errSPIOverrun
}
// Read from RXDR
data := byte(spi.Bus.RXDR.Get())
return data, nil
}
+560
View File
@@ -0,0 +1,560 @@
//go:build stm32 && stm32h7
package machine
import (
"device/arm"
"device/stm32"
"machine/usb"
"runtime/interrupt"
"runtime/volatile"
"unsafe"
)
// Synopsys DesignWare OTG registers
// The SVD-generated Go device file is missing some device-mode registers,
// so we define them here based on the Synopsys OTG IP.
type usbOTGRegs struct {
// Global registers (0x000)
GOTGCTL volatile.Register32 // 0x00
GOTGINT volatile.Register32 // 0x04
GAHBCFG volatile.Register32 // 0x08
GUSBCFG volatile.Register32 // 0xC
GRSTCTL volatile.Register32 // 0x10
GINTSTS volatile.Register32 // 0x14
GINTMSK volatile.Register32 // 0x18
GRXSTSR volatile.Register32 // 0x1C
GRXSTSP volatile.Register32 // 0x20
GRXFSIZ volatile.Register32 // 0x24
GNPTXFSIZ volatile.Register32 // 0x28
GNPTXSTS volatile.Register32 // 0x2C
_ [8]byte
GCCFG volatile.Register32 // 0x38
CID volatile.Register32 // 0x3C
_ [20]byte
GLPMCFG volatile.Register32 // 0x54
GPWRDN volatile.Register32 // 0x58
_ [4]byte
GDFIFO_S volatile.Register32 // 0x60
_ [156]byte
HPTXFSIZ volatile.Register32 // 0x100
DIEPTXF [15]volatile.Register32 // 0x104
_ [1728]byte
// Device registers (0x800)
DCFG volatile.Register32 // 0x800
DCTL volatile.Register32 // 0x804
DSTS volatile.Register32 // 0x808
_ [4]byte
DIEPMSK volatile.Register32 // 0x810
DOEPMSK volatile.Register32 // 0x814
DAINT volatile.Register32 // 0x818
DAINTMSK volatile.Register32 // 0x81C
_ [32]byte
DIEPEMPMSK volatile.Register32 // 0x840
_ [188]byte
// Endpoint registers
INEP [16]struct {
CTL volatile.Register32 // 0x900 + n*0x20
_ [4]byte
INT volatile.Register32 // 0x908 + n*0x20
_ [4]byte
TSIZ volatile.Register32 // 0x910 + n*0x20
DMA volatile.Register32 // 0x914 + n*0x20
TXFSTS volatile.Register32 // 0x918 + n*0x20
_ [4]byte
}
OUTEP [16]struct {
CTL volatile.Register32 // 0xB00 + n*0x20
_ [4]byte
INT volatile.Register32 // 0xB08 + n*0x20
_ [4]byte
TSIZ volatile.Register32 // 0xB10 + n*0x20
DMA volatile.Register32 // 0xB14 + n*0x20
_ [8]byte
}
_ [256]byte
// Power and clock gating registers (0xE00)
PCGCCTL volatile.Register32 // 0xE00
}
// USB2 OTG_FS: the FS-only core wired to PA11/PA12 (Nucleo CN13).
// USB1 OTG_HS at 0x40040000 uses ULPI or its own embedded PHY on PB14/PB15,
// which is NOT routed to the user USB connector on this board.
var usbOTG = (*usbOTGRegs)(unsafe.Pointer(uintptr(0x40080000)))
const (
// GUSBCFG bits
GUSBCFG_PHYSEL = 1 << 6
GUSBCFG_TRDT_Pos = 10
GUSBCFG_FDMOD = 1 << 30
// GAHBCFG bits
GAHBCFG_GINT = 1 << 0
// GRSTCTL bits
GRSTCTL_CSRST = 1 << 0
GRSTCTL_RXFFLSH = 1 << 4
GRSTCTL_TXFFLSH = 1 << 5
GRSTCTL_TXFNUM_ALL = 0x10 << 6
GRSTCTL_AHBIDL = 1 << 31
// GINTSTS / GINTMSK bits
GINT_RXFLVL = 1 << 4
GINT_GINAKEFF = 1 << 6
GINT_GONAKEFF = 1 << 7
GINT_USBSUSP = 1 << 11
GINT_USBRST = 1 << 12
GINT_ENUMDNE = 1 << 13
GINT_IEPINT = 1 << 18
GINT_OEPINT = 1 << 19
// DCFG bits
DCFG_DSPD_FS = 0x3 << 0
// DCTL bits
DCTL_RWUSIG = 1 << 0
DCTL_SDIS = 1 << 1
DCTL_GINSTS = 1 << 2
DCTL_GONSTS = 1 << 3
// DIEPCTL / DOEPCTL bits
DEPCTL_MPSIZ_Pos = 0
DEPCTL_USBAEP = 1 << 15
DEPCTL_EPTYP_Pos = 18
DEPCTL_STALL = 1 << 21
DEPCTL_CNAK = 1 << 26
DEPCTL_SNAK = 1 << 27
DEPCTL_TXFNUM_Pos = 22
DEPCTL_EPDIS = 1 << 30
DEPCTL_EPENA = 1 << 31
// DIEPINT / DOEPINT bits
DEPINT_XFERC = 1 << 0
DEPINT_EPDISD = 1 << 1
DEPINT_SETUP = 1 << 3
NumberOfUSBEndpoints = 9
// FIFO layout in 32-bit words: shared RX FIFO plus one 64-word TX FIFO
// for EP0 and each of the 8 IN endpoints (256 + 9*64 = 832 ≤ 1024).
rxFIFOWords = 256
txFIFOWords = 64
)
var (
// ep0OutReceived signals that an OUT packet was received on EP0.
// Volatile: written from the USB interrupt, busy-waited on from thread mode.
ep0OutReceived volatile.Register8
)
// Configure the USB peripheral.
func (dev *USBDevice) Configure(config UARTConfig) {
if dev.initcomplete {
return
}
// 1. Enable clocks
stm32.RCC.AHB1ENR.SetBits(stm32.RCC_AHB1ENR_USB2OTGHSEN)
// The FS core has no ULPI clock, but AHB1LPENR resets with
// USB2OTGULPILPEN set, so in CPU Sleep mode (the scheduler's WFE) the RCC
// waits on a ULPI clock that never comes and the core's AHB interface
// stalls, killing USB whenever the CPU sleeps.
// Keep the OTG bus clock running in Sleep, drop the ULPI one.
stm32.RCC.AHB1LPENR.SetBits(stm32.RCC_AHB1LPENR_USB2OTGLPEN)
stm32.RCC.AHB1LPENR.ClearBits(stm32.RCC_AHB1LPENR_USB2OTGULPILPEN)
// Enable USB regulator (USB33DEN) for internal PHY.
// Already done in initCLK, but setting here as well for safety.
stm32.PWR.CR3.SetBits(stm32.PWR_CR3_USB33DEN)
// Pulse RCC reset to clear any stale state from a warm reset.
stm32.RCC.AHB1RSTR.SetBits(stm32.RCC_AHB1RSTR_USB2OTGRST)
stm32.RCC.AHB1RSTR.ClearBits(stm32.RCC_AHB1RSTR_USB2OTGRST)
// 2. Setup pins (PA11=DM, PA12=DP) for USB2 OTG_FS — AF10.
PA11.ConfigureAltFunc(PinConfig{Mode: PinModeUSB}, AF10_OTG_HS_FS_SAI2_QUADSPI_SDMMC2)
PA12.ConfigureAltFunc(PinConfig{Mode: PinModeUSB}, AF10_OTG_HS_FS_SAI2_QUADSPI_SDMMC2)
// 3. Select internal FS PHY BEFORE the core reset below — the reset FSM
// samples the PHY clock, which only runs once PHYSEL is set. Give the
// clock a few cycles to start or CSRST can hang / self-clear too early.
usbOTG.GUSBCFG.SetBits(GUSBCFG_PHYSEL)
for j := 0; j < 10_000; j++ {
arm.Asm("nop")
}
// 4. Core Reset — wait for AHB idle then pulse CSRST.
for usbOTG.GRSTCTL.Get()&GRSTCTL_AHBIDL == 0 {
}
usbOTG.GRSTCTL.SetBits(GRSTCTL_CSRST)
for usbOTG.GRSTCTL.Get()&GRSTCTL_CSRST != 0 {
}
// Power up the FS transceiver AFTER the core reset: CSRST wipes GCCFG,
// so setting PWRDWN earlier leaves the transceiver off and the DP
// pull-up never appears (host sees no cable).
// No HW VBUS sensing: CN13's VBUS pin is not wired to the MCU VBUS-sense
// input on this board, so leave GCCFG.VBDEN (bit 21) clear and force
// session/VBUS valid via GOTGCTL instead (below).
usbOTG.GCCFG.Set(1 << 16) // PWRDWN
// Make sure the PHY clock is not gated (e.g. by a bootloader).
usbOTG.PCGCCTL.Set(0)
// Stay soft-disconnected until configuration is complete; CSRST left
// DCTL at its default "connected" state.
usbOTG.DCTL.SetBits(DCTL_SDIS)
// 5. Force device mode now that the core is out of reset. The mode
// change takes effect only after up to 25 ms (RM0433); poll GINTSTS.CMOD
// (bit 0: 0 = device) with a generous busy-wait bound.
usbOTG.GUSBCFG.SetBits(GUSBCFG_FDMOD)
for j := 0; j < 20_000_000 && usbOTG.GINTSTS.Get()&0x1 != 0; j++ {
arm.Asm("nop")
}
// Override all session/VBUS valid bits regardless of hardware pin state.
// GOTGCTL[2]=VBVALOEN, [3]=VBVALOVAL, [6]=BVALOEN, [7]=BVALOVAL.
usbOTG.GOTGCTL.SetBits(0x4 | 0x8 | 0x40 | 0x80)
// Set turnaround time: HCLK=200MHz → TRDT=6 per RM0433 Table 362.
usbOTG.GUSBCFG.ReplaceBits(0x6<<GUSBCFG_TRDT_Pos, 0xF<<GUSBCFG_TRDT_Pos, 0)
// 6. FIFO Configuration (total 1024 words shared by RX + all TX FIFOs).
// initEndpoint assigns TX FIFO n to IN endpoint n, so every IN endpoint
// 1..8 needs a configured FIFO even if the current class uses only a few.
usbOTG.GRXFSIZ.Set(rxFIFOWords)
usbOTG.GNPTXFSIZ.Set(txFIFOWords<<16 | rxFIFOWords)
for i, offset := 0, uint32(rxFIFOWords+txFIFOWords); i < 8; i++ {
usbOTG.DIEPTXF[i].Set(txFIFOWords<<16 | offset) // DIEPTXF[i] = FIFO i+1
offset += txFIFOWords
}
// Flush all FIFOs after (re)sizing them.
usbOTG.GRSTCTL.SetBits(GRSTCTL_RXFFLSH)
for usbOTG.GRSTCTL.Get()&GRSTCTL_RXFFLSH != 0 {
}
usbOTG.GRSTCTL.SetBits(GRSTCTL_TXFFLSH | GRSTCTL_TXFNUM_ALL)
for usbOTG.GRSTCTL.Get()&GRSTCTL_TXFFLSH != 0 {
}
// 7. Device Configuration
// Device Speed (FS)
usbOTG.DCFG.ReplaceBits(DCFG_DSPD_FS, 0x3, 0)
// 8. Per-endpoint interrupt masks
usbOTG.DIEPMSK.Set(DEPINT_XFERC)
usbOTG.DOEPMSK.Set(DEPINT_XFERC | DEPINT_SETUP)
// 9. Interrupts
// Clear anything pending, then unmask Reset, Enumeration Done,
// RX FIFO Non-Empty, Setup Done (via OEPINT).
usbOTG.GINTSTS.Set(0xFFFFFFFF)
usbOTG.GINTMSK.SetBits(GINT_USBSUSP | GINT_USBRST | GINT_ENUMDNE | GINT_RXFLVL | GINT_IEPINT | GINT_OEPINT)
// Global Interrupt Enable
usbOTG.GAHBCFG.SetBits(GAHBCFG_GINT)
// 10. Enable IRQ
i := interrupt.New(stm32.IRQ_OTG_FS, handleUSBIRQ)
i.SetPriority(0)
i.Enable()
dev.initcomplete = true
// Release soft-disconnect: pulls D+ high, making device visible to host.
usbOTG.DCTL.ClearBits(DCTL_SDIS)
}
func initEndpoint(ep, config uint32) {
if ep == 0 {
// Control endpoint
// IN
usbOTG.INEP[0].CTL.ReplaceBits(0, 0x3, DEPCTL_MPSIZ_Pos) // Max packet size 64 (00)
usbOTG.INEP[0].INT.Set(0xFF) // Clear interrupts
// OUT
usbOTG.OUTEP[0].CTL.ReplaceBits(0, 0x3, DEPCTL_MPSIZ_Pos) // Max packet size 64 (00)
usbOTG.OUTEP[0].INT.Set(0xFF) // Clear interrupts
// Unmask interrupts for EP0
usbOTG.DAINTMSK.SetBits(0x10001) // EP0 IN and OUT
} else {
isIn := (config & uint32(usb.EndpointIn)) != 0
typ := config & 0x03
if isIn {
// Configure IN endpoint — do NOT set EPENA; set it only when queuing a transfer.
ctl := uint32(DEPCTL_USBAEP)
ctl |= (typ << DEPCTL_EPTYP_Pos)
ctl |= (ep << DEPCTL_TXFNUM_Pos)
ctl |= (64 << DEPCTL_MPSIZ_Pos) // MPS = 64 bytes
ctl |= DEPCTL_SNAK // Start NAKing until data is ready
usbOTG.INEP[ep].CTL.Set(ctl)
usbOTG.INEP[ep].INT.Set(0xFF) // Clear any stale interrupts
usbOTG.DAINTMSK.SetBits(1 << ep)
} else {
// Configure OUT endpoint — do NOT set EPENA here; AckUsbOutTransfer arms it.
ctl := uint32(DEPCTL_USBAEP)
ctl |= (typ << DEPCTL_EPTYP_Pos)
ctl |= (64 << DEPCTL_MPSIZ_Pos) // MPS = 64 bytes
ctl |= DEPCTL_SNAK
usbOTG.OUTEP[ep].CTL.Set(ctl)
usbOTG.OUTEP[ep].INT.Set(0xFF) // Clear any stale interrupts
usbOTG.DAINTMSK.SetBits(1 << (ep + 16))
// Arm immediately so host can send data.
AckUsbOutTransfer(ep)
}
}
}
func handleUSBSetAddress(setup usb.Setup) bool {
addr := uint32(setup.WValueL)
usbOTG.DCFG.ReplaceBits(addr<<4, 0x7F<<4, 0)
SendZlp()
return true
}
func SendZlp() {
sendUSBPacket(0, nil)
}
func sendUSBPacket(ep uint32, data []byte) {
// 1. Wait until the TX FIFO has room for the whole transfer, so a packet
// queued while the previous one is still draining cannot corrupt the FIFO.
// DTXFSTS reports free space in words; bounded wait in case the endpoint
// is stuck (e.g. host stopped polling).
words := uint32((len(data) + 3) / 4)
for i := 0; i < 1_000_000 && usbOTG.INEP[ep].TXFSTS.Get()&0xFFFF < words; i++ {
}
// 2. Setup transfer size
pktCnt := uint32((len(data) + 63) / 64)
if len(data) == 0 {
pktCnt = 1
}
usbOTG.INEP[ep].TSIZ.Set(uint32(len(data)) | (pktCnt << 19))
// 3. Enable endpoint and clear NAK
usbOTG.INEP[ep].CTL.SetBits(DEPCTL_EPENA | DEPCTL_CNAK)
// 4. Write data to FIFO
// FIFOs are at 0x1000, 0x2000, ... from base
fifo := (*volatile.Register32)(unsafe.Pointer(uintptr(unsafe.Pointer(usbOTG)) + 0x1000 + uintptr(ep)*0x1000))
for i := 0; i < len(data); i += 4 {
var word uint32
for j := 0; j < 4 && i+j < len(data); j++ {
word |= uint32(data[i+j]) << (8 * j)
}
fifo.Set(word)
}
}
func AckUsbOutTransfer(ep uint32) {
// Prepare for next OUT transfer
if ep == 0 {
// EP0 OUT: 1 packet, 64 bytes, 3 SETUP packets
usbOTG.OUTEP[0].TSIZ.Set(64 | (1 << 19) | (3 << 29))
} else {
usbOTG.OUTEP[ep].TSIZ.Set(64 | (1 << 19))
}
usbOTG.OUTEP[ep].CTL.SetBits(DEPCTL_EPENA | DEPCTL_CNAK)
}
func (dev *USBDevice) SetStallEPIn(ep uint32) {
usbOTG.INEP[ep].CTL.SetBits(DEPCTL_STALL)
}
func (dev *USBDevice) SetStallEPOut(ep uint32) {
usbOTG.OUTEP[ep].CTL.SetBits(DEPCTL_STALL)
}
func (dev *USBDevice) ClearStallEPIn(ep uint32) {
usbOTG.INEP[ep].CTL.ClearBits(DEPCTL_STALL)
usbOTG.INEP[ep].CTL.SetBits(1 << 28) // SD0PID
}
func (dev *USBDevice) ClearStallEPOut(ep uint32) {
usbOTG.OUTEP[ep].CTL.ClearBits(DEPCTL_STALL)
usbOTG.OUTEP[ep].CTL.SetBits(1 << 28) // SD0PID
}
// SendUSBInPacket sends a packet for USB (interrupt in / bulk in).
func SendUSBInPacket(ep uint32, data []byte) bool {
sendUSBPacket(ep, data)
return true
}
// ReceiveUSBControlPacket receives a control packet (used for CDC line coding).
//
// This runs inside the setup handler, which itself runs inside handleUSBIRQ.
// The interrupt cannot re-enter to deliver the data stage, so the RX FIFO is
// drained manually here until the EP0 OUT packet arrives.
func ReceiveUSBControlPacket() ([cdcLineInfoSize]byte, error) {
var b [cdcLineInfoSize]byte
ep0OutReceived.Set(0)
for i := 0; i < 1_000_000; i++ {
if usbOTG.GINTSTS.Get()&GINT_RXFLVL != 0 {
handleRxFIFO()
}
if ep0OutReceived.Get() != 0 {
copy(b[:], udd_ep_out_cache_buffer[0][:])
ep0OutReceived.Set(0)
return b, nil
}
}
return b, ErrUSBReadTimeout
}
// handleRxFIFO pops one status entry from the shared RX FIFO and processes it.
// Called from the USB interrupt, and re-entrantly from
// ReceiveUSBControlPacket while a setup handler is waiting for the data stage.
func handleRxFIFO() {
pop := usbOTG.GRXSTSP.Get()
ep := pop & 0xF
byteCnt := (pop >> 4) & 0x7FF
pktSts := (pop >> 17) & 0xF
// All OUT/SETUP data is read from the shared RX FIFO (DFIFO[0]).
fifo := (*volatile.Register32)(unsafe.Pointer(uintptr(unsafe.Pointer(usbOTG)) + 0x1000))
switch pktSts {
case 0x2: // OUT data packet received
// Guard against out-of-range endpoint or oversized packet: both come
// straight from hardware and would panic if used to slice the 64-byte
// cache buffers. Drain and discard instead.
if ep >= NumberOfUSBEndpoints || byteCnt > uint32(len(udd_ep_out_cache_buffer[0])) {
for i := uint32(0); i < byteCnt; i += 4 {
fifo.Get()
}
return
}
buf := udd_ep_out_cache_buffer[ep][:byteCnt]
for i := uint32(0); i < byteCnt; i += 4 {
word := fifo.Get()
for j := uint32(0); j < 4 && i+j < byteCnt; j++ {
buf[i+j] = byte(word >> (8 * j))
}
}
if ep == 0 {
ep0OutReceived.Set(1)
AckUsbOutTransfer(0)
} else if usbRxHandler[ep] != nil {
if usbRxHandler[ep](buf) {
AckUsbOutTransfer(ep)
}
}
case 0x6: // SETUP data packet received (always 8 bytes)
setupBuf := udd_ep_out_cache_buffer[0][:8]
for i := uint32(0); i < 8; i += 4 {
word := fifo.Get()
setupBuf[i] = byte(word)
setupBuf[i+1] = byte(word >> 8)
setupBuf[i+2] = byte(word >> 16)
setupBuf[i+3] = byte(word >> 24)
}
setup := usb.NewSetup(setupBuf)
ok := false
if (setup.BmRequestType & 0x60) == 0 { // Standard request
ok = handleStandardSetup(setup)
} else {
if setup.WIndex < uint16(len(usbSetupHandler)) && usbSetupHandler[setup.WIndex] != nil {
ok = usbSetupHandler[setup.WIndex](setup)
}
}
if !ok {
// Stall EP0 — host will retry.
usbOTG.INEP[0].CTL.SetBits(DEPCTL_STALL)
usbOTG.OUTEP[0].CTL.SetBits(DEPCTL_STALL)
}
// Do NOT re-arm EP0 here. The FIFO will deliver a pktSts=4
// (SETUP complete) entry next; we re-arm there.
case 0x3: // OUT transfer complete (host sent ACK) — no payload.
// Nothing to do; EP already re-armed in case 0x2.
case 0x4: // SETUP transaction complete — re-arm EP0 for next SETUP/OUT.
AckUsbOutTransfer(0)
}
}
func handleUSBIRQ(intr interrupt.Interrupt) {
status := usbOTG.GINTSTS.Get()
// Suppress suspend interrupts — suspend fires before enumeration completes.
if status&GINT_USBSUSP != 0 {
usbOTG.GINTSTS.Set(GINT_USBSUSP)
}
if status&GINT_USBRST != 0 {
usbOTG.GINTSTS.Set(GINT_USBRST)
// Flush all FIFOs.
usbOTG.GRSTCTL.SetBits(GRSTCTL_RXFFLSH)
for usbOTG.GRSTCTL.Get()&GRSTCTL_RXFFLSH != 0 {
}
usbOTG.GRSTCTL.SetBits(GRSTCTL_TXFFLSH | GRSTCTL_TXFNUM_ALL)
for usbOTG.GRSTCTL.Get()&GRSTCTL_TXFFLSH != 0 {
}
// Reset device address.
usbOTG.DCFG.ClearBits(0x7F << 4)
// Init EP0.
initEndpoint(0, 0)
usbConfiguration = 0
// TRDT for HCLK ≥ 30 MHz → 6.
usbOTG.GUSBCFG.ReplaceBits(0x6<<GUSBCFG_TRDT_Pos, 0xF<<GUSBCFG_TRDT_Pos, 0)
// Arm EP0 OUT to receive first SETUP/OUT.
AckUsbOutTransfer(0)
}
if status&GINT_ENUMDNE != 0 {
usbOTG.GINTSTS.Set(GINT_ENUMDNE)
// Enumeration done: activate EP0 at negotiated speed.
usbOTG.INEP[0].CTL.SetBits(DEPCTL_CNAK)
}
if status&GINT_RXFLVL != 0 {
// RXFLVL is level-triggered: mask it while processing, not W1C.
usbOTG.GINTMSK.ClearBits(GINT_RXFLVL)
for usbOTG.GINTSTS.Get()&GINT_RXFLVL != 0 {
handleRxFIFO()
}
usbOTG.GINTMSK.SetBits(GINT_RXFLVL)
}
if status&GINT_IEPINT != 0 {
daint := usbOTG.DAINT.Get() & 0xFFFF
for ep := uint32(0); ep < NumberOfUSBEndpoints; ep++ {
if daint&(1<<ep) != 0 {
epInt := usbOTG.INEP[ep].INT.Get()
usbOTG.INEP[ep].INT.Set(epInt) // W1C
if epInt&DEPINT_XFERC != 0 {
if ep != 0 && usbTxHandler[ep] != nil {
usbTxHandler[ep]()
}
}
}
}
}
if status&GINT_OEPINT != 0 {
daint := (usbOTG.DAINT.Get() >> 16) & 0xFFFF
for ep := uint32(0); ep < NumberOfUSBEndpoints; ep++ {
if daint&(1<<ep) != 0 {
epInt := usbOTG.OUTEP[ep].INT.Get()
usbOTG.OUTEP[ep].INT.Set(epInt) // W1C
}
}
}
}
+174
View File
@@ -0,0 +1,174 @@
//go:build stm32h7
package machine
import (
"device/stm32"
"unsafe"
)
// WindowWatchdog provides access to the Window Watchdog (WWDG) peripheral.
// Unlike IWDG, WWDG must be refreshed within a specific window - not too early
// and not too late. This provides protection against both runaway code and
// code that gets stuck in a loop refreshing the watchdog.
var WindowWatchdog = &windowWatchdogImpl{}
// WindowWatchdogConfig holds configuration for the window watchdog timer.
// The timeout (in microseconds) before the watchdog fires.
// The valid range depends on System frequency.
// At 100MHz: ~40µs to ~335ms
type WindowWatchdogConfig struct {
TimeoutMicros uint32
// The window value as a percentage of timeout (0-100).
// Refresh must occur when counter is below this percentage of max.
// Default (0) sets window to 100% (no window restriction).
WindowPercent uint8
}
// WWDG prescaler values
const (
wwdgPrescaler1 = 0 // CK Counter Clock (PCLK/4096) / 1
wwdgPrescaler2 = 1 // CK Counter Clock (PCLK/4096) / 2
wwdgPrescaler4 = 2 // CK Counter Clock (PCLK/4096) / 4
wwdgPrescaler8 = 3 // CK Counter Clock (PCLK/4096) / 8
wwdgPrescaler16 = 4 // CK Counter Clock (PCLK/4096) / 16
wwdgPrescaler32 = 5 // CK Counter Clock (PCLK/4096) / 32
wwdgPrescaler64 = 6 // CK Counter Clock (PCLK/4096) / 64
wwdgPrescaler128 = 7 // CK Counter Clock (PCLK/4096) / 128
)
// WWDG counter limits
const (
wwdgCounterMin = 0x40 // Minimum counter value (T6 must be set)
wwdgCounterMax = 0x7F // Maximum counter value (7 bits)
wwdgWindowMax = 0x7F // Maximum window value
)
type windowWatchdogImpl struct {
counter uint8 // Configured counter reload value
prescaler uint8 // Configured prescaler
}
// Configure the window watchdog.
//
// This method should not be called after the watchdog is started.
// The WWDG cannot be disabled once started, except by a system reset.
//
// Timeout formula: t_WWDG = (1/PCLK) × 4096 × 2^WDGTB × (T[5:0] + 1)
// Where T[5:0] = counter value - 0x40
func (wd *windowWatchdogImpl) Configure(config WindowWatchdogConfig) error {
// Enable WWDG clock
enableAltFuncClock(unsafe.Pointer(stm32.WWDG))
// Calculate prescaler and counter value from timeout
// Base tick = PCLK / 4096
// With prescaler: tick = PCLK / (4096 * 2^prescaler)
// Timeout = tick * (counter - 0x3F)
pclk := pclk3Freq()
// Base tick = PCLK / 4096
// Tick duration in nanoseconds = 1,000,000,000 / (pclk / 4096)
// Simplified: (4096 * 1,000,000,000) / pclk
baseTickNs := (uint64(4096) * 1000000000) / uint64(pclk)
timeoutUs := config.TimeoutMicros
if timeoutUs == 0 {
timeoutUs = 10000 // Default 10ms
}
timeoutNs := uint64(timeoutUs) * 1000
// Find the best prescaler and counter-combination
var bestPrescaler uint8
var bestCounter uint8
found := false
for prescaler := uint8(0); prescaler <= 7; prescaler++ {
// Tick duration with prescaler in nanoseconds
tickNs := baseTickNs << prescaler
// Counter value needed (counter - 0x3F = timeout / tick)
// Rearranged: counter = (timeoutNs / tickNs) + 0x3F
counterVal := (timeoutNs / tickNs) + 0x3F
if counterVal >= wwdgCounterMin && counterVal <= wwdgCounterMax {
bestPrescaler = prescaler
bestCounter = uint8(counterVal)
found = true
break
}
}
if !found {
// Use maximum timeout
bestPrescaler = wwdgPrescaler128
bestCounter = wwdgCounterMax
}
wd.prescaler = bestPrescaler
wd.counter = bestCounter
// Calculate window value
windowVal := uint8(wwdgWindowMax)
if config.WindowPercent > 0 && config.WindowPercent < 100 {
// Window = 0x40 + ((counter - 0x40) * percent / 100)
counterRange := uint16(bestCounter) - wwdgCounterMin
windowOffset := (counterRange * uint16(config.WindowPercent)) / 100
windowVal = uint8(wwdgCounterMin + windowOffset)
}
stm32.WWDG.CFR.Set((uint32(bestPrescaler) << stm32.WWDG_CFR_WDGTB_Pos) | uint32(windowVal))
return nil
}
// Start enables the window watchdog.
// Once started, the WWDG cannot be disabled except by a system reset.
func (wd *windowWatchdogImpl) Start() error {
stm32.WWDG.CR.Set(uint32(wd.counter) | (1 << 7))
return nil
}
// Update refreshes the window watchdog counter.
// This must be called within the configured window to prevent a reset.
// Calling too early (counter > window) or too late (counter <= 0x3F) causes reset.
func (wd *windowWatchdogImpl) Update() {
stm32.WWDG.CR.Set(uint32(wd.counter) | (1 << 7))
}
// GetCounter returns the current WWDG counter value.
// Useful for timing refresh operations within the window.
func (wd *windowWatchdogImpl) GetCounter() uint8 {
return uint8(stm32.WWDG.CR.Get() & 0x7F)
}
// EnableEarlyWakeupInterrupt enables the Early Wakeup Interrupt (EWI).
// The EWI is triggered when the counter reaches 0x40, giving the application
// a chance to refresh the watchdog or perform cleanup before reset.
func (wd *windowWatchdogImpl) EnableEarlyWakeupInterrupt() {
stm32.WWDG.CFR.SetBits(stm32.WWDG_CFR_EWI)
}
// ClearEarlyWakeupFlag clears the Early Wakeup Interrupt flag.
// Must be called in the interrupt handler.
func (wd *windowWatchdogImpl) ClearEarlyWakeupFlag() {
stm32.WWDG.SR.ClearBits(stm32.WWDG_SR_EWIF) // RM0433 §35.3.4: write 0 to EWIF to clear
}
// IsEarlyWakeupFlagSet returns true if the Early Wakeup Interrupt flag is set.
func (wd *windowWatchdogImpl) IsEarlyWakeupFlagSet() bool {
return stm32.WWDG.SR.Get()&1 != 0
}
// GetMaxTimeout returns the maximum timeout in microseconds for the current PCLK.
// Max timeout = (1/PCLK) × 4096 × 128 × 64
func (wd *windowWatchdogImpl) GetMaxTimeout() uint32 {
pclk := uint64(pclk3Freq())
return uint32((uint64(4096) * 128 * 64 * 1000000) / pclk)
}
// GetMinTimeout returns the minimum timeout in microseconds for the current PCLK.
// Min timeout = (1/PCLK) × 4096 × 1 × 1
func (wd *windowWatchdogImpl) GetMinTimeout() uint32 {
pclk := uint64(pclk3Freq())
return uint32((uint64(4096) * 1000000) / pclk)
}
+1 -1
View File
@@ -1,4 +1,4 @@
//go:build sam || nrf52840 || rp2040 || rp2350 || stm32f4 || stm32f7
//go:build sam || nrf52840 || rp2040 || rp2350 || stm32f4 || stm32f7 || stm32h7
package machine
+179
View File
@@ -0,0 +1,179 @@
//go:build stm32 && stm32h7
package runtime
import (
"device/stm32"
"machine"
_ "machine/usb/cdc"
)
func init() {
initCLK()
initMPU()
machine.InitSerial()
initTickTimer(&machine.TIM3)
}
func putchar(c byte) {
machine.Serial.WriteByte(c)
}
func getchar() byte {
for machine.Serial.Buffered() == 0 {
Gosched()
}
v, _ := machine.Serial.ReadByte()
return v
}
func buffered() int {
return machine.Serial.Buffered()
}
func initCLK() {
// 1. Enable SYSCFG
stm32.RCC.APB4ENR.SetBits(stm32.RCC_APB4ENR_SYSCFGEN)
// H743/H753 have no SMPS; the NUCLEO-H753ZI runs VCORE from the internal
// LDO (the CR3 reset state). Supply bits in CR3 are write-once after POR,
// so keep LDOEN/BYPASS untouched and only enable the USB 3.3V level
// detector needed by the USB transceivers.
stm32.PWR.CR3.SetBits(stm32.PWR_CR3_USB33DEN)
// 3. Configure VOS1 (Scale 1)
// RM0433 §6.8.4: ACTVOSRDY must be 1 (Run mode confirmed) before changing VOS.
for stm32.PWR.CSR1.Get()&stm32.PWR_CSR1_ACTVOSRDY == 0 {
}
// RM0433: VOS1 is 0b11.
stm32.PWR.D3CR.ReplaceBits(0b11<<stm32.PWR_D3CR_VOS_Pos, stm32.PWR_D3CR_VOS_Msk, 0)
for stm32.PWR.D3CR.Get()&stm32.PWR_D3CR_VOSRDY == 0 {
}
// 4. Enable HSE
if machine.HSEBypass() {
stm32.RCC.CR.SetBits(stm32.RCC_CR_HSEBYP | stm32.RCC_CR_HSEON)
} else {
stm32.RCC.CR.SetBits(stm32.RCC_CR_HSEON)
}
for stm32.RCC.CR.Get()&stm32.RCC_CR_HSERDY == 0 {
}
// 5. Configure PLL1
pll := machine.PLLParams400MHz()
// Source: HSE (2)
stm32.RCC.PLLCKSELR.ReplaceBits(stm32.RCC_PLLCKSELR_PLLSRC_HSE, stm32.RCC_PLLCKSELR_PLLSRC_Msk, 0)
// DIVM1
stm32.RCC.PLLCKSELR.ReplaceBits(pll.M<<stm32.RCC_PLLCKSELR_DIVM1_Pos, stm32.RCC_PLLCKSELR_DIVM1_Msk, 0)
// PLL1CFGR: Wide VCO (0), Range based on pll.R (VCO input frequency)
stm32.RCC.PLLCFGR.ReplaceBits(
(stm32.RCC_PLLCFGR_PLL1VCOSEL_WideVCO<<stm32.RCC_PLLCFGR_PLL1VCOSEL_Pos)|
(pll.R<<stm32.RCC_PLLCFGR_PLL1RGE_Pos),
stm32.RCC_PLLCFGR_PLL1VCOSEL_Msk|stm32.RCC_PLLCFGR_PLL1RGE_Msk, 0)
// PLL1DIVR: DIVN1=pll.N, DIVP1=pll.P, DIVQ1=pll.Q
// PLL1P = (VCO VCO_input * N) / P
// PLL1Q = (VCO VCO_input * N) / Q
stm32.RCC.PLL1DIVR.ReplaceBits(
(pll.N-1)<<stm32.RCC_PLL1DIVR_DIVN1_Pos|(pll.P-1)<<stm32.RCC_PLL1DIVR_DIVP1_Pos|(pll.Q-1)<<stm32.RCC_PLL1DIVR_DIVQ1_Pos,
stm32.RCC_PLL1DIVR_DIVN1_Msk|stm32.RCC_PLL1DIVR_DIVP1_Msk|stm32.RCC_PLL1DIVR_DIVQ1_Msk, 0)
// Enable PLL1P (SYSCLK=400MHz) and PLL1Q (SPI1/2/3 kernel=200MHz)
stm32.RCC.PLLCFGR.SetBits(stm32.RCC_PLLCFGR_DIVP1EN | stm32.RCC_PLLCFGR_DIVQ1EN)
// Enable PLL1
stm32.RCC.CR.SetBits(stm32.RCC_CR_PLL1ON)
for stm32.RCC.CR.Get()&stm32.RCC_CR_PLL1RDY == 0 {
}
// 6. Bus Prescalers
// D1CPRE=1 (0), HPRE=2 (8) -> HCLK=200MHz, D1PPRE (APB3)=2 (4) -> PCLK3=100MHz
stm32.RCC.D1CFGR.ReplaceBits(
(stm32.RCC_D1CFGR_D1CPRE_Div1<<stm32.RCC_D1CFGR_D1CPRE_Pos)|
(stm32.RCC_D1CFGR_HPRE_Div2<<stm32.RCC_D1CFGR_HPRE_Pos)|
(stm32.RCC_D1CFGR_D1PPRE_Div2<<stm32.RCC_D1CFGR_D1PPRE_Pos),
stm32.RCC_D1CFGR_D1CPRE_Msk|stm32.RCC_D1CFGR_HPRE_Msk|stm32.RCC_D1CFGR_D1PPRE_Msk, 0)
// D2CFGR: D2PPRE1 (APB1)=2 (4) -> PCLK1=100MHz, D2PPRE2 (APB2)=2 (4) -> PCLK2=100MHz
stm32.RCC.D2CFGR.ReplaceBits(
(stm32.RCC_D2CFGR_D2PPRE1_Div2<<stm32.RCC_D2CFGR_D2PPRE1_Pos)|
(stm32.RCC_D2CFGR_D2PPRE2_Div2<<stm32.RCC_D2CFGR_D2PPRE2_Pos),
stm32.RCC_D2CFGR_D2PPRE1_Msk|stm32.RCC_D2CFGR_D2PPRE2_Msk, 0)
// D3CFGR: D3PPRE (APB4)=2 (4) -> PCLK4=100MHz
stm32.RCC.D3CFGR.ReplaceBits(
stm32.RCC_D3CFGR_D3PPRE_Div2<<stm32.RCC_D3CFGR_D3PPRE_Pos,
stm32.RCC_D3CFGR_D3PPRE_Msk, 0)
// 7. Flash Latency
// VOS1, 200MHz AXI clock -> 2 wait states, WRHIGHFREQ=2 (RM0433 Table 17).
stm32.FLASH.ACR.ReplaceBits(2|2<<stm32.FLASH_ACR_WRHIGHFREQ_Pos,
stm32.FLASH_ACR_LATENCY_Msk|stm32.FLASH_ACR_WRHIGHFREQ_Msk, 0)
for stm32.FLASH.ACR.Get()&stm32.FLASH_ACR_LATENCY_Msk != 2 {
}
// 8. Switch to PLL1
// SW: PLL1 (3)
stm32.RCC.CFGR.ReplaceBits(3<<stm32.RCC_CFGR_SW_Pos, stm32.RCC_CFGR_SW_Msk, 0)
for (stm32.RCC.CFGR.Get() & stm32.RCC_CFGR_SWS_Msk) != (3 << stm32.RCC_CFGR_SWS_Pos) {
}
// 9. Peripheral Kernel Clocks
// I2C1,2,3 source: HSI_KER (2) to keep 64MHz timing compatibility.
stm32.RCC.D2CCIP2R.ReplaceBits(stm32.RCC_D2CCIP2R_I2C123SEL_HSI_KER<<stm32.RCC_D2CCIP2R_I2C123SEL_Pos, stm32.RCC_D2CCIP2R_I2C123SEL_Msk, 0)
// I2C4 source: HSI_KER (2)
stm32.RCC.D3CCIPR.ReplaceBits(stm32.RCC_D3CCIPR_I2C4SEL_HSI_KER<<stm32.RCC_D3CCIPR_I2C4SEL_Pos, stm32.RCC_D3CCIPR_I2C4SEL_Msk, 0)
// SPI1,2,3 source: PLL1_Q (0) -> 200MHz
stm32.RCC.D2CCIP1R.ReplaceBits(stm32.RCC_D2CCIP1R_SPI123SEL_PLL1_Q<<stm32.RCC_D2CCIP1R_SPI123SEL_Pos, stm32.RCC_D2CCIP1R_SPI123SEL_Msk, 0)
// SPI4,5 source: APB (0) -> PCLK2 = 100MHz (PLL1-derived)
stm32.RCC.D2CCIP1R.ReplaceBits(stm32.RCC_D2CCIP1R_SPI45SEL_APB<<stm32.RCC_D2CCIP1R_SPI45SEL_Pos, stm32.RCC_D2CCIP1R_SPI45SEL_Msk, 0)
// SPI6 source: PCLK4 (0) -> 100MHz (PLL1-derived)
stm32.RCC.D3CCIPR.ReplaceBits(stm32.RCC_D3CCIPR_SPI6SEL_RCC_PCLK4<<stm32.RCC_D3CCIPR_SPI6SEL_Pos, stm32.RCC_D3CCIPR_SPI6SEL_Msk, 0)
// 10. HSI48 — used as kernel clock for RNG and USB (RM0433 §33.3 requires ≤48 MHz).
stm32.RCC.CR.SetBits(stm32.RCC_CR_HSI48ON)
for stm32.RCC.CR.Get()&stm32.RCC_CR_HSI48RDY == 0 {
}
// RNGSEL and USBSEL reset value is 0x0 (HSI48 or PLL1_Q); set explicitly to HSI48.
stm32.RCC.D2CCIP2R.ReplaceBits(
stm32.RCC_D2CCIP2R_RNGSEL_HSI48<<stm32.RCC_D2CCIP2R_RNGSEL_Pos|
stm32.RCC_D2CCIP2R_USBSEL_HSI48<<stm32.RCC_D2CCIP2R_USBSEL_Pos,
stm32.RCC_D2CCIP2R_RNGSEL_Msk|stm32.RCC_D2CCIP2R_USBSEL_Msk, 0)
// 11. Enable CRS (Clock Recovery System) for HSI48 stabilization via USB SOF.
stm32.RCC.APB1HENR.SetBits(stm32.RCC_APB1HENR_CRSEN)
stm32.CRS.CFGR.ReplaceBits(stm32.CRS_CFGR_SYNCSRC_USB_SOF<<stm32.CRS_CFGR_SYNCSRC_Pos, stm32.CRS_CFGR_SYNCSRC_Msk, 0)
stm32.CRS.CR.SetBits(stm32.CRS_CR_CEN | stm32.CRS_CR_AUTOTRIMEN)
// 12. Configure PLL2 for ADC (80MHz)
// DIVM2 = pll.M
stm32.RCC.PLLCKSELR.ReplaceBits(pll.M<<stm32.RCC_PLLCKSELR_DIVM2_Pos, stm32.RCC_PLLCKSELR_DIVM2_Msk, 0)
// PLL2CFGR: Wide VCO (0), Range based on pll.R (VCO input frequency)
stm32.RCC.PLLCFGR.ReplaceBits(
(stm32.RCC_PLLCFGR_PLL2VCOSEL_WideVCO<<stm32.RCC_PLLCFGR_PLL2VCOSEL_Pos)|
(pll.R<<stm32.RCC_PLLCFGR_PLL2RGE_Pos),
stm32.RCC_PLLCFGR_PLL2VCOSEL_Msk|stm32.RCC_PLLCFGR_PLL2RGE_Msk, 0)
// PLL2DIVR: DIVN2=pll.N, DIVP2=10 (Value 9)
// PLL2P = (VCO VCO_input * N) / 10 = 80MHz
stm32.RCC.PLL2DIVR.ReplaceBits(
(pll.N-1)<<stm32.RCC_PLL2DIVR_DIVN2_Pos|9<<stm32.RCC_PLL2DIVR_DIVP2_Pos,
stm32.RCC_PLL2DIVR_DIVN2_Msk|stm32.RCC_PLL2DIVR_DIVP2_Msk, 0)
// Enable DIVP2EN
stm32.RCC.PLLCFGR.SetBits(stm32.RCC_PLLCFGR_DIVP2EN)
// Enable PLL2
stm32.RCC.CR.SetBits(stm32.RCC_CR_PLL2ON)
for stm32.RCC.CR.Get()&stm32.RCC_CR_PLL2RDY == 0 {
}
// 12. ADC kernel clock source: PLL2_P (0).
stm32.RCC.D3CCIPR.ReplaceBits(stm32.RCC_D3CCIPR_ADCSEL_PLL2_P<<stm32.RCC_D3CCIPR_ADCSEL_Pos, stm32.RCC_D3CCIPR_ADCSEL_Msk, 0)
}
+128
View File
@@ -0,0 +1,128 @@
//go:build stm32 && stm32h7
package runtime
import (
"device/arm"
"runtime/volatile"
"unsafe"
)
// Cortex-M7 cache size registers (ARM TRM Table 4-2, within SCB address space).
var (
scbCCSIDR = (*volatile.Register32)(unsafe.Pointer(uintptr(0xE000ED80))) // Cache Size ID Register (R)
scbCSELR = (*volatile.Register32)(unsafe.Pointer(uintptr(0xE000ED84))) // Cache Size Selection Register (R/W)
)
// Cortex-M7 cache maintenance registers (ARMv7-M Architecture Ref Manual Table B3-7).
var (
scbICIALLU = (*volatile.Register32)(unsafe.Pointer(uintptr(0xE000EF50))) // Invalidate all I-cache (W)
scbDCISW = (*volatile.Register32)(unsafe.Pointer(uintptr(0xE000EF60))) // Invalidate D-cache by set/way (W)
)
// RASR region attribute presets for this chip's memory map (ARMv7-M
// Architecture Ref Manual §B3.5.5). The register layout itself
// (arm.MPU_Type, arm.MPU_RASR_* field positions) is generic to any
// ARMv7-M core (M3/M4/M7) and lives in device/arm; only these specific
// size/type/permission combinations are STM32H7-specific.
const (
// SIZE field bits[5:1]: value = log2(region_bytes) - 1.
mpuRASRSize2MB = 20 << arm.MPU_RASR_SIZE_Pos // 2MB = 2^21, field=20
mpuRASRSize512KB = 18 << arm.MPU_RASR_SIZE_Pos // 512KB = 2^19, field=18
mpuRASRSize512MB = 28 << arm.MPU_RASR_SIZE_Pos // 512MB = 2^29, field=28
// AP field bits[26:24].
mpuRASRAPReadOnly = 0x6 << arm.MPU_RASR_AP_Pos // Privileged and unprivileged read-only
mpuRASRAPFullAccess = 0x3 << arm.MPU_RASR_AP_Pos // Full access (privileged and unprivileged)
// Memory type encodings: TEX bits[21:19], S bit[18], C bit[17], B bit[16].
// Normal, Write-Through, No Write-Allocate (TEX=000, C=1, B=0, S=0).
mpuRASRNormalWT = arm.MPU_RASR_C
// Normal, Write-Back, Write-Allocate (TEX=001, C=1, B=1, S=0).
mpuRASRNormalWBWA = (1 << arm.MPU_RASR_TEX_Pos) | arm.MPU_RASR_C | arm.MPU_RASR_B
// Shared Device memory (TEX=000, C=0, B=1, S=1).
mpuRASRDevice = arm.MPU_RASR_S | arm.MPU_RASR_B
)
// initMPU configures the Cortex-M7 MPU, then enables L1 instruction and data
// caches. Must be called after initCLK() and before any peripheral access.
//
// Memory map configured:
//
// Region 0: Flash 0x08000000 2MB Normal WT, RO, executable
// Region 1: AXI SRAM 0x24000000 512KB Normal WBWA, RW, no-execute
// Region 2: Peripherals 0x40000000 512MB Shared Device, RW, no-execute
//
// Unmapped regions fall back to the ARMv7-M default privileged map via
// PRIVDEFENA, keeping NVIC/SCB and other PPB accesses strongly-ordered.
func initMPU() {
// Disable MPU before reconfiguring regions.
arm.MPU.CTRL.Set(0)
arm.Asm("dsb 0xF")
arm.Asm("isb 0xF")
// Region 0: Flash — Normal, Write-Through, read-only, executable.
arm.MPU.RNR.Set(0)
arm.MPU.RBAR.Set(0x08000000)
arm.MPU.RASR.Set(mpuRASRNormalWT | mpuRASRAPReadOnly | mpuRASRSize2MB | arm.MPU_RASR_ENABLE)
// Region 1: AXI SRAM — Normal, Write-Back Write-Allocate, full access, no-execute.
arm.MPU.RNR.Set(1)
arm.MPU.RBAR.Set(0x24000000)
arm.MPU.RASR.Set(arm.MPU_RASR_XN | mpuRASRNormalWBWA | mpuRASRAPFullAccess | mpuRASRSize512KB | arm.MPU_RASR_ENABLE)
// Region 2: Peripherals — Shared Device, full access, no-execute.
arm.MPU.RNR.Set(2)
arm.MPU.RBAR.Set(0x40000000)
arm.MPU.RASR.Set(arm.MPU_RASR_XN | mpuRASRDevice | mpuRASRAPFullAccess | mpuRASRSize512MB | arm.MPU_RASR_ENABLE)
// Enable MemManage fault so MPU violations raise a MemFault rather than
// hard-faulting directly.
arm.SCB.SHCSR.SetBits(arm.SCB_SHCSR_MEMFAULTENA)
// Enable MPU with privileged default background map.
arm.MPU.CTRL.Set(arm.MPU_CTRL_ENABLE | arm.MPU_CTRL_PRIVDEFENA)
arm.Asm("dsb 0xF")
arm.Asm("isb 0xF")
// Enable L1 caches now that the MPU defines cacheability for each region.
initICache()
initDCache()
}
// initICache invalidates then enables the L1 instruction cache.
func initICache() {
arm.Asm("dsb 0xF")
arm.Asm("isb 0xF")
scbICIALLU.Set(0) // Invalidate all I-cache lines.
arm.Asm("dsb 0xF")
arm.Asm("isb 0xF")
arm.SCB.CCR.SetBits(arm.SCB_CCR_IC)
arm.Asm("dsb 0xF")
arm.Asm("isb 0xF")
}
// initDCache invalidates all D-cache lines by set/way then enables the cache.
// Iterates over sets and ways read from CCSIDR so it works for any M7 cache
// size (864 KB, always 4-way, 32-byte lines on STM32H743).
func initDCache() {
scbCSELR.Set(0) // Select L1 D-cache.
arm.Asm("dsb 0xF")
ccsidr := scbCCSIDR.Get()
numSets := (ccsidr >> 13) & 0x7FFF // NUMSETS field (value = sets-1)
assoc := (ccsidr >> 3) & 0x3FF // ASSOCIATIVITY field (value = ways-1)
// Invalidate every set/way. For a 4-way cache the way index occupies
// bits[31:30] of DCISW; the set index starts at bit 5 (32-byte line = 2^5).
for set := uint32(0); set <= numSets; set++ {
for way := uint32(0); way <= assoc; way++ {
scbDCISW.Set((way << 30) | (set << 5))
}
}
arm.Asm("dsb 0xF")
arm.SCB.CCR.SetBits(arm.SCB_CCR_DC)
arm.Asm("dsb 0xF")
arm.Asm("isb 0xF")
}