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 <ds@schleef.org>
* 2008 Bernd Porr <berndporr@f2s.com>
*
* This file may be freely modified, distributed, and combined with
* other software, as long as proper attribution is given in the
* source code.
*/
#include <stdio.h>
#include <comedilib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/time.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
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 = &c;
int ret;
int total = 0;
int col;
int i;
int subdev_flags;
lsampl_t raw;
struct parsed_options options;
memset(&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 < 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 < 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 < 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->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 < 0 || ret != cmd->subdev) {
fprintf(stderr,
"failed to change 'read' subdevice from %d to %d\n",
ret, cmd->subdev);
exit(1);
}
/* start the command */
ret = comedi_command(dev, cmd);
if (ret < 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 < 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 & SDF_LSAMPL) {
bytes_per_sample = sizeof(lsampl_t);
} else {
bytes_per_sample = sizeof(sampl_t);
}
for (i = 0; i < ret / bytes_per_sample; i++) {
if (subdev_flags & 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 < 0) {
fprintf(stderr,
"comedi_get_cmd_generic_timed failed\n");
return ret;
}
/* Modify parts of the command */
cmd->chanlist = chanlist;
cmd->chanlist_len = n_chan;
if (cmd->stop_src == TRIG_COUNT) {
cmd->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>
|