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>.
62 The code above is basically the guts of
63 <filename>demo/inp.c</filename>, without error checking or fancy
64 options. Compile the program using
68 cc tut1.c -lcomedi -o tut1
71 (Replace <literal>cc</literal> by your favourite C compiler command.)
75 The <parameter class=function>range</parameter> variable tells
76 &comedi; which gain to use when measuring an analog voltage. Since we
77 don't know (yet) which numbers are valid, or what each means, we'll
78 use <literal>0</literal>, because it won't cause errors. Likewise
79 with <parameter class=function>aref</parameter>, which determines the
80 analog reference used.
85 <section id="convertingsamples">
87 Converting samples to voltages
91 If you selected an analog input subdevice, you probably noticed
92 that the output of <command>tut1</command> is a number between
93 <literal>0</literal> and <literal>4095</literal>, or
94 <literal>0</literal> and <literal>65535</literal>, depending on the
95 number of bits in the A/D converter. &comedi; samples are
96 <emphasis>always</emphasis> unsigned,
97 with <literal>0</literal> representing the lowest voltage of the ADC,
98 and <literal>4095</literal>
99 the highest. &comedi; compensates for anything else the manual for
100 your device says. However, you probably prefer to have this number
101 translated to a voltage. Naturally, as a good programmer, your first
102 question is: <quote>How do I do this in a device-independent
107 Most devices give you a choice of gain and unipolar/bipolar
108 input, and &comedi; allows you to select which of these to use. This
109 parameter is called the <quote>range parameter,</quote> since it
110 specifies the <quote>input range</quote> for analog input (or
111 <quote>output range</quote> for analog output.) The range parameter
112 represents both the gain and the unipolar/bipolar aspects.
116 &comedi; keeps the number of available ranges and the largest
117 sample value for each subdevice/channel combination. (Some
118 devices allow different input/output ranges for different
119 channels in a subdevice.)
123 The largest sample value can be found using the function
125 <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))
127 The number of available ranges can be found using the function:
129 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);
134 For each value of the range parameter for a particular
135 subdevice/channel, you can get range information using:
137 <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,
138 unsigned int subdevice, unsigned int channel, unsigned int range);
140 which returns a pointer to a
141 <link linkend="ref-type-comedi-range">comedi_range</link>
142 structure, which has the following contents:
150 The structure element <parameter class=function>min</parameter>
151 represents the voltage corresponding to
152 <link linkend="func-ref-comedi-data-read">comedi_data_read()</link>
153 returning <literal>0</literal>,
154 and <parameter class=function>max</parameter> represents
155 <link linkend="func-ref-comedi-data-read">comedi_data_read()</link>
156 returning <parameter class=function>maxdata</parameter>,
157 (i.e., <literal>4095</literal> for <literal>12</literal> bit A/C
158 converters, <literal>65535</literal> for <literal>16</literal> bit,
159 or, <literal>1</literal> for digital input; more on this in a bit.)
160 The <parameter class=function>unit</parameter> entry tells you if
161 <parameter class=function>min</parameter> and
162 <parameter class=function>max</parameter> refer to voltage, current,
163 or are dimensionless (e.g., for digital I/O).
167 <quote>Could it get easier?</quote> you say. Well, yes. Use
168 the function <function>comedi_to_phys()</function>
169 <link linkend="func-ref-comedi-to-phys">comedi_to_phys()</link>, which
170 converts data values to physical units. Call it using something like
174 volts=<link linkend="func-ref-comedi-to-phys">comedi_to_phys</link>(it,data,range,maxdata);
182 data=<link linkend="func-ref-comedi-from-phys">comedi_from_phy</link>s(it,volts,range,maxdata);
187 <section id="usingfileinterface">
189 Using the file interface
194 In addition to providing low level routines for data
195 access, the &comedi; library provides higher-level access,
196 much like the standard <acronym>C</acronym> library provides
197 <function>fopen()</function>, etc. as a high-level (and portable)
198 alternative to the direct <acronym>UNIX</acronym> system calls
199 <function>open()</function>, etc. Similarily to
200 <function>fopen()</function>, we have
201 <link linkend="func-ref-comedi-open">comedi_open()</link>:
205 file=<link linkend="func-ref-comedi-open">comedi_open</link>("/dev/comedi0");
209 where <parameter class=function>file</parameter> is of type
210 <parameter>(<link linkend="ref-type-comedi-t">comedi_t</link> *)</parameter>.
211 This function calls <function>open()</function>, as done explicitly in
212 a previous section, but also fills the
213 <link linkend="ref-type-comedi-t">comedi_t</link>
214 structure with lots of goodies; this information will be useful soon.
218 Specifically, you need to know
219 <parameter class=function>maxdata</parameter> for a specific
220 subdevice/channel. How about:
223 maxdata=<link linkend="func-ref-comedi-get-maxdata">comedi_get_maxdata</link>(file,subdevice,channel);
226 Wow! How easy. And the range information?
229 <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);
237 <section id="secondprogram">
239 Your second &comedi; program: simple acquisition
244 Actually, this is the first &comedi; program again, just
245 that we've added what we've learned.
250 #include <stdio.h> /* for printf() */
251 #include <![CDATA[<]]><link linkend="comedi-comedilib-h">comedilib.h</link><![CDATA[>]]>
253 int subdev = 0; /* change this to your input subdevice */
254 int chan = 0; /* change this to your channel */
255 int range = 0; /* more on this later */
256 int aref = 0; /* more on this later */
258 int main(int argc,char *argv[])
260 <link linkend="ref-type-comedi-t">comedi_t</link> *cf;
262 <link linkend="ref-type-lsampl-t">lsampl_t</link> data;
263 int maxdata,rangetype;
266 cf=<link linkend="func-ref-comedi-open">comedi_open</link>("/dev/comedi0");
268 maxdata=<link linkend="func-ref-comedi-get-maxdata">comedi_get_maxdata</link>(cf,subdev,chan);
270 rangetype=comedi_get_rangetype(cf,subdev,chan);
272 <link linkend="func-ref-comedi-data-read">comedi_data_read</link>(cf->fd,subdev,chan,range,aref,&data);
274 volts=<link linkend="func-ref-comedi-to-phys">comedi_to_phys</link>(data,rangetype,range,maxdata);
276 printf("%d %g\n",data,volts);
284 <section id="thirdprogram">
286 Your third &comedi; program: instructions
289 This program (taken from the set of demonstration examples that come
290 with &comedi;) shows how to use a somewhat more flexible acquisition
291 function, the so-called <link linkend="instructions">instruction</link>.
295 #include <]]><link linkend="comedi-comedilib-h">comedilib.h</link><![CDATA[>
299 #include <sys/time.h>
301 #include "examples.h"
305 * This example does 3 instructions in one system call. It does
306 * a gettimeofday() call, then reads N_SAMPLES samples from an
307 * analog input, and the another gettimeofday() call.
310 #define MAX_SAMPLES 128
312 <link linkend="ref-type-comedi-t">comedi_t</link> *device;
314 int main(int argc, char *argv[])
317 <link linkend="ref-type-comedi-insn">comedi_insn</link> insn[3];
318 <link linkend="ref-type-comedi-insnlist">comedi_insnlist</link> il;
319 struct timeval t1,t2;
320 <link linkend="ref-type-lsampl-t">lsampl_t</link> data[MAX_SAMPLES];
322 parse_options(argc,argv);
325 device=<link linkend="func-ref-comedi-open">comedi_open</link>(filename);
327 <link linkend="func-ref-comedi-perror">comedi_perror</link>(filename);
332 printf("measuring device=%s subdevice=%d channel=%d range=%d analog reference=%d\n",
333 filename,subdevice,channel,range,aref);
336 /* Set up a the "instruction list", which is just a pointer
337 * to the array of instructions and the number of instructions.
342 /* Instruction 0: perform a gettimeofday() */
343 insn[0].insn=<link linkend="insn-gtod">INSN_GTOD</link>;
345 insn[0].data=(void *)&t1;
347 /* Instruction 1: do 10 analog input reads */
348 insn[1].insn=<link linkend="insn-read">INSN_READ</link>;
351 insn[1].subdev=subdevice;
352 insn[1].chanspec=<link linkend="ref-macro-CR-PACK">CR_PACK</link>(channel,range,aref);
354 /* Instruction 2: perform a gettimeofday() */
355 insn[2].insn=<link linkend="insn-gtod">INSN_GTOD</link>;
357 insn[2].data=(void *)&t2;
359 ret=<link linkend="func-ref-comedi-do-insnlist">comedi_do_insnlist</link>(device,&il);
360 if(ret<![CDATA[<]]>0){
361 <link linkend="func-ref-comedi-perror">comedi_perror</link>(filename);
365 printf("initial time: %ld.%06ld\n",t1.tv_sec,t1.tv_usec);
366 for(i=0;i<![CDATA[<]]>n_scan;i++){
367 printf("%d\n",data[i]);
369 printf("final time: %ld.%06ld\n",t2.tv_sec,t2.tv_usec);
371 printf("difference (us): %ld\n",(t2.tv_sec-t1.tv_sec)*1000000+
372 (t2.tv_usec-t1.tv_usec));
381 <section id="fourthprogram">
383 Your fourth &comedi; program: commands
387 This example programs an analog output subdevice with &comedi;'s most
388 powerful acquisition function, the asynchronous
389 <link linkend="commandsstreaming">command</link>, to generate a waveform.
393 The waveform in this example is a sine wave, but this can be easily
394 changed to make a generic function generator.
398 The function generation algorithm is the same as what is typically
399 used in digital function generators. A 32-bit accumulator is
400 incremented by a phase factor, which is the amount (in radians) that
401 the generator advances each time step. The accumulator is then
402 shifted right by 20 bits, to get a 12 bit offset into a lookup table.
403 The value in the lookup table at that offset is then put into a buffer
404 for output to the DAC.
409 issued the command, &comedi; expects you to keep the buffer full of
410 data to output to the acquisition card. This is done by
411 <function>write()</function>. Since there may be a delay between the
412 <link linkend="func-ref-comedi-command">comedi_command()</link>
413 and a subsequent <function>write()</function>, you
414 should fill the buffer using <function>write()</function> before you call
415 <link linkend="func-ref-comedi-command">comedi_command()</link>,
420 #include <]]><link linkend="comedi-comedilib-h">comedilib.h</link><![CDATA[>
428 #include "examples.h"
431 double waveform_frequency = 10.0; /* frequency of the sine wave to output */
432 double amplitude = 4000; /* peak-to-peak amplitude, in DAC units (i.e., 0-4095) */
433 double offset = 2048; /* offset, in DAC units */
435 /* This is the size of chunks we deal with when creating and
436 outputting data. This *could* be 1, but that would be
441 int external_trigger_number = 0;
443 sampl_t data[BUF_LEN];
445 void <link linkend="dds-output">dds_output</link>(sampl_t *buf,int n);
446 void <link linkend="dds-init">dds_init</link>(void);
448 /* This define determines which waveform to use. */
449 #define <anchor id="dds-init-function">dds_init_function <link linkend="dds-init-sine">dds_init_sine</link>
451 void <link linkend="dds-init-sine">dds_init_sine</link>(void);
452 void <link linkend="dds-init-pseudocycloid">dds_init_pseudocycloid</link>(void);
453 void <link linkend="dds-init-sawtooth">dds_init_sawtooth</link>(void);
455 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)
457 <link linkend="ref-type-comedi-insn">comedi_insn</link> insn;
458 <link linkend="ref-type-lsampl-t">lsampl_t</link> data[1];
460 memset(<![CDATA[&insn]]>, 0, sizeof(<link linkend="ref-type-comedi-insn">comedi_insn</link>));
461 insn.insn = <link linkend="insn-inttrig">INSN_INTTRIG</link>;
468 return <link linkend="func-ref-comedi-do-insn">comedi_do_insn</link>(dev, <![CDATA[&insn]]>);
472 int main(int argc, char *argv[])
474 <link linkend="ref-type-comedi-cmd">comedi_cmd</link> cmd;
478 <link linkend="ref-type-comedi-t">comedi_t</link> *dev;
479 unsigned int chanlist[16];
480 unsigned int maxdata;
481 <link linkend="ref-type-comedi-range">comedi_range</link> *rng;
483 <link linkend="ref-type-lsampl-t">lsampl_t</link> insn_data = 0;
485 parse_options(argc,argv);
487 /* Force n_chan to be 1 */
490 if(value){ waveform_frequency = value; }
492 dev = <link linkend="func-ref-comedi-open">comedi_open</link>(filename);
494 fprintf(stderr, "error opening %s\n", filename);
497 subdevice = <link linkend="func-ref-comedi-find-subdevice-by-type">comedi_find_subdevice_by_type</link>(dev,COMEDI_SUBD_AO,0);
499 maxdata = <link linkend="func-ref-comedi-get-maxdata">comedi_get_maxdata</link>(dev,subdevice,0);
500 rng = <link linkend="func-ref-comedi-get-range">comedi_get_range</link>(dev,subdevice,0,0);
501 offset = (double)<link linkend="func-ref-comedi-from-phys">comedi_from_phys</link>(0.0,rng,maxdata);
502 amplitude = (double)<link linkend="func-ref-comedi-from-phys">comedi_from_phys</link>(1.0,rng,maxdata) - offset;
504 memset(<![CDATA[&cmd]]>,0,sizeof(cmd));
505 /* fill in the <link linkend="ref-type-comedi-cmd">command data structure</link>: */
506 cmd.subdev = subdevice;
508 cmd.start_src = <link linkend="trig-int-start-src">TRIG_INT</link>;
510 cmd.scan_begin_src = <link linkend="trig-timer">TRIG_TIMER</link>;
511 cmd.scan_begin_arg = 1e9/freq;
512 cmd.convert_src = <link linkend="trig-now">TRIG_NOW</link>;
514 cmd.scan_end_src = <link linkend="trig-count">TRIG_COUNT</link>;
515 cmd.scan_end_arg = n_chan;
516 cmd.stop_src = <link linkend="trig-none">TRIG_NONE</link>;
519 cmd.chanlist = chanlist;
520 cmd.chanlist_len = n_chan;
522 chanlist[0] = <link linkend="ref-macro-CR-PACK">CR_PACK</link>(channel,range,aref);
523 chanlist[1] = <link linkend="ref-macro-CR-PACK">CR_PACK</link>(channel+1,range,aref);
525 <link linkend="dds-init">dds_init</link>();
527 <link linkend="dds-output">dds_output</link>(data,BUF_LEN);
528 <link linkend="dds-output">dds_output</link>(data,BUF_LEN);
530 dump_cmd(stdout,<![CDATA[&cmd]]>);
532 if ((err = <link linkend="func-ref-comedi-command">comedi_command</link>(dev, <![CDATA[&cmd]]>)) < 0) {
533 <link linkend="func-ref-comedi-perror">comedi_perror</link>("comedi_command");
537 m=write(comedi_fileno(dev),data,BUF_LEN*sizeof(sampl_t));
544 ret = <link linkend="comedi-internal-trigger">comedi_internal_trigger</link>(dev, subdevice, 0);
548 perror("comedi_internal_trigger\n");
553 <link linkend="dds-output">dds_output</link>(data,BUF_LEN);
554 n=BUF_LEN*sizeof(sampl_t);
556 m=write(comedi_fileno(dev),(void *)data+(BUF_LEN*sizeof(sampl_t)-n),n);
572 #define WAVEFORM_SHIFT 16
574 #define WAVEFORM_LEN (1<<WAVEFORM_SHIFT)
576 #define WAVEFORM_MASK (WAVEFORM_LEN-1)
578 sampl_t waveform[WAVEFORM_LEN];
583 void <anchor id="dds-init">dds_init(void)
586 adder=waveform_frequency/freq*(1<<16)*(1<<WAVEFORM_SHIFT);
589 <link linkend="dds-init-function">dds_init_function</link>();
592 void <anchor id="dds-output">dds_output(sampl_t *buf,int n)
599 *p=waveform[(acc>>16)&WAVEFORM_MASK];
607 void <anchor id="dds-init-sine">dds_init_sine(void)
612 for(i=0;i<WAVEFORM_LEN;i++){
613 waveform[i]=rint(offset+0.5*amplitude*cos(i*2*M_PI/WAVEFORM_LEN));
618 /* Yes, I know this is not the proper equation for a cycloid. Fix it. */
619 void <anchor id="dds-init-pseudocycloid">dds_init_pseudocycloid(void)
625 for(i=0;i<WAVEFORM_LEN/2;i++){
626 t=2*((double)i)/WAVEFORM_LEN;
627 waveform[i]=rint(offset+amplitude*sqrt(1-4*t*t));
629 for(i=WAVEFORM_LEN/2;i<WAVEFORM_LEN;i++){
630 t=2*(1-((double)i)/WAVEFORM_LEN);
631 waveform[i]=rint(offset+amplitude*sqrt(1-t*t));
636 void <anchor id="dds-init-sawtooth">dds_init_sawtooth(void)
641 for(i=0;i<WAVEFORM_LEN;i++){
642 waveform[i]=rint(offset+amplitude*((double)i)/WAVEFORM_LEN);