From: David Schleef Date: Wed, 2 Feb 2000 05:14:23 +0000 (+0000) Subject: Initial revision X-Git-Tag: r0_7_8~1 X-Git-Url: http://git.tremily.us/gitweb.cgi?a=commitdiff_plain;h=b4bba38d0ffc583975ff99a4d412a65ccb7623b1;p=comedilib.git Initial revision --- b4bba38d0ffc583975ff99a4d412a65ccb7623b1 diff --git a/INSTALL b/INSTALL new file mode 100644 index 0000000..ce3024c --- /dev/null +++ b/INSTALL @@ -0,0 +1,21 @@ + +Comedi has a mailing list. Send "subscribe comedi" to +majordomo@stm.lbl.gov to subscribe. + +If you are upgrading from a previous version, see the upgrade notes +below. + +Compile using 'make'. If this doesn't work, make sure you have the +basic tools installed to compile. If you can successfully compile +other things, consult the author, as he has probably made a mistake. + +Install using 'make install' as root. This installs the files: + /usr/lib/libcomedi.so.0.3 + /usr/include/comedi.h + /usr/include/comedilib.h + and other things... + +To write programs that use comedi, look at examples in the demo/ and +the documentation in the doc/ directory. + + diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..7b8ad6d --- /dev/null +++ b/Makefile @@ -0,0 +1,61 @@ + +# Makefile for comedi + +.EXPORT_ALL_VARIABLES: + +VERSION = 0 +SUBVERSION = 7 +SUBSUBVERSION = 8 + +VERSION_CODE = ${VERSION}.${SUBVERSION}.${SUBSUBVERSION} + +CFLAGS = -Wall -O2 + +all: comedilib + +SUBDIRS= lib demo comedi_calibrate testing + +DOCFILES= README INSTALL `find doc -type f` + +ifeq ($(CONFIG_USRLOCAL),y) +INSTALLDIR=/usr/local +else +INSTALLDIR=/usr +endif + +comedilib: subdirs + +config: dummy + +install: dummy + install -d ${INSTALLDIR}/include + (cd include;install -m 644 comedilib.h ${INSTALLDIR}/include) + install lib/libcomedi.so.${VERSION_CODE} ${INSTALLDIR}/lib + #ln -s ${INSTALLDIR}/lib/libcomedi.so.${VERSION_CODE} ${INSTALLDIR}/lib/libcomedi.so + install -m 644 lib/libcomedi.a ${INSTALLDIR}/lib + /sbin/ldconfig -n ${INSTALLDIR}/lib + install -d ${INSTALLDIR}/doc/comedilib-${VERSION_CODE} + install ${DOCFILES} ${INSTALLDIR}/doc/comedilib-${VERSION_CODE} + +lpr: dummy + find . -name '*.[chs]'|xargs enscript -2r -pit.ps + +subdirs: dummy + set -e;for i in ${SUBDIRS};do ${MAKE} -C $$i ; done + +clean: dummy + set -e;for i in $(SUBDIRS);do ${MAKE} clean -C $$i ; done + +distclean: clean + +dev: dummy + -rm /dev/comedi* + /bin/mknod /dev/comedi0 c 98 0 + /bin/mknod /dev/comedi1 c 98 1 + /bin/mknod /dev/comedi2 c 98 2 + /bin/mknod /dev/comedi3 c 98 3 + chown root.root /dev/comedi* + chmod 666 /dev/comedi* + +dummy: + diff --git a/README b/README new file mode 100644 index 0000000..17439ae --- /dev/null +++ b/README @@ -0,0 +1,56 @@ + + + + Linux Control and Measurement Device Interface (comedi) + + David Schleef + + + +A new minor version of comedi, 0.6.0, is now available at +. The change in minor version +level to 6 represents a complete redesign and rewrite of +the kernel interface. Because of this, 0.6.0 is almost +completely incompatible with previous versions. + +This redesign was necessary for current and future growth +of comedi. The current needs that have been addressed are +SMP and RT compatibility, the need to be more friendly to +high-level programming, and an overall simplification of +the interface. Using the 0.6 series should eventually be +easier and more understandable. + +The major changes from the 0.5 series are: + + - Comedi devices are now divided into "subdevices", + such as a A/D subsystem or D/A subsystem, instead + of channels. This eliminates much of the redundant + information and coding that was present in the 0.5 + series, and also allows proper locking of devices + necessary for proper SMP and RT performance. + + - The 0.6 series will allow multiple methods for + data transfer from hardware to your user-level + process, including read()/write(), ioctl(), and + mmap(), giving users needed flexibility. + + - Hardware drivers that are real-time compatibile will + now be able to perform RTLinux tasks timed by device + interrupts. + +Upgrading to 0.6 should not be too difficult, although it +will require editing and recompiling all programs that use +comedi, since the interface has changed. + +Comedi includes hardware drivers for many boards. See ./drivers +for details. + +If you want to add support for another hardware device, you +will want to look at the comedi hardware driver writing HOWTO +in the doc directory. It is fairly simple to write a new hardware +driver for comedi, but I am always willing to answer any questions +you might have. + +Comedi may be freely distibuted and modified in accordance with the +GNU General Public License. + diff --git a/comedi_calibrate/Makefile b/comedi_calibrate/Makefile new file mode 100644 index 0000000..bffc0af --- /dev/null +++ b/comedi_calibrate/Makefile @@ -0,0 +1,16 @@ + + + +CFLAGS +=-I ../include -I . -O2 -Wall +LDFLAGS=-L../lib/ -lcomedi + + +BINS=comedi_calibrate + +all: $(BINS) + +$(patsubst %,_bins_%,$(BINS)) : dummy + $(CC) -o $(patsubst _bins_%,%,$@) $(patsubst _bins_%,%.o,$@) $(LDFLAGS) + +clean: + -rm *.o $(BINS) diff --git a/comedi_calibrate/comedi_calibrate.c b/comedi_calibrate/comedi_calibrate.c new file mode 100644 index 0000000..81cf1f1 --- /dev/null +++ b/comedi_calibrate/comedi_calibrate.c @@ -0,0 +1,707 @@ +/* + A little auto-calibration utility, for boards + that support it. + + Right now, it only supports NI E series boards, + but it should be easily portable. + + A few things need improvement here: + - current system gets "close", but doesn't + do any fine-tuning + - no pre/post gain discrimination for the + A/D zero offset. + - should read (and use) the actual reference + voltage value from eeprom + - statistics would be nice, to show how good + the calibration is. + - doesn't check unipolar ranges + - "alternate calibrations" would be cool--to + accurately measure 0 in a unipolar range + - more portable + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#define N_CALDACS 16 + +typedef struct{ + int subdev; + int chan; + + int maxdata; + int current; + + int type; + double gain; +}caldac; + +static caldac caldacs[N_CALDACS]; +static int n_caldacs; +static comedi_t *dev; + +static int ad_subdev; +static int eeprom_subdev; + +double read_chan(int adc,int range); +void write_caldac(comedi_t *dev,int subdev,int addr,int val); +void check_gain(int ad_chan,int range); +double check_gain_chan(int ad_chan,int range,int cdac); + + +void update_caldac(int i); +void reset_caldacs(void); +void setup_caldacs(void); +void cal_ni_mio_E(void); +void ni_mio_ai_postgain_cal(void); +void channel_dependence(int adc,int range); +void caldac_dependence(int caldac); +void dump_curve(int adc,int caldac); +void chan_cal(int adc,int caldac,int range,double target); +int read_eeprom(int addr); + +typedef struct { + int n; + + double *y_data; + double *yerr_data; + double *x_data; + + double x0; + double dx; + double yerr; + + /* stats */ + double s1,sx,sy,sxy,sxx; + + /* results */ + double ave_x; + double ave_y; + double slope; + double err_slope; + double err_ave_y; + double S_min; + double dof; + +}linear_fit_t; +int linear_fit_monotonic(linear_fit_t *l); +double linear_fit_func_y(linear_fit_t *l,double x); +double check_gain_chan_x(linear_fit_t *l,int ad_chan,int range,int cdac); + +typedef struct{ + comedi_t *dev; + comedi_trig t; + + unsigned int chanlist[1]; + + int maxdata; + int order; + comedi_range *rng; + + int n; + double average; + double stddev; + double error; +}new_sv_t; + +int new_sv_measure(new_sv_t *sv); +int new_sv_init(new_sv_t *sv,comedi_t *dev,int subdev,int chan,int range,int aref); + +int main(int argc, char *argv[]) +{ + char *fn = NULL; + int c; + char *drivername; + + + fn = "/dev/comedi0"; + while (1) { + c = getopt(argc, argv, "rf"); + if (c == -1) + break; + switch (c) { + case 'f': + fn = argv[optind]; + break; + default: + printf("bad option\n"); + exit(1); + } + } + + dev = comedi_open(fn); + if (dev == NULL ) { + perror(fn); + exit(0); + } + + ad_subdev=0; + setup_caldacs(); + + eeprom_subdev=comedi_find_subdevice_by_type(dev,COMEDI_SUBD_MEMORY,0); + + drivername=comedi_get_driver_name(dev); + + if(!strcmp(drivername,"atmio-E") || !strcmp(drivername,"pcimio-E")) + cal_ni_mio_E(); + + return 0; +} + + +void cal_ni_mio_E(void) +{ + char *boardname; + double ref; + int i; + + boardname=comedi_get_board_name(dev); + + if(!strcmp(boardname,"at-mio-16e-1") || + !strcmp(boardname,"at-mio-16e-2") || + !strcmp(boardname,"at-mio-64e-3") || + !strcmp(boardname,"pci-mio-16e-1")){ +/* + layout + + 0 AI pre-gain offset 1.5e-6 + 1 AI post-gain offset 8.1e-4 + 2 AI unipolar offset 7.9e-4 + 3 AI gain + 4 AO + 5 AO + 6 AO + 7 A0 + 8 AO + 9 AO + 10 analog trigger + 11 unknown + */ + printf("last factory calibration %02d/%02d/%02d\n", + read_eeprom(508),read_eeprom(507),read_eeprom(506)); + + printf("lsb=%d msb=%d\n",read_eeprom(425),read_eeprom(426)); + + ref=5.000+(1e-6*(read_eeprom(425)+read_eeprom(426))); + printf("ref=%g\n",ref); + + reset_caldacs(); + + printf("postgain offset\n"); + ni_mio_ai_postgain_cal(); + + printf("pregain offset\n"); + chan_cal(0,0,7,0.0); + chan_cal(0,0,7,0.0); + + printf("unipolar offset\n"); + chan_cal(0,2,8,0.0); + chan_cal(0,2,8,0.0); + + printf("gain offset\n"); + chan_cal(5,3,0,5.0); + chan_cal(5,3,0,5.0); + + return; + } + if(!strcmp(boardname,"at-mio-16e-10")){ +/* + layout + + 0 AI pre-gain offset 1.5e-6 + 1 AI post-gain offset 8.1e-4 + 2 AI unipolar offset 7.9e-4 + 3 AI gain 3.5e-4 + 4 AO + 5 AO + 6 AO + 7 AO + 8 AO + 9 AO + 10 AI pre-gain offset 6.4e-5 + 11 unknown + */ + printf("last factory calibration %02d/%02d/%02d\n", + read_eeprom(508),read_eeprom(507),read_eeprom(506)); + + printf("lsb=%d msb=%d\n",read_eeprom(423),read_eeprom(424)); + + ref=5.000+(0.001*(read_eeprom(423)+read_eeprom(424))); + printf("ref=%g\n",ref); + + reset_caldacs(); + + printf("postgain offset\n"); + ni_mio_ai_postgain_cal(); + + printf("pregain offset\n"); + chan_cal(0,10,7,0.0); + chan_cal(0,0,7,0.0); + chan_cal(0,0,7,0.0); + + printf("unipolar offset\n"); + chan_cal(0,2,8,0.0); + chan_cal(0,2,8,0.0); + + printf("gain offset\n"); + chan_cal(5,3,0,5.0); + chan_cal(5,3,0,5.0); + + printf("results (offset)\n"); + for(i=0;i<16;i++){ + read_chan(0,i); + } + + return; + } + + { + int n_ranges; + + reset_caldacs(); + printf("please send this output to \n"); + printf("%s\n",comedi_get_board_name(dev)); + + n_ranges=comedi_get_n_ranges(dev,ad_subdev,0); + + printf("channel dependence 0 range 0\n"); + channel_dependence(0,0); + + printf("channel dependence 0 range %d\n",n_ranges/2-1); + channel_dependence(0,n_ranges/2-1); + + printf("channel dependence 0 range %d\n",n_ranges/2); + channel_dependence(0,n_ranges/2); + + printf("channel dependence 5 range 0\n"); + channel_dependence(5,0); + } +} + + +void ni_mio_ai_postgain_cal(void) +{ + linear_fit_t l; + double offset_r0; + double offset_r7; + double gain; + double a; + + check_gain_chan_x(&l,0,0,1); + offset_r0=linear_fit_func_y(&l,caldacs[1].current); + printf("offset r0 %g\n",offset_r0); + + check_gain_chan_x(&l,0,7,1); + offset_r7=linear_fit_func_y(&l,caldacs[1].current); + printf("offset r7 %g\n",offset_r7); + + gain=l.slope; + + a=(offset_r0-offset_r7)/(200.0-1.0); + a=caldacs[1].current-a/gain; + + printf("%g\n",a); + + caldacs[1].current=rint(a); + update_caldac(1); +} + +void chan_cal(int adc,int cdac,int range,double target) +{ + linear_fit_t l; + double offset; + double gain; + double a; + + check_gain_chan_x(&l,adc,range,cdac); + offset=linear_fit_func_y(&l,caldacs[cdac].current); + printf("offset %g\n",offset); + gain=l.slope; + + a=caldacs[cdac].current+(target-offset)/gain; + printf("%g\n",a); + + caldacs[cdac].current=rint(a); + update_caldac(cdac); +} + + + +void channel_dependence(int adc,int range) +{ + int i; + double gain; + + for(i=0;iy_data=malloc(n*sizeof(double)); + + orig=caldacs[cdac].current; + + new_sv_init(&sv,dev,0,ad_chan,range,AREF_OTHER); + + sum_err=0; + for(i=0;iy_data[i]=sv.average; + if(!isnan(sv.average)){ + sum_err+=sv.error; + sum_err_count++; + } + } + + caldacs[cdac].current=orig; + update_caldac(cdac); + + l->yerr=sum_err/sum_err_count; + l->n=n; + l->dx=1; + l->x0=0; + + linear_fit_monotonic(l); + + printf("caldac[%d] gain=%g V/bit err=%g S_min=%g dof=%g\n", + cdac,l->slope,l->err_slope,l->S_min,l->dof); + //printf("--> %g\n",fabs(l.slope/l.err_slope)); + + if(dump_flag){ + for(i=0;iy_data[i]); + } + } + + + free(l->y_data); + + return l->slope; +} + + + + +/* helpers */ + + +int read_eeprom(int addr) +{ + unsigned int data=0; + + comedi_data_read(dev,eeprom_subdev,addr,0,0,&data); + + return data; +} + +double read_chan(int adc,int range) +{ + int n; + new_sv_t sv; + + new_sv_init(&sv,dev,0,adc,range,AREF_OTHER); + sv.order=10; + n=new_sv_measure(&sv); + + printf("chan=%d ave=%g error=%g\n",adc,sv.average,sv.error); + + return sv.average; +} + +void write_caldac(comedi_t *dev,int subdev,int addr,int val) +{ + comedi_trig it; + unsigned int chan=CR_PACK(addr,0,0); + sampl_t data=val; + + memset(&it,0,sizeof(it)); + + it.subdev = subdev; + it.n_chan = 1; + it.chanlist = &chan; + it.data = &data; + it.n = 1; + + if(comedi_trigger(dev,&it)<0){ + perror("write_caldac"); + exit(0); + } +} + + + +int new_sv_init(new_sv_t *sv,comedi_t *dev,int subdev,int chan,int range,int aref) +{ + memset(sv,0,sizeof(*sv)); + + sv->t.subdev=subdev; + sv->t.flags=TRIG_DITHER; + sv->t.n_chan=1; + sv->t.chanlist=sv->chanlist; + + sv->chanlist[0]=CR_PACK(chan,range,aref); + + sv->maxdata=comedi_get_maxdata(dev,subdev,chan); + sv->rng=comedi_get_range(dev,subdev,chan,range); + + sv->order=7; + + return 0; +} + +int new_sv_measure(new_sv_t *sv) +{ + sampl_t *data; + int n,i,ret; + + double a,x,s,s2; + + n=1<order; + + data=malloc(sizeof(sampl_t)*n); + + for(i=0;it.data=data+i; + sv->t.n=n-i; + ret=comedi_trigger(dev,&sv->t); + if(ret<0)goto out; + i+=ret; + } + + s=0; + s2=0; + a=comedi_to_phys(data[0],sv->rng,sv->maxdata); + for(i=0;irng,sv->maxdata); + s+=x-a; + s2+=(x-a)*(x-a); + } + sv->average=a+s/n; + sv->stddev=sqrt(n*s2-s*s)/n; + sv->error=sv->stddev/sqrt(n); + + ret=n; + +out: + free(data); + + return ret; +} + +int new_sv_measure_order(new_sv_t *sv,int order) +{ + sampl_t *data; + int n,i,ret; + double a,x,s,s2; + + n=1<t.data=data+i; + sv->t.n=n-i; + ret=comedi_trigger(dev,&sv->t); + if(ret<0)goto out; + i+=ret; + } + + s=0; + s2=0; + a=comedi_to_phys(data[0],sv->rng,sv->maxdata); + for(i=0;irng,sv->maxdata); + s+=x-a; + s2+=(x-a)*(x-a); + } + sv->average=a+s/n; + sv->stddev=sqrt(n*s2-s*s)/n; + sv->error=sv->stddev/sqrt(n); + + ret=n; + +out: + free(data); + + return ret; +} + + + +/* linear fitting */ + +int calculate_residuals(linear_fit_t *l); + +int linear_fit_monotonic(linear_fit_t *l) +{ + double x,y; + double sxp; + int i; + + l->s1=0; + l->sx=0; + l->sy=0; + l->sxy=0; + l->sxx=0; + for(i=0;in;i++){ + x=l->x0+i*l->dx; + y=l->y_data[i]; + + if(isnan(y))continue; + + l->s1+=1; + l->sx+=x; + l->sy+=y; + l->sxy+=x*y; + l->sxx+=x*x; + } + sxp=l->sxx-l->sx*l->sx/l->s1; + + l->ave_x=l->sx/l->s1; + l->ave_y=l->sy/l->s1; + l->slope=(l->s1*l->sxy-l->sx*l->sy)/(l->s1*l->sxx-l->sx*l->sx); + l->err_slope=l->yerr/sqrt(sxp); + l->err_ave_y=l->yerr/sqrt(l->s1); + + calculate_residuals(l); + + return 0; +} + +int calculate_residuals(linear_fit_t *l) +{ + double x,y; + double res,sum_res2; + int i; + + sum_res2=0; + for(i=0;in;i++){ + x=l->x0+i*l->dx-l->ave_x; + y=l->y_data[i]; + + if(isnan(y))continue; + + res=l->ave_y+l->slope*x-y; + + sum_res2+=res*res; + } + l->S_min=sum_res2/(l->yerr*l->yerr); + l->dof=l->s1-2; + + return 0; +} + +double linear_fit_func_y(linear_fit_t *l,double x) +{ + return l->ave_y+l->slope*(x-l->ave_x); +} + diff --git a/demo/Makefile b/demo/Makefile new file mode 100644 index 0000000..313500e --- /dev/null +++ b/demo/Makefile @@ -0,0 +1,20 @@ + + + +CFLAGS +=-I ../include -I . -O2 +LDFLAGS=-L../lib/ -lcomedi + + +BINS=mode4 mode3 mode2 ao_waveform tut2 cmd tut1 +MBINS=inp inpn sv eeprom_dump info outp + +all: $(patsubst %,_mbins_%,$(MBINS)) $(patsubst %,_bins_%,$(BINS)) + +$(patsubst %,_mbins_%,$(MBINS)) : main.o $(patsubst %,%.o,$(MBINS)) + $(CC) -o $(patsubst _mbins_%,%,$@) main.o $(patsubst _mbins_%,%.o,$@) $(LDFLAGS) + +$(patsubst %,_bins_%,$(BINS)) : $(patsubst %,%.o,$(BINS)) + $(CC) -o $(patsubst _bins_%,%,$@) $(patsubst _bins_%,%.o,$@) $(LDFLAGS) + +clean: + -rm *.o $(BINS) $(MBINS) diff --git a/demo/README b/demo/README new file mode 100644 index 0000000..334a6fb --- /dev/null +++ b/demo/README @@ -0,0 +1,68 @@ + +Examples + + +ao_waveform: + + You need a device (and driver) capable of streaming analog output, + which currently is some of the members of the NI AT-MIO and PCI-MIO + E series. Creates a sine wave on an analog output channel. + +cmd: + Example of how to use the new 0.8 command structure. Not functional. + +eeprom_dump: + Dumps the EEPROM of a card, if it has one. Useful for debugging + devices/drivers. + +info: + Displays some information that Comedi knows about a device. + +inp: + Simple input demo. Reads one sample from an input line. + +inpn: + Slightly more complicated input demo. (It has a for() loop.) + Reads each channel on a subdevice, at every possible input + range, and converts the data to a voltage. + +mode1: + Doesn't work. + +mode2: + Demo for streaming analog input. Only works for boards/drivers + with that capability. + +mode3: + Doesn't work. + +mode4: + Same as mode2, except using an external trigger. + +sv: + Similar to inp, but measures the input using the comedi_sv_*() + functions, which average many samples to try to get a more accurate + estimate of the actual input. + +tut1: +tut2: + Tutorial examples. + + +Many of these demos are linked with the file main.c, which parses +command line options. Some options don't make sense with all programs. +The options are: + + +-a uses analog reference (default: 0) +-c uses channel +-s uses subdevice +-r uses voltage range +-f uses device file (default: /dev/comedi0) +-v verbose +-d set analog reference to differential +-g set analog reference to ground +-o set analog reference to other +-m set analog reference to common + + diff --git a/demo/ao_waveform.c b/demo/ao_waveform.c new file mode 100644 index 0000000..09c8485 --- /dev/null +++ b/demo/ao_waveform.c @@ -0,0 +1,231 @@ +/* + This demo uses an analog output subdevice in timed + mode (mode 2) to generate a waveform. The waveform + in this example is a sine wave (surprise!), but this + can be easily changed to make a general 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. + + [ Actually, the accumulator is only 26 bits, for some + reason. I'll fix this sometime. ] + + On the comedi side of things, the setup for mode 2 + is similar to analog input, except for the TRIG_WRITE + flag. Once you have issued the command, comedi then + expects you to keep the buffer full of data to output + to the DAC. This is done by write(). Since there + may be a delay between the ioctl() and a subsequent + write(), you should fill the buffer using write() before + you call ioctl(), as is done here. + + Also NOTE! The lseek() to offset 1 is used to tell + comedi that you want to write to subdevice 1. This + is not needed for analog input, since AI is usually on + subdevice 0. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define dds_init dds_init_pseudocycloid + + +/* frequency of the sine wave to output */ +double waveform_frequency = 100.0; + +/* update rate for the DAC, typically much higher than + the frequency of the sine wave. */ +double update_frequency = 200000.0; + +/* peak-to-peak amplitude, in DAC units (i.e., 0-4095) */ +double amplitude = 4000; + +/* offset, in DAC units */ +double offset = 2048; + +/* 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 + + +#define N_SCANS 0 +#define N_CHANS 1 + +int subdevice; +int channels[] = { 0 }; +int range = 0; +int aref = AREF_GROUND; +int external_trigger_number = 0; + +sampl_t data[BUF_LEN]; + +void dds_output(sampl_t *buf,int n); +void dds_init_sine(void); +void dds_init_pseudocycloid(void); + +int main(int argc, char *argv[]) +{ + char *fn = NULL; + comedi_trig it; + int err; + int n,m,i; + int total=0; + comedi_t *dev; + double actual_freq; + unsigned int chan[N_CHANS]; + + if(argc>=2){ + waveform_frequency=atof(argv[1]); + } + + fn = "/dev/comedi0"; + + dev = comedi_open(fn); + + subdevice = comedi_find_subdevice_by_type(dev,COMEDI_SUBD_AO,0); + + it.subdev = subdevice; + it.mode = 2; + it.flags = TRIG_WRITE; + it.n_chan = N_CHANS; + it.chanlist = chan; + it.data = NULL; + it.n = N_SCANS; + it.trigsrc = 0; + + /* convert the frequency into a timer value */ + comedi_get_timer(dev,subdevice,update_frequency,&it.trigvar,&actual_freq); + fprintf(stderr,"primary actual frequency=%g timer value=%d\n",actual_freq,it.trigvar); + + /* pack the channel list */ + for(i=0;i0){ + 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; + //printf("%d\n",total); + } + + return 0; +} + + + +#define WAVEFORM_SHIFT 16 +#define WAVEFORM_LEN (1<>16)&WAVEFORM_MASK]; + p++; + acc+=adder; + } +} + diff --git a/demo/cmd.c b/demo/cmd.c new file mode 100644 index 0000000..eb42535 --- /dev/null +++ b/demo/cmd.c @@ -0,0 +1,102 @@ +/* + A little input demo for commands + + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define N_SCANS 10 +#define N_CHANS 16 + +int subdevice = 0; +int chan=0; +int range = 0; +int aref = AREF_GROUND; +double freq = 1000; + +#define BUFSZ 100 +char buf[BUFSZ]; + + +static void do_cmd(comedi_t *dev); + +int main(int argc, char *argv[]) +{ + char *fn = NULL; + comedi_t *dev; + + fn = "/dev/comedi0"; + + dev = comedi_open(fn); + + do_cmd(dev); + + return 0; +} + +static void do_cmd(comedi_t *dev) +{ + comedi_cmd cmd; + unsigned int chanlist[4]; + int ret; + + /* the subdevice that the command is sent to */ + cmd.subdev = subdevice; + + /* flags */ + cmd.flags = 0; + + /* each event requires a trigger, which is specified + by a source and an argument. For example, to specify + an external digital line 3 as a source, you would use + src=TRIG_EXT and arg=3. */ + + cmd.start_src = TRIG_NOW; + cmd.start_arg = 0; + + cmd.scan_begin_src = TRIG_TIMER; + cmd.scan_begin_arg = 1000000; /* in ns */ + + cmd.convert_src = TRIG_TIMER; + cmd.convert_arg = 100000; /* in ns */ + + cmd.scan_end_src = TRIG_COUNT; + cmd.scan_end_arg = 4; /* number of channels */ + + cmd.stop_src = TRIG_COUNT; + cmd.stop_arg = 1000; + + /* the channel list determined which channels are sampled. + In general, chanlist_len is the same as scan_end_arg. Most + boards require this. */ + cmd.chanlist = chanlist; + cmd.chanlist_len = 4; + + chanlist[0]=CR_PACK(0,range,aref); + chanlist[1]=CR_PACK(1,range,aref); + chanlist[2]=CR_PACK(2,range,aref); + chanlist[3]=CR_PACK(3,range,aref); + + /* data and data_len are not used for user-space + programs */ + cmd.data = NULL; + cmd.data_len = 0; + + ret=ioctl(comedi_fileno(dev),COMEDI_CMD,&cmd); + + printf("ioctl returned %d\n",ret); + + do{ + ret=read(comedi_fileno(dev),buf,BUFSZ); + printf("read %d\n",ret); + }while(ret>=0); + printf("errno=%d\n",errno); +} + diff --git a/demo/eeprom_dump.c b/demo/eeprom_dump.c new file mode 100644 index 0000000..570be98 --- /dev/null +++ b/demo/eeprom_dump.c @@ -0,0 +1,123 @@ +/* + Dumps eeproms + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +extern int verbose_flag; +extern int subdevice; +extern int range; +extern int channel; +extern int aref; +extern char *filename; + +comedi_t *device; + +int read_eeprom(comedi_t *it,unsigned int **eeprom); +void dump_eeprom(unsigned int *eeprom,int len); + + +int main(int argc, char *argv[]) +{ + lsampl_t data; + int ret; + int len; + unsigned int *eeprom; + + parse_options(argc,argv); + + device=comedi_open(filename); + if(!device){ + comedi_perror(filename); + exit(0); + } + + len=read_eeprom(device,&eeprom); + dump_eeprom(eeprom,len); + + return 0; +} + + + + +int read_eeprom(comedi_t *it,unsigned int **eeprom) +{ + unsigned int subd; + unsigned int n,i,ret; + lsampl_t data; + unsigned int *ptr; + lsampl_t maxdata; + + subd=comedi_find_subdevice_by_type(it,COMEDI_SUBD_MEMORY,0); + if(subd<0){ + fprintf(stderr,"No memory subdevice\n"); + return 0; + } + + n=comedi_get_n_channels(it,subd); + maxdata=comedi_get_maxdata(it,subd,0); + + if(maxdata!=0xff){ + fprintf(stderr,"Memory subdevice has strange maxdata, aborting\n"); + } + + ptr=malloc(sizeof(unsigned int)*n); + + for(i=0;i +#include +#include +#include +#include +#include + +void help(void) +{ + fprintf(stderr,"info \n"); + exit(0); +} + +char *tobinary(char *s,int bits,int n); + +char *subdevice_types[]={ + "unused", + "analog input", + "analog output", + "digital input", + "digital output", + "digital I/O", + "counter", + "timer", + "memory", + "calibration", + "processor" +}; + +comedi_t *it; +extern char *filename; + + +int main(int argc,char *argv[]) +{ + int i; + int n_subdevices,type; + + parse_options(argc,argv); + + it=comedi_open(filename); + if(!it){ + fprintf(stderr,"cannot open %s\n",filename); + exit(0); + } + + printf("overall info:\n"); + printf(" version code: 0x%06x\n",comedi_get_version_code(it)); + printf(" driver name: %s\n",comedi_get_driver_name(it)); + printf(" board name: %s\n",comedi_get_board_name(it)); + printf(" number of subdevices: %d\n",n_subdevices=comedi_get_n_subdevices(it)); + + for(i=0;i>=1) + *t++=(bits&bit)?'1':'0'; + *t=0; + + return s; +} + diff --git a/demo/inp.c b/demo/inp.c new file mode 100644 index 0000000..a3e810d --- /dev/null +++ b/demo/inp.c @@ -0,0 +1,52 @@ +/* + A little input demo + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +extern int verbose_flag; +extern int subdevice; +extern int range; +extern int channel; +extern int aref; +extern char *filename; + +comedi_t *device; + + +int main(int argc, char *argv[]) +{ + lsampl_t data; + int ret; + + parse_options(argc,argv); + + device=comedi_open(filename); + if(!device){ + comedi_perror(filename); + exit(0); + } + + if(verbose_flag){ + printf("measuring device=%s subdevice=%d channel=%d range=%d analog reference=%d\n", + filename,subdevice,channel,range,aref); + } + + ret=comedi_data_read(device,subdevice,channel,range,aref,&data); + if(ret<0){ + comedi_perror(filename); + exit(0); + } + + printf("%d\n",data); + + return 0; +} + diff --git a/demo/inpn.c b/demo/inpn.c new file mode 100644 index 0000000..4268121 --- /dev/null +++ b/demo/inpn.c @@ -0,0 +1,71 @@ +/* + This demo opens /dev/comedi0 and looks for an analog input + subdevice. If it finds one, it measures one sample on each + channel for each input range. The value NaN indicates that + the measurement was out of range. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +extern int verbose_flag; +extern int subdevice; +extern int range; +extern int channel; +extern int aref; +extern char *filename; + +comedi_t *device; + + +int main(int argc, char *argv[]) +{ + int n_subdevs; + int n_chans,chan; + int n_ranges; + int range; + int rangetype; + int maxdata; + lsampl_t data; + double voltage; + + parse_options(argc,argv); + + device=comedi_open(filename); + if(!device){ + comedi_perror(filename); + exit(0); + } + + subdevice=comedi_find_subdevice_by_type(device,COMEDI_SUBD_AI,0); + if(subdevice<0){ + printf("no analog input subdevice found\n"); + exit(0); + } + + n_chans=comedi_get_n_channels(device,subdevice); + for(chan=0;chan +#include +#include +#include +#include +#include +#include +#include +#include + + +char *filename="/dev/comedi0"; +int verbose_flag; +comedi_t *device; + +int value; +int subdevice; +int channel; +int aref; +int range; + + +int parse_options(int argc, char *argv[]) +{ + int c; + + + while (optind +#include +#include +#include +#include +#include +#include +#include + +#define N_SAMPLES 100000 +#define N_CHANS 4 + +int subdevice = 0; +int channels[N_CHANS] = { 0, 1, 2, 3 }; +double freq = 100000; +int range = 0; +int aref = 3; +int external_trigger_number = 0; + +sampl_t data[N_SAMPLES]; + + +int main(int argc, char *argv[]) +{ + char *fn = NULL; + comedi_trig it; + int err; + int n,i; + comedi_t *dev; + unsigned int chan[N_CHANS]; + double actual_freq; + void *data_ptr; + int n_left; + + fn = "/dev/comedi0"; + + dev = comedi_open(fn); + + it.subdev = 0; + it.mode = 1; + it.flags = 0; + it.n_chan = N_CHANS; + it.chanlist = chan; + it.data = data; + it.n = N_SAMPLES/N_CHANS; + it.trigsrc = 0; + + /* convert the frequency into a timer value */ + comedi_get_timer(dev,subdevice,freq,&it.trigvar,&actual_freq); + printf("primary actual frequency=%g timer value=%d\n",actual_freq,it.trigvar); + + /* pack the channel list */ + for(i=0;i0){ + if((n=read(comedi_fileno(dev),data_ptr,n_left))<0){ + perror("read"); + exit(1); + } + printf("read %d\n",n); + n_left-=n; + data_ptr+=n; + } + printf("number of samples read=%d\ndata[0]=%d\ndata[N-1]=%d\n", + n/sizeof(sampl_t),data[0],data[N_SAMPLES-1]); + + for(i=0;i +#include +#include +#include +#include +#include +#include +#include + +#define N_SCANS 10 +#define N_CHANS 16 + +int subdevice = 0; +int chan=0; +int range = 0; +int aref = AREF_GROUND; +double freq = 1000; + +#define N_SAMPLES 1000 + +double data[N_SAMPLES]; + + +int main(int argc, char *argv[]) +{ + char *fn = NULL; + int i; + comedi_t *dev; + + fn = "/dev/comedi0"; + + dev = comedi_open(fn); + +#if 0 + for(i=0;i<10;i++){ + range=comedi_find_range(dev,subdevice,chan,0,-i,i); + printf("%d\n",range); + } +#endif + comedi_timed_1chan(dev,subdevice,chan,range,aref,freq,N_SAMPLES,data); + + for(i=0;i1, then + a different channel is captured at each external trigger. + + If you have multiple external trigger lines, the + particular trigger line is selected by trigval. + + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define N_SAMPLES 100 +#define N_CHANS 4 + +int subdevice = 0; +int channels[N_CHANS] = { 0, 1, 2, 3 }; +double freq = 100000; +int range = 0; +int aref = 3; +int external_trigger_number = 0; + +sampl_t data[N_SAMPLES]; + + +int main(int argc, char *argv[]) +{ + char *fn = NULL; + comedi_trig it; + int err; + int n,i; + comedi_t *dev; + unsigned int chan[N_CHANS]; + + fn = "/dev/comedi0"; + + dev = comedi_open(fn); + + it.subdev = 0; + it.mode = 3; + it.flags = 0; + it.n_chan = N_CHANS; + it.chanlist = chan; + it.data = data; + it.n = N_SAMPLES/N_CHANS; + it.trigsrc = 0; + + /* external trigger number */ + it.trigvar = external_trigger_number; + + /* pack the channel list */ + for(i=0;i +#include +#include +#include +#include +#include +#include +#include + +#define N_SCANS 4 +#define N_CHANS 4 + +int subdevice = 0; +int channels[N_CHANS] = { 0, 1, 2, 3 }; +double freq = 1000; +int range = 0; +int aref = 3; +int external_trigger_number = 0; + +#define N_SAMPLES (N_CHANS*N_SCANS) + +sampl_t data[N_SAMPLES]; + + +int main(int argc, char *argv[]) +{ + char *fn = NULL; + comedi_trig it; + int err; + int n,i; + comedi_t *dev; + double actual_freq; + unsigned int chan[N_CHANS]; + + fn = "/dev/comedi0"; + + dev = comedi_open(fn); + + it.subdev = 0; + it.mode = 4; + it.flags = 0; + it.n_chan = N_CHANS; + it.chanlist = chan; + it.data = data; + it.n = N_SCANS; + it.trigsrc = 0; + + /* external trigger number */ + it.trigvar = external_trigger_number; + + /* convert the frequency into a timer value */ + comedi_get_timer(dev,subdevice,freq,&it.trigvar1,&actual_freq); + printf("actual frequency=%g timer value=%d\n",actual_freq,it.trigvar1); + + /* pack the channel list */ + for(i=0;i +#include +#include +#include +#include +#include +#include +#include + +extern int verbose_flag; +extern int subdevice; +extern int range; +extern int channel; +extern int aref; +extern int value; +extern char *filename; + +comedi_t *device; + + +int main(int argc, char *argv[]) +{ + lsampl_t data; + int ret; + + parse_options(argc,argv); + + device=comedi_open(filename); + if(!device){ + comedi_perror(filename); + exit(0); + } + + data = value; + if(verbose_flag){ + printf("writing %d to device=%s subdevice=%d channel=%d range=%d analog reference=%d\n", + data,filename,subdevice,channel,range,aref); + } + + ret=comedi_data_write(device,subdevice,channel,range,aref,data); + if(ret<0){ + comedi_perror(filename); + exit(0); + } + + printf("%d\n",data); + + return 0; +} + diff --git a/demo/rt/Makefile b/demo/rt/Makefile new file mode 100644 index 0000000..2cf8771 --- /dev/null +++ b/demo/rt/Makefile @@ -0,0 +1,18 @@ + + +# change this to your rtlinux include directory. +# I don't have RTLinux installed on the computer +# that I use to compile, so I need this: + +RTINCDIR = -I/b/ds/stuff/rtlinux/r-9J/rtl/include + +CFLAGS = -I../../include $(RTINCDIR) -O2 -Wall -D__RT__ -D__KERNEL__ -DMODULE + +all: it.o ai.o + +it.o: it.c + $(CC) $(CFLAGS) -o it.o -c it.c + +ai.o: ai.c + $(CC) $(CFLAGS) -o ai.o -c ai.c + diff --git a/demo/rt/README b/demo/rt/README new file mode 100644 index 0000000..c6e39f3 --- /dev/null +++ b/demo/rt/README @@ -0,0 +1,16 @@ + +I'm using Linux-2.0.36/RTLinux 9J + + +it.o toggles a digital output bit, like the rectangle demo +in the RTLinux source + +ai.o does a timed acquisition on an analog input subdevice, +utilizing a callback function to copy the analog input value +to the analog output channel, in effect, creating a "follower". + +Both demos are configured for an NI AT-MIO E series board. +To use another driver/board, the source will need to be edited. + + + diff --git a/demo/rt/ai.c b/demo/rt/ai.c new file mode 100644 index 0000000..a2ec179 --- /dev/null +++ b/demo/rt/ai.c @@ -0,0 +1,97 @@ + + +#include +#include +#include +#include +#include +#include + + +RT_TASK mytask; + +/* this is the dev,subdev for analog input on my atmio-E board */ +unsigned int ai_dev=0; +unsigned int ai_subdev=0; +unsigned int ai_chan[2]={CR_PACK(6,0,0),CR_PACK(1,0,0)}; + +comedi_trig ai_trig; + +/* this is the dev,subdev for analog output on my atmio-E board */ +unsigned int ao_dev=0; +unsigned int ao_subdev=1; +unsigned int ao_chan=CR_PACK(0,0,0); + +comedi_trig ao_trig; + +sampl_t data2=0; + +sampl_t data[2]; + + +int callback(void *arg) +{ + data2^=0x800; + data[0]^=0x800; + data[0]&=0xfff; + + comedi_trig_ioctl(ao_dev,ao_subdev,&ao_trig); + + /* returning 1 is a little hack to reset user_ptr */ + return 1; +} + +int init_module(void) +{ + int ret; + + /* set up input structure */ + ai_trig.subdev=ai_subdev; + ai_trig.mode=2; + ai_trig.flags=0; + ai_trig.n_chan=2; + ai_trig.chanlist=ai_chan; + ai_trig.data=data; + ai_trig.n=2000; + ai_trig.trigsrc=0; + ai_trig.trigvar=99999; + ai_trig.trigvar1=1999; + + /* IMPORTANT next step: lock the subdevice */ + comedi_lock_ioctl(ai_dev,ai_subdev); + + /* register our callback function */ + ret=comedi_register_callback(ai_dev,ai_subdev,COMEDI_CB_EOS,callback,(void *)0); + printk("comedi_register_callback() returned %d\n",ret); + + /* set up output structure */ + ao_trig.subdev=ao_subdev; + ao_trig.mode=0; + ao_trig.flags=0; + ao_trig.n_chan=1; + ao_trig.chanlist=&ao_chan; + ao_trig.data=data; + ao_trig.n=1; + ao_trig.trigsrc=0; + ao_trig.trigvar=0; + ao_trig.trigvar1=0; + + /* IMPORTANT next step: lock the subdevice */ + comedi_lock_ioctl(ao_dev,ao_subdev); + + /* start acq. */ + ret=comedi_trig_ioctl(ai_dev,ai_subdev,&ai_trig); + printk("comedi_trig_ioctl() returned %d\n",ret); + + return 0; +} + +void cleanup_module(void) +{ + comedi_cancel_ioctl(ai_dev,ai_subdev); + + comedi_unlock_ioctl(ai_dev,ai_subdev); + comedi_unlock_ioctl(ao_dev,ao_subdev); +} + + diff --git a/demo/rt/it.c b/demo/rt/it.c new file mode 100644 index 0000000..642598a --- /dev/null +++ b/demo/rt/it.c @@ -0,0 +1,78 @@ + + +#include +#include +#include +#include +#include +#include + + +RT_TASK mytask; + +/* this is the dev,subdev for digital I/O on my atmio-E board */ +unsigned int dev=0; +unsigned int subdev=2; + +unsigned int channel; +sampl_t data; +comedi_trig trig; + +void do_comedi_toggle(int t) +{ + while(1){ + data^=1; + comedi_trig_ioctl(dev,subdev,&trig); + rt_task_wait(); + } +} + +int init_module(void) +{ + int ret; + RTIME now=rt_get_time(); + + /* set up trigger structure */ + trig.subdev=subdev; + trig.mode=0; + trig.flags=0; + trig.n_chan=1; + trig.chanlist=&channel; + trig.data=&data; + trig.n=1; + trig.trigsrc=0; + trig.trigvar=0; + trig.trigvar1=0; + + channel=CR_PACK(0,0,0); + + /* IMPORTANT next step: lock the subdevice */ + comedi_lock_ioctl(dev,subdev); + + /* configure DIO 0 for output */ + trig.flags=TRIG_CONFIG; + data=COMEDI_OUTPUT; + ret=comedi_trig_ioctl(dev,subdev,&trig); + printk("comedi_trig_ioctl() returned %d\n",ret); + trig.flags=0; + data=1; + + /* a little test */ + ret=comedi_trig_ioctl(dev,subdev,&trig); + printk("comedi_trig_ioctl() returned %d\n",ret); + + rt_task_init(&mytask,do_comedi_toggle, 0xffff, 3000, 4); + + rt_task_make_periodic(&mytask,now+3000,50000); + + return 0; +} + +void cleanup_module(void) +{ + rt_task_delete(&mytask); + + comedi_unlock_ioctl(dev,subdev); +} + + diff --git a/demo/sv.c b/demo/sv.c new file mode 100644 index 0000000..1f1a9b8 --- /dev/null +++ b/demo/sv.c @@ -0,0 +1,60 @@ +/* + Demo of the comedi_sv_*() functions + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +extern int verbose_flag; +extern int subdevice; +extern int range; +extern int channel; +extern int aref; +extern char *filename; + +comedi_t *device; + + +int main(int argc, char *argv[]) +{ + lsampl_t data; + int ret; + comedi_sv_t sv; + double volts; + + parse_options(argc,argv); + + device=comedi_open(filename); + if(!device){ + comedi_perror(filename); + exit(0); + } + + if(verbose_flag){ + printf("measuring device=%s subdevice=%d channel=%d range=%d analog reference=%d\n", + filename,subdevice,channel,range,aref); + } + + comedi_sv_init(&sv,device,subdevice,channel); + + sv.range=range; + sv.aref=aref; + sv.n=100; + + ret=comedi_sv_measure(&sv,&volts); + if(ret<0){ + comedi_perror("comedi_sv_measure()"); + exit(0); + } + + printf("%g\n",volts); + + return 0; +} + diff --git a/demo/tut1.c b/demo/tut1.c new file mode 100644 index 0000000..15dcd6a --- /dev/null +++ b/demo/tut1.c @@ -0,0 +1,24 @@ + +#include /* for printf() */ +#include + +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; + int chan=0; + lsampl_t data; + + it=comedi_open("/dev/comedi0"); + + comedi_data_read(it,subdev,chan,range,aref,&data); + + printf("%d\n",data); + + return 0; +} + diff --git a/demo/tut2.c b/demo/tut2.c new file mode 100644 index 0000000..abae04c --- /dev/null +++ b/demo/tut2.c @@ -0,0 +1,35 @@ + +#include /* for printf() */ +#include /* also included by comedilib.h */ +#include /* for comedi_get() */ + +int subdev = 0; /* change this to your input subdevice */ +int chan = 0; /* change this to your channel */ +int range = 3; /* more on this later */ +int aref = 0; /* more on this later */ + +int main(int argc,char *argv[]) +{ + comedi_t *cf; + int chan=0; + int data; + int maxdata,rangetype; + double volts; + comedi_range *cr; + + cf=comedi_open("/dev/comedi0"); + + maxdata=comedi_get_maxdata(cf,subdev,chan); + + rangetype=comedi_get_rangetype(cf,subdev,chan); + + cr=comedi_get_range(cf,subdev,chan,range); + + comedi_data_read(cf,subdev,chan,range,aref,&data); + + volts=comedi_to_phys(data,cr,maxdata); + + printf("%d %g\n",data,volts); + + return 0; +} diff --git a/doc/FAQ b/doc/FAQ new file mode 100644 index 0000000..611d686 --- /dev/null +++ b/doc/FAQ @@ -0,0 +1,36 @@ + +1. Should I use CONFIG_MODVERSIONS when compiling a Linux kernel? + +Short answer: No. Long answer: MODVERSIONS allows you to safely +use a module on multiple kernel versions. However, making comedi +work with CONFIG_MODVERSIONS is a real pain. Sometimes it works, +sometimes it doesn't. + + +2. I get the error message: + +Makefile:14: /usr/src/linux/.config: No such file or directory +make: *** No rule to make target `/usr/src/linux/.config'. Stop. + + +It means you don't have the file /usr/src/linux/.config. This file +contains all the configuration information about your linux kernel, +which comedi needs in order to compile correctly. Some distributions +don't contain this file, so you will need to create your own, by +compiling your own kernel. This file is automatically created +when you run 'make config', the first step in compiling a kernel. + + +3. When compiling programs that use libcomedi, I get error message: + +/usr/lib/libcomedi.a(range.o): In function `comedi_from_phys': +range.o(.text+0x169): undefined reference to `floor' + + +This error messsage indicates that the linker cannot resolve the +reference to floor(), which is a function in the math library +that is used in the function comedi_from_phys(). This only +happens when you link statically. Add '-lm' to your linking +command. + + diff --git a/doc/comedilib.sgml b/doc/comedilib.sgml new file mode 100644 index 0000000..788f970 --- /dev/null +++ b/doc/comedilib.sgml @@ -0,0 +1,928 @@ + + +
+Comedi Documentation +<author>David Schleef, <tt/ds@stm.lbl.gov/ + + +<sect>Introduction + +<p> +This is preliminary documentation for Comedi and Comedilib. + +<sect>Installation and configuration +<p> + +This section covers compiling, installing, and configuring +comedi and comedlib. + + +<sect1>Compiling and Installing +<p> + +This section has not been written. + +<p> +<sect1>Insmod'ding the kernel module +<p> + +This section has not been written. + +<p> +<sect1>Configuring comedi for your hardware +<p> + + +I assume that your hardware device is in your computer, and that +you know the relevant details about it, i.e., what kind of card +it is, the I/O base, the IRQ, jumper settings related to input +ranges, etc. + +To tell the comedi kernel module that you have a particular device, and +some information about it, you will be running the <tt>comedi_config</tt> +command. Perhaps you should read the man page now. + +In this tutorial, I will go through the process of configuring comedi +for two devices, a National Instruments AT-MIO-16E-10 +and a Data Translation DT2821-F-8DI. + +The NI board is plug-and-play, and the man page tells me that I need +to configure the PnP part of the board with isapnptools. The isapnptools +package is a little cryptic, but the concepts are simple. Once I +learned how to use it, I settled on a /etc/isapnp.conf file that +contained the lines: + + +<tscreen><verb> +# ANSI string -->National Instruments, AT-MIO-16E-10<-- +(CONFIGURE NIC2400/10725401 (LD 0 + (IO 0 (BASE 0x0260)) + (INT 0 (IRQ 3 (MODE +E))) +# (DMA 0 (CHANNEL 5)) +# (DMA 1 (CHANNEL 6)) + (ACT Y) +)) +</verb></tscreen> + + +It also contains a few lines about overall configuration and about my +sound card. I found out after a bit of trial-and-error that the NI +board does not always work with interrupts other than IRQ 3. YMMV. +Currently, the driver doesn't use DMA, but it may in the future, so +I commented out the DMA lines. It is a curious fact that the device +ignores the IRQ and DMA information given here, however, I keep the +information here to remind myself that the numbers aren't arbitrary. + +When I run comedi_config (as root, of course), I provide the same +information. Since I want to have the board configured every time +I boot, I put the line + +<tscreen><verb> +/usr/sbin/comedi_config /dev/comedi0 atmio-E 0x260,3 +</verb></tscreen> + +into <tt>/etc/rc.d/rc.local</tt>. You can, of course, run this command at +a command prompt. The man page tells me that the option list +is supposed to be "(I/O base),(IRQ)", so I used the same numbers +as I put in /etc/isapnp.conf, i.e., 0x260,3. + +For the Data Translation board, I need to have a list of the +jumper settings. Fortunately, I wrote them all down in the +manual -- I hope they are still correct. However, I had to +open the case to figure out which board in the series I had. +It is a DT2821-f-8di. The man page of comedi_config tells +me that I need to know the I/O base, IRQ, DMA 1, DMA 2. However, +since I wrote the driver, I know that it also recognizes the +differential/single-ended and unipolar/bipolar jumpers. As always, +the source is the final authority, and looking in module/dt282x.c +tells me that the options list is interpreted as: + +<itemize> +<item>I/O base +<item>IRQ +<item>1=differential, 0=single ended +<item>ai 0=unipolar, 1=bipolar +<item>ao0 0=unipolar, 1=bipolar +<item>ao1 0=unipolar, 1=bipolar +<item>dma1 +<item>dma2 +</itemize> + +(ai=analog input, ao=analog output.) From this, I decide that +the appropriate options list is + +<tscreen><verb> +0x200,4,,1,1,1 +</verb></tscreen> + +I left the differential/single-ended number blank, since the +driver already knowns (from the board name), that it is +differential. I also left the DMA numbers blank, since I +don't want the driver to use DMA. (Don't want it to interfere +with my sound card -- life is full of difficult choices.) +Keep in mind that things commented in the source, but not in +the documentation are about as likely to change as the weather, +so I put good comments next to the following line when I put +it in rc.local. + +<tscreen><verb> +/usr/sbin/comedi_config /dev/comedi1 dt2821-f-8di 0x200,4,,1,1,1 +</verb></tscreen> + +So now I think that I have my boards configured correctly. +Since data acquisition boards are not typically well-engineered, +comedi sometimes can't figure out if the board is actually there. +If it can't, it assumes you are right. Both of these boards +are well-made, so comedi will give me an error message if it +can't find them. The comedi kernel module, since it is a part +of the kernel, prints messages to the kernel logs, which you +can access through the command 'dmesg' or /var/log/messages. +Here is a configuration failure (from dmesg): + +<tscreen><verb> +comedi0: ni_E: 0x0200 can't find board +</verb></tscreen> + +When it does work, I get: + +<tscreen><verb> +comedi0: ni_E: 0x0260 at-mio-16e-10 ( irq = 3 ) +</verb></tscreen> + +Note that it also correctly identified my board. + + +<p> +<sect1>Getting information from comedi +<p> + + +So now that we have comedi talking to the hardware, we want to +talk to comedi. Here's some pretty low-level information -- +it's sometimes useful for debugging: + +<p> + +<tscreen><verb> +cat /proc/comedi +</verb></tscreen> + +Right now, on my computer, this command gives: + +<tscreen><verb> +comedi version 0.6.4 +format string + 0: atmio-E at-mio-16e-10 7 + 1: dt282x dt2821-f-8di 4 +</verb></tscreen> + +This is a feature that is not well-developed yet. Basically, it +currently tells you driver name, device name, and number of +subdevices. + +In the <tt>demo/</tt> directory, there is a command called +<tt>info</tt>, which provides information about each subdevice on the +board. The output of it is rather long, since I have 7 +subdevices (4 or fewer is common for other boards.) +Here's part of the output of the NI board (which +is on <tt>/dev/comedi0</tt>.) ('demo/info /dev/comedi0') + +<tscreen><verb> +overall info: + version code: 0x000604 + driver name: atmio-E + board name: at-mio-16e-10 + number of subdevices: 7 +subdevice 0: + type: 1 (unknown) + number of channels: 16 + max data value: 4095 +</verb> +... +</tscreen> + +The overall info gives information about the device -- basically +the same information as /proc/comedi. + +This board has 7 subdevices. Devices are separated into +subdevices that each have a distinct purpose -- e.g., analog +input, analog output, digital input/output. This board also +has an EEPROM and calibration DACs that are also subdevices. + +Subdevice 0 is the analog input subdevice. You would have +known this from the 'type: 1 (unknown)' line, if I've updated +demo/info recently, because it would say 'type: 1 (analog input)' +instead. The other lines should be self-explanitory. Comedi +has more information about the device, but demo/info doesn't +currently display this. + +<p> +<sect>Individual drivers +<p> + +This section contains information that is specific to each +hardware driver. The most current information about a driver +is included in the comedi source. + +<sect1>National Instruments AT-MIO E series +<p> + + +<sect1>Data Translation +<p> + + + +<sect>Writing programs that use comedi and comedilib +<p> + +<sect1>Your first comedi program +<p> + +This example requires a card that has analog or +digital input. Right to the source: + +<tscreen><verb> +#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 = AREF_GROUND; /* more on this later */ + +int main(int argc,char *argv[]) +{ + comedi_t *it; + int chan=0; + lsampl_t data; + + it=comedi_open("/dev/comedi0"); + + comedi_data_read(it,subdev,chan,range,aref,& data); + + printf("%d\n",data); + + return 0; +} +</verb></tscreen> + + +Should be understandable. Open the device, get the data, +print it out. This is basically the guts of <tt>demo/inp.c</tt>, +without error checking or fancy options. Including all +the appropriate headers is sometimes a little tricky. +Compile it using + +<tscreen><verb> +cc tut1.c -lcomedi -o tut1 +</verb></tscreen> + +Hopefully it works. + +A few notes: The range variable tells comedi which gain +to use when measuring an analog voltage. Since we don't +know (yet) which numbers are valid, or what each means, +we'll use 0, because it won't cause errors. Likewise with +aref, which determines the analog reference used. + + +<p> +<sect1>Converting samples to voltages +<p> + +If you selected an analog input subdevice, you should notice +that the output of <tt>tut1</tt> 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 <bf>always</bf> 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 translated to +a voltage. Naturally, as a good programmer, your first +question is: "How do I do this in a device-independent +manner?" + +For each subdevice, the comedi kernel module keeps a +'range_type' variable. This variable contains the number +of available ranges (i.e., gains) that you can select, +along with an offset in a list of range information +structures. If you know the range_type variable, you +can use these macros: + + RANGE_OFFSET(range_type) + RANGE_LENGTH(range_type) + +to extract such information. However, you want the +actual voltage information, not some integer offset +in a table. Rather than messing with the library +internals, use the function + + ptr=comedi_get_range(comedi_file,subdevice,channel, + range) + +which returns a pointer to a comedi_range structure. +The comedi_range structure looks like + +<p> +<tscreen><verb> +typedef struct{ + double min; + double max; + unsigned int unit; +}comedi_range; +</verb></tscreen> + +As you might expect, ptr[range] is for range 'range', +which you provided to comedi_data_read() above. '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, etc. + +"Could it get easier?", you say. Well, yes. Use +the function comedi_to_phys(), which converts data +values to physical units. Call it using something like + +<tscreen><verb> +volts=comedi_to_phys(it,data,range,maxdata); +</verb></tscreen> + +and the opposite + +<tscreen><verb> +data=comedi_from_phys(it,volts,range,maxdata); +</verb></tscreen> + +You probably noticed (and were worried) that we haven't +discussed how to determine maxdata and range_type. Well, +you could ask the kernel this information each time you need +it, but since there are other variables, special cases, +and several subdevices to worry about, it would be nice +if the library could take care of this... (read on...) + + +<p> +<sect1>Another section +<p> + + +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(): + +<p> +<tscreen><verb> +file=comedi_open("/dev/comedi0"); +</verb></tscreen> + +where file is of type <tt>(comedi_t *)</tt>. This function +calls <tt>open()</tt>, like we did explicitly in a previous +section, but also fills the <tt>comedi_t</tt> structure with +lots of goodies -- information that we will need to use +soon. + +Specifically, we needed to know maxdata for a specific +subdevice/channel. How about: + +<tscreen><verb> +maxdata=comedi_get_maxdata(file,subdevice,channel); +</verb></tscreen> + +Wow. How easy. And the range type? + +<tscreen><verb> +range_type=comedi_get_rangetype(file,subdevice,channel); +</verb></tscreen> + +Cool. Other information you need to know about a channel +can be gotten in a similar way. + + + +<sect1>Your second comedi program +<p> + + +Actually, this is the first comedi program again, just +that we've added what we've learned. + + +<tscreen><verb> +#include <stdio.h> /* for printf() */ +#include <comedi.h> /* also included by comedilib.h */ +#include <comedilib.h> /* for comedi_get() */ + +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; + int 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); + + data=comedi_get(cf->fd,subdev,chan,range,aref); + + volts=comedi_to_phys(data,rangetype,range,maxdata); + + printf("%d %g\n",data,volts); + + return 0; +} +</verb></tscreen> + + +By now, the <tt>comedi_read_data()</tt> line looks a little archaic, using +the UNIX file descriptor cf->fd instead of just cf. (By the +way, somewhere in the heart of <tt>comedi_open()</tt> is the line +<tt>cf->fd=open(filename,O_RDWR)</tt>.) Well, there isn't one good +replacement, since it highly depends on your application +what additional features you might want in a <tt>comedi_get()</tt> +replacement. But this is the topic of a different section. + + +<p> +<sect>Application-specific functions +<p> + +<sect1>Digital Input/Output +<p> + +Many boards supported by comedi have digital input and output +channels. Some boards allow the direction of a channel to be +specified in software. + +Comedi groups digital channels into subdevice, which is a group +of digital channels that have the same characteristics. For +example, digital output lines will be grouped into a digital +output subdevice, bidirectional digital lines will be grouped +into a digital I/O subdevice. Thus, there can be multiple +digital subdevices on a particular board. + + +<sect1>Timed Input/Output +<p> + +<p> +<sect1>Slowly-varying inputs +<p> + + +Sometimes, your input channels change slowly enough that +you are able to average many sucessive input values to get a +more accurate measurement of the actual value. In general, +the more samples you average, the better your estimate +gets, roughly by a factor of sqrt(number_of_samples). +Obviously, there are limitations to this: + +<p> +<itemize> +<item> +you are ultimately limited by "spurious free dynamic range" + +<item> +you need to have _some_ noise on the input channel, +otherwise you will be averaging the same number N times. + +<item> +the more noise you have, the greater your SFDR, but it +takes many more samples to compensate for the increased +noise + +<item> +if you feel the need to average samples for 2 seconds, +your signal will need to be _very_ slowly-varying, i.e., +not varying more than your target uncertainty for the +entire 2 seconds. + +</itemize> + +As you might have guessed, the comedi library has functions +to help you in your quest to accurately measure slowly varying +inputs. I use these functions to measure thermocouple voltages +-- actually, the library functions came from a section of code +that was previously part of the thermocouple reading program. + +The comedi self-calibration utility also uses these functions. +On some hardware, it is possible to tell it to measure an +internal stable voltage reference, which is typically going +to be very slowly varying -- on the kilosecond time scale +or more. So it is reasonable to measure millions of samples, +to get a very accurate measurement of the A/D converter output +value that corresponds to the voltage reference. Sometimes, +however, this is overkill, since there is no need to +perform a part-per-million calibration to a standard that +is only accurate to part-per-thousand. + + + +<p> + +<sect>Comedilib reference + +<p> +Reference of structures: + +<tscreen><verb> +typedef struct comedi_t_struct comedi_t; + +typedef struct{ + double min; + double max; + unsigned int unit; +}comedi_range; + +typedef struct comedi_sv_struct{ + comedi_t *dev; + unsigned int subdevice; + unsigned int chan; + + /* range policy */ + int range; + int aref; + + /* number of measurements to average (for ai) */ + int n; + + lsampl_t maxdata; +}comedi_sv_t; +</verb></tscreen> + +<sect1>comedi_loglevel() + +<p> + +int comedi_loglevel(int loglevel); + +<p> + +This function affects the output of debugging and error messages +from comedlib. By increasing the loglevel, additional debugging +information will be printed. This function returns the previous +loglevel. Some debugging information will only be printed if +comedilib was compiled with this debugging information included. +The loglevel can also be affected by the environment +variable COMEDI_LOGLEVEL. The meaning of the loglevels is as +follows: + +COMEDILIB_LOGLEVEL=0 + +Comedilib prints nothing. + +COMEDILIB_LOGLEVEL=1 (default) + +Comedilib only prints error messages when there is a +self-consistency error. + +COMEDILIB_LOGLEVEL=2 + +Comedilib prints an error message whenever an invalid +parameter is passed to comedilib. + +COMEDILIB_LOGLEVEL=3 + +Comedilib prints an error message whenever an error is generated +in the comedilib library or is generated in the C library when +called by comedilib. + +COMEDILIB_LOGLEVEL=4 + +Comedilib prints a lot of debugging messages. + +<p> + +<sect1>comedi_open + +<p> + +comedi_t *comedi_open(char *fn); + +Opens a comedi device specified by the filename fn. Returns NULL +on error. Returns a handle that is given as a parameter to other +comedilib functions. + +You are not supposed to have access to the structure comedi_t. + +void comedi_close(comedi_t *it); + +Closes a device previously opened by comedi_open(). + +void comedi_perror(const char *s); +char *comedi_strerror(int errnum); +int comedi_errno(void); + +When a comedilib function fails, it usually returns -1 or +NULL, depending on the return type. An internal library +variable stores an error number, which can be retrieved with +<tt>comedi_errno()</tt>. This error number can be +converted to a human-readable form by the functions +<tt>comedi_perror()</tt> and <tt>comedi_strerror()</tt>. + +These functions are intended to mimic the behavior of the +standard C library functions <tt>perror()</tt>, +<tt>strerror</tt>, and <tt>errno()</tt>. In particular, +comedilib functions sometimes return an error that is generated +inside the C library; the comedi error message in this case +is the same as the C library. + +The function <tt>comedi_perror()</tt> prints an error +message to stderr. The error message consists of the +argument string, a colon, a space, a description of the error +condition, and a new line. + +The function <tt>comedi_strerror()</tt> returns a pointer to a +character string +describing the comedilib error <tt>errnum</tt>. The persistence +of the returned pointer is undefined, and should not be trusted +after the next comedilib call. An unrecognized error number will +return a pointer to the string "undefined error", or similar. + +The function <tt>comedi_errno()</tt> +returns an integer describing the most recent comedilib error. This +integer may be used as the <tt>errnum</tt> parameter for +<tt>comedi_strerror()</tt>. + +<p> +<sect1>comedi_fileno() + +<p> + +int comedi_fileno(comedi_t *it); + +The function <tt>comedi_fileno</tt> +returns the integer descriptor for the handle <tt>it</tt>. If +<tt>it</tt> is an invalid <tt>comedi_t</tt> pointer, the function +returns -1 and sets the appropriate comedilib error value. + + +<p> +<sect1>comedi_get_n_subdevices() +<p> + +int comedi_get_n_subdevices(comedi_t *it); + +The function <tt>comedi_get_n_subdevices</tt> returns the +number of subdevices associated with the comedi descriptor +<tt>it</tt>, or -1 if there is an error. + +<p> +<sect1>comedi_get_version_code() +<p> + +int comedi_get_version_code(comedi_t *it); + +The function <tt>comedi_get_version_code()</tt> returns the +version code of the currently running comedi module. The version +code is of the form 0x010203, which is the version code for +version 1.2.3. + +<p> +<sect1>comedi_get_driver_name() +<p> + +char *comedi_get_driver_name(comedi_t *it); + +The function <tt>comedi_get_driver_name</tt> returns a pointer +to a string containing the name of the driver being used by comedi +for the comedi device represented by <tt>it</tt>. This pointer is +valid until the comedi descriptor <tt>it</tt> is closed. This +function returns NULL if there is an error. + +<p> +<sect1>comedi_get_board_name() +<p> + +char *comedi_get_board_name(comedi_t *it); + +The function <tt>comedi_get_board_name</tt> returns a pointer +to a string containing the name of the device. This pointer is +valid until the comedi descriptor <tt>it</tt> is closed. This +function returns NULL if there is an error. + +<p> +<sect1>comedi_get_subdevice_type() +<p> + +int comedi_get_subdevice_type(comedi_t *it,unsigned int subdevice); + +The function <tt>comedi_get_subdevice_type()</tt> returns an +integer describing the type of subdevice that belongs to the comedi +device <tt>it</tt> and has the index <tt>subdevice</tt>. The +function returns -1 is there is an error. + +Valid subdevice types are: + +<itemize> +<item><tt>COMEDI_SUBD_UNUSED</tt> +Subdevice has no functionality, i.e., a place-holder. +<item><tt>COMEDI_SUBD_AI</tt> Analog input +<item><tt>COMEDI_SUBD_AO</tt> Analog output +<item><tt>COMEDI_SUBD_DI</tt> Digital input +<item><tt>COMEDI_SUBD_DO</tt> Digital output +<item><tt>COMEDI_SUBD_DIO</tt> +Digital input/output. Channels are configurable as to whether they +are inputs or outputs. +<item><tt>COMEDI_SUBD_COUNTER</tt> Counter +<item><tt>COMEDI_SUBD_TIMER</tt> Timer +<item><tt>COMEDI_SUBD_MEMORY</tt> +Memory, e.g., EEPROM or dual-ported RAM +<item><tt>COMEDI_SUBD_CALIB</tt> +Calibration DACs +<item><tt>COMEDI_SUBD_PROC</tt> +Processor or DSP +</itemize> + + +<p> +<sect1>comedi_find_subdevice_by_type() +<p> + +int comedi_find_subdevice_by_type(comedi_t *it,int type,unsigned int start_subdevice) + +The function <tt>comedi_find_subdevice_by_type</tt> tries to +locate a subdevice belonging to comedi device <tt>it</tt>, +having type <tt>type</tt>, starting with the subdevice +<tt>start_subdevice</tt>. If it finds the requested subdevice, +it returns its index. If it does not locate the requested +subdevice, it returns -1 and sets the comedi error number to +"subdevice not found". If there is an error, the function +returns -1 and sets the appropriate error. + +For subdevice types, see the manual page for the function +<tt>comedi_get_subdevice_type()</tt>. + +<p> +<sect1>comedi_get_n_channels() +<p> + + +int comedi_get_n_channels(comedi_t *it,unsigned int subdevice); + +The function <tt>comedi_get_n_channels()</tt> returns the number +of channels of the subdevice belonging to the comedi device <tt>it</tt> +and having index <tt>subdevice</tt>. This function returns -1 on error. + +<p> +<sect1>comedi_get_maxdata() +<p> + +lsampl_t comedi_get_maxdata(comedi_t *it,unsigned int subdevice,unsigned int chan); + +The function <tt>comedi_get_maxdata()</tt> returns the maximum +valid data value for channel <tt>chan</tt> of subdevice +<tt>subdevice</tt> belonging to the comedi device <tt>it</tt> +This function returns 0 on error. + +<p> +<sect1>comedi_get_rangetype() +<p> + +int comedi_get_rangetype(comedi_t *it,unsigned int subdevice,unsigned int chan); + +The function <tt>comedi_get_rangetype()</tt> returns an integer +that represents the number of range specifications available for a +particular channel, as well as a conversion table to convert sample +values to/from physical units. The macro +<tt>RANGE_LENGTH(rangetype)</tt> +can be used to determine the number of range specifications for a given +range type. + +<p> +<sect1>comedi_get_range() +<p> + +comedi_range * comedi_get_range(comedi_t *it,unsigned int subdevice,unsigned int chan,unsigned int range); + +The function <tt>comedi_get_range</tt> returns a pointer to a +comedi_range structure that contains information that can be used to +convert sample values to or from physical units. The pointer is valid +until the comedi device <tt>it</tt> is closed. If there is an +error, NULL is returned. + +<p> +<sect1>comedi_trigger() +<p> + +int comedi_trigger(comedi_t *it,comedi_trig *trig); + +The function <tt>comedi_trigger()</tt> instructs comedi to +perform the command specified by the trigger structure +<tt>trig</tt>. Results depend on the particular command +being issued. If there is an error, -1 is returned. + +Complete information about comedi commands is given in the +manual page comedi(8). + +double comedi_to_phys(lsampl_t data,comedi_range *rng,lsampl_t maxdata); +lsampl_t comedi_from_phys(double data,comedi_range *rng,lsampl_t maxdata); + +The functions <tt>comedi_to_phys()</tt> and +<tt>comedi_from_phys()</tt> convert sample values to/from physical +units. The parameter <tt>rng</tt> represents the conversion +information to use, and the parameter <tt>maxdata</tt> represents +the maximum possible data value for the channel that the data was read/ +will be written to. + + +<p> +<sect1>comedi_data_read() +<p> + +int comedi_data_read(comedi_t *it,unsigned int subd,unsigned int chan, + unsigned int range,unsigned int aref,lsampl_t *data); +int comedi_data_write(comedi_t *it,unsigned int subd,unsigned int chan, + unsigned int range,unsigned int aref,lsampl_t data); + +These functions read or write a single sample on the channel that +is specified by the comedi device <tt>it</tt>, the +subdevice <tt>subd</tt>, and the channel <tt>chan</tt>. +For the operation, +the device is configured to use range specification +<tt>range</tt> and (if appropriate) analog reference type +<tt>aref</tt>. Analog reference types that are not supported +by the device are silently ignored. + +The function <tt>comedi_data_read()</tt> reads one data value from +the specified channel and places the +data value that is read in the location pointed to by +<tt>data</tt>. + +The function <tt>comedi_data_write()</tt> writes the data value +specified by the argument <tt>data</tt> to +the specified channel. + +On sucess, these functions return 0. If there is an error, -1 is +returned. + +Valid analog reference numbers are: + +<itemize> +<item>AREF_GROUND Reference to analog ground +<item>AREF_COMMON Reference to analog common +<item>AREF_DIFF Differential reference +<item>AREF_OTHER Board-specific meaning +</itemize> + +Valid data values used by these functions is an unsigned integer +less than or equal to <tt>maxdata</tt>, which is channel-dependent. +Conversion of these data values to physical units can be performed +by <tt>comedi_to_phys()</tt> and <tt>comedi_from_phys()</tt>. + + +<p> +<sect1>comedi_sv_init() +<p> + +<tscreen><verb> +int comedi_sv_init(comedi_sv_t *it,comedi_t *dev,unsigned int subd,unsigned int chan); +int comedi_sv_update(comedi_sv_t *it); +int comedi_sv_measure(comedi_sv_t *it,double *data); +</verb></tscreen> + +The special functions <tt>comedi_sv_*()</tt> are designed to +make it easy to accurately measure slowly varying analog inputs. +A slowly +varying input is one that is effectively constant over the course +of approximately 100 A/D conversions. However, since these +conversions can sometimes be pre-empted by scheduling, for most +purposes, a slowly varying signal should be effectively constant +for greater than 20 ms (the default Linux timeslice). + +By averaging many A/D conversions of a relatively constant +signal, it is possible to get a better measurement of the signal +than a single A/D conversion. In general, the uncertainty of the +measurement decreases as the square root of the number of samples. +This is limited by the rate that which the signal varies, and +ultimately by the spurious free dynamic range of the A/D converter. + + +<p> +<sect1>comedi_get_timer() +<p> + +<tscreen><verb> +int comedi_get_timer(comedi_t *it,unsigned int subdev,double freq,unsigned int *trigvar, + double *actual_freq); +</verb></tscreen> + + +<p> + +</article> + diff --git a/doc/html/comedilib-1.html b/doc/html/comedilib-1.html new file mode 100644 index 0000000..f3ba92c --- /dev/null +++ b/doc/html/comedilib-1.html @@ -0,0 +1,24 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<HTML> +<HEAD> + <META NAME="GENERATOR" CONTENT="SGML-Tools 1.0.9"> + <TITLE>Comedi Documentation: Introduction + + + + + +Next +Previous +Contents +
+

1. Introduction

+ +

This is preliminary documentation for Comedi and Comedilib. +

+


+Next +Previous +Contents + + diff --git a/doc/html/comedilib-2.html b/doc/html/comedilib-2.html new file mode 100644 index 0000000..4417140 --- /dev/null +++ b/doc/html/comedilib-2.html @@ -0,0 +1,231 @@ + + + + + Comedi Documentation: Installation and configuration + + + + + +Next +Previous +Contents +
+

2. Installation and configuration

+ +

+

This section covers compiling, installing, and configuring +comedi and comedlib. +

+

+

2.1 Compiling and Installing +

+ +

+

This section has not been written. +

+

+

2.2 Insmod'ding the kernel module +

+ +

+

This section has not been written. +

+

+

2.3 Configuring comedi for your hardware +

+ +

+

+

I assume that your hardware device is in your computer, and that +you know the relevant details about it, i.e., what kind of card +it is, the I/O base, the IRQ, jumper settings related to input +ranges, etc. +

To tell the comedi kernel module that you have a particular device, and +some information about it, you will be running the comedi_config +command. Perhaps you should read the man page now. +

In this tutorial, I will go through the process of configuring comedi +for two devices, a National Instruments AT-MIO-16E-10 +and a Data Translation DT2821-F-8DI. +

The NI board is plug-and-play, and the man page tells me that I need +to configure the PnP part of the board with isapnptools. The isapnptools +package is a little cryptic, but the concepts are simple. Once I +learned how to use it, I settled on a /etc/isapnp.conf file that +contained the lines: +

+

+

+
+# ANSI string -->National Instruments, AT-MIO-16E-10<--
+(CONFIGURE NIC2400/10725401 (LD 0
+        (IO 0 (BASE 0x0260))
+        (INT 0 (IRQ 3 (MODE +E)))
+#       (DMA 0 (CHANNEL 5))
+#       (DMA 1 (CHANNEL 6))
+        (ACT Y)
+))
+
+
+

+

It also contains a few lines about overall configuration and about my +sound card. I found out after a bit of trial-and-error that the NI +board does not always work with interrupts other than IRQ 3. YMMV. +Currently, the driver doesn't use DMA, but it may in the future, so +I commented out the DMA lines. It is a curious fact that the device +ignores the IRQ and DMA information given here, however, I keep the +information here to remind myself that the numbers aren't arbitrary. +

When I run comedi_config (as root, of course), I provide the same +information. Since I want to have the board configured every time +I boot, I put the line +

+

+
+/usr/sbin/comedi_config /dev/comedi0 atmio-E 0x260,3
+
+
+

into /etc/rc.d/rc.local. You can, of course, run this command at +a command prompt. The man page tells me that the option list +is supposed to be "(I/O base),(IRQ)", so I used the same numbers +as I put in /etc/isapnp.conf, i.e., 0x260,3. +

For the Data Translation board, I need to have a list of the +jumper settings. Fortunately, I wrote them all down in the +manual -- I hope they are still correct. However, I had to +open the case to figure out which board in the series I had. +It is a DT2821-f-8di. The man page of comedi_config tells +me that I need to know the I/O base, IRQ, DMA 1, DMA 2. However, +since I wrote the driver, I know that it also recognizes the +differential/single-ended and unipolar/bipolar jumpers. As always, +the source is the final authority, and looking in module/dt282x.c +tells me that the options list is interpreted as: +

+

    +
  • I/O base
  • +
  • IRQ
  • +
  • 1=differential, 0=single ended
  • +
  • ai 0=unipolar, 1=bipolar
  • +
  • ao0 0=unipolar, 1=bipolar
  • +
  • ao1 0=unipolar, 1=bipolar
  • +
  • dma1
  • +
  • dma2
  • +
+

(ai=analog input, ao=analog output.) From this, I decide that +the appropriate options list is +

+

+
+0x200,4,,1,1,1
+
+
+

I left the differential/single-ended number blank, since the +driver already knowns (from the board name), that it is +differential. I also left the DMA numbers blank, since I +don't want the driver to use DMA. (Don't want it to interfere +with my sound card -- life is full of difficult choices.) +Keep in mind that things commented in the source, but not in +the documentation are about as likely to change as the weather, +so I put good comments next to the following line when I put +it in rc.local. +

