-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.py
318 lines (266 loc) · 11.3 KB
/
main.py
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
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
# main.py -- put your code here!
#Import relevant modules
import network
import pyb
import utime
import machine
import micropython
import usocket as socket
from machine import Pin
from pyb import ExtInt
from pyb import USB_VCP
from pyb import I2C
from pyb import ADC
from pyb import DAC
from pyb import LED
from array import array
micropython.alloc_emergency_exception_buf(100) # For interrupt debugging
#==================================================================================#
# SETUP
#==================================================================================#
# OBJECT DEFINITIONS
led = LED(1) # define diagnostic LED
#usb = USB_VCP() # init VCP object, NOT IN USE
i2c = I2C(1, I2C.MASTER,
baudrate=400000) # define I2C channel, master/slave protocol and baudrate needed
ti = pyb.Timer(2,freq=1000000) # init timer for interrupts
# PIN SETUP AND INITIAL POLARITY/INTERRUPT MODE
Pin('PULL_SCL', Pin.OUT, value=1) # enable 5.6kOhm X9/SCL pull-up
Pin('PULL_SDA', Pin.OUT, value=1) # enable 5.6kOhm X10/SDA pull-up
adcpin = Pin("X12")
adc = ADC(adcpin, "SingleDMA") # define ADC pin for pulse stretcher measurement
calibpin = Pin("X3") # ADC pin for calibration
calibadc = 0
pin_mode = Pin('X8', Pin.OUT) # define pulse clearing mode pin
pin_mode.value(1) # low -> automatic pulse clearing, high -> manual pulse clear
clearpin = Pin('X7',Pin.OUT) # choose pin used for manually clearing the pulse once ADC measurement is complete
polarpin = Pin('X6', Pin.OUT) # define pin that chooses polarity
testpulsepin = Pin('X4',Pin.OUT) # pin to enable internal test pulses on APIC
polarpin.value(0) # set to 1 for positive polarity
# DATA STORAGE AND COUNTERS
sendbuf = array('H',[720]) # 1440 byte buffer for calibration routine
data = array('H',[0]*4) # buffer for writing adc interrupt data from adc.read_timed() in calibration() and ADC_IT_poll()
calibdata = array('H',[0]*4) # buffer to store ADC data from calibadc
count=0 # counter for pulses read
peakcount = 0
ratecounter = 0 # counter for rate measurements
STATE = "STARTUP" # state variable for applying startup settings etc.
ADC_STATE = "SingleDMA" # monitor state of ADC configs
# SET UP WIRELESS ACCESS POINT
wl_ap = network.WLAN(1) # init wlan object
wl_ap.config(essid='PYBD') # set AP SSID
wl_ap.config(channel=1) # set AP channel
wl_ap.active(1) # enable the AP
# LOOP UNTIL A CONNECTION IS RECEIVED
while wl_ap.status('stations')==[]:
utime.sleep(1)
# SET UP THE NETWORK SOCKET FOR UDP
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.bind(('',8080)) # network listens on port 8080, any IP
destipv4 = ('192.168.4.16', 8080) # destination for sending data
print("SOCKET BOUND")
#==================================================================================#
# BOARD STATE CHECKING
#==================================================================================#
def checkstate():
a = STATE.encode('utf-8')
s.sendto(a, destipv4)
def adc_setstate(state):
#assert(state == 'SingleDMA' or state == "TripleDMA" or state == "Single" or state =="NONE")
global ADC_STATE
if state != ADC_STATE:
ADC_STATE = state
global adc
adc.deinit_setup()
adc = ADC(adcpin,state)
else:
print("ADC STATE UNCHANGED")
def setstate():
utime.sleep(0.1)
STATE = s.recv(32).decode('utf-8')
def drain_socket():
s.settimeout(0)
while True:
try:
s.recv(2048)
except:
break
s.settimeout(None)
#==================================================================================#
# I2C CONTROL
# Ir, read both I2C chips and send via socket.
# Iw, write 8 bit val to an I2C pot.
# Is, scan for the I2C addresses on the pulse stretcher.
# If the I2C chips are not connected, an exception will be raised.
#==================================================================================#
def Ir():
if i2c.is_ready(0x2D) and i2c.is_ready(0x2C):
gain = i2c.recv(1,addr=0x2D)
threshold = i2c.recv(1,addr=0x2C)
s.sendto(gain,destipv4)
s.sendto(threshold,destipv4)
else:
raise Exception
return None
def Iw(address):
if i2c.is_ready(address):
recvd = s.recv(1)
value = int.from_bytes(recvd,'little',False)
b = bytearray([0x00,value])
i2c.send(b,addr=address)
else:
raise Exception
return None
def Is():
scan = bytearray(2)
i2clist = i2c.scan()
if i2clist == []:
pass
else:
for idx,chip in enumerate(i2clist):
scan[idx] = chip
s.sendto(scan, destipv4)
return None
#==================================================================================#
# CALIBRATION CURVE CODE FIXME: Find a way to measure this properly
#==================================================================================#
def calibrate():
global calibint
global count
global calibadc
count = 0 # reset counter
adc_setstate("Single") # set state of adc obj to mode Single
calibadc = ADC(calibpin, "Single") # init the second ADC in mode Single
calibint.enable()
utime.sleep(10) # 10s of calibration
calibint.disable()
def cbcal(line):
global count
# Take measurements
adc.read_timed(data,ti)
calibadc.read_timed(calibdata,ti)
# Add data to UDP packet buffer
sendbuf[4*count:4*count+4] = data
sendbuf[4*(count+1):4*(count+1)+4] = calibdata
count += 1
# send UDP buffer + reset counter
if count == 90:
s.sendto(sendbuf,destipv4)
count = 0
#==================================================================================#
# RATE MEASUREMENT CODE
#==================================================================================#
def rateaq():
print('COUNTING RATE')
global ratecounter
global rateint
ratecounter=0
a=utime.ticks_ms()
rateint.enable()
utime.sleep(4)
rateint.disable()
b = utime.ticks_ms()-a
finalrate = round((ratecounter/(b/1000)))
finalratebyte = finalrate.to_bytes(4,'little',False)
s.sendto(finalratebyte,destipv4)
def ratecount(line):
global ratecounter
ratecounter+=1
clearpin.value(1) # perform pulse clearing
clearpin.value(0)
#==================================================================================#
# ADC INTERRUPT MEASUREMENT CODE:
# Python level legacy function, interrupt to take and send data
# samples mnum peaks, uses schedule to delay measurements for
# concurrent interrupts.
#==================================================================================#
"""
def ADC_IT_poll():
global extint
global count
count = 0
utime.sleep(0.5)
msg, addr = s.recvfrom(8)
mnum = int.from_bytes(msg,'little')
mnum=mnum*1.2
adc_setstate("Single")
utime.sleep(1)
extint.enable()
while count < mnum:
pass
extint.disable()
print("MEASUREMENT COMPLETE")
drain_socket()
# ISR CALLBACK FUNCTION
def callback(arg):
extint.disable()
global count # reference the global count counter
adc.read_timed(data,ti) # 4 microsecond measurement from ADC at X12,
pos = (4*count)%500
if pos == 124:
sendbuf[pos:pos+4] = data
try:
s.sendto(sendbuf, destipv4)
except:
print("SEND FAILED")
else:
sendbuf[pos:pos+4] = data
count+=1 # pulse counter
extint.enable() # re-enable interrupts
# TEMP FIX FOR ISR OVERFLOW
# Uses micropython.schedule to delay interrupts
# that occur during ISR callback - interrupting usocket transfer is v. bad.
def cb(line):
micropython.schedule(callback,'a')
"""
# ENABLE GPIO INTERRUPTs
irqstate=pyb.disable_irq() # disable all interrupts during initialisation
rateint = ExtInt('X1', ExtInt.IRQ_RISING,
pyb.Pin.PULL_NONE, rateaq) # rate measurement interrupts on pin X1
rateint.disable()
calibint = ExtInt('X2',ExtInt.IRQ_RISING,
pyb.Pin.PULL_NONE,cbcal) # interrupts for ADC pulse DAQ on pin X2
calibint.disable()
pyb.enable_irq(irqstate) # re-enable interrupts
#==================================================================================#
# C ADC data stream method
# Wait for this to complete before attempting any other actions.
# Uses a different ADC setup from python level ADC_IT_poll.
#==================================================================================#
def read_DMA():
msg = s.recv(4)
mnum = int.from_bytes(msg,'little')
mnum = mnum
print(mnum)
adc_setstate("SingleDMA")
adc.read_dma(mnum)
#==================================================================================#
# COMMAND CODES:
# Bytearrays used by main loop to execute functions
# expect a 2-byte command.
#==================================================================================#
commands = {
# bytes(bytearray([a,b])) : command function,
bytes(bytearray([0,0])) : Ir, # read first gain potentiometer, then threshold
bytes(bytearray([0,2])) : Is, # scan I2C addresses
bytes(bytearray([1,0])) : lambda : Iw(0x2D), # write gain pot
bytes(bytearray([1,1])) : lambda : Iw(0x2C), # write threshold pot
bytes(bytearray([2,0])) : read_DMA, # testing DMA interrupts measurements,
bytes(bytearray([2,1])) : ADC_IT_poll, # legacy python ADC interrupts method
bytes(bytearray([2,2])) : adc.read_interleaved, # TODO: Implement this feature properly - requires deinit adc ability??
bytes(bytearray([4,0])) : lambda : polarpin.value(0), # Negative polarity
bytes(bytearray([4,1])) : lambda : polarpin.value(1), # Positive polarity
#bytes(bytearray([5,0])) : calibrate, # FIXME: measure detector/apic gain profile
bytes(bytearray([5,1])) : rateaq, # measure sample rate
bytes(bytearray([6,0])) : lambda: testpulsepin.value(0), # disable test pulses
bytes(bytearray([6,1])) : lambda: testpulsepin.value(1), # enable test pulses
bytes(bytearray([7,1])) : checkstate, # check the state of the pybaord
bytes(bytearray([7,0])) : setstate, # set the current state of the board
}
#==================================================================================#
# MAIN LOOP
#==================================================================================#
while True:
mode = s.recv(2) # wait until the board receives the 2 byte command code, no timeout
print("MODE RECEIVED")
commands[mode]() # reference commands dictionary and run the corresponding function