mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-09 21:43:40 +00:00
53c1ccaa42
* - 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
174 lines
5.8 KiB
Go
174 lines
5.8 KiB
Go
//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)
|
||
}
|