File: asyncprogram.html

package info (click to toggle)
comedilib 0.11.0%2B5-1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, bullseye
  • size: 8,540 kB
  • sloc: xml: 19,779; ansic: 14,719; sh: 5,672; cpp: 2,211; ruby: 1,658; perl: 700; makefile: 594; yacc: 439; lex: 86; python: 17
file content (356 lines) | stat: -rw-r--r-- 14,929 bytes parent folder | download | duplicates (3)
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
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
<html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><title>3.4.  Asynchronous acquisition</title><link rel="stylesheet" type="text/css" href="comedilib.css"><meta name="generator" content="DocBook XSL Stylesheets V1.79.1"><link rel="home" href="index.html" title="Comedi"><link rel="up" href="writingprograms.html" title="3.  Writing Comedi programs"><link rel="prev" href="secondprogram.html" title="3.3.  Your second Comedi program"><link rel="next" href="ar01s03s05.html" title="3.5. Further examples"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">3.4. 
      Asynchronous acquisition
    </th></tr><tr><td width="20%" align="left"><a accesskey="p" href="secondprogram.html">Prev</a> </td><th width="60%" align="center">3. 
    Writing <acronym class="acronym">Comedi</acronym> programs
  </th><td width="20%" align="right"> <a accesskey="n" href="ar01s03s05.html">Next</a></td></tr></table><hr></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="asyncprogram"></a>3.4. 
      Asynchronous acquisition
    </h3></div></div></div><p>
      Of special importance is the so called
      "asynchronous data acquisition" where <a class="ulink" href="http://www.comedi.org" target="_top"><acronym class="acronym">Comedi</acronym></a> is sampling
      in the background at a given sample rate. The
      user can retrieve the data whenever it is convenient.
      <a class="ulink" href="http://www.comedi.org" target="_top"><acronym class="acronym">Comedi</acronym></a> stores the data in a ring-buffer so that
      programs can perform other tasks in the foreground, for example
      plotting data or interacting with the user. 
      This technique is used in programs such
      as <span class="command"><strong>ktimetrace</strong></span> or <span class="command"><strong>comedirecord</strong></span>.
    </p><p>
      There are two different ways how a sequence of channels is
      measured during asynchronous acquisition (see also the Figure in
      the introduction):
    </p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">
	The channels are measured with the help
	of a multiplexer which switches to the next channel after each measurement.
	This means that the sampling rate is divided by the number
	of channels.
      </li><li class="listitem">
	The channels are all measured at the same time, for example
	when every channel has its own converter. In this case the
	sampling rate need not to be divided by the number of channels.
      </li></ul></div><p>
    How your <a class="ulink" href="http://www.comedi.org" target="_top"><acronym class="acronym">Comedi</acronym></a> device handles the asynchronous acquisition can be found out
    with the command <span class="command"><strong>comedi_board_info -v</strong></span>.
    </p><p>
      The program <code class="filename">demo/tut3.c</code> demonstrates the
      asynchronous acquisition. The general strategy is always
      the same: first, we tell <a class="ulink" href="http://www.comedi.org" target="_top"><acronym class="acronym">Comedi</acronym></a> all sampling parameters such as
      the sampling rate,
      the number of channels and anything it needs to know
      so that it can run independently in the background.
      Then <a class="ulink" href="http://www.comedi.org" target="_top"><acronym class="acronym">Comedi</acronym></a> checks our request and it might
      modify it. For example we might want to have a sampling rate of
      16kHz but we only get 1kHz. Finally we can start
      the asynchronous acquisition. Once it has been started we
      need to check periodically if data is available and
      request it from <a class="ulink" href="http://www.comedi.org" target="_top"><acronym class="acronym">Comedi</acronym></a> so that its internal buffer
      won't overrun.
    </p><p>
      In summary the asynchonous acquisition is performed in the following
      way:
    </p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">
        Create a command structure of type <span class="type"><a class="link" href="datatypesstructures.html#ref-type-comedi-cmd" title="5.3.7.  comedi_cmd">comedi_cmd</a></span></li><li class="listitem">
        Call the function <code class="function"><a class="link" href="func-ref-comedi-get-cmd-generic-timed.html" title="comedi_get_cmd_generic_timed">comedi_get_cmd_generic_timed</a></code>
	to fill the command structure with your comedi device,
	subdevice, sampling rate and number of channels.
      </li><li class="listitem">
	Create a channel-list and store it in the command structure. This
	tells comedi which channels should be sampled in the background.
      </li><li class="listitem">
        Call <code class="function"><a class="link" href="func-ref-comedi-command-test.html" title="comedi_command_test">comedi_command_test</a></code> with your command structure. Comedi might modify your requested sampling rate and channels.
      </li><li class="listitem">
        Call <code class="function"><a class="link" href="func-ref-comedi-command-test.html" title="comedi_command_test">comedi_command_test</a></code> again which now should return zero for success.
      </li><li class="listitem">
        Call <code class="function"><a class="link" href="func-ref-comedi-command.html" title="comedi_command">comedi_command</a></code> to start the asynchronous acquisition. From now on the kernel ringbuffer will be filled at the specified sampling rate.
      </li><li class="listitem">
	Call periodically the standard
	function <code class="function">read</code> and receive the data. The
	result should always be non zero as long as the acquisition
	is running.
      </li><li class="listitem">
        Convert the received data either into
        <span class="type"><a class="link" href="datatypesstructures.html#ref-type-lsampl-t" title="5.3.4.  lsampl_t">lsampl_t</a></span> or
        <span class="type"><a class="link" href="datatypesstructures.html#ref-type-sampl-t" title="5.3.3.  sampl_t">sampl_t</a></span> depending
        on the subdevice flag <code class="constant">SDF_LSAMPL</code>.
      </li><li class="listitem">
	Poll for data with <code class="function">read</code> as long as it returns
	a positive result or until the program terminates.
      </li></ul></div><p>
      The program below is a stripped down version of the
      program <code class="filename">cmd.c</code> in the demo directory.
      It requests data from two channels at 
      a sampling rate of 1kHz and a total of 10000 samples.
      which are then printed to stdout. You can pipe the data
      into a file and plot it with gnuplot. As mentioned above, central in this
      program is the loop using the standard C <code class="function">read</code> command
      which receives the buffer contents.
    </p><pre class="programlisting">
      /*
 * Example of using commands - asynchronous input
 * Part of Comedilib
 *
 * Copyright (c) 1999,2000,2001 David A. Schleef &lt;ds@schleef.org&gt;
 *               2008 Bernd Porr &lt;berndporr@f2s.com&gt;
 *
 * This file may be freely modified, distributed, and combined with
 * other software, as long as proper attribution is given in the
 * source code.
 */

