mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-18 19:44:00 +00:00
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:
@@ -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)
|
||||
}
|
||||
Reference in New Issue
Block a user