Skip to content

Commit c251601

Browse files
committedJun 5, 2015
pxi6733: refactor, allow multiple channels in one task, cancel any previous task
1 parent 398940f commit c251601

File tree

2 files changed

+73
-28
lines changed

2 files changed

+73
-28
lines changed
 

‎artiq/devices/pxi6733/driver.py

+68-20
Original file line numberDiff line numberDiff line change
@@ -14,22 +14,38 @@ def close(self):
1414
def ping(self):
1515
return True
1616

17+
def string_to_bytes(string, name):
Has conversations. Original line has conversations.
18+
if isinstance(string, str):
19+
string = bytes(string, encoding="ascii")
20+
elif not isinstance(string, bytes):
21+
raise ValueError("{} must be of either str or bytes type".format(name))
22+
return string
1723

1824
class DAQmx:
1925
"""NI PXI6733 DAQ interface."""
2026

21-
def __init__(self, device, analog_output, clock):
27+
def __init__(self, channels, clock):
28+
"""
29+
:param channels: List of channels as a string or bytes(), following
30+
the physical channels lists and ranges NI-DAQmx syntax.
31+
32+
Example: Dev1/ao0, Dev1/ao1:ao3
33+
:param clock: Clock source terminal as a string or bytes(), following
34+
NI-DAQmx terminal names syntax.
35+
36+
Example: PFI5
37+
"""
38+
2239
import PyDAQmx as daq
2340

24-
self.device = device
25-
self.analog_output = analog_output
26-
self.clock = clock
27-
self.tasks = []
41+
self.channels = string_to_bytes(channels, "channels")
Has conversations. Original line has conversations.
42+
self.clock = string_to_bytes(clock, "clock")
43+
self.task = None
2844
self.daq = daq
2945

3046
def done_callback_py(self, taskhandle, status, callback_data):
31-
self.daq.DAQmxClearTask(taskhandle)
32-
self.tasks.remove(taskhandle)
47+
if taskhandle == self.task:
Has conversations. Original line has conversations.
48+
self.clear_pending_task()
3349

3450
def ping(self):
3551
try:
@@ -42,43 +58,75 @@ def ping(self):
4258
def load_sample_values(self, sampling_freq, values):
4359
"""Load sample values into PXI 6733 device.
4460
45-
This loads sample values into the PXI 6733 device and then
46-
configures a task to output those samples at each clock rising
47-
edge.
61+
This loads sample values into the PXI 6733 device.
62+
The device will output samples at each clock rising edge.
63+
The first sample is output at the first clock rising edge.
Has conversations. Original line has conversations.
64+
65+
When using several channels simultaneously, you must concatenate the
66+
values for the different channels in the ``values`` array.
67+
The sample values for the same channel must be grouped together.
68+
69+
Example:
4870
49-
A callback is registered to clear the task (deallocate resources)
50-
when the task has completed.
71+
>>> values = np.array([ch0_samp0, ch0_samp1, ch1_samp0, ch1_samp1],
72+
dtype=float)
73+
74+
In this example the first two samples will be output via the first
75+
channel and the two following samples will be output via the second
76+
channel.
77+
78+
Any call to this method will cancel any previous task even if it has
79+
not yet completed.
5180
5281
:param sampling_freq: The sampling frequency in samples per second.
5382
:param values: A numpy array of sample values to load in the device.
5483
"""
5584

85+
self.clear_pending_task()
86+
5687
t = self.daq.Task()
57-
t.CreateAOVoltageChan(self.device+b"/"+self.analog_output, b"",
88+
t.CreateAOVoltageChan(self.channels, b"",
5889
min(values), max(values),
5990
self.daq.DAQmx_Val_Volts, None)
91+
92+
channel_number = self.daq.int32()
93+
t.GetTaskNumChans(byref(channel_number))
94+
nb_values = len(values)
95+
if nb_values % channel_number.value > 0:
Has conversations. Original line has conversations.
96+
self.daq.DAQmxClearTask(t.taskHandle)
97+
raise ValueError("The size of the values array must be a multiple "
98+
"of the number of channels ({})"
99+
.format(channel_number.value))
100+
samps_per_channel = nb_values // channel_number
101+
60102
t.CfgSampClkTiming(self.clock, sampling_freq,
61103
self.daq.DAQmx_Val_Rising,
62-
self.daq.DAQmx_Val_FiniteSamps, len(values))
104+
self.daq.DAQmx_Val_FiniteSamps, samps_per_channel)
63105
num_samps_written = self.daq.int32()
64106
values = np.require(values, dtype=float,
65107
requirements=["C_CONTIGUOUS", "WRITEABLE"])
66-
ret = t.WriteAnalogF64(len(values), False, 0,
108+
ret = t.WriteAnalogF64(samps_per_channel, False, 0,
67109
self.daq.DAQmx_Val_GroupByChannel, values,
68110
byref(num_samps_written), None)
69-
if num_samps_written.value != len(values):
111+
if num_samps_written.value != nb_values:
70112
raise IOError("Error: only {} sample values were written"
71113
.format(num_samps_written.value))
72114
if ret:
73115
raise IOError("Error while writing samples to the channel buffer")
74116

75117
done_cb = self.daq.DAQmxDoneEventCallbackPtr(self.done_callback_py)
76-
self.tasks.append(t.taskHandle)
118+
self.task = t.taskHandle
77119
self.daq.DAQmxRegisterDoneEvent(t.taskHandle, 0, done_cb, None)
78120
t.StartTask()
79121

122+
def clear_pending_task(self):
123+
"""Clear any pending task."""
124+
125+
if self.task is not None:
126+
self.daq.DAQmxClearTask(self.task)
127+
self.task = None
128+
80129
def close(self):
81-
"""Clear all pending tasks."""
130+
"""Free any allocated resources."""
82131

83-
for t in self.tasks:
84-
self.daq.DAQmxClearTask(t)
132+
self.clear_pending_task()

‎artiq/frontend/pxi6733_controller.py

+5-8
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,11 @@
1111
def get_argparser():
1212
parser = argparse.ArgumentParser(description="NI PXI 6733 controller")
1313
simple_network_args(parser, 3256)
14-
parser.add_argument("-d", "--device", default=None,
15-
help="Device name (e.g. Dev1)."
14+
parser.add_argument("-C", "--channels", default=None,
15+
help="List of channels (e.g. Dev1/ao0, Dev1/ao1:3)."
1616
" Omit for simulation mode.")
1717
parser.add_argument("-c", "--clock", default="PFI5",
1818
help="Input clock pin name (default: PFI5)")
19-
parser.add_argument("-a", "--analog-output", default="ao0",
20-
help="Analog output pin name (default: ao0)")
2119
verbosity_args(parser)
2220
return parser
2321

@@ -26,12 +24,11 @@ def main():
2624
args = get_argparser().parse_args()
2725
init_logger(args)
2826

29-
if args.device is None:
27+
if args.channels is None:
3028
daq = DAQmxSim()
3129
else:
32-
daq = DAQmx(bytes(args.device, "ascii"),
33-
bytes(args.analog_output, "ascii"),
34-
bytes(args.clock, "ascii"))
30+
daq = DAQmx(args.channels,
31+
args.clock)
3532

3633
try:
3734
simple_server_loop({"pxi6733": daq},

0 commit comments

Comments
 (0)
Please sign in to comment.