1
<!-- <!DOCTYPE article PUBLIC "-//OASIS//DTD DocBook V3.1//EN" "docbook/dtd/3.1/docbook.dtd"> -->
3
<section id="writingprograms">
5
Writing &comedi; programs
8
This Section describes how a well-installed and configured &comedi;
9
package can be used in an application, to communicate data with a set
11
<xref linkend="acquisitionfunctions"> gives more details about
12
the various acquisition functions with which the application
13
programmer can perform data acquisition in &comedi;.
16
Also don't forget to take a good look at the
17
<filename class=directory>demo</filename>
18
directory of the Comedilib source code. It contains lots of examples
19
for the basic functionalities of &comedi;.
22
<section id="firstprogram">
24
Your first &comedi; program
28
This example requires a card that has analog or digital input. This
29
progam opens the device, gets the data, and prints it out:
31
#include <![CDATA[<stdio.h>]]> /* for printf() */
32
#include <![CDATA[<]]><link linkend="comedi-comedilib-h">comedilib.h</link><![CDATA[>]]>
34
int subdev = 0; /* change this to your input subdevice */
35
int chan = 0; /* change this to your channel */
36
int range = 0; /* more on this later */
37
int aref = <link linkend="aref-ground">AREF_GROUND</link>; /* more on this later */
39
int main(int argc,char *argv[])
41
<link linkend="ref-type-comedi-t">comedi_t</link> *it;
42
<link linkend="ref-type-lsampl-t">lsampl_t</link> data;
44
it=<link linkend="func-ref-comedi-open">comedi_open</link>("/dev/comedi0");
46
<link linkend="func-ref-comedi-data-read">comedi_data_read</link>(it,subdev,chan,range,aref, & data);
55
<link linkend="func-ref-comedi-open">comedi_open()</link>
56
</function> can only be successful if the
57
<filename>comedi0</filename> device file is configured to point to a
58
valid &comedi; driver. <xref linkend="cardconfiguration"> explains
59
how this driver is linked to the <quote>device file</quote>.
61
The code above is basically the guts of
62
<filename>demo/inp.c</filename>, without error checking or fancy
63
options. Compile the program using
67
cc tut1.c -lcomedi -o tut1
70
(Replace <literal>cc</literal> by your favourite C compiler command.)
74
The <parameter class=function>range</parameter> variable tells
75
&comedi; which gain to use when measuring an analog voltage. Since we
76
don't know (yet) which numbers are valid, or what each means, we'll
77
use <literal>0</literal>, because it won't cause errors. Likewise
78
with <parameter class=function>aref</parameter>, which determines the
79
analog reference used.
84
<section id="convertingsamples">
86
Converting samples to voltages
90
If you selected an analog input subdevice, you probably noticed
91
that the output of <command>tut1</command> is a number between
92
<literal>0</literal> and <literal>4095</literal>, or
93
<literal>0</literal> and <literal>65535</literal>, depending on the
94
number of bits in the A/D converter. &comedi; samples are
95
<emphasis>always</emphasis> unsigned,
96
with <literal>0</literal> representing the lowest voltage of the ADC,
97
and <literal>4095</literal>
98
the highest. &comedi; compensates for anything else the manual for
99
your device says. However, you probably prefer to have this number
100
translated to a voltage. Naturally, as a good programmer, your first
101
question is: <quote>How do I do this in a device-independent
106
Most devices give you a choice of gain and unipolar/bipolar
107
input, and &comedi; allows you to select which of these to use. This
108
parameter is called the <quote>range parameter,</quote> since it
109
specifies the <quote>input range</quote> for analog input (or
110
<quote>output range</quote> for analog output.) The range parameter
111
represents both the gain and the unipolar/bipolar aspects.
115
&comedi; keeps the number of available ranges and the largest
116
sample value for each subdevice/channel combination. (Some
117
devices allow different input/output ranges for different
118
channels in a subdevice.)
122
The largest sample value can be found using the function
124
<link linkend="ref-type-lsampl-t">lsampl_t</link> <link linkend="func-ref-comedi-get-maxdata">comedi_get_maxdata</link>(<link linkend="ref-type-comedi-t">comedi_t</link> * device, unsigned int subdevice, unsigned int channel))
126
The number of available ranges can be found using the function:
128
int <link linkend="func-ref-comedi-get-n-ranges">comedi_get_n_ranges</link>(<link linkend="ref-type-comedi-t">comedi_t</link> * device, unsigned int subdevice, unsigned int channel);
133
For each value of the range parameter for a particular
134
subdevice/channel, you can get range information using:
136
<link linkend="ref-type-comedi-range">comedi_range</link> * <link linkend="func-ref-comedi-get-range">comedi_get_range</link>(<link linkend="ref-type-comedi-t">comedi_t</link> * device,
137
unsigned int subdevice, unsigned int channel, unsigned int range);
139
which returns a pointer to a
140
<link linkend="ref-type-comedi-range">comedi_range</link>
141
structure, which has the following contents:
149
The structure element <parameter class=function>min</parameter>
150
represents the voltage corresponding to
151
<link linkend="func-ref-comedi-data-read">comedi_data_read()</link>
152
returning <literal>0</literal>,
153
and <parameter class=function>max</parameter> represents
154
<link linkend="func-ref-comedi-data-read">comedi_data_read()</link>
155
returning <parameter class=function>maxdata</parameter>,
156
(i.e., <literal>4095</literal> for <literal>12</literal> bit A/C
157
converters, <literal>65535</literal> for <literal>16</literal> bit,
158
or, <literal>1</literal> for digital input; more on this in a bit.)
159
The <parameter class=function>unit</parameter> entry tells you if
160
<parameter class=function>min</parameter> and
161
<parameter class=function>max</parameter> refer to voltage, current,
162
or are dimensionless (e.g., for digital I/O).
166
<quote>Could it get easier?</quote> you say. Well, yes. Use
167
the function <function>comedi_to_phys()</function>
168
<link linkend="func-ref-comedi-to-phys">comedi_to_phys()</link>, which
169
converts data values to physical units. Call it using something like
173
volts=<link linkend="func-ref-comedi-to-phys">comedi_to_phys</link>(it,data,range,maxdata);
181
data=<link linkend="func-ref-comedi-from-phys">comedi_from_phy</link>s(it,volts,range,maxdata);
186
<section id="usingfileinterface">
188
Using the file interface
193
In addition to providing low level routines for data
194
access, the &comedi; library provides higher-level access,
195
much like the standard <acronym>C</acronym> library provides
196
<function>fopen()</function>, etc. as a high-level (and portable)
197
alternative to the direct <acronym>UNIX</acronym> system calls
198
<function>open()</function>, etc. Similarily to
199
<function>fopen()</function>, we have
200
<link linkend="func-ref-comedi-open">comedi_open()</link>:
204
file=<link linkend="func-ref-comedi-open">comedi_open</link>("/dev/comedi0");
208
where <parameter class=function>file</parameter> is of type
209
<parameter>(<link linkend="ref-type-comedi-t">comedi_t</link> *)</parameter>.
210
This function calls <function>open()</function>, as done explicitly in
211
a previous section, but also fills the
212
<link linkend="ref-type-comedi-t">comedi_t</link>
213
structure with lots of goodies; this information will be useful soon.
217
Specifically, you need to know
218
<parameter class=function>maxdata</parameter> for a specific
219
subdevice/channel. How about:
222
maxdata=<link linkend="func-ref-comedi-get-maxdata">comedi_get_maxdata</link>(file,subdevice,channel);
225
Wow! How easy. And the range information?
228
<link linkend="ref-type-comedi-range">comedi_range</link> * <link linkend="func-ref-comedi-get-range">comedi_get_range(<link linkend="ref-type-comedi-t">comedi_t</link>comedi_t</link> *it,unsigned int subdevice,unsigned int chan,unsigned int range);
236
<section id="secondprogram">
238
Your second &comedi; program: simple acquisition
243
Actually, this is the first &comedi; program again, just
244
that we've added what we've learned.
249
#include <stdio.h> /* for printf() */
250
#include <![CDATA[<]]><link linkend="comedi-comedilib-h">comedilib.h</link><![CDATA[>]]>
252
int subdev = 0; /* change this to your input subdevice */
253
int chan = 0; /* change this to your channel */
254
int range = 0; /* more on this later */
255
int aref = 0; /* more on this later */
257
int main(int argc,char *argv[])
259
<link linkend="ref-type-comedi-t">comedi_t</link> *cf;
261
<link linkend="ref-type-lsampl-t">lsampl_t</link> data;
262
int maxdata,rangetype;
265
cf=<link linkend="func-ref-comedi-open">comedi_open</link>("/dev/comedi0");
267
maxdata=<link linkend="func-ref-comedi-get-maxdata">comedi_get_maxdata</link>(cf,subdev,chan);
269
rangetype=comedi_get_rangetype(cf,subdev,chan);
271
<link linkend="func-ref-comedi-data-read">comedi_data_read</link>(cf->fd,subdev,chan,range,aref,&data);
273
volts=<link linkend="func-ref-comedi-to-phys">comedi_to_phys</link>(data,rangetype,range,maxdata);
275
printf("%d %g\n",data,volts);
283
<section id="thirdprogram">
285
Your third &comedi; program: instructions
288
This program (taken from the set of demonstration examples that come
289
with &comedi;) shows how to use a somewhat more flexible acquisition
290
function, the so-called <link linkend="instructions">instruction</link>.
294
#include <]]><link linkend="comedi-comedilib-h">comedilib.h</link><![CDATA[>
298
#include <sys/time.h>
300
#include "examples.h"
304
* This example does 3 instructions in one system call. It does
305
* a gettimeofday() call, then reads N_SAMPLES samples from an
306
* analog input, and the another gettimeofday() call.
309
#define MAX_SAMPLES 128
311
<link linkend="ref-type-comedi-t">comedi_t</link> *device;
313
int main(int argc, char *argv[])
316
<link linkend="ref-type-comedi-insn">comedi_insn</link> insn[3];
317
<link linkend="ref-type-comedi-insnlist">comedi_insnlist</link> il;
318
struct timeval t1,t2;
319
<link linkend="ref-type-lsampl-t">lsampl_t</link> data[MAX_SAMPLES];
321
parse_options(argc,argv);
324
device=<link linkend="func-ref-comedi-open">comedi_open</link>(filename);
326
<link linkend="func-ref-comedi-perror">comedi_perror</link>(filename);
331
printf("measuring device=%s subdevice=%d channel=%d range=%d analog reference=%d\n",
332
filename,subdevice,channel,range,aref);
335
/* Set up a the "instruction list", which is just a pointer
336
* to the array of instructions and the number of instructions.
341
/* Instruction 0: perform a gettimeofday() */
342
insn[0].insn=<link linkend="insn-gtod">INSN_GTOD</link>;
344
insn[0].data=(void *)&t1;
346
/* Instruction 1: do 10 analog input reads */
347
insn[1].insn=<link linkend="insn-read">INSN_READ</link>;
350
insn[1].subdev=subdevice;
351
insn[1].chanspec=<link linkend="ref-macro-CR-PACK">CR_PACK</link>(channel,range,aref);
353
/* Instruction 2: perform a gettimeofday() */
354
insn[2].insn=<link linkend="insn-gtod">INSN_GTOD</link>;
356
insn[2].data=(void *)&t2;
358
ret=<link linkend="func-ref-comedi-do-insnlist">comedi_do_insnlist</link>(device,&il);
359
if(ret<![CDATA[<]]>0){
360
<link linkend="func-ref-comedi-perror">comedi_perror</link>(filename);
364
printf("initial time: %ld.%06ld\n",t1.tv_sec,t1.tv_usec);
365
for(i=0;i<![CDATA[<]]>n_scan;i++){
366
printf("%d\n",data[i]);
368
printf("final time: %ld.%06ld\n",t2.tv_sec,t2.tv_usec);
370
printf("difference (us): %ld\n",(t2.tv_sec-t1.tv_sec)*1000000+
371
(t2.tv_usec-t1.tv_usec));
380
<section id="fourthprogram">
382
Your fourth &comedi; program: commands
386
This example programs an analog output subdevice with &comedi;'s most
387
powerful acquisition function, the asynchronous
388
<link linkend="commandsstreaming">command</link>, to generate a waveform.
392
The waveform in this example is a sine wave, but this can be easily
393
changed to make a generic function generator.
397
The function generation algorithm is the same as what is typically
398
used in digital function generators. A 32-bit accumulator is
399
incremented by a phase factor, which is the amount (in radians) that
400
the generator advances each time step. The accumulator is then
401
shifted right by 20 bits, to get a 12 bit offset into a lookup table.
402
The value in the lookup table at that offset is then put into a buffer
403
for output to the DAC.
408
issued the command, &comedi; expects you to keep the buffer full of
409
data to output to the acquisition card. This is done by
410
<function>write()</function>. Since there may be a delay between the
411
<link linkend="func-ref-comedi-command">comedi_command()</link>
412
and a subsequent <function>write()</function>, you
413
should fill the buffer using <function>write()</function> before you call
414
<link linkend="func-ref-comedi-command">comedi_command()</link>,
419
#include <]]><link linkend="comedi-comedilib-h">comedilib.h</link><![CDATA[>
427
#include "examples.h"
430
double waveform_frequency = 10.0; /* frequency of the sine wave to output */
431
double amplitude = 4000; /* peak-to-peak amplitude, in DAC units (i.e., 0-4095) */
432
double offset = 2048; /* offset, in DAC units */
434
/* This is the size of chunks we deal with when creating and
435
outputting data. This *could* be 1, but that would be
440
int external_trigger_number = 0;
442
sampl_t data[BUF_LEN];
444
void <link linkend="dds-output">dds_output</link>(sampl_t *buf,int n);
445
void <link linkend="dds-init">dds_init</link>(void);
447
/* This define determines which waveform to use. */
448
#define <anchor id="dds-init-function">dds_init_function <link linkend="dds-init-sine">dds_init_sine</link>
450
void <link linkend="dds-init-sine">dds_init_sine</link>(void);
451
void <link linkend="dds-init-pseudocycloid">dds_init_pseudocycloid</link>(void);
452
void <link linkend="dds-init-sawtooth">dds_init_sawtooth</link>(void);
454
int <anchor id="comedi-internal-trigger">comedi_internal_trigger(<link linkend="ref-type-comedi-t">comedi_t</link> *dev, unsigned int subd, unsigned int trignum)
456
<link linkend="ref-type-comedi-insn">comedi_insn</link> insn;
457
<link linkend="ref-type-lsampl-t">lsampl_t</link> data[1];
459
memset(<![CDATA[&insn]]>, 0, sizeof(<link linkend="ref-type-comedi-insn">comedi_insn</link>));
460
insn.insn = <link linkend="insn-inttrig">INSN_INTTRIG</link>;
467
return <link linkend="func-ref-comedi-do-insn">comedi_do_insn</link>(dev, <![CDATA[&insn]]>);
471
int main(int argc, char *argv[])
473
<link linkend="ref-type-comedi-cmd">comedi_cmd</link> cmd;
477
<link linkend="ref-type-comedi-t">comedi_t</link> *dev;
478
unsigned int chanlist[16];
479
unsigned int maxdata;
480
<link linkend="ref-type-comedi-range">comedi_range</link> *rng;
482
<link linkend="ref-type-lsampl-t">lsampl_t</link> insn_data = 0;
484
parse_options(argc,argv);
486
/* Force n_chan to be 1 */
489
if(value){ waveform_frequency = value; }
491
dev = <link linkend="func-ref-comedi-open">comedi_open</link>(filename);
493
fprintf(stderr, "error opening %s\n", filename);
496
subdevice = <link linkend="func-ref-comedi-find-subdevice-by-type">comedi_find_subdevice_by_type</link>(dev,COMEDI_SUBD_AO,0);
498
maxdata = <link linkend="func-ref-comedi-get-maxdata">comedi_get_maxdata</link>(dev,subdevice,0);
499
rng = <link linkend="func-ref-comedi-get-range">comedi_get_range</link>(dev,subdevice,0,0);
500
offset = (double)<link linkend="func-ref-comedi-from-phys">comedi_from_phys</link>(0.0,rng,maxdata);
501
amplitude = (double)<link linkend="func-ref-comedi-from-phys">comedi_from_phys</link>(1.0,rng,maxdata) - offset;
503
memset(<![CDATA[&cmd]]>,0,sizeof(cmd));
504
/* fill in the <link linkend="ref-type-comedi-cmd">command data structure</link>: */
505
cmd.subdev = subdevice;
507
cmd.start_src = <link linkend="trig-int-start-src">TRIG_INT</link>;
509
cmd.scan_begin_src = <link linkend="trig-timer">TRIG_TIMER</link>;
510
cmd.scan_begin_arg = 1e9/freq;
511
cmd.convert_src = <link linkend="trig-now">TRIG_NOW</link>;
513
cmd.scan_end_src = <link linkend="trig-count">TRIG_COUNT</link>;
514
cmd.scan_end_arg = n_chan;
515
cmd.stop_src = <link linkend="trig-none">TRIG_NONE</link>;
518
cmd.chanlist = chanlist;
519
cmd.chanlist_len = n_chan;
521
chanlist[0] = <link linkend="ref-macro-CR-PACK">CR_PACK</link>(channel,range,aref);
522
chanlist[1] = <link linkend="ref-macro-CR-PACK">CR_PACK</link>(channel+1,range,aref);
524
<link linkend="dds-init">dds_init</link>();
526
<link linkend="dds-output">dds_output</link>(data,BUF_LEN);
527
<link linkend="dds-output">dds_output</link>(data,BUF_LEN);
529
dump_cmd(stdout,<![CDATA[&cmd]]>);
531
if ((err = <link linkend="func-ref-comedi-command">comedi_command</link>(dev, <![CDATA[&cmd]]>)) < 0) {
532
<link linkend="func-ref-comedi-perror">comedi_perror</link>("comedi_command");
536
m=write(comedi_fileno(dev),data,BUF_LEN*sizeof(sampl_t));
543
ret = <link linkend="comedi-internal-trigger">comedi_internal_trigger</link>(dev, subdevice, 0);
547
perror("comedi_internal_trigger\n");
552
<link linkend="dds-output">dds_output</link>(data,BUF_LEN);
553
n=BUF_LEN*sizeof(sampl_t);
555
m=write(comedi_fileno(dev),(void *)data+(BUF_LEN*sizeof(sampl_t)-n),n);
571
#define WAVEFORM_SHIFT 16
573
#define WAVEFORM_LEN (1<<WAVEFORM_SHIFT)
575
#define WAVEFORM_MASK (WAVEFORM_LEN-1)
577
sampl_t waveform[WAVEFORM_LEN];
582
void <anchor id="dds-init">dds_init(void)
585
adder=waveform_frequency/freq*(1<<16)*(1<<WAVEFORM_SHIFT);
588
<link linkend="dds-init-function">dds_init_function</link>();
591
void <anchor id="dds-output">dds_output(sampl_t *buf,int n)
598
*p=waveform[(acc>>16)&WAVEFORM_MASK];
606
void <anchor id="dds-init-sine">dds_init_sine(void)
611
for(i=0;i<WAVEFORM_LEN;i++){
612
waveform[i]=rint(offset+0.5*amplitude*cos(i*2*M_PI/WAVEFORM_LEN));
617
/* Yes, I know this is not the proper equation for a cycloid. Fix it. */
618
void <anchor id="dds-init-pseudocycloid">dds_init_pseudocycloid(void)
624
for(i=0;i<WAVEFORM_LEN/2;i++){
625
t=2*((double)i)/WAVEFORM_LEN;
626
waveform[i]=rint(offset+amplitude*sqrt(1-4*t*t));
628
for(i=WAVEFORM_LEN/2;i<WAVEFORM_LEN;i++){
629
t=2*(1-((double)i)/WAVEFORM_LEN);
630
waveform[i]=rint(offset+amplitude*sqrt(1-t*t));
635
void <anchor id="dds-init-sawtooth">dds_init_sawtooth(void)
640
for(i=0;i<WAVEFORM_LEN;i++){
641
waveform[i]=rint(offset+amplitude*((double)i)/WAVEFORM_LEN);