@@ -32,14 +32,21 @@ class Pycoproc:
32
32
CMD_POKE = const (0x01 )
33
33
CMD_MAGIC = const (0x02 )
34
34
CMD_HW_VER = const (0x10 )
35
- CMD_FW_VER = const (0x11 )
36
- CMD_PROD_ID = const (0x12 )
35
+ CMD_FW_VER = const (0x11 ) #define SW_VERSION (15)
36
+ CMD_PROD_ID = const (0x12 ) #USB product ID, e.g. PYSENSE (0xF012)
37
37
CMD_SETUP_SLEEP = const (0x20 )
38
38
CMD_GO_SLEEP = const (0x21 )
39
39
CMD_CALIBRATE = const (0x22 )
40
+ CMD_GO_NAP = const (0x23 )
40
41
CMD_BAUD_CHANGE = const (0x30 )
41
42
CMD_DFU = const (0x31 )
42
43
CMD_RESET = const (0x40 )
44
+ # CMD_GO_NAP options
45
+ SD_CARD_OFF = const (0x1 )
46
+ SENSORS_OFF = const (0x2 )
47
+ ACCELEROMETER_OFF = const (0x4 )
48
+ FIPY_OFF = const (0x8 )
49
+
43
50
44
51
REG_CMD = const (0 )
45
52
REG_ADDRL = const (1 )
@@ -62,17 +69,26 @@ class Pycoproc:
62
69
OPTION_REG_ADDR = const (0x95 )
63
70
64
71
_ADCON0_CHS_POSN = const (0x02 )
72
+ _ADCON0_CHS_AN5 = const (0x5 ) # AN5 / RC1
73
+ _ADCON0_CHS_AN6 = const (0x6 ) # AN6 / RC2
65
74
_ADCON0_ADON_MASK = const (0x01 )
66
- _ADCON1_ADCS_POSN = const (0x04 )
75
+ _ADCON0_ADCS_F_OSC_64 = const (0x6 ) # A/D Conversion Clock
67
76
_ADCON0_GO_nDONE_MASK = const (0x02 )
77
+ _ADCON1_ADCS_POSN = const (0x04 )
68
78
69
79
ADRESL_ADDR = const (0x09B )
70
80
ADRESH_ADDR = const (0x09C )
71
81
72
82
TRISA_ADDR = const (0x08C )
83
+ TRISB_ADDR = const (0x08D )
73
84
TRISC_ADDR = const (0x08E )
74
85
86
+ LATA_ADDR = const (0x10C )
87
+ LATB_ADDR = const (0x10D )
88
+ LATC_ADDR = const (0x10E )
89
+
75
90
PORTA_ADDR = const (0x00C )
91
+ PORTB_ADDR = const (0x00C )
76
92
PORTC_ADDR = const (0x00E )
77
93
78
94
WPUA_ADDR = const (0x20C )
@@ -85,6 +101,16 @@ class Pycoproc:
85
101
86
102
EXP_RTC_PERIOD = const (7000 )
87
103
104
+ @staticmethod
105
+ def wake_up ():
106
+ # P9 is connected to RC1, make P9 an output
107
+ p9 = Pin ("P9" , mode = Pin .OUT )
108
+ # toggle rc1 to trigger wake up
109
+ p9 (1 )
110
+ time .sleep (0.1 )
111
+ p9 (0 )
112
+ time .sleep (0.1 )
113
+
88
114
def __init__ (self , i2c = None , sda = 'P22' , scl = 'P21' ):
89
115
if i2c is not None :
90
116
self .i2c = i2c
@@ -95,37 +121,52 @@ def __init__(self, i2c=None, sda='P22', scl='P21'):
95
121
self .scl = scl
96
122
self .clk_cal_factor = 1
97
123
self .reg = bytearray (6 )
98
- self .wake_int = False
99
- self .wake_int_pin = False
100
- self .wake_int_pin_rising_edge = True
101
124
102
125
# Make sure we are inserted into the
103
126
# correct board and can talk to the PIC
104
- try :
105
- self .read_fw_version ()
106
- except Exception as e :
107
- raise Exception ('Board not detected: {}' .format (e ))
127
+ retry = 0
128
+ while True :
129
+ try :
130
+ self .read_fw_version ()
131
+ break
132
+ except Exception as e :
133
+ if retry > 10 :
134
+ raise Exception ('Board not detected: {}' .format (e ))
135
+ print ("Couldn't init Pycoproc. Maybe the PIC is still napping. Try to wake it. ({}, {})" .format (retry , e ))
136
+ Pycoproc .wake_up ()
137
+ # # p9 is connected to RC1, toggle it to wake PIC
138
+ # p9 = Pin("P9", mode=Pin.OUT)
139
+ # p9(1)
140
+ # time.sleep(0.1)
141
+ # p9(0)
142
+ # time.sleep(0.1)
143
+ # Pin("P9", mode=Pin.IN)
144
+ retry += 1
108
145
109
146
# init the ADC for the battery measurements
110
- self .poke_memory (ANSELC_ADDR , 1 << 2 )
111
- self .poke_memory (ADCON0_ADDR , (0x06 << _ADCON0_CHS_POSN ) | _ADCON0_ADON_MASK )
112
- self .poke_memory (ADCON1_ADDR , (0x06 << _ADCON1_ADCS_POSN ))
113
- # enable the pull-up on RA3
114
- self .poke_memory (WPUA_ADDR , (1 << 3 ))
147
+ self .write_byte (ANSELC_ADDR , 1 << 2 ) # RC2 analog input
148
+ self .write_byte (ADCON0_ADDR , (_ADCON0_CHS_AN6 << _ADCON0_CHS_POSN ) | _ADCON0_ADON_MASK ) # select analog channel and enable ADC
149
+ self .write_byte (ADCON1_ADDR , (_ADCON0_ADCS_F_OSC_64 << _ADCON1_ADCS_POSN )) # ADC conversion clock
115
150
116
- # set RC6 and RC7 as outputs and enable power to the sensors and the GPS
117
- self .mask_bits_in_memory (TRISC_ADDR , ~ (1 << 6 ))
118
- self .mask_bits_in_memory (TRISC_ADDR , ~ (1 << 7 ))
151
+ # enable the pull-up on RA3
152
+ self .write_byte (WPUA_ADDR , (1 << 3 ))
119
153
154
+ # set RC6 and RC7 as outputs
155
+ self .write_bit (TRISC_ADDR , 6 , 0 ) # 3V3SENSOR_A, power to Accelerometer
156
+ self .write_bit (TRISC_ADDR , 7 , 0 ) # PWR_CTRL power to other sensors
120
157
121
- self .gps_standby (False )
122
- self .sensor_power ()
123
- self .sd_power ()
158
+ # enable power to the sensors and the GPS
159
+ self .gps_standby (False ) # GPS, RC4
160
+ self .sensor_power () # PWR_CTRL, RC7
161
+ self .sd_power () # LP_CTRL, RA5
124
162
163
+ usb_pid = self .read_product_id ()
164
+ if usb_pid != 0xf012 :
165
+ raise ValueError ('Not a Pysense2/Pytrack2 ({})' .format (usb_pid ))
125
166
# for Pysense/Pytrack 2.0, the minimum firmware version is 15
126
- if self .read_fw_version () < 15 :
127
- raise ValueError ( 'Firmware out of date' )
128
-
167
+ fw = self .read_fw_version ()
168
+ if fw < 15 :
169
+ raise ValueError ( 'Firmware out of date' , fw )
129
170
130
171
def _write (self , data , wait = True ):
131
172
self .i2c .writeto (I2C_SLAVE_ADDR , data )
@@ -162,11 +203,11 @@ def read_product_id(self):
162
203
d = self ._read (2 )
163
204
return (d [1 ] << 8 ) + d [0 ]
164
205
165
- def peek_memory (self , addr ):
206
+ def read_byte (self , addr ):
166
207
self ._write (bytes ([CMD_PEEK , addr & 0xFF , (addr >> 8 ) & 0xFF ]))
167
208
return self ._read (1 )[0 ]
168
209
169
- def poke_memory (self , addr , value ):
210
+ def write_byte (self , addr , value ):
170
211
self ._write (bytes ([CMD_POKE , addr & 0xFF , (addr >> 8 ) & 0xFF , value & 0xFF ]))
171
212
172
213
def magic_write_read (self , addr , _and = 0xFF , _or = 0 , _xor = 0 ):
@@ -182,6 +223,25 @@ def mask_bits_in_memory(self, addr, mask):
182
223
def set_bits_in_memory (self , addr , bits ):
183
224
self .magic_write_read (addr , _or = bits )
184
225
226
+ def read_bit (self , address , bit ):
227
+ b = self .read_byte (address )
228
+ # print("{0:08b}".format(b))
229
+ mask = (1 << bit )
230
+ # print("{0:08b}".format(mask))
231
+ # print("{0:08b}".format(b & mask))
232
+ if b & mask :
233
+ return 1
234
+ else :
235
+ return 0
236
+
237
+ def write_bit (self , address , bit , level ):
238
+ if level == 1 :
239
+ self .set_bits_in_memory (address , (1 << bit ) )
240
+ elif level == 0 :
241
+ self .mask_bits_in_memory (address , ~ (1 << bit ) )
242
+ else :
243
+ raise Exception ("level" , level )
244
+
185
245
def setup_sleep (self , time_s ):
186
246
try :
187
247
self .calibrate_rtc ()
@@ -192,38 +252,45 @@ def setup_sleep(self, time_s):
192
252
time_s = 2 ** (8 * 3 )- 1
193
253
self ._write (bytes ([CMD_SETUP_SLEEP , time_s & 0xFF , (time_s >> 8 ) & 0xFF , (time_s >> 16 ) & 0xFF ]))
194
254
195
- def go_to_sleep (self , gps = True ):
255
+ def go_to_sleep (self , gps = True , pycom_module_off = True , accelerometer_off = True , wake_interrupt = False ):
196
256
# enable or disable back-up power to the GPS receiver
197
257
self .gps_standby (gps )
198
- self .sensor_power (False )
199
- self .sd_power (False )
200
258
201
259
# disable the ADC
202
- self .poke_memory (ADCON0_ADDR , 0 )
260
+ self .write_byte (ADCON0_ADDR , 0 )
203
261
204
- if self .wake_int :
205
- # Don't touch RA3, RA5 or RC1 so that interrupt wake-up works
206
- self .poke_memory (ANSELA_ADDR , ~ (1 << 3 ))
207
- self .poke_memory (ANSELC_ADDR , ~ ((1 << 6 ) | (1 << 7 ) | (1 << 1 )))
208
- else :
209
- # disable power to the accelerometer, and don't touch RA3 so that button wake-up works
210
- self .poke_memory (ANSELA_ADDR , ~ (1 << 3 ))
211
- self .poke_memory (ANSELC_ADDR , ~ (0 ))
212
-
213
- self .poke_memory (ANSELB_ADDR , 0xFF )
214
-
215
- # check if INT pin (PIC RC1), should be used for wakeup
216
- if self .wake_int_pin :
217
- if self .wake_int_pin_rising_edge :
218
- self .set_bits_in_memory (OPTION_REG_ADDR , 1 << 6 ) # rising edge of INT pin
219
- else :
220
- self .mask_bits_in_memory (OPTION_REG_ADDR , ~ (1 << 6 )) # falling edge of INT pin
262
+ # RC0, RC1, RC2, analog input
263
+ self .set_bits_in_memory (TRISC_ADDR , (1 << 2 ) | (1 << 1 ) | (1 << 0 ) )
264
+ self .set_bits_in_memory (ANSELC_ADDR , (1 << 2 ) | (1 << 1 ) | (1 << 0 ) )
265
+
266
+ # RA4 analog input
267
+ self .set_bits_in_memory (TRISA_ADDR , (1 << 4 ) )
268
+ self .set_bits_in_memory (ANSELA_ADDR , (1 << 4 ) )
269
+
270
+ # RB4, RB5 analog input
271
+ self .set_bits_in_memory (TRISB_ADDR , (1 << 5 ) | (1 << 4 ) )
272
+ self .set_bits_in_memory (ANSELB_ADDR , (1 << 5 ) | (1 << 4 ) )
273
+
274
+ # actually only B4 and B5
275
+ # todo: tris=1?
276
+ self .write_byte (ANSELB_ADDR , 0xff )
277
+
278
+ if wake_interrupt :
279
+ # print("enable wake up PIC from RC1")
280
+ self .set_bits_in_memory (OPTION_REG_ADDR , 1 << 6 ) # rising edge of INT pin
221
281
self .mask_bits_in_memory (ANSELC_ADDR , ~ (1 << 1 )) # disable analog function for RC1 pin
222
282
self .set_bits_in_memory (TRISC_ADDR , 1 << 1 ) # make RC1 input pin
223
283
self .mask_bits_in_memory (INTCON_ADDR , ~ (1 << 1 )) # clear INTF
224
284
self .set_bits_in_memory (INTCON_ADDR , 1 << 4 ) # enable interrupt; set INTE)
225
285
226
- self ._write (bytes ([CMD_GO_SLEEP ]), wait = False )
286
+ nap_options = SD_CARD_OFF | SENSORS_OFF
287
+ if pycom_module_off :
288
+ nap_options |= FIPY_OFF
289
+ if accelerometer_off :
290
+ nap_options |= ACCELEROMETER_OFF
291
+
292
+ # print("CMD_GO_NAP {0:08b}".format(nap_options))
293
+ self ._write (bytes ([CMD_GO_NAP , nap_options ]), wait = False )
227
294
228
295
def calibrate_rtc (self ):
229
296
# the 1.024 factor is because the PIC LF operates at 31 KHz
@@ -248,72 +315,50 @@ def calibrate_rtc(self):
248
315
self .clk_cal_factor = (EXP_RTC_PERIOD / period ) * (1000 / 1024 )
249
316
if self .clk_cal_factor > 1.25 or self .clk_cal_factor < 0.75 :
250
317
self .clk_cal_factor = 1
318
+ time .sleep (0.5 )
251
319
252
320
def button_pressed (self ):
253
- button = self .peek_memory (PORTA_ADDR ) & ( 1 << 3 )
321
+ button = self .read_bit (PORTA_ADDR , 3 )
254
322
return not button
255
323
256
324
def read_battery_voltage (self ):
257
325
self .set_bits_in_memory (ADCON0_ADDR , _ADCON0_GO_nDONE_MASK )
258
326
time .sleep_us (50 )
259
- while self .peek_memory (ADCON0_ADDR ) & _ADCON0_GO_nDONE_MASK :
327
+ while self .read_byte (ADCON0_ADDR ) & _ADCON0_GO_nDONE_MASK :
260
328
time .sleep_us (100 )
261
- adc_val = (self .peek_memory (ADRESH_ADDR ) << 2 ) + (self .peek_memory (ADRESL_ADDR ) >> 6 )
329
+ adc_val = (self .read_byte (ADRESH_ADDR ) << 2 ) + (self .read_byte (ADRESL_ADDR ) >> 6 )
262
330
return (((adc_val * 3.3 * 280 ) / 1023 ) / 180 ) + 0.01 # add 10mV to compensate for the drop in the FET
263
331
264
- def setup_int_wake_up (self , rising , falling ):
265
- """ rising is for activity detection, falling for inactivity """
266
- wake_int = False
267
- if rising :
268
- self .set_bits_in_memory (IOCAP_ADDR , 1 << 5 )
269
- wake_int = True
270
- else :
271
- self .mask_bits_in_memory (IOCAP_ADDR , ~ (1 << 5 ))
272
-
273
- if falling :
274
- self .set_bits_in_memory (IOCAN_ADDR , 1 << 5 )
275
- wake_int = True
276
- else :
277
- self .mask_bits_in_memory (IOCAN_ADDR , ~ (1 << 5 ))
278
- self .wake_int = wake_int
279
-
280
- def setup_int_pin_wake_up (self , rising_edge = True ):
281
- """ allows wakeup to be made by the INT pin (PIC -RC1) """
282
- self .wake_int_pin = True
283
- self .wake_int_pin_rising_edge = rising_edge
284
-
285
332
def gps_standby (self , enabled = True ):
286
333
# make RC4 an output
287
- self .mask_bits_in_memory (TRISC_ADDR , ~ ( 1 << 4 ) )
334
+ self .write_bit (TRISC_ADDR , 4 , 0 )
288
335
if enabled :
289
336
# drive RC4 low
290
- self .mask_bits_in_memory ( PORTC_ADDR , ~ ( 1 << 4 ) )
337
+ self .write_bit ( LATC_ADDR , 4 , 0 )
291
338
else :
292
339
# drive RC4 high
293
- self .set_bits_in_memory ( PORTC_ADDR , 1 << 4 )
340
+ self .write_bit ( LATC_ADDR , 4 , 1 )
294
341
295
342
def sensor_power (self , enabled = True ):
296
343
# make RC7 an output
297
- self .mask_bits_in_memory (TRISC_ADDR , ~ ( 1 << 7 ) )
344
+ self .write_bit (TRISC_ADDR , 7 , 0 )
298
345
if enabled :
299
346
# drive RC7 high
300
- self .set_bits_in_memory ( PORTC_ADDR , 1 << 7 )
347
+ self .write_bit ( LATC_ADDR , 7 , 1 )
301
348
else :
302
349
# drive RC7 low
303
- self .mask_bits_in_memory ( PORTC_ADDR , ~ ( 1 << 7 ) )
350
+ self .write_bit ( LATC_ADDR , 7 , 0 )
304
351
305
352
def sd_power (self , enabled = True ):
306
353
# make RA5 an output
307
- self .mask_bits_in_memory (TRISA_ADDR , ~ ( 1 << 5 ) )
354
+ self .write_bit (TRISA_ADDR , 5 , 0 )
308
355
if enabled :
309
356
# drive RA5 high
310
- self .set_bits_in_memory ( PORTA_ADDR , 1 << 5 )
357
+ self .write_bit ( LATA_ADDR , 5 , 1 )
311
358
else :
312
359
# drive RA5 low
313
- self .mask_bits_in_memory (PORTA_ADDR , ~ (1 << 5 ))
314
-
360
+ self .write_bit (LATA_ADDR , 5 , 0 )
315
361
316
- # at the end:
317
362
def reset_cmd (self ):
318
363
self ._send_cmd (CMD_RESET )
319
364
return
0 commit comments