Thanks to visit codestin.com
Credit goes to github.com

Skip to content
This repository was archived by the owner on Sep 10, 2024. It is now read-only.

Commit f7e2857

Browse files
committed
Add support for Pytrack V2.0X & Pysense V2.0X
Add variable buffer support for Pytrack V1.X
1 parent 52bdd71 commit f7e2857

File tree

14 files changed

+1973
-2
lines changed

14 files changed

+1973
-2
lines changed

lib/pycoproc/pycoproc-2.py

Lines changed: 345 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,345 @@
1+
#!/usr/bin/env python
2+
#
3+
# Copyright (c) 2020, Pycom Limited.
4+
#
5+
# This software is licensed under the GNU GPL version 3 or any
6+
# later version, with permitted additional terms. For more information
7+
# see the Pycom Licence v1.0 document supplied with this file, or
8+
# available at https://www.pycom.io/opensource/licensing
9+
#
10+
11+
# See https://docs.pycom.io for more information regarding library specifics
12+
13+
from machine import Pin
14+
from machine import I2C
15+
import time
16+
import pycom
17+
18+
__version__ = '0.0.3'
19+
20+
""" PIC MCU wakeup reason types """
21+
WAKE_REASON_ACCELEROMETER = 1
22+
WAKE_REASON_PUSH_BUTTON = 2
23+
WAKE_REASON_TIMER = 4
24+
WAKE_REASON_INT_PIN = 8
25+
26+
class Pycoproc:
27+
""" class for handling the interaction with PIC MCU """
28+
29+
I2C_SLAVE_ADDR = const(8)
30+
31+
PYSENSE = const(1)
32+
PYTRACK = const(2)
33+
PYSCAN = const(3)
34+
35+
BOARD_TYPE_SET = (PYSENSE, PYTRACK, PYSCAN)
36+
37+
CMD_PEEK = const(0x0)
38+
CMD_POKE = const(0x01)
39+
CMD_MAGIC = const(0x02)
40+
CMD_HW_VER = const(0x10)
41+
CMD_FW_VER = const(0x11)
42+
CMD_PROD_ID = const(0x12)
43+
CMD_SETUP_SLEEP = const(0x20)
44+
CMD_GO_SLEEP = const(0x21)
45+
CMD_CALIBRATE = const(0x22)
46+
CMD_BAUD_CHANGE = const(0x30)
47+
CMD_DFU = const(0x31)
48+
CMD_RESET = const(0x40)
49+
50+
REG_CMD = const(0)
51+
REG_ADDRL = const(1)
52+
REG_ADDRH = const(2)
53+
REG_AND = const(3)
54+
REG_OR = const(4)
55+
REG_XOR = const(5)
56+
57+
ANSELA_ADDR = const(0x18C)
58+
ANSELB_ADDR = const(0x18D)
59+
ANSELC_ADDR = const(0x18E)
60+
61+
ADCON0_ADDR = const(0x9D)
62+
ADCON1_ADDR = const(0x9E)
63+
64+
IOCAP_ADDR = const(0x391)
65+
IOCAN_ADDR = const(0x392)
66+
67+
INTCON_ADDR = const(0x0B)
68+
OPTION_REG_ADDR = const(0x95)
69+
70+
_ADCON0_CHS_POSN = const(0x02)
71+
_ADCON0_ADON_MASK = const(0x01)
72+
_ADCON1_ADCS_POSN = const(0x04)
73+
_ADCON0_GO_nDONE_MASK = const(0x02)
74+
75+
ADRESL_ADDR = const(0x09B)
76+
ADRESH_ADDR = const(0x09C)
77+
78+
TRISA_ADDR = const(0x08C)
79+
TRISC_ADDR = const(0x08E)
80+
81+
PORTA_ADDR = const(0x00C)
82+
PORTC_ADDR = const(0x00E)
83+
84+
WPUA_ADDR = const(0x20C)
85+
86+
WAKE_REASON_ADDR = const(0x064C)
87+
MEMORY_BANK_ADDR = const(0x0620)
88+
89+
PCON_ADDR = const(0x096)
90+
STATUS_ADDR = const(0x083)
91+
92+
EXP_RTC_PERIOD = const(7000)
93+
94+
def __init__(self, i2c=None, sda='P22', scl='P21'):
95+
if i2c is not None:
96+
self.i2c = i2c
97+
else:
98+
self.i2c = I2C(0, mode=I2C.MASTER, pins=(sda, scl), baudrate=100000)
99+
100+
self.sda = sda
101+
self.scl = scl
102+
self.clk_cal_factor = 1
103+
self.reg = bytearray(6)
104+
self.wake_int = False
105+
self.wake_int_pin = False
106+
self.wake_int_pin_rising_edge = True
107+
108+
# Make sure we are inserted into the
109+
# correct board and can talk to the PIC
110+
try:
111+
self.read_fw_version()
112+
except Exception as e:
113+
raise Exception('Board not detected: {}'.format(e))
114+
115+
# init the ADC for the battery measurements
116+
self.poke_memory(ANSELC_ADDR, 1 << 2)
117+
self.poke_memory(ADCON0_ADDR, (0x06 << _ADCON0_CHS_POSN) | _ADCON0_ADON_MASK)
118+
self.poke_memory(ADCON1_ADDR, (0x06 << _ADCON1_ADCS_POSN))
119+
# enable the pull-up on RA3
120+
self.poke_memory(WPUA_ADDR, (1 << 3))
121+
# make RC5 an input
122+
self.set_bits_in_memory(TRISC_ADDR, 1 << 5)
123+
# set RC6 and RC7 as outputs and enable power to the sensors and the GPS
124+
self.mask_bits_in_memory(TRISC_ADDR, ~(1 << 6))
125+
self.mask_bits_in_memory(TRISC_ADDR, ~(1 << 7))
126+
127+
128+
self.gps_standby(False)
129+
self.sensor_power()
130+
self.sd_power()
131+
132+
if self.read_fw_version() < 6:
133+
raise ValueError('Firmware out of date')
134+
135+
136+
def _write(self, data, wait=True):
137+
self.i2c.writeto(I2C_SLAVE_ADDR, data)
138+
if wait:
139+
self._wait()
140+
141+
def _read(self, size):
142+
return self.i2c.readfrom(I2C_SLAVE_ADDR, size + 1)[1:(size + 1)]
143+
144+
def _wait(self):
145+
count = 0
146+
time.sleep_us(10)
147+
while self.i2c.readfrom(I2C_SLAVE_ADDR, 1)[0] != 0xFF:
148+
time.sleep_us(100)
149+
count += 1
150+
if (count > 500): # timeout after 50ms
151+
raise Exception('Board timeout')
152+
153+
def _send_cmd(self, cmd):
154+
self._write(bytes([cmd]))
155+
156+
def read_hw_version(self):
157+
self._send_cmd(CMD_HW_VER)
158+
d = self._read(2)
159+
return (d[1] << 8) + d[0]
160+
161+
def read_fw_version(self):
162+
self._send_cmd(CMD_FW_VER)
163+
d = self._read(2)
164+
return (d[1] << 8) + d[0]
165+
166+
def read_product_id(self):
167+
self._send_cmd(CMD_PROD_ID)
168+
d = self._read(2)
169+
return (d[1] << 8) + d[0]
170+
171+
def peek_memory(self, addr):
172+
self._write(bytes([CMD_PEEK, addr & 0xFF, (addr >> 8) & 0xFF]))
173+
return self._read(1)[0]
174+
175+
def poke_memory(self, addr, value):
176+
self._write(bytes([CMD_POKE, addr & 0xFF, (addr >> 8) & 0xFF, value & 0xFF]))
177+
178+
def magic_write_read(self, addr, _and=0xFF, _or=0, _xor=0):
179+
self._write(bytes([CMD_MAGIC, addr & 0xFF, (addr >> 8) & 0xFF, _and & 0xFF, _or & 0xFF, _xor & 0xFF]))
180+
return self._read(1)[0]
181+
182+
def toggle_bits_in_memory(self, addr, bits):
183+
self.magic_write_read(addr, _xor=bits)
184+
185+
def mask_bits_in_memory(self, addr, mask):
186+
self.magic_write_read(addr, _and=mask)
187+
188+
def set_bits_in_memory(self, addr, bits):
189+
self.magic_write_read(addr, _or=bits)
190+
191+
def get_wake_reason(self):
192+
""" returns the wakeup reason, a value out of constants WAKE_REASON_* """
193+
return self.peek_memory(WAKE_REASON_ADDR)
194+
195+
def get_sleep_remaining(self):
196+
""" returns the remaining time from sleep, as an interrupt (wakeup source) might have triggered """
197+
c3 = self.peek_memory(WAKE_REASON_ADDR + 3)
198+
c2 = self.peek_memory(WAKE_REASON_ADDR + 2)
199+
c1 = self.peek_memory(WAKE_REASON_ADDR + 1)
200+
time_device_s = (c3 << 16) + (c2 << 8) + c1
201+
# this time is from PIC internal oscilator, so it needs to be adjusted with the calibration value
202+
try:
203+
self.calibrate_rtc()
204+
except Exception:
205+
pass
206+
time_s = int((time_device_s / self.clk_cal_factor) + 0.5) # 0.5 used for round
207+
return time_s
208+
209+
def setup_sleep(self, time_s):
210+
try:
211+
self.calibrate_rtc()
212+
except Exception:
213+
pass
214+
time_s = int((time_s * self.clk_cal_factor) + 0.5) # round to the nearest integer
215+
if time_s >= 2**(8*3):
216+
time_s = 2**(8*3)-1
217+
self._write(bytes([CMD_SETUP_SLEEP, time_s & 0xFF, (time_s >> 8) & 0xFF, (time_s >> 16) & 0xFF]))
218+
219+
def go_to_sleep(self, gps=True):
220+
# enable or disable back-up power to the GPS receiver
221+
if gps:
222+
self.set_bits_in_memory(PORTC_ADDR, 1 << 7)
223+
else:
224+
self.mask_bits_in_memory(PORTC_ADDR, ~(1 << 7))
225+
# disable the ADC
226+
self.poke_memory(ADCON0_ADDR, 0)
227+
228+
if self.wake_int:
229+
# Don't touch RA3, RA5 or RC1 so that interrupt wake-up works
230+
self.poke_memory(ANSELA_ADDR, ~((1 << 3) | (1 << 5)))
231+
self.poke_memory(ANSELC_ADDR, ~((1 << 6) | (1 << 7) | (1 << 1)))
232+
else:
233+
# disable power to the accelerometer, and don't touch RA3 so that button wake-up works
234+
self.poke_memory(ANSELA_ADDR, ~(1 << 3))
235+
self.poke_memory(ANSELC_ADDR, ~(1 << 7))
236+
237+
self.poke_memory(ANSELB_ADDR, 0xFF)
238+
239+
# check if INT pin (PIC RC1), should be used for wakeup
240+
if self.wake_int_pin:
241+
if self.wake_int_pin_rising_edge:
242+
self.set_bits_in_memory(OPTION_REG_ADDR, 1 << 6) # rising edge of INT pin
243+
else:
244+
self.mask_bits_in_memory(OPTION_REG_ADDR, ~(1 << 6)) # falling edge of INT pin
245+
self.mask_bits_in_memory(ANSELC_ADDR, ~(1 << 1)) # disable analog function for RC1 pin
246+
self.set_bits_in_memory(TRISC_ADDR, 1 << 1) # make RC1 input pin
247+
self.mask_bits_in_memory(INTCON_ADDR, ~(1 << 1)) # clear INTF
248+
self.set_bits_in_memory(INTCON_ADDR, 1 << 4) # enable interrupt; set INTE)
249+
250+
self._write(bytes([CMD_GO_SLEEP]), wait=False)
251+
# kill the run pin
252+
Pin('P3', mode=Pin.OUT, value=0)
253+
254+
def calibrate_rtc(self):
255+
# the 1.024 factor is because the PIC LF operates at 31 KHz
256+
# WDT has a frequency divider to generate 1 ms
257+
# and then there is a binary prescaler, e.g., 1, 2, 4 ... 512, 1024 ms
258+
# hence the need for the constant
259+
self._write(bytes([CMD_CALIBRATE]), wait=False)
260+
self.i2c.deinit()
261+
Pin('P21', mode=Pin.IN)
262+
pulses = pycom.pulses_get('P21', 100)
263+
self.i2c.init(mode=I2C.MASTER, pins=(self.sda, self.scl), baudrate=100000)
264+
idx = 0
265+
for i in range(len(pulses)):
266+
if pulses[i][1] > EXP_RTC_PERIOD:
267+
idx = i
268+
break
269+
try:
270+
period = pulses[idx][1] - pulses[(idx - 1)][1]
271+
except:
272+
period = 0
273+
if period > 0:
274+
self.clk_cal_factor = (EXP_RTC_PERIOD / period) * (1000 / 1024)
275+
if self.clk_cal_factor > 1.25 or self.clk_cal_factor < 0.75:
276+
self.clk_cal_factor = 1
277+
278+
def button_pressed(self):
279+
button = self.peek_memory(PORTA_ADDR) & (1 << 3)
280+
return not button
281+
282+
def read_battery_voltage(self):
283+
self.set_bits_in_memory(ADCON0_ADDR, _ADCON0_GO_nDONE_MASK)
284+
time.sleep_us(50)
285+
while self.peek_memory(ADCON0_ADDR) & _ADCON0_GO_nDONE_MASK:
286+
time.sleep_us(100)
287+
adc_val = (self.peek_memory(ADRESH_ADDR) << 2) + (self.peek_memory(ADRESL_ADDR) >> 6)
288+
return (((adc_val * 3.3 * 280) / 1023) / 180) + 0.01 # add 10mV to compensate for the drop in the FET
289+
290+
def setup_int_wake_up(self, rising, falling):
291+
""" rising is for activity detection, falling for inactivity """
292+
wake_int = False
293+
if rising:
294+
self.set_bits_in_memory(IOCAP_ADDR, 1 << 5)
295+
wake_int = True
296+
else:
297+
self.mask_bits_in_memory(IOCAP_ADDR, ~(1 << 5))
298+
299+
if falling:
300+
self.set_bits_in_memory(IOCAN_ADDR, 1 << 5)
301+
wake_int = True
302+
else:
303+
self.mask_bits_in_memory(IOCAN_ADDR, ~(1 << 5))
304+
self.wake_int = wake_int
305+
306+
def setup_int_pin_wake_up(self, rising_edge = True):
307+
""" allows wakeup to be made by the INT pin (PIC -RC1) """
308+
self.wake_int_pin = True
309+
self.wake_int_pin_rising_edge = rising_edge
310+
311+
def gps_standby(self, enabled=True):
312+
# make RC4 an output
313+
self.mask_bits_in_memory(TRISC_ADDR, ~(1 << 4))
314+
if enabled:
315+
# drive RC4 low
316+
self.mask_bits_in_memory(PORTC_ADDR, ~(1 << 4))
317+
else:
318+
# drive RC4 high
319+
self.set_bits_in_memory(PORTC_ADDR, 1 << 4)
320+
321+
def sensor_power(self, enabled=True):
322+
# make RC7 an output
323+
self.mask_bits_in_memory(TRISC_ADDR, ~(1 << 7))
324+
if enabled:
325+
# drive RC7 high
326+
self.set_bits_in_memory(PORTC_ADDR, 1 << 7)
327+
else:
328+
# drive RC7 low
329+
self.mask_bits_in_memory(PORTC_ADDR, ~(1 << 7))
330+
331+
def sd_power(self, enabled=True):
332+
# make RA5 an output
333+
self.mask_bits_in_memory(TRISA_ADDR, ~(1 << 5))
334+
if enabled:
335+
# drive RA5 high
336+
self.set_bits_in_memory(PORTA_ADDR, 1 << 5)
337+
else:
338+
# drive RA5 low
339+
self.mask_bits_in_memory(PORTA_ADDR, ~(1 << 5))
340+
341+
342+
# at the end:
343+
def reset_cmd(self):
344+
self._send_cmd(CMD_RESET)
345+
return

0 commit comments

Comments
 (0)