+

+
+/usr/sbin/comedi_config /dev/comedi1 dt2821-f-8di 0x200,4,,1,1,1
+
+
+

So now I think that I have my boards configured correctly. +Since data acquisition boards are not typically well-engineered, +comedi sometimes can't figure out if the board is actually there. +If it can't, it assumes you are right. Both of these boards +are well-made, so comedi will give me an error message if it +can't find them. The comedi kernel module, since it is a part +of the kernel, prints messages to the kernel logs, which you +can access through the command 'dmesg' or /var/log/messages. +Here is a configuration failure (from dmesg): +

+

+
+comedi0: ni_E: 0x0200 can't find board
+
+
+

When it does work, I get: +

+

+
+comedi0: ni_E: 0x0260 at-mio-16e-10 ( irq = 3 )
+
+
+

Note that it also correctly identified my board. +

+

+

+

2.4 Getting information from comedi +

+ +

+

+

So now that we have comedi talking to the hardware, we want to +talk to comedi. Here's some pretty low-level information -- +it's sometimes useful for debugging: +

+

+

+

+
+cat /proc/comedi
+
+
+

Right now, on my computer, this command gives: +

+

+
+comedi version 0.6.4
+format string
+ 0: atmio-E              at-mio-16e-10           7
+ 1: dt282x               dt2821-f-8di            4
+
+
+

