Skip to content

Commit ad7e508

Browse files
committed
1st pass at Pyboard D port.
1 parent d1cf81d commit ad7e508

File tree

5 files changed

+164
-43
lines changed

5 files changed

+164
-43
lines changed

lowpower/README.md

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
# A low power usayncio adaptation
22

3-
Release 0.1 25th July 2018
3+
Release 0.11 8th April 2019
4+
5+
API change: low power applications must now import `rtc_time_cfg` and set its
6+
`enabled` flag.
7+
Now supports Pyboard D.
48

59
1. [Introduction](./README.md#1-introduction)
610
2. [Installation](./README.md#2-installation)
@@ -62,16 +66,17 @@ tested. Copy the file `rtc_time.py` to the device so that it is on `sys.path`.
6266
## 2.1 Files
6367

6468
* `rtc_time.py` Low power library.
69+
* `rtc_time_cfg` Configuration file to enable `uasyncio` to use above.
6570
* `lpdemo.py` A basic application which waits for a pushbutton to be pressed
6671
before running. A second button press terminates it. While "off" and waiting
6772
very low power is consumed. A normally open pushbutton should be connected
6873
between `X1` and `Gnd`. This program is intended as a basic template for
6974
similar applications.
70-
* `lowpower.py` Send and receive messages on UART4, echoing received messages
71-
to UART2 at a different baudrate. This consumes about 1.4mA and serves to
75+
* `lp_uart.py` Send and receive messages on UART4, echoing received messages
76+
to UART1 at a different baudrate. This consumes about 1.4mA and serves to
7277
demonstrate that interrupt-driven devices operate correctly.
7378

74-
The test program `lowpower.py` requires a link between pins X1 and X2 to enable
79+
The test program `lp_uart.py` requires a link between pins X1 and X2 to enable
7580
UART 4 to receive data via a loopback.
7681

7782
###### [Contents](./README.md#a-low-power-usayncio-adaptation)
@@ -112,6 +117,10 @@ To avoid the power drain caused by `select.poll` the user code must issue the
112117
following:
113118

114119
```python
120+
import rtc_time_cfg
121+
rtc_time_cfg.enabled = True # Must be done before importing uasyncio
122+
123+
import uasyncio as asyncio
115124
try:
116125
if asyncio.version[0] != 'fast_io':
117126
raise AttributeError
@@ -160,6 +169,10 @@ from a separate power source for power measurements.
160169
Applications can detect which timebase is in use by issuing:
161170

162171
```python
172+
import rtc_time_cfg
173+
rtc_time_cfg.enabled = True # Must be done before importing uasyncio
174+
175+
import uasyncio as asyncio
163176
try:
164177
if asyncio.version[0] != 'fast_io':
165178
raise AttributeError
@@ -271,6 +284,10 @@ Attention to detail is required to minimise power consumption, both in terms of
271284
hardware and code. The only *required* change to application code is to add
272285

273286
```python
287+
import rtc_time_cfg
288+
rtc_time_cfg.enabled = True # Must be done before importing uasyncio
289+
290+
import uasyncio as asyncio
274291
try:
275292
if asyncio.version[0] != 'fast_io':
276293
raise AttributeError
@@ -377,9 +394,16 @@ the design of the scheduler beyond the use of a different timebase. It does,
377394
however, rely on the fact that the scheduler algorithm behaves as described
378395
above.
379396

397+
`rtc_time` imports `rtc_time_cfg` and quits if `rtc_time_cfg.enabled` is
398+
`False`. This ensures that `uasyncio` will only be affected by the `rtc_time`
399+
module if `rtc_time` has specifically been enabled by application code.
400+
380401
The `rtc_time` module ensures that `uasyncio` uses `utime` for timing if the
381402
module is present in the path but is unused. This can occur because of an
382-
active USB connection or if running on an an incompatible platform. This
383-
ensures that under such conditions performance is unaffected.
403+
active USB connection or if running on an an incompatible platform.
404+
405+
The above precautions ensures that application behaviour and performance are
406+
unaffected unless `rtc_time` has been enabled, a USB connection is absent, and
407+
the hardware is a Pyboard 1.x or Pyboard D.
384408

385409
###### [Contents](./README.md#a-low-power-usayncio-adaptation)

lowpower/lowpower.py renamed to lowpower/lp_uart.py

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
1-
# lowpower.py Demo of using uasyncio to reduce Pyboard power consumption
1+
# lp_uart.py Demo of using uasyncio to reduce Pyboard power consumption
22
# Author: Peter Hinch
33
# Copyright Peter Hinch 2018 Released under the MIT license
44

5-
# The file rtc_time.py must be on the path.
5+
# The files rtc_time.py and rtc_time_cfg.py must be on the path.
66
# Requires a link between X1 and X2.
7-
# Periodically sends a line on UART4 at 115200 baud.
8-
# This is received on UART4 and re-sent on UART2 (pin X3) at 9600 baud.
7+
# Periodically sends a line on UART4 at 9600 baud.
8+
# This is received on UART4 and re-sent on UART1 (pin Y1) at 115200 baud.
99

1010
import pyb
11+
import rtc_time_cfg
12+
rtc_time_cfg.enabled = True # Must be done before importing uasyncio
13+
1114
import uasyncio as asyncio
1215
try:
1316
if asyncio.version != 'fast_io':
@@ -38,8 +41,8 @@ async def receiver(uart_in, uart_out):
3841
def test(duration):
3942
if rtc_time.use_utime: # Not running in low power mode
4043
pyb.LED(3).on()
41-
uart2 = pyb.UART(2, 9600)
42-
uart4 = pyb.UART(4, 115200)
44+
uart2 = pyb.UART(1, 115200)
45+
uart4 = pyb.UART(4, 9600)
4346
# Instantiate event loop before using it in Latency class
4447
loop = asyncio.get_event_loop()
4548
lp = rtc_time.Latency(50) # ms

lowpower/lpdemo.py

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
# lpdemo.py Demo/test program for MicroPython asyncio low power operation
2+
# Author: Peter Hinch
3+
# Copyright Peter Hinch 2018-2019 Released under the MIT license
4+
5+
import rtc_time_cfg
6+
rtc_time_cfg.enabled = True
7+
8+
from pyb import LED, Pin
9+
import aswitch
10+
import uasyncio as asyncio
11+
try:
12+
if asyncio.version[0] != 'fast_io':
13+
raise AttributeError
14+
except AttributeError:
15+
raise OSError('This requires fast_io fork of uasyncio.')
16+
import rtc_time
17+
18+
class Button(aswitch.Switch):
19+
def __init__(self, pin):
20+
super().__init__(pin)
21+
self.close_func(self._sw_close)
22+
self._flag = False
23+
24+
def pressed(self):
25+
f = self._flag
26+
self._flag = False
27+
return f
28+
29+
def _sw_close(self):
30+
self._flag = True
31+
32+
running = False
33+
def start(loop, leds, tims):
34+
global running
35+
running = True
36+
coros = []
37+
# Demo: assume app requires higher speed (not true in this instance)
38+
rtc_time.Latency().value(50)
39+
# Here you might apply power to external hardware
40+
for x, led in enumerate(leds): # Create a coroutine for each LED
41+
coros.append(toggle(led, tims[x]))
42+
loop.create_task(coros[-1])
43+
return coros
44+
45+
def stop(leds, coros):
46+
global running
47+
running = False
48+
while coros:
49+
asyncio.cancel(coros.pop())
50+
# Remove power from external hardware
51+
for led in leds:
52+
led.off()
53+
rtc_time.Latency().value(200) # Slow down scheduler to conserve power
54+
55+
async def monitor(loop, button):
56+
leds = [LED(x) for x in (1, 2, 3)] # Create list of LED's and times
57+
tims = [200, 700, 1200]
58+
coros = start(loop, leds, tims)
59+
while True:
60+
if button.pressed():
61+
if running:
62+
stop(leds, coros)
63+
else:
64+
coros = start(loop, leds, tims)
65+
await asyncio.sleep_ms(0)
66+
67+
async def toggle(objLED, time_ms):
68+
while True:
69+
await asyncio.sleep_ms(time_ms)
70+
objLED.toggle()
71+
72+
loop = asyncio.get_event_loop()
73+
button = Button(Pin('X1', Pin.IN, Pin.PULL_UP))
74+
loop.create_task(monitor(loop, button))
75+
loop.run_forever()

lowpower/rtc_time.py

Lines changed: 48 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -11,23 +11,28 @@
1111

1212
import sys
1313
import utime
14+
from os import uname
15+
from rtc_time_cfg import enabled
16+
if not enabled:
17+
print('rtc_time module has not been enabled.')
18+
sys.exit(0)
1419

1520
_PERIOD = const(604800000) # ms in 7 days
1621
_PERIOD_2 = const(302400000) # half period
1722
_SS_TO_MS = 1000/256 # Subsecs to ms
18-
23+
d_series = uname().machine[:5] == 'PYBD_'
1924
use_utime = True # Assume the normal utime timebase
25+
2026
if sys.platform == 'pyboard':
2127
import pyb
2228
mode = pyb.usb_mode()
2329
if mode is None: # USB is disabled
2430
use_utime = False # use RTC timebase
2531
elif 'VCP' in mode: # User has enabled VCP in boot.py
26-
usb_conn = pyb.Pin.board.USB_VBUS.value() # USB physically connected to pyb V1.x
27-
if not usb_conn:
28-
usb_conn = hasattr(pyb.Pin.board, 'USB_HS_DP') and pyb.Pin.board.USB_HS_DP.value()
29-
if not usb_conn:
30-
usb_conn = hasattr(pyb.Pin.board, 'USB_DP') and pyb.Pin.board.USB_DP.value()
32+
if d_series: # Detect an active connection to the PC
33+
usb_conn = pyb.USB_VCP().isconnected()
34+
else:
35+
usb_conn = pyb.Pin.board.USB_VBUS.value() # USB physically connected to pyb V1.x
3136
if usb_conn:
3237
print('USB connection: rtc_time disabled.')
3338
else:
@@ -38,10 +43,44 @@
3843

3944
# For lowest power consumption set unused pins as inputs with pullups.
4045
# Note the 4K7 I2C pullups on X9 X10 Y9 Y10.
41-
for pin in [p for p in dir(pyb.Pin.board) if p[0] in 'XY']:
42-
pin_x = pyb.Pin(pin, pyb.Pin.IN, pyb.Pin.PULL_UP)
46+
if d_series:
47+
print('Running on Pyboard D') # Investigate which pins we can do this to TODO
48+
else:
49+
print('Running on Pyboard 1.x')
50+
for pin in [p for p in dir(pyb.Pin.board) if p[0] in 'XY']:
51+
pin_x = pyb.Pin(pin, pyb.Pin.IN, pyb.Pin.PULL_UP)
4352
# User code redefines any pins in use
4453

54+
# sleep_ms is defined to stop things breaking if someone imports uasyncio.core
55+
# Power won't be saved if this is done.
56+
sleep_ms = utime.sleep_ms
57+
if use_utime: # Run utime: Pyboard connected to PC via USB or alien platform
58+
ticks_ms = utime.ticks_ms
59+
ticks_add = utime.ticks_add
60+
ticks_diff = utime.ticks_diff
61+
else:
62+
rtc = pyb.RTC()
63+
# dt: (year, month, day, weekday, hours, minutes, seconds, subseconds)
64+
# weekday is 1-7 for Monday through Sunday.
65+
if d_series:
66+
# Subseconds are μs
67+
def ticks_ms():
68+
dt = rtc.datetime()
69+
return ((dt[3] - 1)*86400000 + dt[4]*3600000 + dt[5]*60000 + dt[6]*1000 +
70+
int(dt[7] / 1000))
71+
else:
72+
# subseconds counts down from 255 to 0
73+
def ticks_ms():
74+
dt = rtc.datetime()
75+
return ((dt[3] - 1)*86400000 + dt[4]*3600000 + dt[5]*60000 + dt[6]*1000 +
76+
int(_SS_TO_MS * (255 - dt[7])))
77+
78+
def ticks_add(a, b):
79+
return (a + b) % _PERIOD
80+
81+
def ticks_diff(end, start):
82+
return ((end - start + _PERIOD_2) % _PERIOD) - _PERIOD_2
83+
4584
import uasyncio as asyncio
4685

4786
# Common version has a needless dict: https://www.python.org/dev/peps/pep-0318/#examples
@@ -70,6 +109,7 @@ def __init__(self, t_ms=100):
70109
raise OSError('Event loop not instantiated.')
71110

72111
def _run(self):
112+
print('Low power mode is ON.')
73113
rtc = pyb.RTC()
74114
rtc.wakeup(self._t_ms)
75115
t_ms = self._t_ms
@@ -88,26 +128,3 @@ def value(self, val=None):
88128
if val is not None and not use_utime:
89129
self._t_ms = max(val, 0)
90130
return self._t_ms
91-
92-
# sleep_ms is defined to stop things breaking if someone imports uasyncio.core
93-
# Power won't be saved if this is done.
94-
sleep_ms = utime.sleep_ms
95-
if use_utime: # Run utime: Pyboard connected to PC via USB or alien platform
96-
ticks_ms = utime.ticks_ms
97-
ticks_add = utime.ticks_add
98-
ticks_diff = utime.ticks_diff
99-
else:
100-
rtc = pyb.RTC()
101-
# dt: (year, month, day, weekday, hours, minutes, seconds, subseconds)
102-
# weekday is 1-7 for Monday through Sunday.
103-
# subseconds counts down from 255 to 0
104-
def ticks_ms():
105-
dt = rtc.datetime()
106-
return ((dt[3] - 1)*86400000 + dt[4]*3600000 + dt[5]*60000 + dt[6]*1000 +
107-
int(_SS_TO_MS * (255 - dt[7])))
108-
109-
def ticks_add(a, b):
110-
return (a + b) % _PERIOD
111-
112-
def ticks_diff(end, start):
113-
return ((end - start + _PERIOD_2) % _PERIOD) - _PERIOD_2

lowpower/rtc_time_cfg.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# rtc_time_cfg.py
2+
enabled = False

0 commit comments

Comments
 (0)