diff --git a/doc/intro.xml b/doc/intro.xml
index 96a3559..5c52de6 100644
--- a/doc/intro.xml
+++ b/doc/intro.xml
@@ -12,9 +12,7 @@
drivers, tools, and libraries for various forms of
data acquisition: reading and writing of analog
signals; reading and writing of digital inputs/outputs; pulse and
- frequency counting; pulse generation; reading encoders; etc. The
- project's home page may be found at
- http://www.comedi.org.
+ frequency counting; pulse generation; reading encoders; etc.
The source code is distributed in two main packages, comedi and
comedilib:
diff --git a/doc/tutorial.xml b/doc/tutorial.xml
index 6eb4c20..ad1ee8d 100644
--- a/doc/tutorial.xml
+++ b/doc/tutorial.xml
@@ -5,13 +5,13 @@
%comedilib_entities;
]>
-
+
Writing &comedi; programs
-This Section describes how a well-installed and configured &comedi;
-package can be used in an application, to communicate data with a set
+This section describes how &comedi;
+can be used in an application, to communicate data with a set
of &comedi; devices.
gives more details about
the various acquisition functions with which the application
@@ -33,49 +33,26 @@ Your first &comedi; program
This example requires a card that has analog or digital input. This
progam opens the device, gets the data, and prints it out:
-#include ]]> /* for printf() */
-#include comedilib.h]]>
-
-int subdev = 0; /* change this to your input subdevice */
-int chan = 0; /* change this to your channel */
-int range = 0; /* more on this later */
-int aref = AREF_GROUND; /* more on this later */
-
-int main(int argc,char *argv[])
-{
- comedi_t *it;
- lsampl_t data;
-
- it=comedi_open("/dev/comedi0");
-
- comedi_data_read(it,subdev,chan,range,aref, &data);
-
- printf("%d\n",data);
-
- return 0;
-}
+
-The
-
- comedi_open()
- can only be successful if the
-comedi0 device file is configured to point to a
-valid &comedi; driver. explains
-how this driver is linked to the device file.
-The code above is basically the guts of
-demo/inp.c, without error checking or fancy
-options. Compile the program using
+The source code file for the above program can be found in Comedilib,
+at demo/tut1.c. You can compile the program using
cc tut1.c -lcomedi -o tut1
-(Replace cc by your favourite C compiler command.)
+The
+
+ comedi_open
+ call can only be successful if the
+comedi0 device file is configured with a
+valid &comedi; driver. explains
+how this driver is linked to the device file.
-
The range variable tells
&comedi; which gain to use when measuring an analog voltage. Since we
@@ -89,568 +66,78 @@ analog reference used.
-Converting samples to voltages
+Converting between integer data and physical units
If you selected an analog input subdevice, you probably noticed
-that the output of tut1 is a number between
-0 and 4095, or
-0 and 65535, depending on the
-number of bits in the A/D converter. &comedi; samples are
-always unsigned,
+that the output of tut1 is an unsigned number, for
+example between 0 and 65535
+for a 16 bit analog input. &comedi; samples are
+unsigned,
with 0 representing the lowest voltage of the ADC,
-and 4095
-the highest. &comedi; compensates for anything else the manual for
-your device says. However, you probably prefer to have this number
+and a hardware-dependent maximum value representing the highest voltage.
+&comedi; compensates for anything else the manual for
+your device says (for example, many boards represent bipolar
+analog input voltages as signed integers). However, you probably prefer to have this number
translated to a voltage. Naturally, as a good programmer, your first
question is: How do I do this in a device-independent
manner?
-Most devices give you a choice of gain and unipolar/bipolar
-input, and &comedi; allows you to select which of these to use. This
-parameter is called the range parameter, since it
-specifies the input range for analog input (or
-output range for analog output.) The range parameter
-represents both the gain and the unipolar/bipolar aspects.
+The functions
+comedi_to_physical and
+comedi_from_physical
+are used to convert between &comedi;'s integer data and floating point numbers corresponding
+to physical values (voltages, etc.). In order to use the conversion functions, you must
+first obtain a comedi_polynomial_t
+corresponding to the subdevice, range, and possibly channel the integer data is associated
+with. The comedi_polynomial_t object specifies the polynomial function that will be applied
+to convert between &comedi;'s integer data and physical values.
-&comedi; keeps the number of available ranges and the largest
-sample value for each subdevice/channel combination. (Some
-devices allow different input/output ranges for different
-channels in a subdevice.)
-
-
-
-The largest sample value can be found using the function
-
- lsampl_t comedi_get_maxdata(comedi_t * device, unsigned int subdevice, unsigned int channel))
-
-The number of available ranges can be found using the function:
-
- int comedi_get_n_ranges(comedi_t * device, unsigned int subdevice, unsigned int channel);
-
-
-
-
-For each value of the range parameter for a particular
-subdevice/channel, you can get range information using:
-
- comedi_range * comedi_get_range(comedi_t * device,
- unsigned int subdevice, unsigned int channel, unsigned int range);
-
-which returns a pointer to a
-comedi_range
-structure, which has the following contents:
-
-typedef struct{
- double min;
- double max;
- unsigned int unit;
-}comedi_range;
-
-The structure element min
-represents the voltage corresponding to
-comedi_data_read()
-returning 0,
-and max represents
-comedi_data_read()
-returning maxdata,
-(i.e., 4095 for 12 bit A/C
-converters, 65535 for 16 bit,
-or, 1 for digital input; more on this in a bit.)
-The unit entry tells you if
-min and
-max refer to voltage, current,
-or are dimensionless (e.g., for digital I/O).
-
-
-
-Could it get easier? you say. Well, yes. Use
-the function comedi_to_phys()
-comedi_to_phys(), which
-converts data values to physical units. Call it using something like
-
-
-
-volts=comedi_to_phys(it,data,range,maxdata);
-
-
-
-and the opposite
-
-
-
-data=comedi_from_phys(it,volts,range,maxdata);
-
-
-
-
-
-
-Using the file interface
-
-
-
-
-In addition to providing low level routines for data
-access, the &comedi; library provides higher-level access,
-much like the standard C library provides
-fopen(), etc. as a high-level (and portable)
-alternative to the direct UNIX system calls
-open(), etc. Similarily to
-fopen(), we have
-comedi_open():
-
-
-
-file=comedi_open("/dev/comedi0");
-
-
-
-where file is of type
-(comedi_t *).
-This function calls open(), as done explicitly in
-a previous section, but also fills the
-comedi_t
-structure with lots of goodies; this information will be useful soon.
-
-
-
-Specifically, you need to know
-maxdata for a specific
-subdevice/channel. How about:
-
-
-maxdata=comedi_get_maxdata(file,subdevice,channel);
-
-
-Wow! How easy. And the range information?
-
-
-comedi_range * comedi_get_range
-(comedi_tcomedi_t *it,unsigned int subdevice,unsigned int chan,unsigned int range);
-
-
+A comedi_polynomial_t may be obtained
+from one of two functions:
+comedi_get_hardcal_converter or
+comedi_get_softcal_converter.
+Which function to use depends on whether your board does calibration in hardware, or relies on
+the host computer for a software calibration. The
+SDF_SOFT_CALIBRATED flag (queried by calling
+comedi_get_subdevice_flags)
+will be set for boards that use software calibration.
-
-Your second &comedi; program: simple acquisition
+Your second &comedi; program
-Actually, this is the first &comedi; program again, just
-that we've added what we've learned.
+Actually, this is the first &comedi; program again, except
+we've added code to convert the integer data value to physical units.
+The program handles both the case of a software-calibrated
+board and a hardware-calibrated board.
-
-#include <stdio.h> /* for printf() */
-#include comedilib.h]]>
-
-int subdev = 0; /* change this to your input subdevice */
-int chan = 0; /* change this to your channel */
-int range = 0; /* more on this later */
-int aref = 0; /* more on this later */
-
-int main(int argc,char *argv[])
-{
- comedi_t *cf;
- int chan=0;
- lsampl_t data;
- int maxdata,rangetype;
- double volts;
-
- cf=comedi_open("/dev/comedi0");
-
- maxdata=comedi_get_maxdata(cf,subdev,chan);
-
- rangetype=comedi_get_rangetype(cf,subdev,chan);
-
- comedi_data_read(cf->fd,subdev,chan,range,aref,&data);
-
- volts=comedi_to_phys(data,rangetype,range,maxdata);
-
- printf("%d %g\n",data,volts);
-
- return 0;
-}
+
-
-
-
-
-
-Your third &comedi; program: instructions
-
-
-This program (taken from the set of demonstration examples that come
-with &comedi;) shows how to use a somewhat more flexible acquisition
-function, the so-called instruction.
-
-
-#include <]]>comedilib.h
-#include
-#include
-#include
-#include
-#include
-#include "examples.h"
-]]>
-
-/*
- * This example does 3 instructions in one system call. It does
- * a gettimeofday() call, then reads N_SAMPLES samples from an
- * analog input, and the another gettimeofday() call.
- *
- * (Note: The gettimeofday() value is obtained using an INSN_GTOD
- * instruction, which places the seconds value in data[0] and the
- * microseconds in data[1], so the seconds value is limited to
- * 32-bits even on 64-bit systems.)
- */
-
-#define MAX_SAMPLES 128
-
-comedi_t *device;
-
-int main(int argc, char *argv[])
-{
- int ret,i;
- comedi_insn insn[3];
- comedi_insnlist il;
- lsampl_t t1[2], t2[2];
- lsampl_t data[MAX_SAMPLES];
-
- parse_options(argc,argv);
-
-
- device=comedi_open(filename);
- if(!device){
- comedi_perror(filename);
- exit(0);
- }
-
- if(verbose){
- printf("measuring device=%s subdevice=%d channel=%d range=%d analog reference=%d\n",
- filename,subdevice,channel,range,aref);
- }
-
- /* Set up a the "instruction list", which is just a pointer
- * to the array of instructions and the number of instructions.
- */
- il.n_insns=3;
- il.insns=insn;
-
- /* Instruction 0: perform a gettimeofday() */
- insn[0].insn=INSN_GTOD;
- insn[0].n=2;
- insn[0].data=t1;
-
- /* Instruction 1: do 10 analog input reads */
- insn[1].insn=INSN_READ;
- insn[1].n=n_scan;
- insn[1].data=data;
- insn[1].subdev=subdevice;
- insn[1].chanspec=CR_PACK(channel,range,aref);
-
- /* Instruction 2: perform a gettimeofday() */
- insn[2].insn=INSN_GTOD;
- insn[2].n=2;
- insn[2].data=t2;
-
- ret=comedi_do_insnlist(device,&il);
- if(ret0){
- comedi_perror(filename);
- exit(0);
- }
-
- printf("initial time: %d.%06d\n",t1[0],t1[1]);
- for(i=0;in_scan;i++){
- printf("%d\n",data[i]);
- }
- printf("final time: %d.%06d\n",t2[0],t2[1]);
-
- printf("difference (us): %ld\n",(long)(t2[0]-t1[0])*1000000+(t2[1]-t1[1]));
-
- return 0;
-}
-
-
-
-
-
-
-
-Your fourth &comedi; program: commands
-
-
-
-This example programs an analog output subdevice with &comedi;'s most
-powerful acquisition function, the asynchronous
-command, to generate a waveform.
-
-
-
-The waveform in this example is a sine wave, but this can be easily
-changed to make a generic function generator.
-
-
-
-The function generation algorithm is the same as what is typically
-used in digital function generators. A 32-bit accumulator is
-incremented by a phase factor, which is the amount (in radians) that
-the generator advances each time step. The accumulator is then
-shifted right by 20 bits, to get a 12 bit offset into a lookup table.
-The value in the lookup table at that offset is then put into a buffer
-for output to the DAC.
-
-
-
-Once you have
-issued the command, &comedi; expects you to keep the buffer full of
-data to output to the acquisition card. This is done by
-write(). Since there may be a delay between the
-comedi_command()
-and a subsequent write(), you
-should fill the buffer using write() before you call
-comedi_command(),
-as is done here.
-
-
-#include <]]>comedilib.h
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include "examples.h"
-]]>
-
-double waveform_frequency = 10.0; /* frequency of the sine wave to output */
-double amplitude = 4000; /* peak-to-peak amplitude, in DAC units (i.e., 0-4095) */
-double offset = 2048; /* offset, in DAC units */
-
-/* This is the size of chunks we deal with when creating and
- outputting data. This *could* be 1, but that would be
- inefficient */
-#define BUF_LEN 4096
-
-int subdevice;
-int external_trigger_number = 0;
-
-sampl_t data[BUF_LEN];
-
-void dds_output(sampl_t *buf,int n);
-void dds_init(void);
-
-/* This define determines which waveform to use. */
-#define dds_init_function dds_init_sine
-
-void dds_init_sine(void);
-void dds_init_pseudocycloid(void);
-void dds_init_sawtooth(void);
-
-int comedi_internal_trigger(comedi_t *dev, unsigned int subd, unsigned int trignum)
-{
- comedi_insn insn;
- lsampl_t data[1];
-
- memset(, 0, sizeof(comedi_insn));
- insn.insn = INSN_INTTRIG;
- insn.subdev = subd;
- insn.data = data;
- insn.n = 1;
-
- data[0] = trignum;
-
- return comedi_do_insn(dev, );
-}
-
-
-int main(int argc, char *argv[])
-{
- comedi_cmd cmd;
- int err;
- int n,m;
- int total=0;
- comedi_t *dev;
- unsigned int chanlist[16];
- unsigned int maxdata;
- comedi_range *rng;
- int ret;
- lsampl_t insn_data = 0;
-
- parse_options(argc,argv);
-
- /* Force n_chan to be 1 */
- n_chan = 2;
-
- if(value){ waveform_frequency = value; }
-
- dev = comedi_open(filename);
- if(dev == NULL){
- fprintf(stderr, "error opening %s\n", filename);
- return -1;
- }
- subdevice = comedi_find_subdevice_by_type(dev,COMEDI_SUBD_AO,0);
-
- maxdata = comedi_get_maxdata(dev,subdevice,0);
- rng = comedi_get_range(dev,subdevice,0,0);
- offset = (double)comedi_from_phys(0.0,rng,maxdata);
- amplitude = (double)comedi_from_phys(1.0,rng,maxdata) - offset;
-
- memset(,0,sizeof(cmd));
- /* fill in the command data structure: */
- cmd.subdev = subdevice;
- cmd.flags = 0;
- cmd.start_src = TRIG_INT;
- cmd.start_arg = 0;
- cmd.scan_begin_src = TRIG_TIMER;
- cmd.scan_begin_arg = 1e9/freq;
- cmd.convert_src = TRIG_NOW;
- cmd.convert_arg = 0;
- cmd.scan_end_src = TRIG_COUNT;
- cmd.scan_end_arg = n_chan;
- cmd.stop_src = TRIG_NONE;
- cmd.stop_arg = 0;
-
- cmd.chanlist = chanlist;
- cmd.chanlist_len = n_chan;
-
- chanlist[0] = CR_PACK(channel,range,aref);
- chanlist[1] = CR_PACK(channel+1,range,aref);
-
- dds_init();
-
- dds_output(data,BUF_LEN);
- dds_output(data,BUF_LEN);
-
- dump_cmd(stdout,&cmd);
-
- if ((err = comedi_command(dev, &cmd)) < 0) {
- comedi_perror("comedi_command");
- exit(1);
- }
-
- m=write(comedi_fileno(dev),data,BUF_LEN*sizeof(sampl_t));
- if(m < 0){
- perror("write");
- exit(1);
- }
- printf("m=%d\n",m);
-
- ret = comedi_internal_trigger(dev, subdevice, 0);
- if(ret < 0){
- perror("comedi_internal_trigger\n");
- exit(1);
- }
-
- while(1){
- dds_output(data,BUF_LEN);
- n=BUF_LEN*sizeof(sampl_t);
- while(n>0){
- m=write(comedi_fileno(dev),(void *)data+(BUF_LEN*sizeof(sampl_t)-n),n);
- if(m < 0){
- perror("write");
- exit(0);
- }
- printf("m=%d\n",m);
- n-=m;
- }
- total+=BUF_LEN;
- }
-
- return 0;
-}
-
-#define WAVEFORM_SHIFT 16
-#define WAVEFORM_LEN (1 << WAVEFORM_SHIFT)
-#define WAVEFORM_MASK (WAVEFORM_LEN - 1)
-
-sampl_t waveform[WAVEFORM_LEN];
-
-unsigned int acc;
-unsigned int adder;
-
-void dds_init(void)
-{
-
-
- dds_init_function();
-}
-
-void dds_output(sampl_t *buf,int n)
-{
- int i;
- sampl_t *p=buf;
-
- >16)&WAVEFORM_MASK];
- ]]>
- p++;
- acc+=adder;
- }
-}
-
-
-void dds_init_sine(void)
-{
- int i;
-
-
- }
-}
-
-/* Yes, I know this is not the proper equation for a cycloid. Fix it. */
-void dds_init_pseudocycloid(void)
-{
- int i;
- double t;
-
-
-}
-
-void dds_init_sawtooth(void)
-{
- int i;
-
-
- }
-}
-
-
-
+
+The source code file for the above program can be found in Comedilib, at demo/tut2.c.
+
+
+ Further examples
+
+ See the demo subdirectory of Comedilib for more example programs. The directory contains
+ a README file with descriptions of the various demo programs.
+
+