This is a feature that is not well-developed yet. Basically, it +currently tells you driver name, device name, and number of +subdevices. +

In the demo/ directory, there is a command called +info, which provides information about each subdevice on the +board. The output of it is rather long, since I have 7 +subdevices (4 or fewer is common for other boards.) +Here's part of the output of the NI board (which +is on /dev/comedi0.) ('demo/info /dev/comedi0') +

+

+
+overall info:
+  version code: 0x000604
+  driver name: atmio-E
+  board name: at-mio-16e-10
+  number of subdevices: 7
+subdevice 0:
+  type: 1 (unknown)
+  number of channels: 16
+  max data value: 4095
+
+ +... +
+

The overall info gives information about the device -- basically +the same information as /proc/comedi. +

This board has 7 subdevices. Devices are separated into +subdevices that each have a distinct purpose -- e.g., analog +input, analog output, digital input/output. This board also +has an EEPROM and calibration DACs that are also subdevices. +

Subdevice 0 is the analog input subdevice. You would have +known this from the 'type: 1 (unknown)' line, if I've updated +demo/info recently, because it would say 'type: 1 (analog input)' +instead. The other lines should be self-explanitory. Comedi +has more information about the device, but demo/info doesn't +currently display this. +

+

+


+Next +Previous +Contents + + diff --git a/doc/html/comedilib-3.html b/doc/html/comedilib-3.html new file mode 100644 index 0000000..e0c7981 --- /dev/null +++ b/doc/html/comedilib-3.html @@ -0,0 +1,40 @@ + + + + + Comedi Documentation: Individual drivers + + + + + +Next +Previous +Contents +
+

