-
Notifications
You must be signed in to change notification settings - Fork 11
Expand file tree
/
Copy pathlowpower.py
More file actions
160 lines (129 loc) · 4.84 KB
/
lowpower.py
File metadata and controls
160 lines (129 loc) · 4.84 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
# Raspberry Pico micropython low-power workaround
# Copyright (C) 2021 Tom Jorquera
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 3 of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
REG_IO_BANK0_BASE = 0x40014000
REG_IO_BANK0_INTR0 = 0x0F0
REG_IO_BANK0_DORMANT_WAKE_INTE0 = 0x160
IO_BANK0_DORMANT_WAKE_INTE0_GPIO0_EDGE_HIGH_BITS = 0x00000008
IO_BANK0_DORMANT_WAKE_INTE0_GPIO0_EDGE_LOW_BITS = 0x00000004
IO_BANK0_DORMANT_WAKE_INTE0_GPIO0_LEVEL_HIGH_BITS = 0x00000002
IO_BANK0_DORMANT_WAKE_INTE0_GPIO0_LEVEL_LOW_BITS = 0x00000001
REG_XOSC_BASE = 0x40024000
REG_XOSC_DORMANT = 0x08
REG_XOSC_STATUS = 0x04
XOSC_DORMANT_VALUE_DORMANT = 0x636F6D61
XOSC_STATUS_STABLE_BITS = 0x80000000
# Helper values to set individual pin modes
EDGE_HIGH = IO_BANK0_DORMANT_WAKE_INTE0_GPIO0_EDGE_HIGH_BITS
EDGE_LOW = IO_BANK0_DORMANT_WAKE_INTE0_GPIO0_EDGE_LOW_BITS
LEVEL_HIGH = IO_BANK0_DORMANT_WAKE_INTE0_GPIO0_LEVEL_HIGH_BITS
LEVEL_LOW = IO_BANK0_DORMANT_WAKE_INTE0_GPIO0_LEVEL_LOW_BITS
ROSC_BASE = 0x40060000
# Clock
CLOCKS_BASE = 0x40008000
CLK_REF_CTRL = 0x30
CLK_SYS_CTRL = 0x3C
CLK_PERI_CTRL = 0x48
CLK_USB_CTRL = 0x54
CLK_ADC_CTRL = 0x60
CLK_RTC_CTRL = 0x6C
PLL_SYS_BASE = 0x40028000
PLL_USB_BASE = 0x4002C000
PLL_PWR = 0x4
@micropython.asm_thumb
def _read_bits(r0):
ldr(r0, [r0, 0])
@micropython.asm_thumb
def _write_bits(r0, r1):
str(r1, [r0, 0])
def dormant_with_modes(pin_modes):
registers_events = {}
for gpio_pin, pin_mode in pin_modes.items():
if not isinstance(gpio_pin, int) or gpio_pin < 0 or gpio_pin > 28:
raise RuntimeError(
"Invalid value for pin "
+ str(gpio_pin)
+ " (expect int between 0 and 27)"
)
if not isinstance(pin_mode, int) or pin_mode < 1 or pin_mode > 15:
raise RuntimeError(
"Invalid value for pin_mode "
+ str(pin_mode)
+ " (expect int between 0 and 15)"
)
# clear irq
_write_bits(
REG_IO_BANK0_BASE + REG_IO_BANK0_INTR0 + int(gpio_pin / 8) * 4,
pin_mode << 4 * (gpio_pin % 8),
)
en_reg = (
REG_IO_BANK0_BASE + REG_IO_BANK0_DORMANT_WAKE_INTE0 + int(gpio_pin / 8) * 4
)
if en_reg not in registers_events:
registers_events[en_reg] = 0
registers_events[en_reg] = registers_events[en_reg] | (
pin_mode << 4 * (gpio_pin % 8)
)
# Enable Wake-up from GPIO IRQ
for en_reg, events in registers_events.items():
_write_bits(en_reg, events)
# Setup clocks for going dormant
_write_bits(CLOCKS_BASE + CLK_REF_CTRL, 0x02)
_write_bits(CLOCKS_BASE + CLK_SYS_CTRL, 0x00)
_write_bits(CLOCKS_BASE + CLK_USB_CTRL, 0x00)
_write_bits(CLOCKS_BASE + CLK_ADC_CTRL, 0x00)
_write_bits(CLOCKS_BASE + CLK_RTC_CTRL, 0x60)
_write_bits(CLOCKS_BASE + CLK_PERI_CTRL, 0x00)
_write_bits(PLL_USB_BASE + PLL_PWR, 0x2D)
_write_bits(ROSC_BASE, 0xD1EAA0)
# Go dormant
_write_bits(REG_XOSC_BASE + REG_XOSC_DORMANT, XOSC_DORMANT_VALUE_DORMANT)
###
# This part will happen after the pico wakes up
###
# Normalize clocks
_write_bits(CLOCKS_BASE + CLK_REF_CTRL, 0x02)
_write_bits(CLOCKS_BASE + CLK_SYS_CTRL, 0x01)
_write_bits(CLOCKS_BASE + CLK_USB_CTRL, 0x800)
_write_bits(CLOCKS_BASE + CLK_ADC_CTRL, 0x800)
_write_bits(CLOCKS_BASE + CLK_RTC_CTRL, 0x800)
_write_bits(CLOCKS_BASE + CLK_PERI_CTRL, 0x800)
_write_bits(PLL_USB_BASE + PLL_PWR, 0x04)
_write_bits(ROSC_BASE, 0xFFFAA0)
while not _read_bits(REG_XOSC_BASE + REG_XOSC_STATUS) & XOSC_STATUS_STABLE_BITS:
pass
for gpio_pin, pin_mode in pin_modes.items():
# clear irq
_write_bits(
REG_IO_BANK0_BASE + REG_IO_BANK0_INTR0 + int(gpio_pin / 8) * 4,
pin_mode << 4 * (gpio_pin % 8),
)
def dormant_until_pins(gpio_pins, edge=True, high=True):
low = not high
level = not edge
if level and low:
event = LEVEL_LOW
if level and high:
event = LEVEL_HIGH
if edge and low:
event = EDGE_LOW
if edge and high:
event = EDGE_HIGH
dormant_with_modes({pin: event for pin in gpio_pins})
def dormant_until_pin(gpio_pin, edge=True, high=True):
dormant_until_pins([gpio_pin], edge, high)
@micropython.asm_thumb
def lightsleep():
wfi()