STM32G0B1 Watchdog + ADC Support (#5158)

* - Fix SPI initialization by disabling interface before configuration.
- Ensure proper 8-bit configuration for STM32G0 SPI data register access.
- Add default pin initialization for SPI.

* Add Window Watchdog (WWDG) and ADC support for STM32G0

* Comment cleanup

* Refactored STM32G0 ADC sampling time configuration to use `switch` for improved readability
This commit is contained in:
Amken USA
2026-01-08 08:52:14 -05:00
committed by GitHub
parent 72f667cac9
commit 53c1ccaa42
3 changed files with 386 additions and 11 deletions
+173
View File
@@ -0,0 +1,173 @@
//go:build stm32g0
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 64MHz: ~64µs to ~524ms
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
// Refer RM0444 Rev 6 861/1384
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 := CPUFrequency() // Assuming PCLK = CPU frequency (no APB prescaler)
baseTick := (4096 * 1000000) / pclk // Base tick in nanoseconds * 1000 for precision
timeout := config.TimeoutMicros
if timeout == 0 {
timeout = 10000 // Default 10ms
}
// Find the best prescaler and counter-combination
var bestPrescaler uint8
var bestCounter uint8
found := false
for prescaler := uint8(0); prescaler <= 7; prescaler++ {
// Tick duration in nanoseconds * 1000
tickNs := baseTick << prescaler
// Counter value needed (counter - 0x3F = timeout / tick)
// Rearranged: counter = (timeout * 1000 / tickNs) + 0x3F
counterVal := (uint32(timeout) * 1000000 / 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.Set(0) // Write 0 to clear EWIF
}
// 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
// At 64MHz: ~524ms = 524288µs
func (wd *windowWatchdogImpl) GetMaxTimeout() uint32 {
pclk := uint64(CPUFrequency())
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
// At 64MHz: ~64µs
func (wd *windowWatchdogImpl) GetMinTimeout() uint32 {
pclk := uint64(CPUFrequency())
return uint32((uint64(4096) * 1000000) / pclk)
}