#include &lt;stdio.h&gt;
#include &lt;comedilib.h&gt;
#include &lt;fcntl.h&gt;
#include &lt;unistd.h&gt;
#include &lt;sys/time.h&gt;
#include &lt;errno.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;string.h&gt;

extern comedi_t *device;

struct parsed_options {
	char *filename;
	double value;
	int subdevice;
	int channel;
	int aref;
	int range;
	int verbose;
	int n_chan;
	int n_scan;
	double freq;
};



#define BUFSZ 10000
char buf[BUFSZ];

#define N_CHANS 256
static unsigned int chanlist[N_CHANS];
static comedi_range * range_info[N_CHANS];
static lsampl_t maxdata[N_CHANS];


int prepare_cmd_lib(comedi_t *dev, int subdevice, int n_scan,
		    int n_chan, unsigned period_nanosec,
		    comedi_cmd *cmd);

void do_cmd(comedi_t *dev,comedi_cmd *cmd);

void print_datum(lsampl_t raw, int channel_index);

char *cmdtest_messages[] = {
	"success",
	"invalid source",
	"source conflict",
	"invalid argument",
	"argument conflict",
	"invalid chanlist",
};

int main(int argc, char *argv[])
{
	comedi_t *dev;
	comedi_cmd c,*cmd = &amp;c;
	int ret;
	int total = 0;
	int col;
	int i;
	int subdev_flags;
	lsampl_t raw;

	struct parsed_options options;

	memset(&amp;options, 0, sizeof(options));
	options.filename = "/dev/comedi0";
	options.subdevice = 0;
	options.channel = 0;
	options.range = 0;
	options.aref = AREF_GROUND;
	options.n_chan = 2;
	options.n_scan = 10000;
	options.freq = 1000.0;

	/* open the device */
	dev = comedi_open(options.filename);
	if (!dev) {
		comedi_perror(options.filename);
		exit(1);
	}

	/* Print numbers for clipped inputs */
	comedi_set_global_oor_behavior(COMEDI_OOR_NUMBER);

	/* Set up channel list */
	for (i = 0; i &lt; options.n_chan; i++) {
		chanlist[i] =
			CR_PACK(options.channel + i, options.range,
				options.aref);
		range_info[i] =
			comedi_get_range(dev, options.subdevice,
					 options.channel, options.range);
		maxdata[i] =
			comedi_get_maxdata(dev, options.subdevice,
					   options.channel);
	}

	/* prepare_cmd_lib() uses a Comedilib routine to find a
	 * good command for the device.  prepare_cmd() explicitly
	 * creates a command, which may not work for your device. */
	prepare_cmd_lib(dev, options.subdevice, options.n_scan,
			options.n_chan, 1e9 / options.freq, cmd);

	/* comedi_command_test() tests a command to see if the
	 * trigger sources and arguments are valid for the subdevice.
	 * If a trigger source is invalid, it will be logically ANDed
	 * with valid values (trigger sources are actually bitmasks),
	 * which may or may not result in a valid trigger source.
	 * If an argument is invalid, it will be adjusted to the
	 * nearest valid value.  In this way, for many commands, you
	 * can test it multiple times until it passes.  Typically,
	 * if you can't get a valid command in two tests, the original
	 * command wasn't specified very well. */
	ret = comedi_command_test(dev, cmd);
	if (ret &lt; 0) {
		comedi_perror("comedi_command_test");
		if(errno == EIO){
			fprintf(stderr,
				"Ummm... this subdevice doesn't support commands\n");
		}
		exit(1);
	}
	ret = comedi_command_test(dev, cmd);
	if (ret &lt; 0) {
		comedi_perror("comedi_command_test");
		exit(1);
	}
	fprintf(stderr,"second test returned %d (%s)\n", ret,
		cmdtest_messages[ret]);
	if (ret != 0) {
		fprintf(stderr, "Error preparing command\n");
		exit(1);
	}

	/* comedi_set_read_subdevice() attempts to change the current
	 * 'read' subdevice to the specified subdevice if it is
	 * different.  Changing the read or write subdevice might not be
	 * supported by the version of Comedi you are using.  */
	comedi_set_read_subdevice(dev, cmd-&gt;subdev);
	/* comedi_get_read_subdevice() gets the current 'read'
	 * subdevice. if any.  This is the subdevice whose buffer the
	 * read() call will read from.  Check that it is the one we want
	 * to use.  */
	ret = comedi_get_read_subdevice(dev);
	if (ret &lt; 0 || ret != cmd-&gt;subdev) {
		fprintf(stderr,
			"failed to change 'read' subdevice from %d to %d\n",
			ret, cmd-&gt;subdev);
		exit(1);
	}

	/* start the command */
	ret = comedi_command(dev, cmd);
	if (ret &lt; 0) {
		comedi_perror("comedi_command");
		exit(1);
	}
	subdev_flags = comedi_get_subdevice_flags(dev, options.subdevice);
	col = 0;
	while (1) {
		ret = read(comedi_fileno(dev),buf,BUFSZ);
		if (ret &lt; 0) {
			/* some error occurred */
			perror("read");
			break;
		} else if (ret == 0) {
			/* reached stop condition */
			break;
		} else {
			int bytes_per_sample;

			total += ret;
			if (options.verbose) {
				fprintf(stderr, "read %d %d\n", ret,
					total);
			}
			if (subdev_flags &amp; SDF_LSAMPL) {
				bytes_per_sample = sizeof(lsampl_t);
			} else {
				bytes_per_sample = sizeof(sampl_t);
			}
			for (i = 0; i &lt; ret / bytes_per_sample; i++) {
				if (subdev_flags &amp; SDF_LSAMPL) {
					raw = ((lsampl_t *)buf)[i];
				} else {
					raw = ((sampl_t *)buf)[i];
				}
				print_datum(raw, col);
				col++;
				if (col == options.n_chan) {
					printf("\n");
					col=0;
				}
			}
		}
	}

	return 0;
}

