Skip to content
Permalink
Branch: master
Find file Copy path
Find file Copy path
0 contributors

Users who have contributed to this file

199 lines (159 sloc) 6.24 KB
#!/usr/bin/env python
#
# Copyright (c) 2019, Pycom Limited.
#
# This software is licensed under the GNU GPL version 3 or any
# later version, with permitted additional terms. For more information
# see the Pycom Licence v1.0 document supplied with this file, or
# available at https://www.pycom.io/opensource/licensing
#
from machine import UART
from machine import Pin
import pycom
import gc
__version__ = '1.0.5'
PIN_MASK = 0b1010
COMM_PIN = 'P10'
PIN_WAKE = 0
TIMER_WAKE = 1 << 4
POWER_ON_WAKE = 1 << 5
class DeepSleep:
WPUA_ADDR = const(0x09)
OPTION_REG_ADDR = const(0x0E)
IOCAP_ADDR = const(0x1A)
IOCAN_ADDR = const(0x1B)
WAKE_STATUS_ADDR = const(0x40)
MIN_BAT_ADDR = const(0x41)
SLEEP_TIME_ADDR = const(0x42)
CTRL_0_ADDR = const(0x45)
EXP_RTC_PERIOD = const(7000)
def __init__(self):
self.uart = UART(1, baudrate=10000, pins=(COMM_PIN, ), timeout_chars=5)
self.clk_cal_factor = 1
self.uart.read()
# enable the weak pull-ups control
self.clearbits(OPTION_REG_ADDR, 1 << 7)
def _send(self, data):
self.uart.write(bytes(data))
def _start(self):
self.uart.sendbreak(12)
self._send([0x55])
def _magic(self, address, and_val, or_val, xor_val, expected=None):
self._start()
self._send([address, and_val & 0xFF, or_val & 0xFF, xor_val & 0xFF])
if expected is None:
return self.uart.read()
else:
if expected > 0:
return self.uart.read(expected)
def _add_to_pin_mask(self, mask, pin):
if pin == 'P10' or pin == 'G17':
mask |= 0x01
elif pin == 'P17' or pin == 'G31':
mask |= 0x02
elif pin == 'P18' or pin == 'G30':
mask |= 0x08
else:
raise ValueError('Invalid Pin specified: {}'.format(pin))
return mask
def _create_pin_mask(self, pins):
mask = 0
if type(pins) is str:
mask = self._add_to_pin_mask(mask, pins)
else:
for pin in pins:
mask = self._add_to_pin_mask(mask, pin)
return mask & PIN_MASK
def poke(self, address, value):
self._magic(address, 0, value, 0)
def peek(self, address):
try:
return self._magic(address, 0xFF, 0, 0)[6]
except:
return self._magic(address, 0xFF, 0, 0)[6]
def setbits(self, address, mask):
self._magic(address, 0xFF, mask, 0)
def clearbits(self, address, mask):
self._magic(address, ~mask, 0, 0)
def togglebits(self, address, mask):
self._magic(address, 0xFF, 0, mask)
def calibrate(self):
""" The microcontroller will send the value of CTRL_0 after setting the bit
and then will send the following pattern through the data line:
val | 1 | 0 | 1*| 0 | 1*| 0 | 1
ms | 1 | 1 | 1 | 1 | 8 | 1 | -
The idea is to measure the real life duration of periods marked with *
and substract them. That will remove any errors common to both measurements
The result is 7 ms as generated by the PIC LF clock.
It can be used to scale any future sleep value. """
# setbits, but limit the number of received bytes to avoid confusion with pattern
self._magic(CTRL_0_ADDR, 0xFF, 1 << 2, 0, 0)
self.uart.deinit()
self._pulses = pycom.pulses_get(COMM_PIN, 150)
self.uart.init(baudrate=10000, pins=(COMM_PIN, ), timeout_chars=5)
idx = 0
for i in range(len(self._pulses)):
if self._pulses[i][1] > EXP_RTC_PERIOD:
idx = i
break
try:
self.clk_cal_factor = (self._pulses[idx][1] - self._pulses[(idx - 1)][1]) / EXP_RTC_PERIOD
except:
self.clk_cal_factor = 1
if self.clk_cal_factor > 1.25 or self.clk_cal_factor < 0.75:
self.clk_cal_factor = 1
def enable_auto_poweroff(self):
self.setbits(CTRL_0_ADDR, 1 << 1)
def enable_pullups(self, pins):
mask = self._create_pin_mask(pins)
self.setbits(WPUA_ADDR, mask)
def disable_pullups(self, pins):
mask = self._create_pin_mask(pins)
self.clearbits(WPUA_ADDR, mask)
def enable_wake_on_raise(self, pins):
mask = self._create_pin_mask(pins)
self.setbits(IOCAP_ADDR, mask)
def disable_wake_on_raise(self, pins):
mask = self._create_pin_mask(pins)
self.clearbits(IOCAP_ADDR, mask)
def enable_wake_on_fall(self, pins):
mask = self._create_pin_mask(pins)
self.setbits(IOCAN_ADDR, mask)
def disable_wake_on_fall(self, pins):
mask = self._create_pin_mask(pins)
self.clearbits(IOCAN_ADDR, mask)
def get_wake_status(self):
# bits as they are returned from PIC:
# 0: PIN 0 value after awake
# 1: PIN 1 value after awake
# 2: PIN 2 value after awake
# 3: PIN 3 value after awake
# 4: TIMEOUT
# 5: POWER ON
wake_r = self.peek(WAKE_STATUS_ADDR)
return {'wake': wake_r & (TIMER_WAKE | POWER_ON_WAKE),
'P10': wake_r & 0x01, 'P17': (wake_r & 0x02) >> 1,
'P18': (wake_r & 0x08) >> 3}
def set_min_voltage_limit(self, value):
# voltage value passed in volts (e.g. 3.6) and round it to the nearest integer
value = int(((256 * 2.048) + (value / 2)) / value)
self.poke(MIN_BAT_ADDR, value)
def go_to_sleep(self, seconds):
gc.collect()
while True:
try:
self.calibrate()
except Exception:
pass
# the 1.024 factor is because the PIC LF operates at 31 KHz
# WDT has a frequency divider to generate 1 ms
# and then there is a binary prescaler, e.g., 1, 2, 4 ... 512, 1024 ms
# hence the need for the constant
# round to the nearest integer
seconds = int((seconds / (1.024 * self.clk_cal_factor)) + 0.5)
self.poke(SLEEP_TIME_ADDR, (seconds >> 16) & 0xFF)
self.poke(SLEEP_TIME_ADDR + 1, (seconds >> 8) & 0xFF)
self.poke(SLEEP_TIME_ADDR + 2, seconds & 0xFF)
self.setbits(CTRL_0_ADDR, 1 << 0)
def hw_reset(self):
self.setbits(CTRL_0_ADDR, 1 << 4)
You can’t perform that action at this time.