From b0b84c48eccbed01ae7deae23c490235b02e04e0 Mon Sep 17 00:00:00 2001 From: kenbell Date: Thu, 18 Mar 2021 03:54:15 -0700 Subject: [PATCH] Harmonize stm32 ticks and sleep (#1673) machine/stm32f*: move to harmonized tick / sleep logic code --- src/runtime/runtime_stm32_timers.go | 201 ++++++++++++++++++++++++++++ src/runtime/runtime_stm32f103.go | 191 +++++--------------------- src/runtime/runtime_stm32f405.go | 130 +++++------------- src/runtime/runtime_stm32f407.go | 179 +++++++------------------ src/runtime/runtime_stm32f7x2.go | 174 +++++++----------------- src/runtime/runtime_stm32l0.go | 187 ++++---------------------- src/runtime/runtime_stm32l5x2.go | 176 +++++++----------------- 7 files changed, 446 insertions(+), 792 deletions(-) create mode 100644 src/runtime/runtime_stm32_timers.go diff --git a/src/runtime/runtime_stm32_timers.go b/src/runtime/runtime_stm32_timers.go new file mode 100644 index 00000000..eb0aeedf --- /dev/null +++ b/src/runtime/runtime_stm32_timers.go @@ -0,0 +1,201 @@ +// +build stm32 + +package runtime + +// This file implements a common implementation of implementing 'ticks' and 'sleep' for STM32 devices. The +// implementation uses two 'basic' timers, so should be compatible with a broad range of STM32 MCUs. +// +// This implementation is of 'sleep' is for running in a normal power mode. Use of the RTC to enter and +// resume from low-power states is out of scope. +// +// Interface +// --------- +// For each MCU, the following constants should be defined: +// TICK_RATE The desired frequency of ticks, e.g. 1000 for 1KHz ticks +// TICK_TIMER_IRQ Which timer to use for counting ticks (e.g. stm32.IRQ_TIM7) +// TICK_TIMER_FREQ The frequency the clock feeding the sleep timer is set to (e.g. 84MHz) +// SLEEP_TIMER_IRQ Which timer to use for sleeping (e.g. stm32.IRQ_TIM3) +// SLEEP_TIMER_FREQ The frequency the clock feeding the sleep timer is set to (e.g. 84MHz) +// +// The type alias `arrtype` should be defined to either uint32 or uint16 depending on the +// size of that register in the MCU's TIM_Type structure + +import ( + "device/stm32" + "runtime/interrupt" + "runtime/volatile" +) + +type timerInfo struct { + EnableRegister *volatile.Register32 + EnableFlag uint32 + Device *stm32.TIM_Type +} + +const ( + TICKS_PER_NS = 1000000000 / TICK_RATE +) + +var ( + // Tick count since boot + tickCount volatile.Register64 + + // The timer used for counting ticks + tickTimer *timerInfo + + // The timer used for sleeping + sleepTimer *timerInfo +) + +func ticksToNanoseconds(ticks timeUnit) int64 { + return int64(ticks) * TICKS_PER_NS +} + +func nanosecondsToTicks(ns int64) timeUnit { + return timeUnit(ns / TICKS_PER_NS) +} + +// number of ticks (microseconds) since start. +func ticks() timeUnit { + return timeUnit(tickCount.Get()) +} + +// +// -- Ticks --- +// + +// Enable the timer used to count ticks +func initTickTimer(ti *timerInfo) { + tickTimer = ti + ti.EnableRegister.SetBits(ti.EnableFlag) + + psc := uint32(TICK_TIMER_FREQ / TICK_RATE) + period := uint32(1) + + // Get the pre-scale into range, with interrupt firing + // once per tick. + for psc > 0x10000 || period == 1 { + psc >>= 1 + period <<= 1 + } + + // Clamp overflow + if period > 0x10000 { + period = 0x10000 + } + + ti.Device.PSC.Set(psc - 1) + ti.Device.ARR.Set(arrtype(period - 1)) + + // Auto-repeat + ti.Device.EGR.SetBits(stm32.TIM_EGR_UG) + + // Register the interrupt handler + intr := interrupt.New(TICK_TIMER_IRQ, handleTick) + intr.SetPriority(0xc1) + intr.Enable() + + // Clear update flag + ti.Device.SR.ClearBits(stm32.TIM_SR_UIF) + + // Enable the hardware interrupt + ti.Device.DIER.SetBits(stm32.TIM_DIER_UIE) + + // Enable the timer + ti.Device.CR1.SetBits(stm32.TIM_CR1_CEN) +} + +func handleTick(interrupt.Interrupt) { + if tickTimer.Device.SR.HasBits(stm32.TIM_SR_UIF) { + // clear the update flag + tickTimer.Device.SR.ClearBits(stm32.TIM_SR_UIF) + + // increment tick count + tickCount.Set(tickCount.Get() + 1) + } +} + +// +// --- Sleep --- +// + +func sleepTicks(d timeUnit) { + // If there is a scheduler, we sleep until any kind of CPU event up to + // a maximum of the requested sleep duration. + // + // The scheduler will call again if there is nothing to do and a further + // sleep is required. + if hasScheduler { + timerSleep(ticksToNanoseconds(d)) + return + } + + // There's no scheduler, so we sleep until at least the requested number + // of ticks has passed. + end := ticks() + d + for ticks() < end { + timerSleep(ticksToNanoseconds(d)) + } +} + +// Enable the Sleep clock +func initSleepTimer(ti *timerInfo) { + sleepTimer = ti + + ti.EnableRegister.SetBits(ti.EnableFlag) + + // No auto-repeat + ti.Device.EGR.SetBits(stm32.TIM_EGR_UG) + + // Enable the hardware interrupt. + ti.Device.DIER.SetBits(stm32.TIM_DIER_UIE) + + intr := interrupt.New(SLEEP_TIMER_IRQ, handleSleep) + intr.SetPriority(0xc3) + intr.Enable() +} + +// timerSleep sleeps for 'at most' ns nanoseconds, but possibly less. +func timerSleep(ns int64) { + // Calculate initial pre-scale value. + // delay (in ns) and clock freq are both large values, so do the nanosecs + // conversion (divide by 1G) by pre-dividing each by 1000 to avoid overflow + // in any meaningful time period. + psc := ((ns / 1000) * (SLEEP_TIMER_FREQ / 1000)) / 1000 + period := int64(1) + + // Get the pre-scale into range, with interrupt firing + // once per tick. + for psc > 0x10000 || period == 1 { + psc >>= 1 + period <<= 1 + } + + // Clamp overflow + if period > 0x10000 { + period = 0x10000 + } + + // Set the desired duration and enable + sleepTimer.Device.PSC.Set(uint32(psc) - 1) + sleepTimer.Device.ARR.Set(arrtype(period) - 1) + sleepTimer.Device.CR1.SetBits(stm32.TIM_CR1_CEN) + + // Wait till either the timer or some other event wakes + // up the CPU + waitForEvents() + + // In case it was not the sleep timer that woke the + // CPU, disable the timer now. + disableSleepTimer() +} + +func handleSleep(interrupt.Interrupt) { + disableSleepTimer() +} + +func disableSleepTimer() { + // Disable and clear the update flag. + sleepTimer.Device.CR1.ClearBits(stm32.TIM_CR1_CEN) + sleepTimer.Device.SR.ClearBits(stm32.TIM_SR_UIF) +} diff --git a/src/runtime/runtime_stm32f103.go b/src/runtime/runtime_stm32f103.go index d1af5201..a7c8563d 100644 --- a/src/runtime/runtime_stm32f103.go +++ b/src/runtime/runtime_stm32f103.go @@ -3,18 +3,45 @@ package runtime import ( - "device/arm" "device/stm32" "machine" - "runtime/interrupt" - "runtime/volatile" ) +/* + timer settings used for tick and sleep. + + note: TICK_TIMER_FREQ and SLEEP_TIMER_FREQ are controlled by PLL / clock + settings configured in initCLK, so must be kept in sync if the clock settings + are changed. +*/ +const ( + TICK_RATE = 1000 // 1 KHz + SLEEP_TIMER_IRQ = stm32.IRQ_TIM3 + SLEEP_TIMER_FREQ = 72000000 // 72 MHz + TICK_TIMER_IRQ = stm32.IRQ_TIM4 + TICK_TIMER_FREQ = 72000000 // 72 MHz +) + +type arrtype = uint32 + +const asyncScheduler = false + func init() { initCLK() - initRTC() - initTIM() + + initSleepTimer(&timerInfo{ + EnableRegister: &stm32.RCC.APB1ENR, + EnableFlag: stm32.RCC_APB1ENR_TIM3EN, + Device: stm32.TIM3, + }) + machine.UART0.Configure(machine.UARTConfig{}) + + initTickTimer(&timerInfo{ + EnableRegister: &stm32.RCC.APB1ENR, + EnableFlag: stm32.RCC_APB1ENR_TIM4EN, + Device: stm32.TIM4, + }) } func putchar(c byte) { @@ -52,157 +79,3 @@ func initCLK() { for !stm32.RCC.CFGR.HasBits(stm32.RCC_CFGR_SWS_PLL << stm32.RCC_CFGR_SWS_Pos) { } } - -var ( - timestamp timeUnit // microseconds since boottime - timerLastCounter uint64 -) - -var timerWakeup volatile.Register8 - -func initRTC() { - // Enable the PWR and BKP. - stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_PWREN | stm32.RCC_APB1ENR_BKPEN) - - // access to backup register - stm32.PWR.CR.SetBits(stm32.PWR_CR_DBP) - - // Enable LSE - stm32.RCC.BDCR.SetBits(stm32.RCC_BDCR_LSEON) - - // wait until LSE is ready - for !stm32.RCC.BDCR.HasBits(stm32.RCC_BDCR_LSERDY) { - } - - // Select LSE - stm32.RCC.BDCR.SetBits(stm32.RCC_BDCR_RTCSEL_LSE << stm32.RCC_BDCR_RTCSEL_Pos) - - // set prescaler to "max" per datasheet - stm32.RTC.PRLH.Set(stm32.RTC_PRLH_PRLH_Msk) - stm32.RTC.PRLL.Set(stm32.RTC_PRLL_PRLL_Msk) - - // set count to zero - stm32.RTC.CNTH.Set(0x0) - stm32.RTC.CNTL.Set(0x0) - - // Enable RTC - stm32.RCC.BDCR.SetBits(stm32.RCC_BDCR_RTCEN) - - // Clear RSF - stm32.RTC.CRL.ClearBits(stm32.RTC_CRL_RSF) - - // Wait till flag is set - for !stm32.RTC.CRL.HasBits(stm32.RTC_CRL_RSF) { - } -} - -// Enable the TIM3 clock. -func initTIM() { - stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_TIM3EN) - - intr := interrupt.New(stm32.IRQ_TIM3, handleTIM3) - intr.SetPriority(0xc3) - intr.Enable() -} - -const asyncScheduler = false - -func ticksToNanoseconds(ticks timeUnit) int64 { - return int64(ticks) * 1000 -} - -func nanosecondsToTicks(ns int64) timeUnit { - return timeUnit(ns / 1000) -} - -// sleepTicks should sleep for specific number of microseconds. -func sleepTicks(d timeUnit) { - for d != 0 { - ticks() // update timestamp - ticks := uint32(d) // current scaling only supports 100 usec to 6553 msec - timerSleep(ticks) - d -= timeUnit(ticks) - } -} - -// number of ticks (microseconds) since start. -func ticks() timeUnit { - // convert RTC counter from seconds to microseconds - timerCounter := uint64(stm32.RTC.CNTH.Get()<<16|stm32.RTC.CNTL.Get()) * 1000 * 1000 - - // add the fractional part of current time using DIV register - timerCounter += uint64(0x8000-stm32.RTC.DIVL.Get()) * 31 - - // change since last measurement - offset := (timerCounter - timerLastCounter) - timerLastCounter = timerCounter - timestamp += timeUnit(offset) - return timestamp -} - -// ticks are in microseconds -func timerSleep(ticks uint32) { - timerWakeup.Set(0) - - // STM32 timer update event period is calculated as follows: - // - // Update_event = TIM_CLK/((PSC + 1)*(ARR + 1)*(RCR + 1)) - // - // Where: - // - // TIM_CLK = timer clock input - // PSC = 16-bit prescaler register - // ARR = 16/32-bit Autoreload register - // RCR = 16-bit repetition counter - // - // Example: - // - // TIM_CLK = 72 MHz - // Prescaler = 1 - // Auto reload = 65535 - // No repetition counter RCR = 0 - // Update_event = 72*(10^6)/((1 + 1)*(65535 + 1)*(1)) - // Update_event = 549.3 Hz - // - // Set the timer prescaler/autoreload timing registers. - - // TODO: support smaller or larger scales (autoscaling) based - // on the length of sleep time requested. - // The current scaling only supports a range of 200 usec to 6553 msec. - - // prescale counter down from 72mhz to 10khz aka 0.1 ms frequency. - stm32.TIM3.PSC.Set(machine.CPUFrequency()/10000 - 1) // 7199 - - // Set duty aka duration. - // STM32 dividers use n-1, i.e. n counts from 0 to n-1. - // As a result, with these prescaler settings, - // the minimum allowed duration is 200 microseconds. - if ticks < 200 { - ticks = 200 - } - stm32.TIM3.ARR.Set(ticks/100 - 1) // convert from microseconds to 0.1 ms - - // Enable the hardware interrupt. - stm32.TIM3.DIER.SetBits(stm32.TIM_DIER_UIE) - - // Enable the timer. - stm32.TIM3.CR1.SetBits(stm32.TIM_CR1_CEN) - - // wait till timer wakes up - for timerWakeup.Get() == 0 { - arm.Asm("wfi") - } -} - -func handleTIM3(interrupt.Interrupt) { - if stm32.TIM3.SR.HasBits(stm32.TIM_SR_UIF) { - // Disable the timer. - stm32.TIM3.CR1.ClearBits(stm32.TIM_CR1_CEN) - - // clear the update flag - stm32.TIM3.SR.ClearBits(stm32.TIM_SR_UIF) - - // timer was triggered - timerWakeup.Set(1) - } -} diff --git a/src/runtime/runtime_stm32f405.go b/src/runtime/runtime_stm32f405.go index 6be208dd..7596c864 100644 --- a/src/runtime/runtime_stm32f405.go +++ b/src/runtime/runtime_stm32f405.go @@ -3,20 +3,10 @@ package runtime import ( - "device/arm" "device/stm32" "machine" - "runtime/interrupt" - "runtime/volatile" ) -func init() { - initOSC() // configure oscillators - initCLK() // configure CPU, AHB, and APB bus clocks - initTIM() // configure timers - initCOM() // configure serial comm interfaces -} - const ( // +----------------------+ // | Clock Settings | @@ -74,6 +64,43 @@ const ( FLASH_OPTIONS = stm32.FLASH_ACR_ICEN | stm32.FLASH_ACR_DCEN | stm32.FLASH_ACR_PRFTEN ) +/* + timer settings used for tick and sleep. + + note: TICK_TIMER_FREQ and SLEEP_TIMER_FREQ are controlled by PLL / clock + settings above, so must be kept in sync if the clock settings are changed. +*/ +const ( + TICK_RATE = 1000 // 1 KHz + SLEEP_TIMER_IRQ = stm32.IRQ_TIM3 + SLEEP_TIMER_FREQ = PCLK1_FREQ_HZ * 2 + TICK_TIMER_IRQ = stm32.IRQ_TIM7 + TICK_TIMER_FREQ = PCLK1_FREQ_HZ * 2 +) + +type arrtype = uint32 + +const asyncScheduler = false + +func init() { + initOSC() // configure oscillators + initCLK() + + initSleepTimer(&timerInfo{ + EnableRegister: &stm32.RCC.APB1ENR, + EnableFlag: stm32.RCC_APB1ENR_TIM3EN, + Device: stm32.TIM3, + }) + + initCOM() + + initTickTimer(&timerInfo{ + EnableRegister: &stm32.RCC.APB1ENR, + EnableFlag: stm32.RCC_APB1ENR_TIM7EN, + Device: stm32.TIM7, + }) +} + func initOSC() { // enable voltage regulator stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_PWREN) @@ -154,95 +181,12 @@ func initCLK() { stm32.RCC.AHB1ENR.SetBits(CLK_CCM_RAM) } -func initTIM() { - // enable sleep counter (TIM3) - stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_TIM3EN) - - tim3 := interrupt.New(stm32.IRQ_TIM3, handleTIM3) - tim3.SetPriority(0xC3) - tim3.Enable() - - // enable tick counter (TIM7) - stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_TIM7EN) - - stm32.TIM7.PSC.Set((PCLK1_FREQ_HZ*2)/10000 - 1) // 84mhz to 10khz(0.1ms) - stm32.TIM7.ARR.Set(10 - 1) // interrupt per 1ms - - stm32.TIM7.DIER.SetBits(stm32.TIM_DIER_UIE) // enable interrupt - stm32.TIM7.CR1.SetBits(stm32.TIM_CR1_CEN) // enable timer - - tim7 := interrupt.New(stm32.IRQ_TIM7, handleTIM7) - tim7.SetPriority(0xC1) - tim7.Enable() -} - func initCOM() { if machine.NUM_UART_INTERFACES > 0 { machine.UART0.Configure(machine.UARTConfig{}) } } -var ( - // tick in milliseconds - tickCount timeUnit - timerWakeup volatile.Register8 -) - -const asyncScheduler = false - -func ticksToNanoseconds(ticks timeUnit) int64 { - return int64(ticks) * 1000 -} - -func nanosecondsToTicks(ns int64) timeUnit { - return timeUnit(ns / 1000) -} - -// sleepTicks should sleep for specific number of microseconds. -func sleepTicks(d timeUnit) { - timerSleep(uint32(d)) -} - -// number of ticks (microseconds) since start. -func ticks() timeUnit { - return tickCount * 1000 // milliseconds to microseconds -} - -// ticks are in microseconds -func timerSleep(ticks uint32) { - timerWakeup.Set(0) - - stm32.TIM3.PSC.Set((PCLK1_FREQ_HZ*2)/10000 - 1) // 8399 - arr := (ticks / 100) - 1 // microseconds to 0.1 ms - if arr == 0 { - arr = 1 // avoid blocking - } - stm32.TIM3.ARR.Set(arr) - - stm32.TIM3.DIER.SetBits(stm32.TIM_DIER_UIE) // enable interrupt - stm32.TIM3.CR1.SetBits(stm32.TIM_CR1_CEN) // enable the timer - - // wait for timer - for timerWakeup.Get() == 0 { - arm.Asm("wfi") - } -} - -func handleTIM3(interrupt.Interrupt) { - if stm32.TIM3.SR.HasBits(stm32.TIM_SR_UIF) { - stm32.TIM3.CR1.ClearBits(stm32.TIM_CR1_CEN) // disable the timer - stm32.TIM3.SR.ClearBits(stm32.TIM_SR_UIF) // clear the update flag - timerWakeup.Set(1) // flag timer ISR - } -} - -func handleTIM7(interrupt.Interrupt) { - if stm32.TIM7.SR.HasBits(stm32.TIM_SR_UIF) { - stm32.TIM7.SR.ClearBits(stm32.TIM_SR_UIF) // clear the update flag - tickCount++ - } -} - func putchar(c byte) { machine.UART0.WriteByte(c) } diff --git a/src/runtime/runtime_stm32f407.go b/src/runtime/runtime_stm32f407.go index 57739eeb..5a30a81c 100644 --- a/src/runtime/runtime_stm32f407.go +++ b/src/runtime/runtime_stm32f407.go @@ -3,31 +3,8 @@ package runtime import ( - "device/arm" "device/stm32" "machine" - "runtime/interrupt" - "runtime/volatile" -) - -func init() { - initCLK() - initTIM3() - machine.UART0.Configure(machine.UARTConfig{}) - initTIM7() -} - -func putchar(c byte) { - machine.UART0.WriteByte(c) -} - -const ( - HSE_STARTUP_TIMEOUT = 0x0500 - /* PLL Options - See RM0090 Reference Manual pg. 95 */ - PLL_M = 8 /* PLL_VCO = (HSE_VALUE or HSI_VLAUE / PLL_M) * PLL_N */ - PLL_N = 336 - PLL_P = 2 /* SYSCLK = PLL_VCO / PLL_P */ - PLL_Q = 7 /* USB OTS FS, SDIO and RNG Clock = PLL_VCO / PLL_Q */ ) /* @@ -40,8 +17,56 @@ const ( | APB1(PCLK1) | 42mhz | +-------------+--------+ */ -func initCLK() { +const ( + HSE_STARTUP_TIMEOUT = 0x0500 + // PLL Options - See RM0090 Reference Manual pg. 95 + PLL_M = 8 // PLL_VCO = (HSE_VALUE or HSI_VLAUE / PLL_M) * PLL_N + PLL_N = 336 + PLL_P = 2 // SYSCLK = PLL_VCO / PLL_P + PLL_Q = 7 // USB OTS FS, SDIO and RNG Clock = PLL_VCO / PLL_Q +) +/* + timer settings used for tick and sleep. + + note: TICK_TIMER_FREQ and SLEEP_TIMER_FREQ are controlled by PLL / clock + settings above, so must be kept in sync if the clock settings are changed. +*/ +const ( + TICK_RATE = 1000 // 1 KHz + TICK_TIMER_IRQ = stm32.IRQ_TIM7 + TICK_TIMER_FREQ = 84000000 // 84 MHz + SLEEP_TIMER_IRQ = stm32.IRQ_TIM3 + SLEEP_TIMER_FREQ = 84000000 // 84 MHz +) + +type arrtype = uint32 + +const asyncScheduler = false + +func init() { + initCLK() + + initSleepTimer(&timerInfo{ + EnableRegister: &stm32.RCC.APB1ENR, + EnableFlag: stm32.RCC_APB1ENR_TIM3EN, + Device: stm32.TIM3, + }) + + machine.UART0.Configure(machine.UARTConfig{}) + + initTickTimer(&timerInfo{ + EnableRegister: &stm32.RCC.APB1ENR, + EnableFlag: stm32.RCC_APB1ENR_TIM7EN, + Device: stm32.TIM7, + }) +} + +func putchar(c byte) { + machine.UART0.WriteByte(c) +} + +func initCLK() { // Reset clock registers // Set HSION stm32.RCC.CR.SetBits(stm32.RCC_CR_HSION) @@ -104,111 +129,7 @@ func initCLK() { for { } } + // Enable the CCM RAM clock stm32.RCC.AHB1ENR.SetBits(1 << 20) - -} - -var ( - // tick in milliseconds - tickCount timeUnit -) - -var timerWakeup volatile.Register8 - -func ticksToNanoseconds(ticks timeUnit) int64 { - return int64(ticks) * 1000 -} - -func nanosecondsToTicks(ns int64) timeUnit { - return timeUnit(ns / 1000) -} - -// Enable the TIM3 clock.(sleep count) -func initTIM3() { - stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_TIM3EN) - - intr := interrupt.New(stm32.IRQ_TIM3, handleTIM3) - intr.SetPriority(0xc3) - intr.Enable() -} - -// Enable the TIM7 clock.(tick count) -func initTIM7() { - stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_TIM7EN) - - // CK_INT = APB1 x2 = 84mhz - stm32.TIM7.PSC.Set(84000000/10000 - 1) // 84mhz to 10khz(0.1ms) - stm32.TIM7.ARR.Set(10 - 1) // interrupt per 1ms - - // Enable the hardware interrupt. - stm32.TIM7.DIER.SetBits(stm32.TIM_DIER_UIE) - - // Enable the timer. - stm32.TIM7.CR1.SetBits(stm32.TIM_CR1_CEN) - - intr := interrupt.New(stm32.IRQ_TIM7, handleTIM7) - intr.SetPriority(0xc1) - intr.Enable() -} - -const asyncScheduler = false - -// sleepTicks should sleep for specific number of microseconds. -func sleepTicks(d timeUnit) { - timerSleep(uint32(d)) -} - -// number of ticks (microseconds) since start. -func ticks() timeUnit { - // milliseconds to microseconds - return tickCount * 1000 -} - -// ticks are in microseconds -func timerSleep(ticks uint32) { - timerWakeup.Set(0) - - // CK_INT = APB1 x2 = 84mhz - // prescale counter down from 84mhz to 10khz aka 0.1 ms frequency. - stm32.TIM3.PSC.Set(84000000/10000 - 1) // 8399 - - // set duty aka duration - arr := (ticks / 100) - 1 // convert from microseconds to 0.1 ms - if arr == 0 { - arr = 1 // avoid blocking - } - stm32.TIM3.ARR.Set(arr) - - // Enable the hardware interrupt. - stm32.TIM3.DIER.SetBits(stm32.TIM_DIER_UIE) - - // Enable the timer. - stm32.TIM3.CR1.SetBits(stm32.TIM_CR1_CEN) - - // wait till timer wakes up - for timerWakeup.Get() == 0 { - arm.Asm("wfi") - } -} - -func handleTIM3(interrupt.Interrupt) { - if stm32.TIM3.SR.HasBits(stm32.TIM_SR_UIF) { - // Disable the timer. - stm32.TIM3.CR1.ClearBits(stm32.TIM_CR1_CEN) - - // clear the update flag - stm32.TIM3.SR.ClearBits(stm32.TIM_SR_UIF) - - // timer was triggered - timerWakeup.Set(1) - } -} - -func handleTIM7(interrupt.Interrupt) { - if stm32.TIM7.SR.HasBits(stm32.TIM_SR_UIF) { - // clear the update flag - stm32.TIM7.SR.ClearBits(stm32.TIM_SR_UIF) - tickCount++ - } } diff --git a/src/runtime/runtime_stm32f7x2.go b/src/runtime/runtime_stm32f7x2.go index da6d9aa5..a80c7e92 100644 --- a/src/runtime/runtime_stm32f7x2.go +++ b/src/runtime/runtime_stm32f7x2.go @@ -3,30 +3,8 @@ package runtime import ( - "device/arm" "device/stm32" "machine" - "runtime/interrupt" - "runtime/volatile" -) - -func init() { - initCLK() - initTIM3() - machine.UART0.Configure(machine.UARTConfig{}) - initTIM7() -} - -func putchar(c byte) { - machine.UART0.WriteByte(c) -} - -const ( - HSE_STARTUP_TIMEOUT = 0x0500 - PLL_M = 4 - PLL_N = 216 - PLL_P = 2 - PLL_Q = 2 ) /* @@ -39,6 +17,54 @@ const ( | APB2(PCLK2) | 108mhz | +-------------+--------+ */ +const ( + HSE_STARTUP_TIMEOUT = 0x0500 + PLL_M = 4 + PLL_N = 216 + PLL_P = 2 + PLL_Q = 2 +) + +/* + timer settings used for tick and sleep. + + note: TICK_TIMER_FREQ and SLEEP_TIMER_FREQ are controlled by PLL / clock + settings above, so must be kept in sync if the clock settings are changed. +*/ +const ( + TICK_RATE = 1000 // 1 KHz + SLEEP_TIMER_IRQ = stm32.IRQ_TIM3 + SLEEP_TIMER_FREQ = 54000000 // 54 MHz (2x APB1) + TICK_TIMER_IRQ = stm32.IRQ_TIM7 + TICK_TIMER_FREQ = 54000000 // 54 MHz (2x APB1) +) + +type arrtype = uint32 + +const asyncScheduler = false + +func init() { + initCLK() + + initSleepTimer(&timerInfo{ + EnableRegister: &stm32.RCC.APB1ENR, + EnableFlag: stm32.RCC_APB1ENR_TIM3EN, + Device: stm32.TIM3, + }) + + machine.UART0.Configure(machine.UARTConfig{}) + + initTickTimer(&timerInfo{ + EnableRegister: &stm32.RCC.APB1ENR, + EnableFlag: stm32.RCC_APB1ENR_TIM7EN, + Device: stm32.TIM7, + }) +} + +func putchar(c byte) { + machine.UART0.WriteByte(c) +} + func initCLK() { // PWR_CLK_ENABLE stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_PWREN) @@ -102,107 +128,3 @@ func initOsc() { for !stm32.RCC.CR.HasBits(stm32.RCC_CR_PLLRDY) { } } - -var ( - // tick in milliseconds - tickCount timeUnit -) - -var timerWakeup volatile.Register8 - -func ticksToNanoseconds(ticks timeUnit) int64 { - return int64(ticks) * 1000 -} - -func nanosecondsToTicks(ns int64) timeUnit { - return timeUnit(ns / 1000) -} - -// Enable the TIM3 clock.(sleep count) -func initTIM3() { - stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_TIM3EN) - - intr := interrupt.New(stm32.IRQ_TIM3, handleTIM3) - intr.SetPriority(0xc3) - intr.Enable() -} - -// Enable the TIM7 clock.(tick count) -func initTIM7() { - stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_TIM7EN) - - // CK_INT = APB1 x2 = 54mhz - stm32.TIM7.PSC.Set(54000000/10000 - 1) // 54mhz to 10khz(0.1ms) - stm32.TIM7.ARR.Set(10 - 1) // interrupt per 1ms - - // Enable the hardware interrupt. - stm32.TIM7.DIER.SetBits(stm32.TIM_DIER_UIE) - - // Enable the timer. - stm32.TIM7.CR1.SetBits(stm32.TIM_CR1_CEN) - - intr := interrupt.New(stm32.IRQ_TIM7, handleTIM7) - intr.SetPriority(0xc1) - intr.Enable() -} - -const asyncScheduler = false - -// sleepTicks should sleep for specific number of microseconds. -func sleepTicks(d timeUnit) { - timerSleep(uint32(d)) -} - -// number of ticks (microseconds) since start. -func ticks() timeUnit { - // milliseconds to microseconds - return tickCount * 1000 -} - -// ticks are in microseconds -func timerSleep(ticks uint32) { - timerWakeup.Set(0) - - // CK_INT = APB1 x2 = 54mhz - // prescale counter down from 54mhz to 10khz aka 0.1 ms frequency. - stm32.TIM3.PSC.Set(54000000/10000 - 1) - - // set duty aka duration - arr := (ticks / 100) - 1 // convert from microseconds to 0.1 ms - if arr == 0 { - arr = 1 // avoid blocking - } - stm32.TIM3.ARR.Set(arr) - - // Enable the hardware interrupt. - stm32.TIM3.DIER.SetBits(stm32.TIM_DIER_UIE) - - // Enable the timer. - stm32.TIM3.CR1.SetBits(stm32.TIM_CR1_CEN) - - // wait till timer wakes up - for timerWakeup.Get() == 0 { - arm.Asm("wfi") - } -} - -func handleTIM3(interrupt.Interrupt) { - if stm32.TIM3.SR.HasBits(stm32.TIM_SR_UIF) { - // Disable the timer. - stm32.TIM3.CR1.ClearBits(stm32.TIM_CR1_CEN) - - // clear the update flag - stm32.TIM3.SR.ClearBits(stm32.TIM_SR_UIF) - - // timer was triggered - timerWakeup.Set(1) - } -} - -func handleTIM7(interrupt.Interrupt) { - if stm32.TIM7.SR.HasBits(stm32.TIM_SR_UIF) { - // clear the update flag - stm32.TIM7.SR.ClearBits(stm32.TIM_SR_UIF) - tickCount++ - } -} diff --git a/src/runtime/runtime_stm32l0.go b/src/runtime/runtime_stm32l0.go index 8a3bc44e..db14a520 100644 --- a/src/runtime/runtime_stm32l0.go +++ b/src/runtime/runtime_stm32l0.go @@ -3,18 +3,42 @@ package runtime import ( - "device/arm" "device/stm32" "machine" - "runtime/interrupt" - "runtime/volatile" ) +/* + timer settings used for tick and sleep. + + note: TICK_TIMER_FREQ and SLEEP_TIMER_FREQ are controlled by PLL / clock + settings above, so must be kept in sync if the clock settings are changed. +*/ +const ( + TICK_RATE = 1000 // 1 KHz + TICK_TIMER_IRQ = stm32.IRQ_TIM7 + TICK_TIMER_FREQ = 32000000 // 32 MHz + SLEEP_TIMER_IRQ = stm32.IRQ_TIM3 + SLEEP_TIMER_FREQ = 32000000 // 32 MHz +) + +type arrtype = uint16 + func init() { initCLK() - initRTC() - initTIM() + + initSleepTimer(&timerInfo{ + EnableRegister: &stm32.RCC.APB1ENR, + EnableFlag: stm32.RCC_APB1ENR_TIM3EN, + Device: stm32.TIM3, + }) + machine.UART0.Configure(machine.UARTConfig{}) + + initTickTimer(&timerInfo{ + EnableRegister: &stm32.RCC.APB1ENR, + EnableFlag: stm32.RCC_APB1ENR_TIM7EN, + Device: stm32.TIM7, + }) } func putchar(c byte) { @@ -58,157 +82,4 @@ func initCLK() { } -var ( - timestamp timeUnit // microseconds since boottime - timerLastCounter uint64 -) - -var timerWakeup volatile.Register8 - -func initRTC() { - - // Enable power - stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_PWREN) - - // access to backup register - stm32.PWR.CR.SetBits(stm32.PWR_CR_DBP) - - // Enable LSE - stm32.RCC.CSR.SetBits(stm32.RCC_CSR_LSEON) - - // wait until LSE is ready - for !stm32.RCC.CSR.HasBits(stm32.RCC_CSR_LSERDY) { - } - - // Select Clock Source LSE - stm32.RCC.CSR.SetBits(0x01 << stm32.RCC_CSR_RTCSEL_Pos) - stm32.RCC.CSR.ClearBits(0x02 << stm32.RCC_CSR_RTCSEL_Pos) - - // Enable clock - stm32.RCC.CSR.SetBits(stm32.RCC_CSR_RTCEN) - - stm32.RTC.WPR.Set(0xCA) // Enable Write Access for RTC Registers - stm32.RTC.WPR.Set(0x53) // Enable Write Access for RTC Registers - stm32.RTC.ISR.SetBits(stm32.RTC_ISR_INIT) // Enable init phase - - // Wait for initialization state - for !stm32.RTC.ISR.HasBits(stm32.RTC_ISR_INITF) { - } - - stm32.RTC.PRER.Set(0x003F0270) // set prescaler, 40kHz/64 => 625Hz, 625Hz/625 => 1Hz - - // Set initial date - //RTC->TR = RTC_TR_PM | 0; - - stm32.RTC.ISR.ClearBits(stm32.RTC_ISR_INIT) // Disable init phase - stm32.RTC.WPR.Set(0xFE) // Disable Write Access for RTC Registers - stm32.RTC.WPR.Set(0x64) // Disable Write Access for RTC Registers -} - -// Enable the TIM3 clock. -func initTIM() { - stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_TIM3EN) - - intr := interrupt.New(stm32.IRQ_TIM3, handleTIM3) - intr.SetPriority(0xc3) - intr.Enable() -} - const asyncScheduler = false - -func ticksToNanoseconds(ticks timeUnit) int64 { - return int64(ticks) * 1000 -} - -func nanosecondsToTicks(ns int64) timeUnit { - return timeUnit(ns / 1000) -} - -// sleepTicks should sleep for specific number of microseconds. -func sleepTicks(d timeUnit) { - for d != 0 { - ticks() // update timestamp - ticks := uint32(d) // current scaling only supports 100 usec to 6553 msec - timerSleep(ticks) - d -= timeUnit(ticks) - } -} - -// number of ticks (microseconds) since start. -func ticks() timeUnit { - - // Read twice to force shadow register cache update - rSubSec := stm32.RTC.SSR.Get() & stm32.RTC_SSR_SS_Msk - rSubSec = stm32.RTC.SSR.Get() & stm32.RTC_SSR_SS_Msk - rDate := stm32.RTC.DR.Get() - rDate = stm32.RTC.DR.Get() - rDate++ - rTime := stm32.RTC.TR.Get() - rTime = stm32.RTC.TR.Get() - prediv := stm32.RTC.PRER.Get() & stm32.RTC_PRER_PREDIV_S_Msk - - var tsec uint64 - - // Timestamp in seconds - tsec = uint64(((rTime & 0x300000) >> 20) * 36000) // Hours Tens - tsec += uint64(((rTime & 0xf0000) >> 16) * 3600) // Hours Units - tsec += uint64(((rTime & 0x7000) >> 12) * 600) // Minutes Tens - tsec += uint64(((rTime & 0xf00) >> 8) * 60) // Minutes Units - tsec += uint64(((rTime & 0x70) >> 4) * 10) // Second Tens - tsec += uint64(rTime & 0xf) // Seconds Units - - //Second fraction in milliseconds - ssec := uint64((1000 * (prediv - rSubSec)) / (prediv + 1)) - - timerCounter := uint64(tsec * 1000) // Timestamp in millis - timerCounter += ssec // Add sub-seconds - timerCounter *= 1000 // Convert to micros - - // change since last measurement - offset := (timerCounter - timerLastCounter) - timerLastCounter = timerCounter - timestamp += timeUnit(offset) - return timestamp -} - -// ticks are in microseconds -func timerSleep(ticks uint32) { - timerWakeup.Set(0) - - // prescale counter down from 32mhz to 10khz aka 0.1 ms frequency. - clk := machine.CPUFrequency() / 2 - stm32.TIM3.PSC.Set(clk/10000 - 1) - - // Set duty aka duration. - // STM32 dividers use n-1, i.e. n counts from 0 to n-1. - // As a result, with these prescaler settings, - // the minimum allowed duration is 200 microseconds. - if ticks < 200 { - ticks = 200 - } - stm32.TIM3.ARR.Set(uint16(ticks/100 - 1)) // convert from microseconds to 0.1 ms - - // Enable the hardware interrupt. - stm32.TIM3.DIER.SetBits(stm32.TIM_DIER_UIE) - - // Enable the timer. - stm32.TIM3.CR1.SetBits(stm32.TIM_CR1_CEN) - - // wait till timer wakes up - for timerWakeup.Get() == 0 { - arm.Asm("wfi") - } -} - -func handleTIM3(interrupt.Interrupt) { - if stm32.TIM3.SR.HasBits(stm32.TIM_SR_UIF) { - // Disable the timer. - stm32.TIM3.CR1.ClearBits(stm32.TIM_CR1_CEN) - - // clear the update flag - stm32.TIM3.SR.ClearBits(stm32.TIM_SR_UIF) - - // timer was triggered - timerWakeup.Set(1) - } -} diff --git a/src/runtime/runtime_stm32l5x2.go b/src/runtime/runtime_stm32l5x2.go index 2249ffa2..1bf9c7ee 100644 --- a/src/runtime/runtime_stm32l5x2.go +++ b/src/runtime/runtime_stm32l5x2.go @@ -3,31 +3,8 @@ package runtime import ( - "device/arm" "device/stm32" "machine" - "runtime/interrupt" - "runtime/volatile" -) - -func init() { - initCLK() - initTIM15() - machine.UART0.Configure(machine.UARTConfig{}) - initTIM16() -} - -func putchar(c byte) { - machine.UART0.WriteByte(c) -} - -const ( - HSE_STARTUP_TIMEOUT = 0x0500 - PLL_M = 1 - PLL_N = 55 - PLL_P = 7 // RCC_PLLP_DIV7 - PLL_Q = 2 // RCC_PLLQ_DIV2 - PLL_R = 2 // RCC_PLLR_DIV2 ) /* @@ -40,6 +17,55 @@ const ( | APB2(PCLK2) | 110mhz | +-------------+-----------+ */ +const ( + HSE_STARTUP_TIMEOUT = 0x0500 + PLL_M = 1 + PLL_N = 55 + PLL_P = 7 // RCC_PLLP_DIV7 + PLL_Q = 2 // RCC_PLLQ_DIV2 + PLL_R = 2 // RCC_PLLR_DIV2 +) + +/* + timer settings used for tick and sleep. + + note: TICK_TIMER_FREQ and SLEEP_TIMER_FREQ are controlled by PLL / clock + settings above, so must be kept in sync if the clock settings are changed. +*/ +const ( + TICK_RATE = 1000 // 1 KHz + SLEEP_TIMER_IRQ = stm32.IRQ_TIM15 + SLEEP_TIMER_FREQ = 110000000 // 110 MHz + TICK_TIMER_IRQ = stm32.IRQ_TIM16 + TICK_TIMER_FREQ = 110000000 // 110 MHz +) + +type arrtype = uint32 + +const asyncScheduler = false + +func init() { + initCLK() + + initSleepTimer(&timerInfo{ + EnableRegister: &stm32.RCC.APB2ENR, + EnableFlag: stm32.RCC_APB2ENR_TIM15EN, + Device: stm32.TIM15, + }) + + machine.UART0.Configure(machine.UARTConfig{}) + + initTickTimer(&timerInfo{ + EnableRegister: &stm32.RCC.APB2ENR, + EnableFlag: stm32.RCC_APB2ENR_TIM16EN, + Device: stm32.TIM16, + }) +} + +func putchar(c byte) { + machine.UART0.WriteByte(c) +} + func initCLK() { // PWR_CLK_ENABLE @@ -137,107 +163,3 @@ func initOsc() { } } - -var ( - // tick in milliseconds - tickCount timeUnit -) - -var timerWakeup volatile.Register8 - -func ticksToNanoseconds(ticks timeUnit) int64 { - return int64(ticks) * 1000 -} - -func nanosecondsToTicks(ns int64) timeUnit { - return timeUnit(ns / 1000) -} - -// Enable the TIM15 clock.(sleep count) -func initTIM15() { - stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_TIM15EN) - - intr := interrupt.New(stm32.IRQ_TIM15, handleTIM15) - intr.SetPriority(0xc3) - intr.Enable() -} - -// Enable the TIM16 clock.(tick count) -func initTIM16() { - stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_TIM16EN) - - // CK_INT = APB1 = 110mhz - stm32.TIM16.PSC.Set(110000000/10000 - 1) // 110mhz to 10khz(0.1ms) - stm32.TIM16.ARR.Set(10 - 1) // interrupt per 1ms - - // Enable the hardware interrupt. - stm32.TIM16.DIER.SetBits(stm32.TIM_DIER_UIE) - - // Enable the timer. - stm32.TIM16.CR1.SetBits(stm32.TIM_CR1_CEN) - - intr := interrupt.New(stm32.IRQ_TIM16, handleTIM16) - intr.SetPriority(0xc1) - intr.Enable() -} - -const asyncScheduler = false - -// sleepTicks should sleep for specific number of microseconds. -func sleepTicks(d timeUnit) { - timerSleep(uint32(d)) -} - -// number of ticks (microseconds) since start. -func ticks() timeUnit { - // milliseconds to microseconds - return tickCount * 1000 -} - -// ticks are in microseconds -func timerSleep(ticks uint32) { - timerWakeup.Set(0) - - // CK_INT = APB1 = 110mhz - // prescale counter down from 110mhz to 10khz aka 0.1 ms frequency. - stm32.TIM15.PSC.Set(110000000/10000 - 1) - - // set duty aka duration - arr := (ticks / 100) - 1 // convert from microseconds to 0.1 ms - if arr == 0 { - arr = 1 // avoid blocking - } - stm32.TIM15.ARR.Set(arr) - - // Enable the hardware interrupt. - stm32.TIM15.DIER.SetBits(stm32.TIM_DIER_UIE) - - // Enable the timer. - stm32.TIM15.CR1.SetBits(stm32.TIM_CR1_CEN) - - // wait till timer wakes up - for timerWakeup.Get() == 0 { - arm.Asm("wfi") - } -} - -func handleTIM15(interrupt.Interrupt) { - if stm32.TIM15.SR.HasBits(stm32.TIM_SR_UIF) { - // Disable the timer. - stm32.TIM15.CR1.ClearBits(stm32.TIM_CR1_CEN) - - // clear the update flag - stm32.TIM15.SR.ClearBits(stm32.TIM_SR_UIF) - - // timer was triggered - timerWakeup.Set(1) - } -} - -func handleTIM16(interrupt.Interrupt) { - if stm32.TIM16.SR.HasBits(stm32.TIM_SR_UIF) { - // clear the update flag - stm32.TIM16.SR.ClearBits(stm32.TIM_SR_UIF) - tickCount++ - } -}