/*
 * This prepares a command in a pretty generic way.  We ask the
 * library to create a stock command that supports periodic
 * sampling of data, then modify the parts we want.
 */
int prepare_cmd_lib(comedi_t *dev, int subdevice, int n_scan,
		    int n_chan, unsigned scan_period_nanosec,
		    comedi_cmd *cmd)
{
	int ret;

	memset(cmd,0,sizeof(*cmd));

	/* This comedilib function will get us a generic timed
	 * command for a particular board.  If it returns -1,
	 * that's bad. */
	ret = comedi_get_cmd_generic_timed(dev, subdevice, cmd, n_chan,
					   scan_period_nanosec);
	if (ret &lt; 0) {
		fprintf(stderr,
			"comedi_get_cmd_generic_timed failed\n");
		return ret;
	}

	/* Modify parts of the command */
	cmd-&gt;chanlist = chanlist;
	cmd-&gt;chanlist_len = n_chan;
	if (cmd-&gt;stop_src == TRIG_COUNT) {
		cmd-&gt;stop_arg = n_scan;
	}

	return 0;
}

void print_datum(lsampl_t raw, int channel_index)
{
	double physical_value;

	physical_value = comedi_to_phys(raw, range_info[channel_index],
					maxdata[channel_index]);
	printf("%#8.6g ", physical_value);
}

    </pre><p>
      The source code file for the above program can be found in Comedilib,
      at <code class="filename">demo/tut3.c</code>.  You can compile the program using
    </p><pre class="screen">
      cc tut3.c -lcomedi -lm -o tut3
    </pre><p>
      For advanced programmers the
      function <code class="function"><a class="link" href="func-ref-comedi-get-buffer-contents.html" title="comedi_get_buffer_contents">comedi_get_buffer_contents</a></code>
      is useful to check if there is actually data in the ringbuffer
      so that a call of <code class="function">read</code> can be avoided for example
      when the data readout is called by a timer call-back function.
    </p></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="secondprogram.html">Prev</a> </td><td width="20%" align="center"><a accesskey="u" href="writingprograms.html">Up</a></td><td width="40%" align="right"> <a accesskey="n" href="ar01s03s05.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">3.3. 
      Your second <acronym class="acronym">Comedi</acronym> program
     </td><td width="20%" align="center"><a accesskey="h" href="index.html">Home</a></td><td width="40%" align="right" valign="top"> 3.5. Further examples</td></tr></table></div></body></html>