3. Individual drivers

+ +

+

This section contains information that is specific to each +hardware driver. The most current information about a driver +is included in the comedi source. +

+

3.1 National Instruments AT-MIO E series +

+ +

+

+

+

3.2 Data Translation +

+ +

+

+

+

+


+Next +Previous +Contents + + diff --git a/doc/html/comedilib-4.html b/doc/html/comedilib-4.html new file mode 100644 index 0000000..689e47b --- /dev/null +++ b/doc/html/comedilib-4.html @@ -0,0 +1,251 @@ + + + + + Comedi Documentation: Writing programs that use comedi and comedilib + + + + + +Next +Previous +Contents +
+

4. Writing programs that use comedi and comedilib

+ +

+

+

4.1 Your first comedi program +

+ +

+

This example requires a card that has analog or +digital input. Right to the source: +

+

+
+#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 = AREF_GROUND; /* more on this later */
+
+int main(int argc,char *argv[])
+{
+        comedi_t *it;
+        int chan=0;
+        lsampl_t data;
+        
+        it=comedi_open("/dev/comedi0");
+        
+        comedi_data_read(it,subdev,chan,range,aref,& data);
+        
+        printf("%d\n",data);
+        
+        return 0;
+}
+
+
+

+

Should be understandable. Open the device, get the data, +print it out. This is basically the guts of demo/inp.c, +without error checking or fancy options. Including all +the appropriate headers is sometimes a little tricky. +Compile it using +

+

+
+cc tut1.c -lcomedi -o tut1
+
+
+

Hopefully it works. +

A few notes: The range variable tells comedi which gain +to use when measuring an analog voltage. Since we don't +know (yet) which numbers are valid, or what each means, +we'll use 0, because it won't cause errors. Likewise with +aref, which determines the analog reference used. +

+

+

+

4.2 Converting samples to voltages +

+ +

+

If you selected an analog input subdevice, you should notice +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, +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 translated to +a voltage. Naturally, as a good programmer, your first +question is: "How do I do this in a device-independent +manner?" +

For each subdevice, the comedi kernel module keeps a +'range_type' variable. This variable contains the number +of available ranges (i.e., gains) that you can select, +along with an offset in a list of range information +structures. If you know the range_type variable, you +can use these macros: +

RANGE_OFFSET(range_type) +RANGE_LENGTH(range_type) +

to extract such information. However, you want the +actual voltage information, not some integer offset +in a table. Rather than messing with the library +internals, use the function +

ptr=comedi_get_range(comedi_file,subdevice,channel, +range) +

which returns a pointer to a comedi_range structure. +The comedi_range structure looks like +

+

+

+
+typedef struct{
+        double min;
+        double max;
+        unsigned int unit;
+}comedi_range;
+
+
+

As you might expect, ptr[range] is for range 'range', +which you provided to comedi_data_read() above. '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, etc. +

"Could it get easier?", you say. Well, yes. Use +the function 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);
+
+
+

You probably noticed (and were worried) that we haven't +discussed how to determine maxdata and range_type. Well, +you could ask the kernel this information each time you need +it, but since there are other variables, special cases, +and several subdevices to worry about, it would be nice +if the library could take care of this... (read on...) +

+

+

+

4.3 Another section +

+ +

+

+

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(), like we did explicitly in a previous +section, but also fills the comedi_t structure with +lots of goodies -- information that we will need to use +soon. +

Specifically, we needed to know maxdata for a specific +subdevice/channel. How about: +

+

+
+maxdata=comedi_get_maxdata(file,subdevice,channel);
+
+
+

Wow. How easy. And the range type? +

+

+
+range_type=comedi_get_rangetype(file,subdevice,channel);
+
+
+

Cool. Other information you need to know about a channel +can be gotten in a similar way. +

+

+

+

4.4 Your second comedi program +

+ +

+

+

Actually, this is the first comedi program again, just +that we've added what we've learned. +

+

+

+
+#include <stdio.h>      /* for printf() */
+#include <comedi.h>     /* also included by comedilib.h */
+#include <comedilib.h>  /* for comedi_get() */
+
+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;
+        int 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);
+
+        data=comedi_get(cf->fd,subdev,chan,range,aref);
+
+        volts=comedi_to_phys(data,rangetype,range,maxdata);
+
+        printf("%d %g\n",data,volts);
+
+        return 0;
+}
+
+
+

+

By now, the comedi_read_data() line looks a little archaic, using +the UNIX file descriptor cf->fd instead of just cf. (By the +way, somewhere in the heart of comedi_open() is the line +cf->fd=open(filename,O_RDWR).) Well, there isn't one good +replacement, since it highly depends on your application +what additional features you might want in a comedi_get() +replacement. But this is the topic of a different section. +

+

+

+


+Next +Previous +Contents + + diff --git a/doc/html/comedilib-5.html b/doc/html/comedilib-5.html new file mode 100644 index 0000000..92e74ba --- /dev/null +++ b/doc/html/comedilib-5.html @@ -0,0 +1,94 @@ + + + + + Comedi Documentation: Application-specific functions + + + + + +Next +Previous +Contents +
+

5. Application-specific functions

+ +

+

+

5.1 Digital Input/Output +

+ +

+

Many boards supported by comedi have digital input and output +channels. Some boards allow the direction of a channel to be +specified in software. +

Comedi groups digital channels into subdevice, which is a group +of digital channels that have the same characteristics. For +example, digital output lines will be grouped into a digital +output subdevice, bidirectional digital lines will be grouped +into a digital I/O subdevice. Thus, there can be multiple +digital subdevices on a particular board. +

+

+

5.2 Timed Input/Output +

+ +

+

+

+

5.3 Slowly-varying inputs +

+ +

+

+

Sometimes, your input channels change slowly enough that +you are able to average many sucessive input values to get a +more accurate measurement of the actual value. In general, +the more samples you average, the better your estimate +gets, roughly by a factor of sqrt(number_of_samples). +Obviously, there are limitations to this: +

+

+

    +
  • you are ultimately limited by "spurious free dynamic range" +
  • +
  • you need to have _some_ noise on the input channel, +otherwise you will be averaging the same number N times. +
  • +
  • the more noise you have, the greater your SFDR, but it +takes many more samples to compensate for the increased +noise +
  • +
  • if you feel the need to average samples for 2 seconds, +your signal will need to be _very_ slowly-varying, i.e., +not varying more than your target uncertainty for the +entire 2 seconds. +
  • +
+

As you might have guessed, the comedi library has functions +to help you in your quest to accurately measure slowly varying +inputs. I use these functions to measure thermocouple voltages +-- actually, the library functions came from a section of code +that was previously part of the thermocouple reading program. +

The comedi self-calibration utility also uses these functions. +On some hardware, it is possible to tell it to measure an +internal stable voltage reference, which is typically going +to be very slowly varying -- on the kilosecond time scale +or more. So it is reasonable to measure millions of samples, +to get a very accurate measurement of the A/D converter output +value that corresponds to the voltage reference. Sometimes, +however, this is overkill, since there is no need to +perform a part-per-million calibration to a standard that +is only accurate to part-per-thousand. +

+

+

+

+

+


+Next +Previous +Contents + + diff --git a/doc/html/comedilib-6.html b/doc/html/comedilib-6.html new file mode 100644 index 0000000..49afcdb --- /dev/null +++ b/doc/html/comedilib-6.html @@ -0,0 +1,384 @@ + + + + + Comedi Documentation: Comedilib reference + + + + +Next +Previous +Contents +
+

6. Comedilib reference

+ +

Reference of structures: +

+

+
+typedef struct comedi_t_struct comedi_t;
+
+typedef struct{
+        double min;
+        double max;
+        unsigned int unit;
+}comedi_range;
+
+typedef struct comedi_sv_struct{
+        comedi_t *dev;
+        unsigned int subdevice;
+        unsigned int chan;
+
+        /* range policy */
+        int range;
+        int aref;
+        
+        /* number of measurements to average (for ai) */
+        int n;
+
+        lsampl_t maxdata;
+}comedi_sv_t;
+
+
+

+

6.1 comedi_loglevel() +

+ +

+

int comedi_loglevel(int loglevel); +

+

+

This function affects the output of debugging and error messages +from comedlib. By increasing the loglevel, additional debugging +information will be printed. This function returns the previous +loglevel. Some debugging information will only be printed if +comedilib was compiled with this debugging information included. +The loglevel can also be affected by the environment +variable COMEDI_LOGLEVEL. The meaning of the loglevels is as +follows: +

COMEDILIB_LOGLEVEL=0 +

Comedilib prints nothing. +

COMEDILIB_LOGLEVEL=1 (default) +

Comedilib only prints error messages when there is a +self-consistency error. +

COMEDILIB_LOGLEVEL=2 +

Comedilib prints an error message whenever an invalid +parameter is passed to comedilib. +

COMEDILIB_LOGLEVEL=3 +

Comedilib prints an error message whenever an error is generated +in the comedilib library or is generated in the C library when +called by comedilib. +

COMEDILIB_LOGLEVEL=4 +

Comedilib prints a lot of debugging messages. +

+

+

+

6.2 comedi_open +

+ +

+

comedi_t *comedi_open(char *fn); +

Opens a comedi device specified by the filename fn. Returns NULL +on error. Returns a handle that is given as a parameter to other +comedilib functions. +

You are not supposed to have access to the structure comedi_t. +

void comedi_close(comedi_t *it); +

Closes a device previously opened by comedi_open(). +

void comedi_perror(const char *s); +char *comedi_strerror(int errnum); +int comedi_errno(void); +

When a comedilib function fails, it usually returns -1 or +NULL, depending on the return type. An internal library +variable stores an error number, which can be retrieved with +comedi_errno(). This error number can be +converted to a human-readable form by the functions +comedi_perror() and comedi_strerror(). +

These functions are intended to mimic the behavior of the +standard C library functions perror(), +strerror, and errno(). In particular, +comedilib functions sometimes return an error that is generated +inside the C library; the comedi error message in this case +is the same as the C library. +

The function comedi_perror() prints an error +message to stderr. The error message consists of the +argument string, a colon, a space, a description of the error +condition, and a new line. +

The function comedi_strerror() returns a pointer to a +character string +describing the comedilib error errnum. The persistence +of the returned pointer is undefined, and should not be trusted +after the next comedilib call. An unrecognized error number will +return a pointer to the string "undefined error", or similar. +

The function comedi_errno() +returns an integer describing the most recent comedilib error. This +integer may be used as the errnum parameter for +comedi_strerror(). +

+

+

6.3 comedi_fileno() +

+ +

+

int comedi_fileno(comedi_t *it); +

The function comedi_fileno +returns the integer descriptor for the handle it. If +it is an invalid comedi_t pointer, the function +returns -1 and sets the appropriate comedilib error value. +

+

+

+

6.4 comedi_get_n_subdevices() +

+ +

+

int comedi_get_n_subdevices(comedi_t *it); +

The function comedi_get_n_subdevices returns the +number of subdevices associated with the comedi descriptor +it, or -1 if there is an error. +

+

+

6.5 comedi_get_version_code() +

+ +

+

int comedi_get_version_code(comedi_t *it); +

The function comedi_get_version_code() returns the +version code of the currently running comedi module. The version +code is of the form 0x010203, which is the version code for +version 1.2.3. +

+

+

6.6 comedi_get_driver_name() +

+ +

+

char *comedi_get_driver_name(comedi_t *it); +

The function comedi_get_driver_name returns a pointer +to a string containing the name of the driver being used by comedi +for the comedi device represented by it. This pointer is +valid until the comedi descriptor it is closed. This +function returns NULL if there is an error. +

+

+

6.7 comedi_get_board_name() +

+ +

+

char *comedi_get_board_name(comedi_t *it); +

The function comedi_get_board_name returns a pointer +to a string containing the name of the device. This pointer is +valid until the comedi descriptor it is closed. This +function returns NULL if there is an error. +

+

+

6.8 comedi_get_subdevice_type() +

+ +

+

int comedi_get_subdevice_type(comedi_t *it,unsigned int subdevice); +

The function comedi_get_subdevice_type() returns an +integer describing the type of subdevice that belongs to the comedi +device it and has the index subdevice. The +function returns -1 is there is an error. +

Valid subdevice types are: +

+

    +
  • COMEDI_SUBD_UNUSED +Subdevice has no functionality, i.e., a place-holder.
  • +
  • COMEDI_SUBD_AI Analog input
  • +
  • COMEDI_SUBD_AO Analog output
  • +
  • COMEDI_SUBD_DI Digital input
  • +
  • COMEDI_SUBD_DO Digital output
  • +
  • COMEDI_SUBD_DIO +Digital input/output. Channels are configurable as to whether they +are inputs or outputs.
  • +
  • COMEDI_SUBD_COUNTER Counter
  • +
  • COMEDI_SUBD_TIMER Timer
  • +
  • COMEDI_SUBD_MEMORY +Memory, e.g., EEPROM or dual-ported RAM
  • +
  • COMEDI_SUBD_CALIB +Calibration DACs
  • +
  • COMEDI_SUBD_PROC +Processor or DSP
  • +
+

+

+

+

6.9 comedi_find_subdevice_by_type() +

+ +

+

int comedi_find_subdevice_by_type(comedi_t *it,int type,unsigned int start_subdevice) +

The function comedi_find_subdevice_by_type tries to +locate a subdevice belonging to comedi device it, +having type type, starting with the subdevice +start_subdevice. If it finds the requested subdevice, +it returns its index. If it does not locate the requested +subdevice, it returns -1 and sets the comedi error number to +"subdevice not found". If there is an error, the function +returns -1 and sets the appropriate error. +

For subdevice types, see the manual page for the function +comedi_get_subdevice_type(). +

+

+

6.10 comedi_get_n_channels() +

+ +

+

+

int comedi_get_n_channels(comedi_t *it,unsigned int subdevice); +

The function comedi_get_n_channels() returns the number +of channels of the subdevice belonging to the comedi device it +and having index subdevice. This function returns -1 on error. +

+

+

6.11 comedi_get_maxdata() +

+ +

+

lsampl_t comedi_get_maxdata(comedi_t *it,unsigned int subdevice,unsigned int chan); +

The function comedi_get_maxdata() returns the maximum +valid data value for channel chan of subdevice +subdevice belonging to the comedi device it +This function returns 0 on error. +

+

+

6.12 comedi_get_rangetype() +

+ +

+

int comedi_get_rangetype(comedi_t *it,unsigned int subdevice,unsigned int chan); +

The function comedi_get_rangetype() returns an integer +that represents the number of range specifications available for a +particular channel, as well as a conversion table to convert sample +values to/from physical units. The macro +RANGE_LENGTH(rangetype) +can be used to determine the number of range specifications for a given +range type. +

+

+

6.13 comedi_get_range() +

+ +

+

comedi_range * comedi_get_range(comedi_t *it,unsigned int subdevice,unsigned int chan,unsigned int range); +

The function comedi_get_range returns a pointer to a +comedi_range structure that contains information that can be used to +convert sample values to or from physical units. The pointer is valid +until the comedi device it is closed. If there is an +error, NULL is returned. +

+

+

6.14 comedi_trigger() +

+ +

+

int comedi_trigger(comedi_t *it,comedi_trig *trig); +

The function comedi_trigger() instructs comedi to +perform the command specified by the trigger structure +trig. Results depend on the particular command +being issued. If there is an error, -1 is returned. +

Complete information about comedi commands is given in the +manual page comedi(8). +

double comedi_to_phys(lsampl_t data,comedi_range *rng,lsampl_t maxdata); +lsampl_t comedi_from_phys(double data,comedi_range *rng,lsampl_t maxdata); +

The functions comedi_to_phys() and +comedi_from_phys() convert sample values to/from physical +units. The parameter rng represents the conversion +information to use, and the parameter maxdata represents +the maximum possible data value for the channel that the data was read/ +will be written to. +

+

+

+

6.15 comedi_data_read() +

+ +

+

int comedi_data_read(comedi_t *it,unsigned int subd,unsigned int chan, +unsigned int range,unsigned int aref,lsampl_t *data); +int comedi_data_write(comedi_t *it,unsigned int subd,unsigned int chan, +unsigned int range,unsigned int aref,lsampl_t data); +

These functions read or write a single sample on the channel that +is specified by the comedi device it, the +subdevice subd, and the channel chan. +For the operation, +the device is configured to use range specification +range and (if appropriate) analog reference type +aref. Analog reference types that are not supported +by the device are silently ignored. +

The function comedi_data_read() reads one data value from +the specified channel and places the +data value that is read in the location pointed to by +data. +

The function comedi_data_write() writes the data value +specified by the argument data to +the specified channel. +

On sucess, these functions return 0. If there is an error, -1 is +returned. +

Valid analog reference numbers are: +

+

    +
  • AREF_GROUND Reference to analog ground
  • +
  • AREF_COMMON Reference to analog common
  • +
  • AREF_DIFF Differential reference
  • +
  • AREF_OTHER Board-specific meaning
  • +
+

Valid data values used by these functions is an unsigned integer +less than or equal to maxdata, which is channel-dependent. +Conversion of these data values to physical units can be performed +by comedi_to_phys() and comedi_from_phys(). +

+

+

+

6.16 comedi_sv_init() +

+ +

+

+

+
+int comedi_sv_init(comedi_sv_t *it,comedi_t *dev,unsigned int subd,unsigned int chan);
+int comedi_sv_update(comedi_sv_t *it);
+int comedi_sv_measure(comedi_sv_t *it,double *data);
+
+
+

The special functions comedi_sv_*() are designed to +make it easy to accurately measure slowly varying analog inputs. +A slowly +varying input is one that is effectively constant over the course +of approximately 100 A/D conversions. However, since these +conversions can sometimes be pre-empted by scheduling, for most +purposes, a slowly varying signal should be effectively constant +for greater than 20 ms (the default Linux timeslice). +

By averaging many A/D conversions of a relatively constant +signal, it is possible to get a better measurement of the signal +than a single A/D conversion. In general, the uncertainty of the +measurement decreases as the square root of the number of samples. +This is limited by the rate that which the signal varies, and +ultimately by the spurious free dynamic range of the A/D converter. +

+

+

+

6.17 comedi_get_timer() +

+ +

+

+

+
+int comedi_get_timer(comedi_t *it,unsigned int subdev,double freq,unsigned int *trigvar,
+        double *actual_freq);
+
+
+

+

+

+

+


+Next +Previous +Contents + + diff --git a/doc/html/comedilib.html b/doc/html/comedilib.html new file mode 100644 index 0000000..490972c --- /dev/null +++ b/doc/html/comedilib.html @@ -0,0 +1,81 @@ + + + + + Comedi Documentation + + + + + +Next +Previous +Contents +
+

Comedi Documentation

+ +

David Schleef, ds@stm.lbl.gov

+

+

1. Introduction

+ +

+

2. Installation and configuration

+ + +

+

3. Individual drivers

+ + +

+

4. Writing programs that use comedi and comedilib

+ + +

+

5. Application-specific functions

+ + +

+

6. Comedilib reference

