Skip to content

Commit aa94766

Browse files
committed
- Added Frame_trigger device which outputs pulses at a specified rate on a pin and saves the pulse times in an analog data file. It is designed for triggering camera frames or similar applications.
- Analog_channel now takes an optional 'plot' argument when initiated which determines whether the signal streamed back to the computer is plotted by the GUI. - Fixed bug in analog channel where the sample timestamps were out by 1 sample, such that the timestamp for the 2nd sample was actually correct for the 1st sample etc.
1 parent d363b93 commit aa94766

File tree

4 files changed

+58
-24
lines changed

4 files changed

+58
-24
lines changed

devices/frame_trigger.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import pyb
2+
import pyControl.hardware as hw
3+
import pyControl.utility as ut
4+
5+
class Frame_trigger(hw.IO_object):
6+
7+
def __init__(self, pin, pulse_rate, name='frame_trigger'):
8+
self.timer = pyb.Timer(hw.available_timers.pop())
9+
self.pin = pyb.Pin(pin, mode=pyb.Pin.OUT, value=0)
10+
self.name = name
11+
self.pulse_rate = pulse_rate
12+
self.analog_channel = hw.Analog_channel(name, pulse_rate, plot=False)
13+
hw.assign_ID(self)
14+
15+
def _run_start(self):
16+
self.pulse_n = 0
17+
self.pinstate = False
18+
self.pin.value(False)
19+
self.timer.init(freq=2*self.pulse_rate) # Set timer to trigger subsequent pulses.
20+
self.timer.callback(self.ISR)
21+
ut.print('{} outputting pulses at {}Hz'.format(self.name, self.pulse_rate))
22+
23+
def _run_stop(self):
24+
self.pin.value(False)
25+
self.timer.deinit()
26+
27+
def ISR(self,t):
28+
self.pinstate = not self.pinstate
29+
self.pin.value(self.pinstate)
30+
if self.pinstate:
31+
self.pulse_n += 1
32+
self.analog_channel.put(self.pulse_n)

gui/plotting.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,7 @@ def set_state_machine(self, sm_info):
4949
self.states_plot.set_state_machine(sm_info)
5050
self.events_plot.set_state_machine(sm_info)
5151
self.analog_plot.set_state_machine(sm_info)
52-
53-
if sm_info['analog_inputs']:
52+
if self.analog_plot.inputs:
5453
self.analog_plot.axis.setVisible(True)
5554
self.events_plot.axis.getAxis('bottom').setLabel('')
5655
else:
@@ -210,12 +209,12 @@ def __init__(self, parent=None, data_dur=10):
210209
self.axis.setLimits(xMax=0)
211210

212211
def set_state_machine(self, sm_info):
213-
self.inputs = sm_info['analog_inputs']
212+
self.inputs = {ID: ai for ID,ai in sm_info['analog_inputs'].items() if ai['plot']}
214213
if not self.inputs: return # State machine may not have analog inputs.
215214
self.axis.clear()
216215
self.legend = self.axis.addLegend(offset=(10, 10))
217-
self.plots = {ai['ID']: self.axis.plot(name=name,
218-
pen=pg.mkPen(pg.intColor(i,len(self.inputs)))) for i, (name, ai) in enumerate(sorted(self.inputs.items()))}
216+
self.plots = {ai['ID']: self.axis.plot(name=name, pen=pg.mkPen(pg.intColor(i,len(self.inputs))))
217+
for i, (name, ai) in enumerate(sorted(self.inputs.items()))}
219218
self.axis.getAxis('bottom').setLabel('Time (seconds)')
220219
max_len = max([len(n) for n in list(sm_info['states'])+list(sm_info['events'])])
221220
self.axis.getAxis('right').setWidth(5*max_len)

pyControl/framework.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ def run():
153153
recieve_data()
154154
# Priority 6: Stream analog data.
155155
elif hw.stream_data_queue.available:
156-
hw.IO_dict[hw.stream_data_queue.get()]._process_streaming()
156+
hw.IO_dict[hw.stream_data_queue.get()].send_buffer()
157157
# Priority 7: Output framework data.
158158
elif data_output_queue.available:
159159
output_data(data_output_queue.get())

pyControl/hardware.py

Lines changed: 21 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ def off():
9191

