Skip to content

Commit

Permalink
rp2040: rtc deep sleep
Browse files Browse the repository at this point in the history
  • Loading branch information
ysoldak committed Jan 24, 2023
1 parent 0e1cf06 commit 841ae84
Show file tree
Hide file tree
Showing 5 changed files with 166 additions and 3 deletions.
2 changes: 2 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -467,6 +467,8 @@ smoketest:
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=nano-rp2040 examples/rtcinterrupt
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=nano-rp2040 examples/rtcsleep
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=pca10040 examples/serial
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=pca10040 examples/systick
Expand Down
75 changes: 75 additions & 0 deletions src/examples/rtcsleep/rtcsleep.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
//go:build rp2040

package main

// This example shows how to send a board to a low power mode (deep sleep) and wake it up on timer.
//
// There are 3 states the board can be in this example:
// - Base power: on-board LED is off, does not consume power (5 seconds);
// - High power: on-board LED shines, consumes power (5 seconds);
// - Low power: almost everything is off but crystal oscilator, rtc, sys and ref clocks (5 seconds).
//
// Measured power consumption for Arduino Nano RP2040 Connect board powered from 2S battery (~8.2v)
// - Base power: 13.55 mA (0.11W) bare minimum on fully powered up RP2040 chip
// - High power: 15.55 mA (0.13W) on-board LED consumes 2 mA (same for power LED, see below)
// - Deep sleep: 4.00 mA (0.03W) power LED consumes 2 mA
// 2.00 mA (0.015W) power LED removed => ~month life time, see below
//
// Life expectancy on 2S 2200 Li-Ion battery.
// 4.2v->3.2v, avg 3.7v per cell (7.4v total).
// Remaining charge ~1000mAh not to ruin the battery => 1.2Ah consumable.
//
// Expected number of hours = 3.7*2*1.2 / w, there "w" is power consumption, in Watts.
//
// High power: 68h (~3 days)
// Base power: 81h (~3.5 days)
// Deep sleep: 296h (~12 days)
// 592h (~24 days)

import (
"machine"
"time"
)

const led = machine.LED

func main() {

led.Configure(machine.PinConfig{Mode: machine.PinOutput})

// Pause and let user connect to serial console
time.Sleep(5 * time.Second)

// Fire RTC interrupt every 15 seconds: ~10 seconds we spend in high+base power and sleep the rest
machine.RTC.SetInterrupt(10+5, true, nil)

for {

// Base power
// Nano RP2040 Connect: 0.11W
log("Base power, 5 sec, 0.11W")
led.Low()
time.Sleep(5 * time.Second) // light sleep

// High power
// Nano RP2040 Connect: 0.13W
log("High power, 5 sec, 0.13W")
led.High()
time.Sleep(5 * time.Second) // light sleep

// Low power
// Nano RP2040 Connect: 0.03W
log(" Low power, 5 sec, 0.03W")
led.Low()
// machine.RTC.SetInterrupt(10, false, nil) // alternatively non-recurring RTC interrupt can be scheduled every time
machine.Sleep() // deep sleep

}

}