+ + +
+Next +Previous +Contents + + diff --git a/doc/tutorial b/doc/tutorial new file mode 100644 index 0000000..b468e36 --- /dev/null +++ b/doc/tutorial @@ -0,0 +1,432 @@ + +Comedi tutorial + +0. Compiling and Installing +0. Insmod'ding the kernel module +0. Configuring comedi to use your hardware +0. Getting information from comedi +0. Your first comedi program +0. Converting samples to voltages + + + +0. Compiling and Installing + +needs to be written + + + + +0. Insmod'ding the kernel module + +needs to be written + + + +0. Configuring comedi to use your hardware + + +I assume that your hardware device is in your computer, and that +you know the relevant details about it, i.e., what kind of card +it is, the I/O base, the IRQ, jumper settings related to input +ranges, etc. + +To tell the comedi kernel module that you have a particular device, and +some information about it, you will be running the 'comedi_config' +command. Perhaps you should read the man page now. + +For this tutorial, I have two devices, a National Instruments AT-MIO-16E-10 +and a Data Translation DT2821-F-8DI. + +The NI board is plug-and-play, and the man page tells me that I need +to configure the PnP part of the board with isapnptools. The isapnptools +package is a little cryptic, but the concepts are simple. Once I +learned how to use it, I settled on a /etc/isapnp.conf file that +contained the lines: + + +# ANSI string -->National Instruments, AT-MIO-16E-10<-- +(CONFIGURE NIC2400/10725401 (LD 0 + (IO 0 (BASE 0x0260)) + (INT 0 (IRQ 3 (MODE +E))) +# (DMA 0 (CHANNEL 5)) +# (DMA 1 (CHANNEL 6)) + (ACT Y) +)) + + +It also contains a few lines about overall configuration and about my +sound card. I found out after a bit of trial-and-error that the NI +board does not always work with interrupts other than IRQ 3. YMMV. +Currently, the driver doesn't use DMA, but it may in the future, so +I commented out the DMA lines. It is a curious fact that the device +ignores the IRQ and DMA information given here, however, I keep the +information here to remind myself that the numbers aren't arbitrary. + +When I run comedi_config (as root, of course), I provide the same +information. Since I want to have the board configured every time +I boot, I put the line + + /usr/sbin/comedi_config /dev/comedi0 atmio-E 0x260,3 + +into /etc/rc.d/rc.local. You can, of course, run this command at +a command prompt. The man page tells me that the option list +is supposed to be ",", so I used the same numbers +as I put in /etc/isapnp.conf, i.e., 0x260,3. + +For the Data Translation board, I need to have a list of the +jumper settings. Fortunately, I wrote them all down in the +manual -- I hope they are still correct. However, I had to +open the case to figure out which board in the series I had. +It is a DT2821-f-8di. The man page of comedi_config tells +me that I need to know the I/O base, IRQ, DMA 1, DMA 2. However, +since I wrote the driver, I know that it also recognizes the +differential/single-ended and unipolar/bipolar jumpers. As always, +the source is the final authority, and looking in module/dt282x.c +tells me that the options list is interpreted as: + + i/o base + irq + 1=differential, 0=single ended + ai 0=unipolar, 1=bipolar + ao0 0=unipolar, 1=bipolar + ao1 0=unipolar, 1=bipolar + dma1 + dma2 + +(ai=analog input, ao=analog output.) From this, I decide that +the appropriate options list is + + 0x200,4,,1,1,1 + +I left the differential/single-ended number blank, since the +driver already knowns (from the board name), that it is +differential. I also left the DMA numbers blank, since I +don't want the driver to use DMA. (Don't want it to interfere +with my sound card -- life is full of difficult choices.) +Keep in mind that things commented in the source, but not in +the documentation are about as likely to change as the weather, +so I put good comments next to the following line when I put +it in rc.local. + + /usr/sbin/comedi_config /dev/comedi1 dt2821-f-8di 0x200,4,,1,1,1 + +So now I think that I have my boards configured correctly. +Since data acquisition boards are not typically well-engineered, +comedi sometimes can't figure out if the board is actually there. +If it can't, it assumes you are right. Both of these boards +are well-made, so comedi will give me an error message if it +can't find them. The comedi kernel module, since it is a part +of the kernel, prints messages to the kernel logs, which you +can access through the command 'dmesg' or /var/log/messages. +Here is a configuration failure (from dmesg): + + comedi0: ni_E: 0x0200 can't find board + +When it does work, I get: + + comedi0: ni_E: 0x0260 at-mio-16e-10 ( irq = 3 ) + +Note that it also correctly identified my board. + + + + +0. Getting information from comedi + + +So now that we have comedi talking to the hardware, we want to +talk to comedi. Here's some pretty low-level information -- +it's sometimes useful for debugging: + + cat /proc/comedi + +Right now, on my computer, this command gives: + + comedi version 0.6.4 + format string + 0: atmio-E at-mio-16e-10 7 + 1: dt282x dt2821-f-8di 4 + +This is a feature that is not well-developed yet. Basically, it +currently tells you driver name, device name, and number of +subdevices. + +In the demo/ directory, there is a command called +'info', which provides information about each subdevice on the +board. The output of it is rather long, since I have 7 +subdevices (4 or fewer is more common.) +Here's part of the output of the NI board (which +is on /dev/comedi0.) ('demo/info /dev/comedi0') + +overall info: + version code: 0x000604 + driver name: atmio-E + board name: at-mio-16e-10 + number of subdevices: 7 +subdevice 0: + type: 1 (unknown) + number of channels: 16 + max data value: 4095 + + +The overall info gives information about the device -- basically +the same information as /proc/comedi. + +This board has 7 subdevices. Devices are separated into +subdevices that each have a distinct purpose -- e.g., analog +input, analog output, digital input/output. This board also +has an EEPROM and calibration DACs that are also subdevices. + +Subdevice 0 is the analog input subdevice. You would have +known this from the 'type: 1 (unknown)' line, if I've updated +demo/info recently, because it would say 'type: 1 (analog input)' +instead. The other lines should be self-explanitory. Comedi +has more information about the device, but demo/info doesn't +currently display this. + + + +0. Your first comedi program + +This example requires a card that has analog or +digital input. Right to the source: + +#include /* for printf() */ +#include + +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; + int chan=0; + lsampl_t data; + + it=comedi_open("/dev/comedi0"); + + comedi_data_read(it,subdev,chan,range,aref,&data); + + printf("%d\n",data); + + return 0; +} + + +Should be understandable. Open the device, get the data, +print it out. This is basically the guts of demo/inp.c, +without error checking or fancy options. Including all +the appropriate headers is sometimes a little tricky. +Compile it using 'cc tut1.c -lcomedi -o tut1'. Hopefully +it works. + +A few notes: The range variable tells comedi which gain +to use when measuring an analog voltage. Since we don't +know (yet) which numbers are valid, or what each means, +we'll use 0, because it won't cause errors. Likewise with +aref, which determines the analog reference used. + + +0. Converting samples to voltages + + +If you selected an analog input subdevice, you should notice +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, +with 0 representing the lowest voltage of the ADC, and 4095 +the highest. The hardware driver compensates for +anything else the manual for your device says. 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?" + +For each subdevice, the comedi kernel module keeps a +'range_type' variable. This variable contains the number +of available ranges (i.e., gains) that you can select, +along with an offset in a list of range information +structures. If you know the range_type variable, you +can use these macros: + + RANGE_OFFSET(range_type) + RANGE_LENGTH(range_type) + +to extract such information. However, you want the +actual voltage information, not some integer offset +in a table. Rather than messing with the library +internals, use the function + + ptr=comedi_get_range(comedi_file,subdevice,channel, + range) + +which returns a pointer to a comedi_range structure. +The comedi_range structure looks like + + typedef struct{ + double min; + double max; + unsigned int unit; + }comedi_range; + +As you might expect, ptr[range] is for range 'range', +which you provided to comedi_data_read() above. '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, etc. + +"Could it get easier?", you say. Well, yes. Use +the function 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); + +You probably noticed (and were worried) that we haven't +discussed how to determine maxdata and range_type. Well, +you could ask the kernel this information each time you need +it, but since there are other variables, special cases, +and several subdevices to worry about, it would be nice +if the library could take care of this... (read on...) + + + +0. + + +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(), like we did explicitly in a previous +section, but also fills the comedi_t structure with +lots of goodies -- information that we will need to use +soon. + +Specifically, we needed to know maxdata for a specific +subdevice/channel. How about: + + maxdata=comedi_get_maxdata(file,subdevice,channel); + +Wow. How easy. And the range type? + + range_type=comedi_get_rangetype(file,subdevice,channel); + +Cool. Other information you need to know about a channel +can be gotten in a similar way. + + + +0. Your second comedi program + + +Actually, this is the first comedi program again, just +that we've added what we've learned. + + +#include /* for printf() */ +#include /* also included by comedilib.h */ +#include /* for comedi_get() */ + +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; + int 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); + + data=comedi_get(cf->fd,subdev,chan,range,aref); + + volts=comedi_to_phys(data,rangetype,range,maxdata); + + printf("%d %g\n",data,volts); + + return 0; +} + + +By now, the comedi_read_data() line looks a little archaic, using +the UNIX file descriptor cf->fd instead of just cf. (By the +way, somewhere in the heart of comedi_open() is the line +cf->fd=open(filename,O_RDWR).) Well, there isn't one good +replacement, since it highly depends on your application +what additional features you might want in a comedi_get() +replacement. But this is the topic of a different section. + + +0. stuff + + + +0. Slowly-varying inputs + + +Sometimes, your input channels change slowly enough that +you are able to average many sucessive input values to get a +more accurate measurement of the actual value. In general, +the more samples you average, the better your estimate +gets, roughly by a factor of sqrt(number_of_samples). +Obviously, there are limitations to this: + + - you are ultimately limited by "spurious free dynamic range" + + - you need to have _some_ noise on the input channel, + otherwise you will be averaging the same number N times. + + - the more noise you have, the greater your SFDR, but it + takes many more samples to compensate for the increased + noise + + - if you feel the need to average samples for 2 seconds, + your signal will need to be _very_ slowly-varying, i.e., + not varying more than your target uncertainty for the + entire 2 seconds. + +As you might have guessed, the comedi library has functions +to help you in your quest to accurately measure slowly varying +inputs. I use these functions to measure thermocouple voltages +-- actually, the library functions came from a section of code +that was previously part of the thermocouple reading program. + +The comedi self-calibration utility also uses these functions. +On some hardware, it is possible to tell it to measure an +internal stable voltage reference, which is typically going +to be very slowly varying -- on the kilosecond time scale +or more. So it is reasonable to measure millions of samples, +to get a very accurate measurement of the A/D converter output +value that corresponds to the voltage reference. Sometimes, +however, this is overkill, since there is no need to +perform a part-per-million calibration to a standard that +is only accurate to part-per-thousand. + + + + + diff --git a/include/comedilib.h b/include/comedilib.h new file mode 100644 index 0000000..2949165 --- /dev/null +++ b/include/comedilib.h @@ -0,0 +1,149 @@ +/* + include/comedilib.h + header file for the comedi library routines + + COMEDI - Linux Control and Measurement Device Interface + Copyright (C) 1998 David A. Schleef + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + + +#ifndef _COMEDILIB_H +#define _COMEDILIB_H + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct comedi_t_struct comedi_t; + +typedef struct{ + double min; + double max; + unsigned int unit; +}comedi_range; + +typedef struct comedi_sv_struct{ + comedi_t *dev; + unsigned int subdevice; + unsigned int chan; + + /* range policy */ + int range; + int aref; + + /* number of measurements to average (for ai) */ + int n; + + lsampl_t maxdata; +}comedi_sv_t; + + + + +comedi_t *comedi_open(const char *fn); +void comedi_close(comedi_t *it); + +int comedi_loglevel(int loglevel); +void comedi_perror(const char *s); +char *comedi_strerror(int errnum); +int comedi_errno(void); +int comedi_fileno(comedi_t *it); + +/* queries */ + +int comedi_get_n_subdevices(comedi_t *it); +int comedi_get_version_code(comedi_t *it); +char *comedi_get_driver_name(comedi_t *it); +char *comedi_get_board_name(comedi_t *it); + +int comedi_get_subdevice_type(comedi_t *it,unsigned int subdevice); +int comedi_find_subdevice_by_type(comedi_t *it,int type,unsigned int subd); +int comedi_get_n_channels(comedi_t *it,unsigned int subdevice); +lsampl_t comedi_get_maxdata(comedi_t *it,unsigned int subdevice,unsigned int chan); +int comedi_get_rangetype(comedi_t *it,unsigned int subdevice,unsigned int chan); +comedi_range * comedi_get_range(comedi_t *it,unsigned int subdevice,unsigned int chan,unsigned int range); +int comedi_find_range(comedi_t *it,unsigned int subd,unsigned int chan,unsigned int unit,double min,double max); +int comedi_get_n_ranges(comedi_t *it,unsigned int subdevice,unsigned int chan); + +/* triggers and commands */ + +int comedi_cancel(comedi_t *it,unsigned int subdevice); +int comedi_trigger(comedi_t *it,comedi_trig *trig); +int comedi_command(comedi_t *it,comedi_cmd *cmd); + +/* physical units */ + +double comedi_to_phys(lsampl_t data,comedi_range *rng,lsampl_t maxdata); +lsampl_t comedi_from_phys(double data,comedi_range *rng,lsampl_t maxdata); + +/* synchronous stuff */ + +int comedi_data_read(comedi_t *it,unsigned int subd,unsigned int chan,unsigned int range, + unsigned int aref,lsampl_t *data); +int comedi_data_write(comedi_t *it,unsigned int subd,unsigned int chan,unsigned int range, + unsigned int aref,lsampl_t data); + +/* slowly varying stuff */ + +int comedi_sv_init(comedi_sv_t *it,comedi_t *dev,unsigned int subd,unsigned int chan); +int comedi_sv_update(comedi_sv_t *it); +int comedi_sv_measure(comedi_sv_t *it,double *data); + +/* dio config */ + +int comedi_dio_config(comedi_t *it,unsigned int subd,unsigned int chan,unsigned int dir); +int comedi_dio_read(comedi_t *it,unsigned int subd,unsigned int chan,unsigned int *bit); +int comedi_dio_write(comedi_t *it,unsigned int subd,unsigned int chan,unsigned int bit); +int comedi_dio_bitfield(comedi_t *it,unsigned int subd,unsigned int write_mask, + unsigned int *bits); + +/* timer stuff */ + +int comedi_get_timer(comedi_t *it,unsigned int subdev,double freq,unsigned int *trigvar, + double *actual_freq); + +int comedi_timed_1chan(comedi_t *it,unsigned int subdev,unsigned int chan,unsigned int range, + unsigned int aref,double freq,unsigned int n_samples,double *data); + + +/* + The following prototypes are ALPHA, indicating that they might change + in future releases of comedilib, without regard to backward compatibility. + */ + +enum comedi_oor_behavior { + COMEDI_OOR_NUMBER = 0, + COMEDI_OOR_NAN +}; + +enum comedi_oor_behavior comedi_set_global_oor_behavior(enum comedi_oor_behavior behavior); + + + + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/lib/Makefile b/lib/Makefile new file mode 100644 index 0000000..99af04d --- /dev/null +++ b/lib/Makefile @@ -0,0 +1,16 @@ + +CFLAGS=-fPIC -Wall -O2 -I../include -I. + +OBJS=comedi.o timer.o sv.o range.o ioctl.o filler.o timed.o error.o \ + dio.o data.o get.o + +libcomedi.a: $(OBJS) + #gcc -shared -Wl,-soname,libcomedi.so,-T,version_script -o libcomedi.so.${VERSION_CODE} $(OBJS) -lm + gcc -shared -Wl,-soname,libcomedi.so -o libcomedi.so.${VERSION_CODE} $(OBJS) -lm + ar rs libcomedi.a $(OBJS) + ln -sf libcomedi.so.${VERSION_CODE} libcomedi.so + +clean: + rm -f libcomedi.a libcomedi.so* *.o + + diff --git a/lib/comedi.c b/lib/comedi.c new file mode 100644 index 0000000..f4d7aa5 --- /dev/null +++ b/lib/comedi.c @@ -0,0 +1,142 @@ +/* + lib/comedi.c + comedi library routines + + COMEDI - Linux Control and Measurement Device Interface + Copyright (C) 1997-8 David A. Schleef + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +int __comedi_init=0; + +void initialize(void) +{ + char *s; + + __comedi_init=1; + + if( (s=getenv("COMEDILIB_LOGLEVEL")) ){ + sscanf(s,"%d",&__comedi_loglevel); + fprintf(stderr,"setting loglevel to %d\n",__comedi_loglevel); + } +} + +comedi_t *comedi_open(const char *fn) +{ + comedi_t *it; + + if(!__comedi_init) + initialize(); + + if(!(it=malloc(sizeof(comedi_t)))) + goto cleanup; + memset(it,0,sizeof(comedi_t)); + + if((it->fd=open(fn,O_RDWR))<0){ + libc_error(); + goto cleanup; + } + + if(ioctl_devinfo(it->fd,&it->devinfo)<0) + goto cleanup; + + it->n_subdevices=it->devinfo.n_subdevs; + + get_subdevices(it); + + it->magic=COMEDILIB_MAGIC; + + return it; +cleanup: + if(it) + free(it); + + return NULL; +} + +#if 0 +/* this is an example of how we do versioned symbols */ +__asm__(".symver comedi_open_0,comedi_open@"); +#endif + +void comedi_close(comedi_t *it) +{ + it->magic=0; + + /* XXX should free all memory */ + + close(it->fd); +} + +int comedi_cancel(comedi_t *it,unsigned int subdevice) +{ + return ioctl(it->fd,COMEDI_CANCEL,subdevice); +} + +int comedi_poll(comedi_t *it,unsigned int subdevice) +{ +#ifdef HAVE_COMEDI_POLL + return ioctl(it->fd,COMEDI_POLL,subdevice); +#else + return -1; +#endif +} + +int comedi_fileno(comedi_t *it) +{ + if(!it) + return -1; + + return it->fd; +} + +int comedi_trigger(comedi_t *it,comedi_trig *t) +{ + if(!it || !t) + return -1; + + return ioctl_trigger(it->fd,t); +} + +int comedi_command(comedi_t *it,comedi_cmd *t) +{ +#ifdef HAVE_COMEDI_CMD + if(!it || !t) + return -1; + + return ioctl_cmd(it->fd,t); +#else + return -1; +#endif +} + + + diff --git a/lib/data.c b/lib/data.c new file mode 100644 index 0000000..aa4922e --- /dev/null +++ b/lib/data.c @@ -0,0 +1,156 @@ +/* + lib/data.c + comedi library routines + + COMEDI - Linux Control and Measurement Device Interface + Copyright (C) 1997-8 David A. Schleef + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + + + +int comedi_data_write(comedi_t *it,unsigned int subdev,unsigned int chan,unsigned int range, + unsigned int aref,lsampl_t data) +{ + comedi_trig cmd={ + mode: 0, + flags: TRIG_WRITE, + n_chan: 1, + n: 1, + trigsrc: 0, + trigvar: 0, + trigvar1: 0, + }; + sampl_t sdata=data; + + if(!valid_chan(it,subdev,chan)) + return -1; + + chan=CR_PACK(chan,range,aref); + + cmd.subdev=subdev; + if(it->subdevices[subdev].subd_flags & SDF_LSAMPL){ + cmd.data=(sampl_t *)(&data); + }else{ + cmd.data=&sdata; + } + cmd.chanlist=&chan; + + return ioctl_trigger(it->fd,&cmd); +} + + +int comedi_data_read(comedi_t *it,unsigned int subdev,unsigned int chan,unsigned int range, + unsigned int aref,lsampl_t *data) +{ + comedi_trig cmd={ + mode: 0, + flags: 0, + n_chan: 1, + n: 1, + trigsrc: 0, + trigvar: 0, + trigvar1: 0, + }; + int ret; + sampl_t sdata; + + if(!valid_chan(it,subdev,chan)) + return -1; + + chan=CR_PACK(chan,range,aref); + + cmd.subdev=subdev; + cmd.chanlist=&chan; + if(it->subdevices[subdev].subd_flags & SDF_LSAMPL){ + cmd.data=(sampl_t *)data; + }else{ + cmd.data=&sdata; + } + + ret=ioctl_trigger(it->fd,&cmd); + if(ret<0) + return ret; + + if(!(it->subdevices[subdev].subd_flags & SDF_LSAMPL)){ + *data=sdata; + } + + return 0; +} + +#if 1 +/* + I don't like this function, which is why it is marked out. + The problem is the sampl_t/lsampl_t fiasco, which is beginning + to be a PITA. + */ +int comedi_data_read_n(comedi_t *it,unsigned int subdev,unsigned int chan,unsigned int range, + unsigned int aref,unsigned int n,lsampl_t *data) +{ + comedi_trig cmd; + unsigned int i; + int ret; + + if(!valid_chan(it,subdev,chan)) + return -1; + + if(n==0) + return 0; + + chan=CR_PACK(chan,range,aref); + + memset(&cmd,0,sizeof(cmd)); + cmd.mode=0; + cmd.n_chan=1; + cmd.subdev=subdev; + cmd.chanlist=&chan; + + i=0; + while(ifd,&cmd); + if(ret<0) + goto out; + + i+=ret; + } +out: + if(i==0)return -1; + + return (int)i; +} +#endif + + + diff --git a/lib/dio.c b/lib/dio.c new file mode 100644 index 0000000..ac2581c --- /dev/null +++ b/lib/dio.c @@ -0,0 +1,152 @@ +/* + lib/dio.c + comedi library routines + + COMEDI - Linux Control and Measurement Device Interface + Copyright (C) 1997-8 David A. Schleef + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + + +int comedi_dio_config(comedi_t *it,unsigned int subdev,unsigned int chan,unsigned int io) +{ + comedi_trig trig; + lsampl_t data=io; + + if(!valid_chan(it,subdev,chan)) + return -1; + + if(it->subdevices[subdev].type!=COMEDI_SUBD_DIO) + return -1; + + if(io!=COMEDI_INPUT && io!=COMEDI_OUTPUT) + return -1; + + memset(&trig,0,sizeof(trig)); + trig.flags=TRIG_CONFIG|TRIG_WRITE; + trig.n_chan=1; + trig.n=1; + trig.subdev=subdev; + trig.chanlist=&chan; + trig.data=(sampl_t *)&data; + + return ioctl_trigger(it->fd,&trig); +} + +int comedi_dio_read(comedi_t *it,unsigned int subdev,unsigned int chan, + unsigned int *val) +{ + comedi_trig trig; + lsampl_t data; + int ret; + + if(!valid_chan(it,subdev,chan)) + return -1; + + if(it->subdevices[subdev].type!=COMEDI_SUBD_DIO && + it->subdevices[subdev].type!=COMEDI_SUBD_DO && + it->subdevices[subdev].type!=COMEDI_SUBD_DI) + return -1; + + memset(&trig,0,sizeof(trig)); + trig.n_chan=1; + trig.n=1; + trig.subdev=subdev; + trig.chanlist=&chan; + trig.data=(sampl_t *)&data; + + ret=ioctl_trigger(it->fd,&trig); + + if(ret>=0 && val)*val=data; + + return ret; +} + +int comedi_dio_write(comedi_t *it,unsigned int subdev,unsigned int chan, + unsigned int val) +{ + comedi_trig trig; + lsampl_t data; + + if(!valid_chan(it,subdev,chan)) + return -1; + + if(it->subdevices[subdev].type!=COMEDI_SUBD_DIO && + it->subdevices[subdev].type!=COMEDI_SUBD_DO) + return -1; + + data=val; + + memset(&trig,0,sizeof(trig)); + trig.n_chan=1; + trig.n=1; + trig.flags=TRIG_WRITE; + trig.subdev=subdev; + trig.chanlist=&chan; + trig.data=(sampl_t *)&data; + + return ioctl_trigger(it->fd,&trig); +} + +int comedi_dio_bitfield(comedi_t *it,unsigned int subdev,unsigned int mask,unsigned int *bits) +{ + int ret; + unsigned int i,n_chan; + unsigned int m,bit; + subdevice *s; + + if(!valid_subd(it,subdev)) + return -1; + + if(it->subdevices[subdev].type!=COMEDI_SUBD_DIO && + it->subdevices[subdev].type!=COMEDI_SUBD_DO && + it->subdevices[subdev].type!=COMEDI_SUBD_DI) + return -1; + + s=it->subdevices+subdev; + + n_chan=comedi_get_n_channels(it,subdev); + if(n_chan>32)n_chan=32; + for(i=0,m=1;i + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include + +#include +#include +#include + +char *__comedilib_error_strings[]={ + "No error", + "Unknown error", + "Bad comedi_t structure", + "Invalid subdevice", + "Invalid channel", +}; + +int __comedi_loglevel=1; +int __comedi_errno=0; + +int comedi_loglevel(int loglevel) +{ + int old_loglevel=__comedi_loglevel; + + __comedi_loglevel=loglevel; + + return old_loglevel; +} + +int comedi_errno(void) +{ + return __comedi_errno; +} + +char *comedi_strerror(int errnum) +{ + if(errnum=COMEDILIB_NOERROR+sizeof(__comedilib_error_strings) + /sizeof(__comedilib_error_strings[0])) + return strerror(errnum); + + return __comedilib_error_strings[errnum-COMEDILIB_NOERROR]; +} + +void comedi_perror(const char *s) +{ + if(__comedi_loglevel>=3){ + fprintf(stderr,"comedi_perror(): __comedi_errno=%d\n",__comedi_errno); + } + if(!s)s="comedilib"; + fprintf(stderr,"%s: %s\n",s,comedi_strerror(__comedi_errno)); +} + +void libc_error(void) +{ + __comedi_errno=errno; + if(__comedi_loglevel>=2){ + comedi_perror("libc error"); + } +} + +void internal_error(int err) +{ + __comedi_errno=err; + if(__comedi_loglevel>=2){ + comedi_perror("internal error"); + } +} + + + +int valid_dev(comedi_t *it) +{ + if(!it || it->magic!=COMEDILIB_MAGIC){ + internal_error(COMEDILIB_BADDEV); + return 0; + } + + return 1; +} + +int valid_subd(comedi_t *it,unsigned int subd) +{ + if(!valid_dev(it))return 0; + if(subd>=it->n_subdevices){ + internal_error(COMEDILIB_BADSUBD); + return 0; + } + + return 1; +} + +int valid_chan(comedi_t *it,unsigned int subd,unsigned int chan) +{ + if(!valid_subd(it,subd))return 0; + if(chan>=it->subdevices[subd].n_chan){ + internal_error(COMEDILIB_BADCHAN); + return 0; + } + + return 1; +} + + diff --git a/lib/filler.c b/lib/filler.c new file mode 100644 index 0000000..c657fe4 --- /dev/null +++ b/lib/filler.c @@ -0,0 +1,121 @@ +/* + lib/filler.c + comedi library routines + + COMEDI - Linux Control and Measurement Device Interface + Copyright (C) 1997-8 David A. Schleef + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + + +/* these functions download information from the comedi module. */ + + + +int get_subdevices(comedi_t *it) +{ + int i,j; + int ret; + comedi_subdinfo *s; + subdevice *r; + + s=malloc(sizeof(comedi_subdinfo)*it->n_subdevices); + ret=ioctl_subdinfo(it->fd,s); + debug_int(ret); + + r=it->subdevices=realloc(it->subdevices, + sizeof(subdevice)*it->n_subdevices); + debug_ptr(r); + memset(r,0,sizeof(subdevice)*it->n_subdevices); + + for(i=0;in_subdevices;i++){ + r[i].type = s[i].type; + r[i].n_chan = s[i].n_chan; + r[i].subd_flags = s[i].subd_flags; + r[i].timer_type = s[i].timer_type; + r[i].len_chanlist = s[i].len_chanlist; + r[i].maxdata = s[i].maxdata; + r[i].flags = s[i].flags; + r[i].range_type = s[i].range_type; + + if(r[i].subd_flags&SDF_FLAGS){ + r[i].flags_list=malloc(sizeof(*r[i].flags_list)*r[i].n_chan); + debug_ptr(r[i].flags_list); + } + if(r[i].subd_flags&SDF_MAXDATA){ + r[i].maxdata_list=malloc(sizeof(*r[i].maxdata_list)*r[i].n_chan); + debug_ptr(r[i].maxdata_list); + } + if(r[i].subd_flags&SDF_RANGETYPE){ + r[i].range_type_list=malloc(sizeof(*r[i].range_type_list)*r[i].n_chan); + debug_ptr(r[i].range_type_list); + } + ret=ioctl_chaninfo(it->fd,i,r[i].maxdata_list,r[i].flags_list,r[i].range_type_list); + debug_int(ret); + + if(r[i].subd_flags&SDF_RANGETYPE){ + r[i].rangeinfo_list=malloc(sizeof(*r[i].rangeinfo_list)*r[i].n_chan); + debug_ptr(r[i].rangeinfo_list); + for(j=0;jfd,r[i].range_type_list[j]); + } + }else{ + r[i].rangeinfo=get_rangeinfo(it->fd,r[i].range_type); + } + } + + free(s); + + return 0; +} + +comedi_range *get_rangeinfo(int fd,unsigned int range_type) +{ + comedi_krange *kr; + comedi_range *r; + int i; + + kr=malloc(sizeof(comedi_krange)*RANGE_LENGTH(range_type)); + r=malloc(sizeof(comedi_range)*RANGE_LENGTH(range_type)); + + ioctl_rangeinfo(fd,range_type,kr); + + for(i=0;i + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + + +int comedi_get_n_subdevices(comedi_t *it) +{ + if(!valid_dev(it)) + return -1; + + return it->n_subdevices; +} + +int comedi_get_version_code(comedi_t *it) +{ + if(!valid_dev(it)) + return -1; + + return it->devinfo.version_code; +} + +char *comedi_get_driver_name(comedi_t *it) +{ + if(!valid_dev(it)) + return NULL; + + return it->devinfo.driver_name; +} + +char *comedi_get_board_name(comedi_t *it) +{ + if(!valid_dev(it)) + return NULL; + + return it->devinfo.board_name; +} + +int comedi_get_subdevice_type(comedi_t *it,unsigned int subd) +{ + if(!valid_dev(it)) + return -1; + + return it->subdevices[subd].type; +} + +int comedi_find_subdevice_by_type(comedi_t *it,int type,unsigned int subd) +{ + if(!valid_subd(it,subd)) + return -1; + + for(;subdn_subdevices;subd++){ + if(it->subdevices[subd].type==type) + return subd; + } + return -1; +} + + +int comedi_get_n_channels(comedi_t *it,unsigned int subd) +{ + if(!valid_subd(it,subd)) + return -1; + + return it->subdevices[subd].n_chan; +} + + +/* */ + +lsampl_t comedi_get_maxdata(comedi_t *it,unsigned int subdevice,unsigned int chan) +{ + if(!valid_chan(it,subdevice,chan)) + return 0; + + if(it->subdevices[subdevice].maxdata_list) + return it->subdevices[subdevice].maxdata_list[chan]; + + return it->subdevices[subdevice].maxdata; +} + +int comedi_get_rangetype(comedi_t *it,unsigned int subdevice,unsigned int chan) +{ + if(!valid_chan(it,subdevice,chan)) + return -1; + + if(it->subdevices[subdevice].range_type_list) + return it->subdevices[subdevice].range_type_list[chan]; + + return it->subdevices[subdevice].range_type; +} + + +comedi_range * comedi_get_range(comedi_t *it,unsigned int subdevice,unsigned int chan,unsigned int range) +{ + int range_type; + + if(!valid_chan(it,subdevice,chan)) + return NULL; + + range_type=comedi_get_rangetype(it,subdevice,chan); + + if(range>=RANGE_LENGTH(range_type)) + return NULL; + + if(it->subdevices[subdevice].rangeinfo_list) + return it->subdevices[subdevice].rangeinfo_list[chan]+range; + + return it->subdevices[subdevice].rangeinfo+range; +} + + + diff --git a/lib/ioctl.c b/lib/ioctl.c new file mode 100644 index 0000000..781c07a --- /dev/null +++ b/lib/ioctl.c @@ -0,0 +1,100 @@ +/* + lib/ioctl.c + comedi library routines + + COMEDI - Linux Control and Measurement Device Interface + Copyright (C) 1997-8 David A. Schleef + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + + +/* ioctl wrappers */ + +int ioctl_devinfo(int fd,comedi_devinfo *it) +{ + return ioctl(fd,COMEDI_DEVINFO,it); +} + +int ioctl_subdinfo(int fd,comedi_subdinfo *it) +{ + return ioctl(fd,COMEDI_SUBDINFO,it); +} + +int ioctl_chaninfo(int fd,unsigned int subdev,lsampl_t *maxdata_list,unsigned int *flaglist,unsigned int *rangelist) +{ + comedi_chaninfo ci; + + ci.subdev=subdev; + ci.flaglist=flaglist; + ci.rangelist=rangelist; + ci.maxdata_list=maxdata_list; + + return ioctl(fd,COMEDI_CHANINFO,&ci); +} + +int ioctl_trigger(int fd,comedi_trig *it) +{ + return ioctl(fd,COMEDI_TRIG,it); +} + +#ifdef HAVE_COMEDI_CMD +int ioctl_cmd(int fd,comedi_cmd *it) +{ + return ioctl(fd,COMEDI_CMD,it); +} +#endif + +int ioctl_lock(int fd,int subdevice) +{ + return ioctl(fd,COMEDI_LOCK,subdevice); +} + +int ioctl_unlock(int fd,int subdevice) +{ + return ioctl(fd,COMEDI_UNLOCK,subdevice); +} + +int ioctl_cancel(int fd,int subdevice) +{ + return ioctl(fd,COMEDI_CANCEL,subdevice); +} + +int ioctl_rangeinfo(int fd,int range_type,comedi_krange *range_ptr) +{ + comedi_rangeinfo it; + + it.range_type=range_type; + it.range_ptr=range_ptr; + + return ioctl(fd,COMEDI_RANGEINFO,&it); +} + + diff --git a/lib/libinternal.h b/lib/libinternal.h new file mode 100644 index 0000000..89dd314 --- /dev/null +++ b/lib/libinternal.h @@ -0,0 +1,137 @@ +/* + lib/libinternal.h + header file for comedilib internals + + COMEDI - Linux Control and Measurement Device Interface + Copyright (C) 1998 David A. Schleef + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + + +#ifndef _LIBINTERNAL_H +#define _LIBINTERNAL_H + +#include +#include +#include +#include + + +#define debug_ptr(a) if(!(a))fprintf(stderr," ** NULL pointer: " __FILE__ ", line %d\n",__LINE__); +#define debug_int(a) if((a)<0)fprintf(stderr," ** error: " __FILE__ ", line %d\n",__LINE__); + +#define COMEDILIB_MAGIC 0xc001dafe + + +extern int __comedi_init; +extern int __comedi_loglevel; +extern int __comedi_errno; + +#if 0 + +#define libc_error() (__comedi_errno=errno) +#define internal_error(a) (__comedi_errno=(a)) + +#else + +void libc_error(void); +void internal_error(int error_number); + +#endif + + +typedef struct subdevice_struct subdevice; +typedef struct device_struct device; + +struct comedi_t_struct{ + int magic; + + int fd; + int n_subdevices; + + comedi_devinfo devinfo; + + subdevice *subdevices; +}; + +struct subdevice_struct{ + unsigned int type; + unsigned int n_chan; + unsigned int subd_flags; + unsigned int timer_type; + unsigned int len_chanlist; + lsampl_t maxdata; + unsigned int flags; + unsigned int range_type; + + lsampl_t *maxdata_list; + unsigned int *range_type_list; + unsigned int *flags_list; + + comedi_range *rangeinfo; + comedi_range **rangeinfo_list; +}; + + + +/* ioctl wrappers */ + +int ioctl_devinfo(int fd,comedi_devinfo *it); +int ioctl_subdinfo(int fd,comedi_subdinfo *it); +int ioctl_chaninfo(int fd,unsigned int subdev,lsampl_t *maxdata_list, + unsigned int *flaglist,unsigned int *rangelist); +int ioctl_trigger(int fd,comedi_trig *it); +int ioctl_cmd(int fd,comedi_cmd *it); +int ioctl_lock(int fd,int subdevice); +int ioctl_unlock(int fd,int subdevice); +int ioctl_cancel(int fd,int subdevice); +int ioctl_rangeinfo(int fd,int range_type,comedi_krange *range_ptr); + +/* filler routines */ + +int get_subdevices(comedi_t *it); +comedi_range *get_rangeinfo(int fd,unsigned int range_type); + +/* validators */ + +int valid_dev(comedi_t *it); +int valid_subd(comedi_t *it,unsigned int subdevice); +int valid_chan(comedi_t *it,unsigned int subdevice,unsigned int chan); + +enum{ + COMEDILIB_NOERROR = 0x1000, + COMEDILIB_UNKNOWN, + COMEDILIB_BADDEV, + COMEDILIB_BADSUBD, + COMEDILIB_BADCHAN, +}; + + +/* comedi version compatibility */ + +#ifndef TRIG_WRITE +#define TRIG_WRITE 0x0040 +#endif + +#ifdef COMEDI_CMD +#define HAVE_COMEDI_CMD +#endif + + + +#endif + diff --git a/lib/range.c b/lib/range.c new file mode 100644 index 0000000..3452761 --- /dev/null +++ b/lib/range.c @@ -0,0 +1,131 @@ +/* + lib/range.c + comedi library routines for voltage ranges + + COMEDI - Linux Control and Measurement Device Interface + Copyright (C) 1997-8 David A. Schleef + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include + +#define __USE_GNU + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + + +/* sometimes we can't find a definition of NAN */ + +#ifndef NAN +#define NAN \ + (__extension__ ((union { unsigned char __c[8]; \ + double __d; }) \ + { { 0, 0, 0, 0, 0, 0, 0xf8, 0x7f } }).__d) +#endif + + +static enum comedi_oor_behavior comedi_oor_is_nan = COMEDI_OOR_NAN; + +enum comedi_oor_behavior comedi_set_global_oor_behavior( + enum comedi_oor_behavior behavior) +{ + int old_behavior=comedi_oor_is_nan; + + comedi_oor_is_nan=behavior; + + return old_behavior; +} + + +double comedi_to_phys(lsampl_t data,comedi_range *rng,lsampl_t maxdata) +{ + double x; + + if(!rng)return NAN; + if(!maxdata)return NAN; + + if(comedi_oor_is_nan==COMEDI_OOR_NAN && (data==0 || data==maxdata)) + return NAN; + + x=data; + x/=maxdata; + x*=(rng->max-rng->min); + x+=rng->min; + + return x; +} + +lsampl_t comedi_from_phys(double data,comedi_range *rng,lsampl_t maxdata) +{ + double s; + + if(!rng)return 0; + if(!maxdata)return 0; + + s=(data-rng->min)/(rng->max-rng->min)*maxdata; + if(s<0)return 0; + if(s>maxdata)return maxdata; + + return (lsampl_t)(floor(s+0.5)); +} + +int comedi_find_range(comedi_t *it,unsigned int subd,unsigned int chan,unsigned int unit,double min,double max) +{ + unsigned int range_type; + int best; + comedi_range *range_ptr,*best_ptr; + int i; + + if(!valid_chan(it,subd,chan))return -1; + + range_type=comedi_get_rangetype(it,subd,chan); + best=-1; + best_ptr=NULL; + for(i=0;imin<=min && range_ptr->max>=max){ + if(best<0 || (range_ptr->max-range_ptr->min) < + (best_ptr->max-best_ptr->min)){ + best=i; + best_ptr=range_ptr; + } + } + } + return best; +} + +int comedi_get_n_ranges(comedi_t *it,unsigned int subd,unsigned int chan) +{ + unsigned int range_type; + + if(!valid_chan(it,subd,chan))return -1; + + range_type=comedi_get_rangetype(it,subd,chan); + return RANGE_LENGTH(range_type); +} + diff --git a/lib/sv.c b/lib/sv.c new file mode 100644 index 0000000..2145690 --- /dev/null +++ b/lib/sv.c @@ -0,0 +1,188 @@ +/* + lib/sv.c + comedi library routines - sv section + + COMEDI - Linux Control and Measurement Device Interface + Copyright (C) 1997-8 David A. Schleef + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + + + +int sv_measure_l(comedi_sv_t *it,double *data); +int sv_measure_s(comedi_sv_t *it,double *data); + + +int comedi_sv_init(comedi_sv_t *it,comedi_t *dev,unsigned int subd,unsigned int chan) +{ + if(!valid_chan(dev,subd,chan))return -1; + if(!it)return -1; + + memset(it,0,sizeof(*it)); + + it->dev=dev; + it->subdevice=subd; + it->chan=chan; + it->n=100; + + return comedi_sv_update(it); +} + +int comedi_sv_update(comedi_sv_t *it) +{ + if(!it)return -1; + if(!valid_chan(it->dev,it->subdevice,it->chan))return -1; + + it->maxdata=comedi_get_maxdata(it->dev,it->subdevice,it->chan); + + /* check current range policy */ + /* check n */ + + return 0; +} + +int comedi_sv_measure(comedi_sv_t *it,double *data) +{ + if(it->dev->subdevices[it->subdevice].subd_flags & SDF_LSAMPL){ + return sv_measure_l(it,data); + }else{ + return sv_measure_s(it,data); + } +} + +int sv_measure_l(comedi_sv_t *it,double *data) +{ + comedi_trig t; + int ret=0; + lsampl_t *val; + unsigned int chan; + comedi_range *rng; + double sum; + int i; + int n; + + val=malloc(sizeof(*val)*it->n); + + chan=CR_PACK(it->chan,it->range,it->aref); + + t.subdev=it->subdevice; + t.mode=0; + t.flags=TRIG_DITHER; + t.n_chan=1; + t.chanlist=&chan; + t.trigsrc=0; + t.trigvar=0; + t.trigvar1=0; + + rng=comedi_get_range(it->dev,it->subdevice,it->chan,it->range); + + for(n=0;nn;){ + t.data=(void *)(val+n); + t.n=it->n-n; + i=ioctl_trigger(it->dev->fd,&t); + if(i<=0){ + ret=i; + goto out; + } + n+=i; + } + + sum=0; + for(i=0;in;i++){ + sum+=comedi_to_phys(val[i],rng,it->maxdata); + } + *data=sum/it->n; + +out: + free(val); + + return ret; +} + +/* yes, these functions are _almost_ exactly the same... */ + +int sv_measure_s(comedi_sv_t *it,double *data) +{ + comedi_trig t; + int ret=0; + sampl_t *val; + unsigned int chan; + comedi_range *rng; + double sum; + int i; + int n; + + val=malloc(sizeof(*val)*it->n); + + chan=CR_PACK(it->chan,it->range,it->aref); + + t.subdev=it->subdevice; + t.mode=0; + t.flags=TRIG_DITHER; + t.n_chan=1; + t.chanlist=&chan; + t.data=val; + t.n=it->n; + t.trigsrc=0; + t.trigvar=0; + t.trigvar1=0; + + rng=comedi_get_range(it->dev,it->subdevice,it->chan,it->range); + + for(n=0;nn;){ + t.data=val+n; + t.n=it->n-n; + i=ioctl_trigger(it->dev->fd,&t); + if(i<=0){ + ret=i; + goto out; + } + n+=i; + } + + sum=0; + for(i=0;in;i++){ + sum+=comedi_to_phys(val[i],rng,it->maxdata); + } + *data=sum/it->n; + +out: + free(val); + + return ret; +} + + + + + + + diff --git a/lib/timed.c b/lib/timed.c new file mode 100644 index 0000000..432cb2e --- /dev/null +++ b/lib/timed.c @@ -0,0 +1,94 @@ +/* + lib/sv.c + comedi library routines - sv section + + COMEDI - Linux Control and Measurement Device Interface + Copyright (C) 1997-8 David A. Schleef + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#define BUFSZ 100 + +int comedi_timed_1chan(comedi_t *dev,unsigned int subd,unsigned int chan,unsigned int range, + unsigned int aref,double freq,unsigned int n_samples,double *data) +{ + comedi_trig t; + double act_freq; + sampl_t *buffer; + comedi_range *the_range; + unsigned int maxdata; + int i,n,m; + + if(!valid_chan(dev,subd,chan))return -1; + if(!data)return -1; + + memset(&t,0,sizeof(t)); + + /* check range */ + + chan=CR_PACK(chan,range,aref); + + t.subdev=subd; + t.mode=2; + t.n_chan=1; + t.chanlist=&chan; + t.n=n_samples; + comedi_get_timer(dev,subd,freq,&t.trigvar,&act_freq); + t.trigvar1=1; + + the_range=comedi_get_range(dev,subd,chan,range); + maxdata=comedi_get_maxdata(dev,subd,chan); + + buffer=malloc(sizeof(sampl_t)*BUFSZ); + if(!buffer)return -1; + + comedi_trigger(dev,&t); + n=0; + while(nBUFSZ)m=BUFSZ; + if((m=read(dev->fd,buffer,m*sizeof(sampl_t)))<0){ + /* ack! */ + return -1; + } + m/=sizeof(sampl_t); + for(i=0;i + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + + + +/* dt282x timer */ + +static int dt282x_timer(double freq,unsigned int *trigvar,double *actual_freq) +{ + int divider,prescaler; + double basefreq=4e6; + + divider=floor(4e6/(freq)); + prescaler=0; + while(divider>255){ + prescaler++; + divider>>=1; + basefreq/=2; + } + if(prescaler==1){ + prescaler++; + divider>>=1; + basefreq/=2; + } + if(prescaler>=16)return -1; + *trigvar=(prescaler<<8)|(255-divider); + *actual_freq=basefreq/divider; + + return 0; +} + + +/* dt2814 timer */ +static int dt2814_timer(double freq,unsigned int *trigvar,double *actual_freq) +{ + double f; + int i; + + f=1e5; + for(i=0;i<8;i++){ + if(f-freq32767){ + prescaler*=2; + divider>>=1; + } + + *trigvar = (prescaler<<16) | divider; + *actual_freq=basefreq/(divider*prescaler); + + return 0; +} + +/* nanosec timer */ +static int nanosec_timer(double freq,unsigned int *trigvar,double *actual_freq) +{ + *trigvar=(1e9/freq); + *actual_freq=1e9/(*trigvar); + + return 0; +} + +typedef int (*timerfunc)(double freq,unsigned int *trigvar,double *actual_freq); + +static timerfunc timer_functions[]={ + NULL, + dt282x_timer, + dt2814_timer, + atmio_timer, + acl8112_timer, + nanosec_timer, +}; +#define N_TIMERTYPES 6 + +int comedi_get_timer(comedi_t *it,unsigned int subdev,double freq, + unsigned int *trigvar,double *actual_freq) +{ + int timer_type; + + if(!it || !trigvar || !actual_freq) + return -1; + + timer_type=it->subdevices[subdev].timer_type; + + if(timer_type==0 || timer_type>=N_TIMERTYPES) + return -1; + + return (timer_functions[timer_type])(freq,trigvar,actual_freq); +} + diff --git a/lib/version_script b/lib/version_script new file mode 100644 index 0000000..9b2c0a7 --- /dev/null +++ b/lib/version_script @@ -0,0 +1,11 @@ + +VERSION{ + +v0.7.0{ + global: + comedi_*; + local: + *; +}; + +} diff --git a/perl/Changes b/perl/Changes new file mode 100644 index 0000000..0b2cf7f --- /dev/null +++ b/perl/Changes @@ -0,0 +1,7 @@ +Revision history for Perl extension Comedi. + +0.01 Tue Nov 9 01:17:02 1999 + - original version; created by h2xs 1.18 + +0.02 Tue Nov 16 11:49:11 EST 1999 + - (Comedi::Lib only) track changes to comedilib 0.7.6 (jes) diff --git a/perl/Comedi.pm b/perl/Comedi.pm new file mode 100644 index 0000000..87e64b5 --- /dev/null +++ b/perl/Comedi.pm @@ -0,0 +1,289 @@ +# Copyright (c) 1999 Joseph E. Smith +# +# See the 'COPYRIGHT' section below for complete copyright information. + +package Comedi; + +use strict; +use Carp; +use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS $AUTOLOAD); + +require Exporter; +require DynaLoader; +require AutoLoader; + +@ISA = qw(Exporter DynaLoader); + +%EXPORT_TAGS = ( + + Constants => [ qw( + AREF_COMMON + AREF_DIFF + AREF_GROUND + AREF_OTHER + COMEDI_INPUT + COMEDI_MAJOR + COMEDI_NAMELEN + COMEDI_NDEVCONFOPTS + COMEDI_NDEVICES + COMEDI_OUTPUT + ) ], + + Ioctls => [ qw( + COMEDI_CANCEL + COMEDI_CHANINFO + COMEDI_DEVCONFIG + COMEDI_DEVINFO + COMEDI_LOCK + COMEDI_RANGEINFO + COMEDI_SUBDINFO + COMEDI_TRIG + COMEDI_UNLOCK + ) ], + + SubdeviceTypes => [ qw( + COMEDI_SUBD_AI + COMEDI_SUBD_AO + COMEDI_SUBD_CALIB + COMEDI_SUBD_COUNTER + COMEDI_SUBD_DI + COMEDI_SUBD_DIO + COMEDI_SUBD_DO + COMEDI_SUBD_MEMORY + COMEDI_SUBD_PROC + COMEDI_SUBD_TIMER + COMEDI_SUBD_UNUSED + ) ], + + SubdeviceFlags => [ qw( + SDF_BUSY + SDF_BUSY_OWNER + SDF_COMMON + SDF_DEGLITCH + SDF_DIFF + SDF_DITHER + SDF_FLAGS + SDF_GROUND + SDF_INTERNAL + SDF_LOCKED + SDF_LOCK_OWNER + SDF_LSAMPL + SDF_MAXDATA + SDF_MMAP + SDF_MODE0 + SDF_MODE1 + SDF_MODE2 + SDF_MODE3 + SDF_MODE4 + SDF_OTHER + SDF_RANGETYPE + SDF_READABLE + SDF_RT + SDF_RUNNING + SDF_WRITEABLE + ) ], + + TriggerFlags => [ qw ( + TRIG_BOGUS + TRIG_CONFIG + TRIG_DEGLITCH + TRIG_DITHER + TRIG_RT + TRIG_WAKE_EOS + TRIG_WRITE + ) ], + + Units => [ qw ( + UNIT_mA + UNIT_none + UNIT_volt + ) ], + + Functions => [ qw( + CR_PACK + ) ], +); + + + +# Items to export into callers namespace by default. Note: do not export +# names by default without a very good reason. Use EXPORT_OK instead. +# Do not simply export all your public functions/methods/constants. +@EXPORT = (); + +@EXPORT_OK = ( + @{$EXPORT_TAGS{'Functions'}}, + @{$EXPORT_TAGS{'Constants'}}, + @{$EXPORT_TAGS{'Ioctls'}}, + @{$EXPORT_TAGS{'SubdeviceTypes'}}, + @{$EXPORT_TAGS{'SubdeviceFlags'}}, + @{$EXPORT_TAGS{'TriggerFlags'}}, + @{$EXPORT_TAGS{'Units'}}, +); + +$VERSION = '0.02'; + +sub AUTOLOAD { + # This AUTOLOAD is used to 'autoload' constants from the constant() + # XS function. If a constant is not found then control is passed + # to the AUTOLOAD in AutoLoader. + + my $constname; + ($constname = $AUTOLOAD) =~ s/.*:://; + my $val = constant($constname, @_ ? $_[0] : 0); + if ($! != 0) { + if ($! =~ /Invalid/) { + $AutoLoader::AUTOLOAD = $AUTOLOAD; + goto &AutoLoader::AUTOLOAD; + } + else { + croak "Your vendor has not defined Comedi macro $constname"; + } + } + eval "sub $AUTOLOAD { $val }"; + goto &$AUTOLOAD; +} + +bootstrap Comedi $VERSION; + +# Preloaded methods go here. + +sub CR_PACK { + my ($chan, $rng, $aref) = @_; + + return ( ((($aref)&0x3)<<24) | ((($rng)&0xff)<<16) | (($chan)&0xffff) ); +} + + +# Autoload methods go after =cut, and are processed by the autosplit program. + +1; +__END__ + +=head1 NAME + +Comedi - Perl extension for data acquisition using comedi + +=head1 SYNOPSIS + +use Comedi qw( :Functions :Constants :Ioctls :SubdeviceTypes :SubdeviceFlags :TriggerFlags :Units ); + +=head1 DESCRIPTION + +The B module provides constants and data types for using the comedi data acquisition module. + +By itself, this module will allow you to access the B module using standard I/O functions and ioctls. +For more robust access to B, see the B module. + +=head1 Exported constants + +The large number of constants defined in C are divided into +several functional categories for export. No constants are exported +by default. + +=item Constants + +AREF_COMMON +AREF_DIFF +AREF_GROUND +AREF_OTHER +COMEDI_INPUT +COMEDI_MAJOR +COMEDI_NAMELEN +COMEDI_NDEVCONFOPTS +COMEDI_NDEVICES +COMEDI_OUTPUT + +=item Ioctls + +COMEDI_CANCEL +COMEDI_CHANINFO +COMEDI_DEVCONFIG +COMEDI_DEVINFO +COMEDI_LOCK +COMEDI_RANGEINFO +COMEDI_SUBDINFO +COMEDI_TRIG +COMEDI_UNLOCK + +=item SubdeviceTypes + +COMEDI_SUBD_AI +COMEDI_SUBD_AO +COMEDI_SUBD_CALIB +COMEDI_SUBD_COUNTER +COMEDI_SUBD_DI +COMEDI_SUBD_DIO +COMEDI_SUBD_DO +COMEDI_SUBD_MEMORY +COMEDI_SUBD_PROC +COMEDI_SUBD_TIMER +COMEDI_SUBD_UNUSED + +=item SubdeviceFlags + +SDF_BUSY +SDF_BUSY_OWNER +SDF_COMMON +SDF_DEGLITCH +SDF_DIFF +SDF_DITHER +SDF_FLAGS +SDF_GROUND +SDF_INTERNAL +SDF_LOCKED +SDF_LOCK_OWNER +SDF_LSAMPL +SDF_MAXDATA +SDF_MMAP +SDF_MODE0 +SDF_MODE1 +SDF_MODE2 +SDF_MODE3 +SDF_MODE4 +SDF_OTHER +SDF_RANGETYPE +SDF_READABLE +SDF_RT +SDF_RUNNING +SDF_WRITEABLE + +=item TriggerFlags + +TRIG_BOGUS +TRIG_CONFIG +TRIG_DEGLITCH +TRIG_DITHER +TRIG_RT +TRIG_WAKE_EOS +TRIG_WRITE + +=item Units + +UNIT_mA +UNIT_none +UNIT_volt + +=item Functions + +CR_PACK + +=head1 VERSION + +Version 0.01 09-Nov-1999 + +=head1 AUTHOR + +Joe Smith >. + +=head1 COPYRIGHT + +Copyright (c) 1999 Joseph E. Smith. All rights reserved. This +program is free software. You may redistribute it and/or modify it +under the same terms as Perl itself. + +=head1 SEE ALSO + +Comedi::Trigger(1), Comedi::Lib(1). + +=cut diff --git a/perl/Comedi.xs b/perl/Comedi.xs new file mode 100644 index 0000000..6b3862b --- /dev/null +++ b/perl/Comedi.xs @@ -0,0 +1,621 @@ +/* + * Copyright (c) 1999 Joseph E. Smith + * + * All rights reserved. This program is free software. You may + * redistribute it and/or modify it under the same terms as Perl itself. + */ + +#ifdef __cplusplus +extern "C" { +#endif +#include "EXTERN.h" +#include "perl.h" +#include "XSUB.h" +#ifdef __cplusplus +} +#endif + +#include + +static int +not_here(s) +char *s; +{ + croak("%s not implemented on this architecture", s); + return -1; +} + +static double +constant(name, arg) +char *name; +int arg; +{ + errno = 0; + switch (*name) { + case 'A': + if (strEQ(name, "AREF_COMMON")) +#ifdef AREF_COMMON + return AREF_COMMON; +#else + goto not_there; +#endif + if (strEQ(name, "AREF_DIFF")) +#ifdef AREF_DIFF + return AREF_DIFF; +#else + goto not_there; +#endif + if (strEQ(name, "AREF_GROUND")) +#ifdef AREF_GROUND + return AREF_GROUND; +#else + goto not_there; +#endif + if (strEQ(name, "AREF_OTHER")) +#ifdef AREF_OTHER + return AREF_OTHER; +#else + goto not_there; +#endif + break; + case 'B': + break; + case 'C': + if (strEQ(name, "CIO")) +#ifdef CIO + return CIO; +#else + goto not_there; +#endif + if (strEQ(name, "COMEDI_CANCEL")) +#ifdef COMEDI_CANCEL + return COMEDI_CANCEL; +#else + goto not_there; +#endif + if (strEQ(name, "COMEDI_CB_BLOCK")) +#ifdef COMEDI_CB_BLOCK + return COMEDI_CB_BLOCK; +#else + goto not_there; +#endif + if (strEQ(name, "COMEDI_CB_EOA")) +#ifdef COMEDI_CB_EOA + return COMEDI_CB_EOA; +#else + goto not_there; +#endif + if (strEQ(name, "COMEDI_CB_EOBUF")) +#ifdef COMEDI_CB_EOBUF + return COMEDI_CB_EOBUF; +#else + goto not_there; +#endif + if (strEQ(name, "COMEDI_CB_EOS")) +#ifdef COMEDI_CB_EOS + return COMEDI_CB_EOS; +#else + goto not_there; +#endif + if (strEQ(name, "COMEDI_CHANINFO")) +#ifdef COMEDI_CHANINFO + return COMEDI_CHANINFO; +#else + goto not_there; +#endif + if (strEQ(name, "COMEDI_CMD")) +#ifdef COMEDI_CMD + return COMEDI_CMD; +#else + goto not_there; +#endif + if (strEQ(name, "COMEDI_DEVCONFIG")) +#ifdef COMEDI_DEVCONFIG + return COMEDI_DEVCONFIG; +#else + goto not_there; +#endif + if (strEQ(name, "COMEDI_DEVINFO")) +#ifdef COMEDI_DEVINFO + return COMEDI_DEVINFO; +#else + goto not_there; +#endif + if (strEQ(name, "COMEDI_INPUT")) +#ifdef COMEDI_INPUT + return COMEDI_INPUT; +#else + goto not_there; +#endif + if (strEQ(name, "COMEDI_LOCK")) +#ifdef COMEDI_LOCK + return COMEDI_LOCK; +#else + goto not_there; +#endif + if (strEQ(name, "COMEDI_MAJOR")) +#ifdef COMEDI_MAJOR + return COMEDI_MAJOR; +#else + goto not_there; +#endif + if (strEQ(name, "COMEDI_NAMELEN")) +#ifdef COMEDI_NAMELEN + return COMEDI_NAMELEN; +#else + goto not_there; +#endif + if (strEQ(name, "COMEDI_NDEVCONFOPTS")) +#ifdef COMEDI_NDEVCONFOPTS + return COMEDI_NDEVCONFOPTS; +#else + goto not_there; +#endif + if (strEQ(name, "COMEDI_NDEVICES")) +#ifdef COMEDI_NDEVICES + return COMEDI_NDEVICES; +#else + goto not_there; +#endif + if (strEQ(name, "COMEDI_OUTPUT")) +#ifdef COMEDI_OUTPUT + return COMEDI_OUTPUT; +#else + goto not_there; +#endif + if (strEQ(name, "COMEDI_RANGEINFO")) +#ifdef COMEDI_RANGEINFO + return COMEDI_RANGEINFO; +#else + goto not_there; +#endif + if (strEQ(name, "COMEDI_SUBDINFO")) +#ifdef COMEDI_SUBDINFO + return COMEDI_SUBDINFO; +#else + goto not_there; +#endif + if (strEQ(name, "COMEDI_SUBD_AI")) +#ifdef COMEDI_SUBD_AI + return COMEDI_SUBD_AI; +#else + goto not_there; +#endif + if (strEQ(name, "COMEDI_SUBD_AO")) +#ifdef COMEDI_SUBD_AO + return COMEDI_SUBD_AO; +#else + goto not_there; +#endif + if (strEQ(name, "COMEDI_SUBD_CALIB")) +#ifdef COMEDI_SUBD_CALIB + return COMEDI_SUBD_CALIB; +#else + goto not_there; +#endif + if (strEQ(name, "COMEDI_SUBD_COUNTER")) +#ifdef COMEDI_SUBD_COUNTER + return COMEDI_SUBD_COUNTER; +#else + goto not_there; +#endif + if (strEQ(name, "COMEDI_SUBD_DI")) +#ifdef COMEDI_SUBD_DI + return COMEDI_SUBD_DI; +#else + goto not_there; +#endif + if (strEQ(name, "COMEDI_SUBD_DIO")) +#ifdef COMEDI_SUBD_DIO + return COMEDI_SUBD_DIO; +#else + goto not_there; +#endif + if (strEQ(name, "COMEDI_SUBD_DO")) +#ifdef COMEDI_SUBD_DO + return COMEDI_SUBD_DO; +#else + goto not_there; +#endif + if (strEQ(name, "COMEDI_SUBD_MEMORY")) +#ifdef COMEDI_SUBD_MEMORY + return COMEDI_SUBD_MEMORY; +#else + goto not_there; +#endif + if (strEQ(name, "COMEDI_SUBD_PROC")) +#ifdef COMEDI_SUBD_PROC + return COMEDI_SUBD_PROC; +#else + goto not_there; +#endif + if (strEQ(name, "COMEDI_SUBD_TIMER")) +#ifdef COMEDI_SUBD_TIMER + return COMEDI_SUBD_TIMER; +#else + goto not_there; +#endif + if (strEQ(name, "COMEDI_SUBD_UNUSED")) +#ifdef COMEDI_SUBD_UNUSED + return COMEDI_SUBD_UNUSED; +#else + goto not_there; +#endif + if (strEQ(name, "COMEDI_TRIG")) +#ifdef COMEDI_TRIG + return COMEDI_TRIG; +#else + goto not_there; +#endif + if (strEQ(name, "COMEDI_UNLOCK")) +#ifdef COMEDI_UNLOCK + return COMEDI_UNLOCK; +#else + goto not_there; +#endif + break; + case 'D': + break; + case 'E': + break; + case 'F': + break; + case 'G': + break; + case 'H': + break; + case 'I': + break; + case 'J': + break; + case 'K': + break; + case 'L': + break; + case 'M': + break; + case 'N': + break; + case 'O': + break; + case 'P': + break; + case 'Q': + break; + case 'R': + if (strEQ(name, "RF_EXTERNAL")) +#ifdef RF_EXTERNAL + return RF_EXTERNAL; +#else + goto not_there; +#endif + break; + case 'S': + if (strEQ(name, "SDF_BUSY")) +#ifdef SDF_BUSY + return SDF_BUSY; +#else + goto not_there; +#endif + if (strEQ(name, "SDF_BUSY_OWNER")) +#ifdef SDF_BUSY_OWNER + return SDF_BUSY_OWNER; +#else + goto not_there; +#endif + if (strEQ(name, "SDF_COMMON")) +#ifdef SDF_COMMON + return SDF_COMMON; +#else + goto not_there; +#endif + if (strEQ(name, "SDF_DEGLITCH")) +#ifdef SDF_DEGLITCH + return SDF_DEGLITCH; +#else + goto not_there; +#endif + if (strEQ(name, "SDF_DIFF")) +#ifdef SDF_DIFF + return SDF_DIFF; +#else + goto not_there; +#endif + if (strEQ(name, "SDF_DITHER")) +#ifdef SDF_DITHER + return SDF_DITHER; +#else + goto not_there; +#endif + if (strEQ(name, "SDF_FLAGS")) +#ifdef SDF_FLAGS + return SDF_FLAGS; +#else + goto not_there; +#endif + if (strEQ(name, "SDF_GROUND")) +#ifdef SDF_GROUND + return SDF_GROUND; +#else + goto not_there; +#endif + if (strEQ(name, "SDF_INTERNAL")) +#ifdef SDF_INTERNAL + return SDF_INTERNAL; +#else + goto not_there; +#endif + if (strEQ(name, "SDF_LOCKED")) +#ifdef SDF_LOCKED + return SDF_LOCKED; +#else + goto not_there; +#endif + if (strEQ(name, "SDF_LOCK_OWNER")) +#ifdef SDF_LOCK_OWNER + return SDF_LOCK_OWNER; +#else + goto not_there; +#endif + if (strEQ(name, "SDF_LSAMPL")) +#ifdef SDF_LSAMPL + return SDF_LSAMPL; +#else + goto not_there; +#endif + if (strEQ(name, "SDF_MAXDATA")) +#ifdef SDF_MAXDATA + return SDF_MAXDATA; +#else + goto not_there; +#endif + if (strEQ(name, "SDF_MMAP")) +#ifdef SDF_MMAP + return SDF_MMAP; +#else + goto not_there; +#endif + if (strEQ(name, "SDF_MODE0")) +#ifdef SDF_MODE0 + return SDF_MODE0; +#else + goto not_there; +#endif + if (strEQ(name, "SDF_MODE1")) +#ifdef SDF_MODE1 + return SDF_MODE1; +#else + goto not_there; +#endif + if (strEQ(name, "SDF_MODE2")) +#ifdef SDF_MODE2 + return SDF_MODE2; +#else + goto not_there; +#endif + if (strEQ(name, "SDF_MODE3")) +#ifdef SDF_MODE3 + return SDF_MODE3; +#else + goto not_there; +#endif + if (strEQ(name, "SDF_MODE4")) +#ifdef SDF_MODE4 + return SDF_MODE4; +#else + goto not_there; +#endif + if (strEQ(name, "SDF_OTHER")) +#ifdef SDF_OTHER + return SDF_OTHER; +#else + goto not_there; +#endif + if (strEQ(name, "SDF_RANGETYPE")) +#ifdef SDF_RANGETYPE + return SDF_RANGETYPE; +#else + goto not_there; +#endif + if (strEQ(name, "SDF_READABLE")) +#ifdef SDF_READABLE + return SDF_READABLE; +#else + goto not_there; +#endif + if (strEQ(name, "SDF_RT")) +#ifdef SDF_RT + return SDF_RT; +#else + goto not_there; +#endif + if (strEQ(name, "SDF_RUNNING")) +#ifdef SDF_RUNNING + return SDF_RUNNING; +#else + goto not_there; +#endif + if (strEQ(name, "SDF_WRITEABLE")) +#ifdef SDF_WRITEABLE + return SDF_WRITEABLE; +#else + goto not_there; +#endif + break; + case 'T': + if (strEQ(name, "TRIG_ANY")) +#ifdef TRIG_ANY + return TRIG_ANY; +#else + goto not_there; +#endif + if (strEQ(name, "TRIG_BOGUS")) +#ifdef TRIG_BOGUS + return TRIG_BOGUS; +#else + goto not_there; +#endif + if (strEQ(name, "TRIG_CONFIG")) +#ifdef TRIG_CONFIG + return TRIG_CONFIG; +#else + goto not_there; +#endif + if (strEQ(name, "TRIG_COUNT")) +#ifdef TRIG_COUNT + return TRIG_COUNT; +#else + goto not_there; +#endif + if (strEQ(name, "TRIG_DEGLITCH")) +#ifdef TRIG_DEGLITCH + return TRIG_DEGLITCH; +#else + goto not_there; +#endif + if (strEQ(name, "TRIG_DITHER")) +#ifdef TRIG_DITHER + return TRIG_DITHER; +#else + goto not_there; +#endif + if (strEQ(name, "TRIG_EXT")) +#ifdef TRIG_EXT + return TRIG_EXT; +#else + goto not_there; +#endif + if (strEQ(name, "TRIG_FOLLOW")) +#ifdef TRIG_FOLLOW + return TRIG_FOLLOW; +#else + goto not_there; +#endif + if (strEQ(name, "TRIG_INT")) +#ifdef TRIG_INT + return TRIG_INT; +#else + goto not_there; +#endif + if (strEQ(name, "TRIG_NONE")) +#ifdef TRIG_NONE + return TRIG_NONE; +#else + goto not_there; +#endif + if (strEQ(name, "TRIG_NOW")) +#ifdef TRIG_NOW + return TRIG_NOW; +#else + goto not_there; +#endif + if (strEQ(name, "TRIG_RT")) +#ifdef TRIG_RT + return TRIG_RT; +#else + goto not_there; +#endif + if (strEQ(name, "TRIG_TIME")) +#ifdef TRIG_TIME + return TRIG_TIME; +#else + goto not_there; +#endif + if (strEQ(name, "TRIG_TIMER")) +#ifdef TRIG_TIMER + return TRIG_TIMER; +#else + goto not_there; +#endif + if (strEQ(name, "TRIG_WAKE_EOS")) +#ifdef TRIG_WAKE_EOS + return TRIG_WAKE_EOS; +#else + goto not_there; +#endif + if (strEQ(name, "TRIG_WRITE")) +#ifdef TRIG_WRITE + return TRIG_WRITE; +#else + goto not_there; +#endif + break; + case 'U': + if (strEQ(name, "UNIT_mA")) +#ifdef UNIT_mA + return UNIT_mA; +#else + goto not_there; +#endif + if (strEQ(name, "UNIT_none")) +#ifdef UNIT_none + return UNIT_none; +#else + goto not_there; +#endif + if (strEQ(name, "UNIT_volt")) +#ifdef UNIT_volt + return UNIT_volt; +#else + goto not_there; +#endif + break; + case 'V': + break; + case 'W': + break; + case 'X': + break; + case 'Y': + break; + case 'Z': + break; + } + errno = EINVAL; + return 0; + +not_there: + errno = ENOENT; + return 0; +} + + +typedef struct comedi_trig_struct Trigger; + + +MODULE = Comedi PACKAGE = Comedi + + +double +constant(name,arg) + char * name + int arg + + + + +MODULE = Comedi PACKAGE = TriggerPtr PREFIX = tptr_ + +# create a new trigger +# +Trigger * +tptr_new(CLASS, subd, n) + char *CLASS + int subd + int n + CODE: + RETVAL = (Trigger *) malloc(sizeof(Trigger)); + RETVAL->subdev = subd; + RETVAL->n = n; + fprintf(stderr, "new Trigger at 0x%08x: sub=%d, n=%d\n", RETVAL, RETVAL->subdev, RETVAL->n); + OUTPUT: + RETVAL + +# destruct a trigger +# +void +tptr_DESTROY(self) + Trigger *self + CODE: + fprintf(stderr, "destroy Trigger at 0x%08x\n", self); + free((void *) self); diff --git a/perl/INSTALL b/perl/INSTALL new file mode 100644 index 0000000..fc86237 --- /dev/null +++ b/perl/INSTALL @@ -0,0 +1,11 @@ + + perl Makefile.PL + make + make install + +Yow! Who'da guessed? + +You can add '-DLIB_DEBUG' to Makefile.PL for a little extra verbosity. + +You can run the examples before 'make install' using the 'lperl' script. + diff --git a/perl/Lib/Changes b/perl/Lib/Changes new file mode 100644 index 0000000..f2c3788 --- /dev/null +++ b/perl/Lib/Changes @@ -0,0 +1,7 @@ +Revision history for Perl extension Comedi::Lib. + +0.01 Tue Nov 9 01:17:02 1999 + - original version; created by h2xs 1.18 + +0.02 Tue Nov 16 11:49:11 EST 1999 + - (Lib.xs) track changes to comedilib 0.7.6 (jes) diff --git a/perl/Lib/Lib.pm b/perl/Lib/Lib.pm new file mode 100644 index 0000000..0beac74 --- /dev/null +++ b/perl/Lib/Lib.pm @@ -0,0 +1,350 @@ +# Copyright (c) 1999 Joseph E. Smith +# +# See the 'COPYRIGHT' section below for complete copyright information. + +package Comedi::Lib; + +use strict; +use Carp; +use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS $AUTOLOAD); + +require Exporter; +require DynaLoader; +require AutoLoader; + +@ISA = qw(Exporter DynaLoader); + +%EXPORT_TAGS = ( + + Functions => [ qw( + comedi_open + comedi_close + comedi_loglevel + comedi_error + comedi_perror + comedi_strerror + comedi_errno + comedi_fileno + comedi_get_n_subdevices + comedi_get_version + comedi_get_version_code + comedi_get_driver_name + comedi_get_board_name + comedi_get_subdevice_type + comedi_find_subdevice_by_type + comedi_get_n_channels + comedi_get_maxdata + comedi_get_rangetype + comedi_get_range + comedi_find_range + comedi_trigger + comedi_to_phys + comedi_from_phys + comedi_data_read + comedi_data_write + comedi_sv_init + comedi_sv_update + comedi_sv_measure + comedi_get_timer + comedi_timed_1chan + ) ], +); + +# Items to export into callers namespace by default. Note: do not export +# names by default without a very good reason. Use EXPORT_OK instead. +# Do not simply export all your public functions/methods/constants. + +@EXPORT = ( @{$EXPORT_TAGS{'Functions'}}); + +$VERSION = '0.02'; + +sub AUTOLOAD { + # This AUTOLOAD is used to 'autoload' constants from the constant() + # XS function. If a constant is not found then control is passed + # to the AUTOLOAD in AutoLoader. + + my $constname; + ($constname = $AUTOLOAD) =~ s/.*:://; + my $val = constant($constname, @_ ? $_[0] : 0); + if ($! != 0) { + if ($! =~ /Invalid/) { + $AutoLoader::AUTOLOAD = $AUTOLOAD; + goto &AutoLoader::AUTOLOAD; + } + else { + croak "Your vendor has not defined Comedilib macro $constname"; + } + } + eval "sub $AUTOLOAD { $val }"; + goto &$AUTOLOAD; +} + +bootstrap Comedi::Lib $VERSION; + +# Preloaded methods go here. + +sub comedi_get_version { + my $it = shift || return (); + my $code = comedi_get_version_code($it); + + return (($code & 0xff0000) >> 16, + ($code & 0x00ff00) >> 8, + ($code & 0x0000ff)); +} + +sub comedi_error { + my $err = shift || comedi_errno(); + + return comedi_strerror($err); +} + +sub comedi_trigger { + my ($d, $t) = @_; + + if (ref($t) eq 'Comedi::Trigger') { + _comedi_trigger($d, $t->{struct}); + } + else { + _comedi_trigger($d, $t); + } +} + +# Autoload methods go after =cut, and are processed by the autosplit program. + +1; +__END__ +# Below is the stub of documentation for your module. You better edit it! + +=head1 NAME + +Comedi::Lib - Perl API for B + +=head1 SYNOPSIS + + use Comedi::Lib; + +=head1 DESCRIPTION + + + +=head2 Core functions + +=item comedi_open + + $dev = comedi_open('/dev/comedi0'); + +=item comedi_close + + comedi_close($dev); + +=item comedi_loglevel + + $l = comedi_loglevel(4); + +=item comedi_perror + + comedi_perror('Oops!'); + +=item comedi_strerror + + $s = comedi_strerror($errno); + +=item comedi_errno + + $errno = comedi_errno(); + +=item comedi_error + + $s = comedi_error(); # get current error string + $s = comedi_error($errno); # same as comedi_strerror + +=item comedi_fileno + + $fd = comedi_fileno($dev); + + + + +=head2 Query functions + +=item comedi_get_n_subdevices + + $n = comedi_get_n_subdevices($dev); + +=item comedi_get_version_code + + $n = comedi_get_version_code($dev); + +=item comedi_get_version + + ($major, $minor, $rev) = comedi_get_version(); + +=item comedi_get_driver_name + + $s = comedi_get_driver_name($dev); + +=item comedi_get_board_name + + $s = comedi_get_board_name($dev); + +=item comedi_get_subdevice_type + + $n = comedi_get_subdevice_type($dev, $subd); + +=item comedi_find_subdevice_by_type + + $n = comedi_find_subdevice_by_type($dev, $type, $subd) + +=item comedi_get_n_channels + + $n = comedi_get_n_channels($dev, $subd); + +=item comedi_get_maxdata + + $n = comedi_get_maxdata($dev, $subd, $channel); + +=item comedi_get_rangetype + + $n = comedi_get_rangetype($dev, $subd, $channel); + +=item comedi_find_range + + $n = comedi_find_range($dev, $subd, $unit, $min, $max); + + + + + +=head2 Trigger and command functions + +=item comedi_cancel + + $n = comedi_cancel($dev, $subd) + +=item comedi_trigger + +=item _comedi_trigger + + $t_obj = new Comedi::Trigger(mode => 2, n => 100, ...); + $ret = comedi_trigger($dev, $t_obj); + + # or equivalently... + $ret = _comedi_trigger($dev, $t_obj->struct); + +=item comedi_command + + $n = comedi_command($dev, $cmd) + +This function is untested. +B<$cmd> is a scalar holding a packed C structure. + + + + +=head2 Functions dealing with physical units + +=item comedi_to_phys + + $v = comedi_to_phys($sample, $range, $maxdata); + +=item comedi_from_phys + + $n = comedi_from_phys($v, $range, $maxdata); + + + +=head2 Synchronous i/o functions + +=item comedi_data_read + + $n = comedi_data_read($dev, $subd, $range, $aref, $data); + +=item comedi_data_write + + $n = comedi_data_write($dev, $subd, $chan, $range, $aref, $data) + + + + +=head2 Slowly-varying i/o functions + +=item comedi_sv_init + + $n = comedi_sv_init($svdev, $dev, $subd, $chan); + +=item comedi_sv_update + + $n = comedi_sv_update($svdev); + +=item comedi_sv_measure + + $n = comedi_sv_measure($svdev, $v); + + + + +=head2 Digital i/o functions + +=item comedi_dio_config + + $n = comedi_dio_config($dev, $subd, $chan, $dir) + +=item comedi_dio_read + + $n = comedi_dio_read($dev, $subd, $chan, $bit) + +=item comedi_dio_write + + $n = comedi_dio_write($dev, $subd, $chan, $bit) + +=item comedi_dio_bitfield + + $n = comedi_dio_bitfield($dev, $subd, $mask, $bits) + + + + + +=head2 Timer functions + +=item comedi_get_timer + + $n = comedi_get_timer($dev, $subd, $freq, $ticks, $actual_freq); + +=item comedi_timed_1chan + + $n = comedi_timed_1chan($dev, $subd, $chan, $range, $aref, $freq, $n, $vdata); + + + + + +=head2 Range functions + +=item comedi_get_range + + $range = comedi_get_range($dev, $subd, $channel, $range_num); + + + + + +=head1 VERSION + +Version 0.01 09-Nov-1999 + +=head1 AUTHOR + +Joe Smith >. + +=head1 COPYRIGHT + +Copyright (c) 1999 Joseph E. Smith. All rights reserved. This +program is free software. You may redistribute it and/or modify it +under the same terms as Perl itself. + +=head1 SEE ALSO + +Comedi(1), Comedi::Trigger(1). + +=cut diff --git a/perl/Lib/Lib.xs b/perl/Lib/Lib.xs new file mode 100644 index 0000000..8c3908d --- /dev/null +++ b/perl/Lib/Lib.xs @@ -0,0 +1,312 @@ +/* + * Copyright (c) 1999 Joseph E. Smith + * + * All rights reserved. This program is free software. You may + * redistribute it and/or modify it under the same terms as Perl itself. + */ + +#ifdef __cplusplus +extern "C" { +#endif +#include "EXTERN.h" +#include "perl.h" +#include "XSUB.h" +#ifdef __cplusplus +} +#endif + +#include +#include + +static int +not_here(s) +char *s; +{ + croak("%s not implemented on this architecture", s); + return -1; +} + +static double +constant(name, arg) +char *name; +int arg; +{ + errno = 0; + switch (*name) { + } + errno = EINVAL; + return 0; + +not_there: + errno = ENOENT; + return 0; +} + +typedef struct comedi_trig_struct Trigger; + +MODULE = Comedi::Lib PACKAGE = Comedi::Lib + + +double +constant(name,arg) + char * name + int arg + +comedi_t * +comedi_open(fn) + const char * fn + +void +comedi_close(it) + comedi_t * it + +int +comedi_loglevel(level) + int level + +void +comedi_perror(s) + const char * s + +char * +comedi_strerror(n) + int n + +int +comedi_errno() + +int +comedi_fileno(it) + comedi_t * it + +#/* queries */ + +int +comedi_get_n_subdevices(it) + comedi_t * it + +int +comedi_get_version_code(it) + comedi_t * it + +char * +comedi_get_driver_name(it) + comedi_t * it + +char * +comedi_get_board_name(it) + comedi_t * it + +int +comedi_get_subdevice_type(it, subdevice) + comedi_t * it + unsigned int subdevice + +int +comedi_find_subdevice_by_type(it, type, subd) + comedi_t * it + int type + unsigned int subd + +int +comedi_get_n_channels(it, subdevice) + comedi_t * it + unsigned int subdevice + +lsampl_t +comedi_get_maxdata(it, subdevice, chan) + comedi_t * it + unsigned int subdevice + unsigned int chan + +int +comedi_get_rangetype(it, subdevice, chan) + comedi_t * it + unsigned int subdevice + unsigned int chan + +int +comedi_find_range(it, subd, chan, unit, min, max) + comedi_t * it + unsigned int subd + unsigned int chan + unsigned int unit + double min + double max + +#/* triggers and commands */ + +int +comedi_cancel(it, subd) + comedi_t * it + unsigned int subd + +#/* +# * This function takes a scalar holding a packed comedi trigger structure. +# * The 'comedi_trigger' function (found in Lib.pm) takes a Perl trigger +# * object and passes a packed structure to this function. +# */ + +int +_comedi_trigger(it, trig) + comedi_t * it + comedi_trig * trig + PREINIT: + int i; + CODE: +#ifdef LIB_DEBUG + fprintf(stderr, "trigger: it=%08x, trig=%08x\n", it, trig); + fprintf(stderr, "trigger: mode=%d, n=%d, tv=(%d,%d), nch=%d\n", + trig->mode, trig->n, trig->trigvar, trig->trigvar1, trig->n_chan); + fprintf(stderr, "chan list @%08x:", trig->chanlist); + for(i = 0; i < trig->n_chan; ++i) { + fprintf(stderr, " %08x", trig->chanlist[i]); + } + fprintf(stderr, "\n"); + fprintf(stderr, "data buffer @%08x:", trig->data); + for(i = 0; i < 6; ++i) { + fprintf(stderr, " %04x", trig->data[i]); + } + fprintf(stderr, "\n"); +#endif + RETVAL = comedi_trigger(it, trig); + OUTPUT: + RETVAL + +int +comedi_command(it, cmd) + comedi_t * it + comedi_cmd * cmd + +#/* physical units */ + +double +comedi_to_phys(data, rng, maxdata) + lsampl_t data + comedi_range * rng + lsampl_t maxdata + +lsampl_t +comedi_from_phys(data, rng, maxdata) + double data + comedi_range * rng + lsampl_t maxdata + +#/* synchronous stuff */ + +int +comedi_data_read(it, subd, chan, range, aref, data) + comedi_t * it + unsigned int subd + unsigned int chan + unsigned int range + unsigned int aref + lsampl_t * data + + +int +comedi_data_write(it, subd, chan, range, aref, data) + comedi_t * it + unsigned int subd + unsigned int chan + unsigned int range + unsigned int aref + lsampl_t data + +#/* slowly varying stuff */ + +int +comedi_sv_init(it, dev, subd, chan) + comedi_sv_t * it + comedi_t * dev + unsigned int subd + unsigned int chan + OUTPUT: + it + +int +comedi_sv_update(it) + comedi_sv_t * it + +int +comedi_sv_measure(it, data) + comedi_sv_t * it + double * data + +#/* dio config */ + +int +comedi_dio_config(it, subd, chan, dir) + comedi_t * it + unsigned int subd + unsigned int chan + unsigned int dir + +int +comedi_dio_read(it, subd, chan, bit) + comedi_t * it + unsigned int subd + unsigned int chan + unsigned int &bit + OUTPUT: + bit + +int +comedi_dio_write(it, subd, chan, bit) + comedi_t * it + unsigned int subd + unsigned int chan + unsigned int bit + +int +comedi_dio_bitfield(it, subd, write_mask, bits) + comedi_t * it + unsigned int subd + unsigned int write_mask + unsigned int &bits + OUTPUT: + bits + +#/* timer stuff */ + +int +comedi_get_timer(it, subdev, freq, trigvar, actual_freq) + comedi_t * it + unsigned int subdev + double freq + unsigned int &trigvar + double &actual_freq + OUTPUT: + trigvar + actual_freq + +int +comedi_timed_1chan(it, subdev, chan, range, aref, freq, n_samples, data) + comedi_t * it + unsigned int subdev + unsigned int chan + unsigned int range + unsigned int aref + double freq + unsigned int n_samples + double * data + CODE: +#ifdef LIB_DEBUG + fprintf(stderr, "timed_1chan: CR=(%d,%d,%d), f=%g, n=%d, data->0x%08x\n", + chan, range, aref, freq, n_samples, data); +#endif + RETVAL = comedi_timed_1chan(it, subdev, chan, range, aref, freq, n_samples, data); + OUTPUT: + RETVAL + + + +#/* range stuff */ + +comedi_range * +comedi_get_range(it, subdevice, chan, range) + comedi_t * it + unsigned int subdevice + unsigned int chan + unsigned int range + diff --git a/perl/Lib/Makefile.PL b/perl/Lib/Makefile.PL new file mode 100644 index 0000000..9cc83f5 --- /dev/null +++ b/perl/Lib/Makefile.PL @@ -0,0 +1,13 @@ +use ExtUtils::MakeMaker; + +# See lib/ExtUtils/MakeMaker.pm for details of how to influence +# the contents of the Makefile that is written. + +WriteMakefile( + 'NAME' => 'Comedi::Lib', + 'VERSION_FROM' => 'Lib.pm', + 'LIBS' => ['-lcomedi'], + 'DEFINE' => '', + 'INC' => '', + 'TYPEMAPS' => ['../typemap'], +); diff --git a/perl/MANIFEST b/perl/MANIFEST new file mode 100644 index 0000000..cfdd7a7 --- /dev/null +++ b/perl/MANIFEST @@ -0,0 +1,24 @@ +README +INSTALL +MANIFEST +Changes +Makefile.PL +Comedi.pm +Comedi.xs +test.pl +typemap +lperl +info.perl +example.perl +mode1.perl +mode2.perl +mode2lib.perl +Trigger/Makefile.PL +Trigger/Trigger.pm +Trigger/test.pl +Trigger/Changes +Lib/Makefile.PL +Lib/Lib.pm +Lib/Lib.xs +Lib/Changes + diff --git a/perl/Makefile.PL b/perl/Makefile.PL new file mode 100644 index 0000000..6c81b2e --- /dev/null +++ b/perl/Makefile.PL @@ -0,0 +1,12 @@ +use ExtUtils::MakeMaker; +# See lib/ExtUtils/MakeMaker.pm for details of how to influence +# the contents of the Makefile that is written. +WriteMakefile( + 'NAME' => 'Comedi', + 'VERSION_FROM' => 'Comedi.pm', + 'LIBS' => [], + 'DEFINE' => '', + 'INC' => '', + + 'dist' => { COMPRESS=>"gzip", SUFFIX=>"gz" }, +); diff --git a/perl/README b/perl/README new file mode 100644 index 0000000..8da8e5a --- /dev/null +++ b/perl/README @@ -0,0 +1,30 @@ + +Comedi and Comedi::Lib: A Perl API for comedi and comedilib + +Version 0.02 16-Nov-1999 + +This is a first pass at a Perl extension for using comedi and comedilib +(http://stm.lbl.gov/comedi). + +This is also my first attempt at a Perl extension, so be warned! I +started with h2xs and hacked away at it from there. Please pass the +clues if you're an XS wizard. + +There are some example scripts but no tests (yet). You can run the +examples before actually installing the modules (make install) using +the little script 'lperl', e.g., + + [jes@rtc Comedi]$ ./lperl example.perl + +The modules and examples were tested with comedi 0.7.{19,26} and +comedilib 0.7.{3,5,6}, using an NI-MIO-16e. There should be no +board-specific stuff here (except possibly the subdevice codes); +please let me know if it (does|doesn't) work with other boards. + +There is very little object oriented code here, and what is here (a +Trigger object) need not be used. Right now, I'm not anxious to add +another layer over comedi, which is still alpha. Any ideas or +suggestions are welcome, however. + + +Joe Smith diff --git a/perl/Trigger/Changes b/perl/Trigger/Changes new file mode 100644 index 0000000..ad7f9b4 --- /dev/null +++ b/perl/Trigger/Changes @@ -0,0 +1,5 @@ +Revision history for Perl extension Comedi::Trigger. + +0.01 Tue Nov 9 10:18:34 1999 + - original version; created by h2xs 1.18 + diff --git a/perl/Trigger/Makefile.PL b/perl/Trigger/Makefile.PL new file mode 100644 index 0000000..fb3497b --- /dev/null +++ b/perl/Trigger/Makefile.PL @@ -0,0 +1,9 @@ +use ExtUtils::MakeMaker; + +# See lib/ExtUtils/MakeMaker.pm for details of how to influence +# the contents of the Makefile that is written. + +WriteMakefile( + 'NAME' => 'Comedi::Trigger', + 'VERSION_FROM' => 'Trigger.pm', +); diff --git a/perl/Trigger/Trigger.pm b/perl/Trigger/Trigger.pm new file mode 100644 index 0000000..aa8d3e1 --- /dev/null +++ b/perl/Trigger/Trigger.pm @@ -0,0 +1,234 @@ +# Copyright (c) 1999 Joseph E. Smith +# +# See the 'COPYRIGHT' section below for complete copyright information. + +package Comedi::Trigger; + +use strict; +use vars qw($VERSION $AUTOLOAD @ISA @EXPORT @EXPORT_OK); + +use Carp; + +require Exporter; +require AutoLoader; + +@ISA = qw(Exporter AutoLoader); + +# Nothing exported by default; all access through Trigger object + +@EXPORT; + +$VERSION = '0.01'; + + +# Preloaded methods go here. + +sub new { + my $class = shift; + my $self = bless {}; + + while (@_) { + my ($member, $value) = (shift, shift); + + $self->$member($value); + } + + return $self->pack; +} + +sub pack { + my $self = shift; + my $struct = $self->_pack; + + $self->{'struct'} = $struct; + + return $struct ? $self : undef; +} + +sub _pack { + my $self = shift; + + my $chl = $self->{'chanlist'} || return undef; + + my $n_chan = (exists($self->{'n_chan'}) ? + $self->{'n_chan'} : @$chl) || return undef; + + # keep these buffers in the object so they don't go away + # + $self->{'_chanlist'} = pack('L*', @{$self->{'chanlist'}}); + + my ($n, $buf); + + # n can be zero + # + if (exists($self->{'n'}) && exists($self->{'data'})) { + $n = $self->{'n'}; + $buf = $self->{'data'}; + } + elsif (exists($self->{'n'})) { + $n = $self->{'n'}; + $self->{'_data'} = "\0" x ($n * $n_chan * 2); + $buf = $self->{'data'} = \$self->{'_data'}; + } + elsif (exists($self->{'data'})) { + $buf = $self->{'data'}; + $n = length($$buf)/(2*$n_chan); + } + else { + return undef; + } + + # TODO: more error-checking (return undef) + # + # minimal sanity checks + # + croak("Data buffer is not a scalar reference") + unless ref($buf) eq 'SCALAR'; + croak("Sample count (2*$n) too large for buffer (" . + length($$buf) . ")") + unless ((2*$n) <= length $$buf); + + return pack('L4 p2 L5 L3', + $self->{'subdev'} || 0, + $self->{'mode'} || 0, + $self->{'flags'} || 0, + $n_chan, + $self->{'_chanlist'}, + $$buf, + $n, + $self->{'trigsrc'} || $self->{'source'}, + $self->{'trigvar'} || $self->{'major'}, + $self->{'trigvar1'} || $self->{'minor'}, + length $$buf, + 0, 0, 0); +} + + +sub AUTOLOAD { + my $self = shift; + ref($self) || croak "$self is not an object"; + + (my $name = $AUTOLOAD) =~ s/.*://; # strip leading class name(s) + + return @_ ? $self->{$name} = shift : $self->{$name}; +} + +# Autoload methods go after =cut, and are processed by the autosplit program. + +1; +__END__ +# Below is the stub of documentation for your module. You better edit it! + +=head1 NAME + +Comedi::Trigger - Object oriented interface to comedi trigger struct + +=head1 SYNOPSIS + + use Comedi::Trigger; + + $t = new Comedi::Trigger; + + $t->mode(2); + $t->chanlist([ CR_PACK(0,0,AREF_GROUND), CR_PACK(1,0,AREF_GROUND) ]); + $t->n(100); + $t->major(1999); + $t->minor(20); + + $ret = comedi_trigger($dev, $t); + +=head1 DESCRIPTION + +This module provides an object oriented interface to B trigger +structures. It provides some convenience over Cing one by hand: +you need only specify a minimum of information, the rest will be +calculated if possible. + +=head1 METHODS + +Each element of the B trigger structure is available as a +method which either retrieves or sets the value of that element. In +addition, some of the elements have aliases which may be used to make +the programmer's intention clear. + +The following methods function analogously to the C struct elements: + +=over 4 + +=item subdev + +=item mode + +=item flags + +=item n_chan + +=item n + +=back + +These elements function analogously, but have aliases: + +=over 4 + +=item trigsrc or source + +=item trigvar or major + +=item trigvar1 or minor + +=back + +These elements function somewhat differently than the C implementation: + +=over 4 + +=item chanlist + +This should be a reference to a Perl list of channel descriptors +packed using B (see the example above). + +If the number of channels (B)is not specified, it will be set +to the number of elements in this list. + +=item data + +This should be a scalar, pre-allocated to a length large enough to +hold the requested data. If no value is set, a scalar will be created +of the appropriate size. + +=item data_len + +This element should be set to the size of the data buffer (in bytes). +If not specified, it will be set automatically. + +=back + +This method is specific to the Perl module. + +=item pack + +This method packs the current state of the trigger elements into a +binary form compatible with the C C. This method +is normally called automatically by B functions that take +trigger arguments. + +=head1 VERSION + +Version 0.01 09-Nov-1999 + +=head1 AUTHOR + +Joe Smith >. + +=head1 COPYRIGHT + +Copyright (c) 1999 Joseph E. Smith. All rights reserved. This +program is free software. You may redistribute it and/or modify it +under the same terms as Perl itself. + +=head1 SEE ALSO + +Comedi(1), Comedi::Lib(1). + +=cut diff --git a/perl/Trigger/test.pl b/perl/Trigger/test.pl new file mode 100644 index 0000000..52a701f --- /dev/null +++ b/perl/Trigger/test.pl @@ -0,0 +1,20 @@ +# Before `make install' is performed this script should be runnable with +# `make test'. After `make install' it should work as `perl test.pl' + +######################### We start with some black magic to print on failure. + +# Change 1..1 below to 1..last_test_to_print . +# (It may become useful if the test is moved to ./t subdirectory.) + +BEGIN { $| = 1; print "1..1\n"; } +END {print "not ok 1\n" unless $loaded;} +use Comedi::Trigger; +$loaded = 1; +print "ok 1\n"; + +######################### End of black magic. + +# Insert your test code below (better if it prints "ok 13" +# (correspondingly "not ok 13") depending on the success of chunk 13 +# of the test code): + diff --git a/perl/example.perl b/perl/example.perl new file mode 100644 index 0000000..dea28ed --- /dev/null +++ b/perl/example.perl @@ -0,0 +1,41 @@ + +# A little input demo + +use constant N_SAMPLES => 64; + +use Comedi qw( :Functions :Constants :Ioctls ) ; + +# allocate a data buffer + +# create a channel list +$ch = pack('L*', CR_PACK(0, 0, AREF_GROUND)); + +# create a data buffer +$buf = pack('S' . N_SAMPLES); + +# create a trigger object +# +$it = pack('L4 p2 L5 L3', + 0, + 0, + 0, + 1, + $ch, + $buf, + N_SAMPLES, + 0, + 0, + 0 + ); + +$fn = '/dev/comedi0'; + +sysopen(FILE, $fn, O_RDWR) || die "Can't open '$fn': $!"; + +ioctl(FILE, COMEDI_TRIG, $it) || die "ioctl failed: $!"; + +foreach (unpack('S*', $buf)) { + printf("%d\n", $_); +} + + diff --git a/perl/info.perl b/perl/info.perl new file mode 100644 index 0000000..1e4dc92 --- /dev/null +++ b/perl/info.perl @@ -0,0 +1,44 @@ + +use Comedi qw( :SubdeviceTypes ); +use Comedi::Lib; + +sub help { + print STDERR "info \n"; + exit(0); +} + +%subdevice_types = ( + COMEDI_SUBD_UNUSED, 'unused', + COMEDI_SUBD_AI, 'analog input', + COMEDI_SUBD_AO, 'analog output', + COMEDI_SUBD_DI, 'digital input', + COMEDI_SUBD_DO, 'digital output', + COMEDI_SUBD_DIO, 'digital I/O', + COMEDI_SUBD_COUNTER, 'counter', + COMEDI_SUBD_TIMER, 'timer', + COMEDI_SUBD_MEMORY, 'memory', + COMEDI_SUBD_CALIB, 'calibration', + COMEDI_SUBD_PROC, 'processor', +); + +# +# Main Program +# + +help() if (@ARGV != 1); + +$it = comedi_open($ARGV[0]) || die "cannot open $ARGV[0]: $!"; + +printf("overall info:\n"); +printf(" version code: %d.%d.%d (0x%06x)\n", comedi_get_version($it), comedi_get_version_code($it)); +printf(" driver name: %s\n", comedi_get_driver_name($it)); +printf(" board name: %s\n", comedi_get_board_name($it)); +printf(" number of subdevices: %d\n", $n_subdevices = comedi_get_n_subdevices($it)); + +for ($i = 0; $i < $n_subdevices; $i++) { + printf("subdevice %d:\n", $i); + $type = comedi_get_subdevice_type($it, $i); + printf(" type: %d (%s)\n", $type, $subdevice_types{$type}); + printf(" number of channels: %d\n", comedi_get_n_channels($it, $i)); + printf(" max data value: %d\n", comedi_get_maxdata($it, $i, 0)); +} diff --git a/perl/lperl b/perl/lperl new file mode 100755 index 0000000..612dfc3 --- /dev/null +++ b/perl/lperl @@ -0,0 +1,3 @@ +#!/bin/sh + +PERL_DL_NONLAZY=1 /usr/local/bin/perl -I./blib/arch -I./blib/lib -I/usr/lib/perl5/i386-linux/5.00404 -I/usr/lib/perl5 "$@" diff --git a/perl/mode1.perl b/perl/mode1.perl new file mode 100644 index 0000000..67016ec --- /dev/null +++ b/perl/mode1.perl @@ -0,0 +1,54 @@ + +# A little input demo + +use Comedi::Trigger; + +use Comedilib qw( :DEFAULT :Constants :Ioctls ) ; + +use constant ( + AI_DEV => 0, + AI_SUB => 0, + AO_DEV => 0, + AO_SUB => 1, +); + +$fn = '/dev/comedi0'; + +# create a channel list +# +@ch = ( CR_PACK(0, 0, AREF_GROUND), + CR_PACK(1, 0, AREF_GROUND) ); + +$d = comedi_open($fn) || die "Can't open '$fn': " . comedi_error(); + +$freq = $opt_f || 1000; + +comedi_loglevel(4); + +# convert the requested frequency into a timer value +# +($ret = comedi_get_timer($d, AI_SUB, $freq, $ticks, $actual_freq)) == 0 || + die "Can't get timer: " . comedi_strerror($ret); + +$buf = pack('S*', 100..119); + +# create a trigger object +# +$it = new Comedi::Trigger( + mode => 1, + chanlist => \@ch, + n => 20, + data => \$buf, + major => $ticks); + + +die "Mode 1 doesn't work yet..."; + +($ret = comedi_trigger($d, $it)) > 0 || + die "Analog input error: " . comedi_strerror(comedi_errno()) ; + +foreach (unpack('S*', ${$it->data})) { + printf("%d\n", $_); +} + + diff --git a/perl/mode2.perl b/perl/mode2.perl new file mode 100644 index 0000000..8cb6bb4 --- /dev/null +++ b/perl/mode2.perl @@ -0,0 +1,57 @@ + +# A little input demo + +use Comedi qw( :Functions :Constants :Ioctls ) ; +use Comedi::Trigger; +use Comedi::Lib; + +use constant AI_DEV => 0; +use constant AI_SUB => 0; +use constant AO_DEV => 0; +use constant AO_SUB => 1; + +use constant N_SAMPLES => 100; + +$fn = '/dev/comedi0'; + +# create a channel list +# +@ch = ( CR_PACK(0, 0, AREF_GROUND), + CR_PACK(1, 0, AREF_GROUND) ); + +$d = comedi_open($fn) || die "Can't open '$fn': " . comedi_error(); + +$fd = comedi_fileno($d); + +open(COMDEV, "<&$fd") || die "Can't get filehandle from fd $fd: $!"; + +$freq = $opt_f || 1000; + +comedi_loglevel(4); + +# convert the requested frequency into a timer value +# +($ret = comedi_get_timer($d, AI_SUB, $freq, $ticks, $actual_freq)) == 0 || + die "Can't get timer: " . comedi_strerror($ret); + +$buf = pack('S*', 100..(100+N_SAMPLES-1)); + +# create a trigger object +# +$it = new Comedi::Trigger( + mode => 2, + chanlist => \@ch, + n => 20, + data => \$buf, + major => $ticks, + minor => 20); + + +die "Analog input trigger error ($ret): " . comedi_strerror(comedi_errno()) + if (($ret = comedi_trigger($d, $it)) < 0); + +($ret = sysread(COMDEV, $buf, 2*N_SAMPLES)) || die "Read error ($ret): $!"; + +foreach (unpack('s*', $buf)) { + printf("%d\n", $_); +} diff --git a/perl/mode2lib.perl b/perl/mode2lib.perl new file mode 100644 index 0000000..e32be8a --- /dev/null +++ b/perl/mode2lib.perl @@ -0,0 +1,38 @@ + +# A little input demo + +use Comedi qw( :Functions :Constants ); +use Comedi::Lib; + +use constant AI_DEV => 0; +use constant AI_SUB => 0; +use constant AO_DEV => 0; +use constant AO_SUB => 1; +use constant N_SAMPLES => 100; + +$fn = '/dev/comedi0'; + +# create a channel list +# +@ch = ( CR_PACK(0, 0, AREF_GROUND)); + +$d = comedi_open($fn) || die "Can't open '$fn': " . comedi_error(); + +$freq = $opt_f || 1000; + +# convert the requested frequency into a timer value +# +($ret = comedi_get_timer($d, AI_SUB, $freq, $ticks, $actual_freq)) == 0 || + die "Can't get timer: " . comedi_strerror($ret); + +$buf = pack('d*', 100..(100+N_SAMPLES-1)); + +($ret = + comedi_timed_1chan($d, AI_SUB, 0, 0, AREF_GROUND, 1000, N_SAMPLES, $buf)) == 0 || + die "Analog input error ($ret): " . comedi_strerror(comedi_errno()) ; + +foreach (unpack('d*', $buf)) { + printf("%g\n", $_); +} + + diff --git a/perl/test.pl b/perl/test.pl new file mode 100644 index 0000000..c5f1e2c --- /dev/null +++ b/perl/test.pl @@ -0,0 +1,20 @@ +# Before `make install' is performed this script should be runnable with +# `make test'. After `make install' it should work as `perl test.pl' + +######################### We start with some black magic to print on failure. + +# Change 1..1 below to 1..last_test_to_print . +# (It may become useful if the test is moved to ./t subdirectory.) + +BEGIN { $| = 1; print "1..1\n"; } +END {print "not ok 1\n" unless $loaded;} +use Comedi; +$loaded = 1; +print "ok 1\n"; + +######################### End of black magic. + +# Insert your test code below (better if it prints "ok 13" +# (correspondingly "not ok 13") depending on the success of chunk 13 +# of the test code): + diff --git a/perl/typemap b/perl/typemap new file mode 100644 index 0000000..2f4d7b6 --- /dev/null +++ b/perl/typemap @@ -0,0 +1,21 @@ +TYPEMAP +const char * T_PV + +lsampl_t T_U_INT +sampl_t T_U_SHORT + +comedi_t * T_PTRREF +comedi_sv_t * T_PTRREF +comedi_range * T_PTRREF + +# these are buffers: perl scalar holding binary data +# +comedi_trig * T_PV +comedi_cmd * T_PV +sampl_t * T_PV +lsampl_t * T_PV +double * T_PV +buffer_t * T_PV + +Trigger * T_PTROBJ + diff --git a/testing/Makefile b/testing/Makefile new file mode 100644 index 0000000..2c04b65 --- /dev/null +++ b/testing/Makefile @@ -0,0 +1,19 @@ + + + +CFLAGS +=-I ../include -I . -O2 -Wall +LDFLAGS=-L../lib/ -lcomedi + + +TARG=comedi_test +OBJS=main.o mode1.o + +all: $(TARG) + +$(TARG): $(OBJS) + $(CC) -o $(TARG) $(OBJS) $(LDFLAGS) + +clean: + -rm $(TARG) $(OBJS) + +distclean: clean diff --git a/testing/inpn.c b/testing/inpn.c new file mode 100644 index 0000000..4268121 --- /dev/null +++ b/testing/inpn.c @@ -0,0 +1,71 @@ +/* + This demo opens /dev/comedi0 and looks for an analog input + subdevice. If it finds one, it measures one sample on each + channel for each input range. The value NaN indicates that + the measurement was out of range. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +extern int verbose_flag; +extern int subdevice; +extern int range; +extern int channel; +extern int aref; +extern char *filename; + +comedi_t *device; + + +int main(int argc, char *argv[]) +{ + int n_subdevs; + int n_chans,chan; + int n_ranges; + int range; + int rangetype; + int maxdata; + lsampl_t data; + double voltage; + + parse_options(argc,argv); + + device=comedi_open(filename); + if(!device){ + comedi_perror(filename); + exit(0); + } + + subdevice=comedi_find_subdevice_by_type(device,COMEDI_SUBD_AI,0); + if(subdevice<0){ + printf("no analog input subdevice found\n"); + exit(0); + } + + n_chans=comedi_get_n_channels(device,subdevice); + for(chan=0;chan +#include +#include +#include +#include +#include +#include +#include +#include + + +char *filename="/dev/comedi0"; +int verbose_flag; +comedi_t *device; + +int subdevice; +int channel; +int aref; +int range; + + +int parse_options(int argc, char *argv[]) +{ + int c; + + + while (1) { + c = getopt(argc, argv, "acsrfvdgom"); + if (c == -1) + break; + switch (c) { + case 'f': + filename = argv[optind]; + break; + case 's': + sscanf(argv[optind],"%d",&subdevice); + break; + case 'c': + sscanf(argv[optind],"%d",&channel); + break; + case 'a': + sscanf(argv[optind],"%d",&aref); + break; + case 'r': + sscanf(argv[optind],"%d",&range); + break; + case 'v': + verbose_flag = 1; + break; + case 'd': + aref=AREF_DIFF; + break; + case 'g': + aref=AREF_GROUND; + break; + case 'o': + aref=AREF_OTHER; + break; + case 'm': + aref=AREF_COMMON; + break; + default: + printf("bad option\n"); + exit(1); + } + } + + return argc; +} + + + diff --git a/testing/mode1.c b/testing/mode1.c new file mode 100644 index 0000000..6c7752c --- /dev/null +++ b/testing/mode1.c @@ -0,0 +1,149 @@ +/* + A little input demo for mode 1 + + Mode 1 uses a timer to acquire samples at regular intervals. + It scans through the channel list, and then repeats. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define BUFSZ 1000 +#define N_CHANS 1000 + +extern int verbose_flag; +extern int subdevice; +extern int range; +extern int channel; +extern int aref; +extern char *filename; + +comedi_t *device; + +void parse_options(int argc,char *argv[]); + +sampl_t data[BUFSZ]; + +unsigned int chanlist[N_CHANS]; + +int ai_mode1_test(double freq,int n_chans,int n_scans); + +int main(int argc, char *argv[]) +{ + parse_options(argc,argv); + + device=comedi_open(filename); + if(!device){ + comedi_perror(filename); + exit(0); + } + + subdevice=comedi_find_subdevice_by_type(device,COMEDI_SUBD_AI,0); + if(subdevice<0){ + printf("no analog input subdevice found\n"); + exit(0); + } + +while(1){ + printf("Testing mode 1:\n"); + + printf("10 hz, 1 chan\n"); + ai_mode1_test(10.0,1,10); + + printf("100 hz, 1 chan\n"); + ai_mode1_test(100.0,1,100); + + printf("1000 hz, 1 chan\n"); + ai_mode1_test(1000.0,1,1000); + + printf("10000 hz, 1 chan\n"); + ai_mode1_test(10000.0,1,10000); + + printf("100000 hz, 1 chan\n"); + ai_mode1_test(100000.0,1,100000); + + printf("100000 hz, 2 chan\n"); + ai_mode1_test(100000.0,2,50000); + + printf("100000 hz, 16 chans\n"); + ai_mode1_test(100000.0,16,100000/16); +} + + + return 0; +} + +int ai_mode1_test(double freq,int n_chans,int n_scans) +{ + comedi_trig it; + int err; + int n,i; + double actual_freq; + void *data_ptr; + int n_left; + int maxchan; + struct timeval start,stop; + int m; + + memset(&it,0,sizeof(it)); + it.subdev = subdevice; + it.mode = 1; + it.n_chan = n_chans; + it.chanlist = chanlist; + it.data = data; + it.n = n_scans; + + maxchan = comedi_get_n_channels(device,subdevice); + + /* pack the channel list */ + for(i=0;i0){ + m=n_left; + if(m>=BUFSZ)m=BUFSZ; + if((n=read(comedi_fileno(device),data_ptr,m))<0){ + perror("read"); + exit(1); + } + //printf("read %d\n",n); + n_left-=n; + //data_ptr+=n; + } + + gettimeofday(&stop,NULL); + + if(stop.tv_usec<=start.tv_usec){ + stop.tv_usec+=1000000; + stop.tv_sec--; + } + stop.tv_sec-=start.tv_sec; + stop.tv_usec-=start.tv_usec; + + printf("actual time elapsed: %d.%06d s.\n",(int)stop.tv_sec,(int)stop.tv_usec); + printf("expected time: %g\n",n_scans*n_chans/actual_freq); + + return 0; +} + diff --git a/testing/mode2.c b/testing/mode2.c new file mode 100644 index 0000000..45013e3 --- /dev/null +++ b/testing/mode2.c @@ -0,0 +1,63 @@ +/* + A little input demo for mode 2 + + Mode 2 uses two different timers to convert samples. + The primary timer determines the time between scans, + and the secondary timer determines the time between + samples in a scan. + + The time between scans is in trigval; the time + between samples is selected by trigval1. Conversion + from seconds or Hz is done using the standard timer + routines. + + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define N_SCANS 10 +#define N_CHANS 16 + +int subdevice = 0; +int chan=0; +int range = 0; +int aref = AREF_GROUND; +double freq = 1000; + +#define N_SAMPLES 1000 + +double data[N_SAMPLES]; + + +int main(int argc, char *argv[]) +{ + char *fn = NULL; + int i; + comedi_t *dev; + + fn = "/dev/comedi0"; + + dev = comedi_open(fn); + +#if 0 + for(i=0;i<10;i++){ + range=comedi_find_range(dev,subdevice,chan,0,-i,i); + printf("%d\n",range); + } +#endif + comedi_timed_1chan(dev,subdevice,chan,range,aref,freq,N_SAMPLES,data); + + for(i=0;i