From: Frank Mori Hess Date: Fri, 8 Feb 2008 18:48:27 +0000 (+0000) Subject: Removed some wrong or obsolete information. Made it xinclude the X-Git-Url: http://git.tremily.us/?a=commitdiff_plain;h=e3526fbd7caeb6ade94d3be8e722c7992547db83;p=comedilib.git Removed some wrong or obsolete information. Made it xinclude the tutorial programs tut1.c and tut2.c --- 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. - - - -&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(): +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. - -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; -} - +The source code file for the above program can be found in Comedilib, at demo/tut2.c. -
-
- -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; - - - } -} - - - +
+ 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. +
-