func log(message string) {
time.Sleep(10 * time.Millisecond) // ensure output subsystem fully initialised
println(time.Now().Format(time.RFC3339) + " " + message)
time.Sleep(10 * time.Millisecond) // ensure output subsystem doe not go sleep before message printed out
}
49 changes: 46 additions & 3 deletions src/machine/machine_rp2040_clocks.go
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,7 @@ func (clk *clock) configure(src, auxsrc, srcFreq, freq uint32) {
// init initializes the clock hardware.
//
// Must be called before any other clock function.
// Must be called on start and wake up from deep sleep
func (clks *clocksType) init() {
// Start the watchdog tick
watchdog.startTick(xoscFreq)
Expand Down Expand Up @@ -235,11 +236,11 @@ func (clks *clocksType) init() {
48*MHz,
48*MHz)

// clkRTC = pllUSB (48MHz) / 1024 = 46875Hz
// clkRTC = xosc (12MHz) / 256 = 46875Hz
clkrtc := clks.clock(clkRTC)
clkrtc.configure(0, // No GLMUX
rp.CLOCKS_CLK_RTC_CTRL_AUXSRC_CLKSRC_PLL_USB,
48*MHz,
rp.CLOCKS_CLK_RTC_CTRL_AUXSRC_XOSC_CLKSRC,
12*MHz,
46875)

// clkPeri = clkSys. Used as reference clock for Peripherals.
Expand All @@ -251,3 +252,45 @@ func (clks *clocksType) init() {
125*MHz,
125*MHz)
}

// stop the clock
func (clk *clock) stop() {
// Disable clock. On clkRef and clkSys this does nothing,
// all other clocks have the ENABLE bit in the same position.
clk.ctrl.ClearBits(rp.CLOCKS_CLK_GPOUT0_CTRL_ENABLE)
configuredFreq[clk.cix] = 0
}

// Stop most clocks & PLLs in preparation for sleep.
// Ref, Sys and Rtc clocks run from XOSC.
// After awakening, need to call configure() to re-initialize.
func (clks *clocksType) sleep() {

// Configure clocks
// clkRef = xosc (12MHz) / 1 = 12MHz

// clkSys = xosc (12MHz) / 1 = 12MHz
clksys := clks.clock(clkSys)
clksys.configure(rp.CLOCKS_CLK_SYS_CTRL_SRC_CLKSRC_CLK_SYS_AUX,
rp.CLOCKS_CLK_SYS_CTRL_AUXSRC_XOSC_CLKSRC,
12*MHz,
12*MHz)

// clkUSB = 0MHz
clkusb := clks.clock(clkUSB)
clkusb.stop()

// clkADC = 0MHz
clkadc := clks.clock(clkADC)
clkadc.stop()

// clkRTC = xosc (12MHz) / 256 = 46875Hz

// clkPeri = 0MHz
clkperi := clks.clock(clkPeri)
clkperi.stop()

// Stop the PLLs
pllSys.stop()
pllUSB.stop()
}
6 changes: 6 additions & 0 deletions src/machine/machine_rp2040_pll.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,3 +98,9 @@ func (pll *pll) init(refdiv, vcoFreq, postDiv1, postDiv2 uint32) {
pll.pwr.ClearBits(rp.PLL_SYS_PWR_POSTDIVPD)

}

func (pll *pll) stop() {
// Turn off PLL
pwr := uint32(rp.PLL_SYS_PWR_PD | rp.PLL_SYS_PWR_VCOPD | rp.PLL_SYS_PWR_POSTDIVPD)
pll.pwr.SetBits(pwr)
}
37 changes: 37 additions & 0 deletions src/machine/machine_rp2040_sleep.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
//go:build rp2040

package machine

import (
"device/arm"
"device/rp"
)

// Sleep until RTC interrupt
func Sleep() {

scb_orig := arm.SCB.SCR.Get()
clock0_orig := clocks.sleepEN0.Get()
clock1_orig := clocks.sleepEN1.Get()

// Stop all clocks but RTC
clocks.sleep()

// Turn off all clocks when in sleep mode except for RTC
clocks.sleepEN0.Set(rp.CLOCKS_SLEEP_EN0_CLK_RTC_RTC)
clocks.sleepEN1.Set(0x0)

// Enable deep sleep at the proc
arm.SCB.SCR.SetBits(arm.SCB_SCR_SLEEPDEEP)

// Go to sleep
arm.Asm("wfi")

// Restore state
arm.SCB.SCR.Set(scb_orig)
clocks.sleepEN0.Set(clock0_orig)
clocks.sleepEN1.Set(clock1_orig)

// Enable clocks
clocks.init()
}

0 comments on commit 841ae84

Please sign in to comment.