9292
def get_analog_inputs():
9393
# Print dict of analog inputs {name: {'ID': ID, 'Fs':sampling rate}}
94-
print({io.name:{'ID': io.ID, 'Fs': io.sampling_rate}
94+
print({io.name:{'ID': io.ID, 'Fs': io.sampling_rate, 'plot': io.plot}
9595
for io in IO_dict.values() if isinstance(io, Analog_channel)})
9696

9797
# IO_object -------------------------------------------------------------------
@@ -233,8 +233,7 @@ def __init__(self, pin, name, sampling_rate, threshold=None, rising_event=None,
233233
def _run_start(self):
234234
self.timer.init(freq=self.Analog_channel.sampling_rate)
235235
self.timer.callback(self._timer_ISR)
236-
if self.threshold:
237-
self.threshold.run_start(self.read_sample())
236+
self.threshold.run_start(self.read_sample())
238237

239238
def _run_stop(self):
240239
self.timer.deinit()
@@ -265,14 +264,15 @@ class Analog_channel(IO_object):
265264
# k checksum (2 bytes)
266265
# D data array bytes (variable)
267266

268-
def __init__(self, name, sampling_rate, data_type='l'):
267+
def __init__(self, name, sampling_rate, data_type='l', plot=True):
269268
assert data_type in ('b','B','h','H','l','L'), 'Invalid data_type.'
270269
assert not any([name == io.name for io in IO_dict.values()
271270
if isinstance(io, Analog_channel)]), 'Analog signals must have unique names.'
272271
self.name = name
273272
assign_ID(self)
274273
self.sampling_rate = sampling_rate
275274
self.data_type = data_type
275+
self.plot = plot
276276
self.bytes_per_sample = {'b':1,'B':1,'h':2,'H':2,'l':4,'L':4}[data_type]
277277
self.buffer_size = max(4, min(256 // self.bytes_per_sample, sampling_rate//10))
278278
self.buffers = (array(data_type, [0]*self.buffer_size), array(data_type, [0]*self.buffer_size))
@@ -285,38 +285,41 @@ def __init__(self, name, sampling_rate, data_type='l'):
285285

286286
def _run_start(self):
287287
self.write_index = 0 # Buffer index to write new data to.
288-
self.buffer_start_times[self.write_buffer] = fw.current_time
289288

290289
def _run_stop(self):
291290
if self.write_index != 0:
292-
self._send_buffer(self.write_buffer, self.write_index)
291+
self.send_buffer(run_stop=True)
293292

294293
@micropython.native
295294
def put(self, sample: int):
296-
# load the buffer.
295+
# Put a sample in the buffer.
296+
if self.write_index == 0: # Record buffer start timestamp.
297+
self.buffer_start_times[self.write_buffer] = fw.current_time
297298
self.buffers[self.write_buffer][self.write_index] = sample
298299
self.write_index = (self.write_index + 1) % self.buffer_size
299300
if self.write_index == 0: # Buffer full, switch buffers.
300301
self.write_buffer = 1 - self.write_buffer
301-
self.buffer_start_times[self.write_buffer] = fw.current_time
302302
stream_data_queue.put(self.ID)
303303

304-
def _process_streaming(self):
305-
# Stream full buffer to computer.
306-
self._send_buffer(1-self.write_buffer)
307-
308-
def _send_buffer(self, buffer_n, n_samples=False):
309-
# Send specified buffer to host computer.
310-
n_bytes = self.bytes_per_sample*n_samples if n_samples else self.bytes_per_sample*self.buffer_size
304+
@micropython.native
305+
def send_buffer(self, run_stop=False):
306+
# Send buffer to host computer.
307+
if run_stop: # Send the contents of the current write buffer.
308+
buffer_n = self.write_buffer
309+
n_samples = self.write_index
310+
else: # Send the buffer not currently being written to.
311+
buffer_n = 1-self.write_buffer
312+
n_samples = self.buffer_size
313+
n_bytes = self.bytes_per_sample*n_samples
311314
self.data_header[7:9] = n_bytes.to_bytes(2,'little')
312315
self.data_header[9:13] = self.buffer_start_times[buffer_n].to_bytes(4,'little')
313-
checksum = sum(self.buffers_mv[buffer_n][:n_samples] if n_samples else self.buffers[buffer_n])
316+
checksum = sum(self.buffers_mv[buffer_n][:n_samples] if run_stop else self.buffers[buffer_n])
314317
checksum += sum(self.data_header[2:13])
315318
self.data_header[13:15] = checksum.to_bytes(2,'little')
316319
fw.usb_serial.write(self.data_header)
317-
if n_samples: # Send first n_samples from buffer.
320+
if run_stop:
318321
fw.usb_serial.send(self.buffers_mv[buffer_n][:n_samples])
319-
else: # Send entire buffer.
322+
else:
320323
fw.usb_serial.send(self.buffers[buffer_n])
321324

322325
class Analog_threshold(IO_object):

0 commit comments

Comments
 (0)