--- /dev/null
+
+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.
+
+
--- /dev/null
+
+# 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:
+
--- /dev/null
+
+
+
+ Linux Control and Measurement Device Interface (comedi)
+
+ David Schleef
+ <ds@stm.lbl.gov>
+
+
+A new minor version of comedi, 0.6.0, is now available at
+<ftp://stm.lbl.gov/pub/comedi/>. 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.
+
--- /dev/null
+
+
+
+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)
--- /dev/null
+/*
+ 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 <stdio.h>
+#include <comedilib.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <getopt.h>
+#include <ctype.h>
+#include <math.h>
+#include <stdlib.h>
+#include <string.h>
+
+
+#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 <ds@stm.lbl.gov>\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;i<n_caldacs;i++){
+ gain=check_gain_chan(adc,range,i);
+ }
+}
+
+void caldac_dependence(int caldac)
+{
+ int i;
+ double gain;
+
+ for(i=0;i<8;i++){
+ gain=check_gain_chan(i,0,caldac);
+ }
+}
+
+int dump_flag;
+
+void dump_curve(int adc,int caldac)
+{
+ linear_fit_t l;
+
+ dump_flag=1;
+ check_gain_chan_x(&l,adc,0,caldac);
+ dump_flag=0;
+}
+
+
+void setup_caldacs(void)
+{
+ int s,n_chan,i;
+
+ s=comedi_find_subdevice_by_type(dev,COMEDI_SUBD_CALIB,0);
+ if(s<0){
+ printf("no calibration subdevice\n");
+ return;
+ }
+ //printf("calibration subdevice is %d\n",s);
+
+ n_chan=comedi_get_n_channels(dev,s);
+
+ for(i=0;i<n_chan;i++){
+ caldacs[i].subdev=s;
+ caldacs[i].chan=i;
+ caldacs[i].maxdata=comedi_get_maxdata(dev,s,i);
+/* XXX */
+caldacs[i].maxdata=255;
+ caldacs[i].current=0;
+ //printf("caldac %d, %d\n",i,caldacs[i].maxdata);
+ }
+
+ n_caldacs=n_chan;
+}
+
+void reset_caldacs(void)
+{
+ int i;
+
+ for(i=0;i<n_caldacs;i++){
+ caldacs[i].current=caldacs[i].maxdata/2;
+ update_caldac(i);
+ }
+}
+
+void update_caldac(int i)
+{
+ int ret;
+
+ //printf("update %d %d %d\n",caldacs[i].subdev,caldacs[i].chan,caldacs[i].current);
+ //ret=comedi_data_write(dev,caldacs[i].subdev,caldacs[i].chan,0,0,caldacs[i].current);
+ write_caldac(dev,caldacs[i].subdev,caldacs[i].chan,caldacs[i].current);
+ ret=0;
+ if(ret<0)
+ printf("comedi_data_write() error\n");
+}
+
+
+void check_gain(int ad_chan,int range)
+{
+ int i;
+
+ for(i=0;i<n_caldacs;i++){
+ check_gain_chan(ad_chan,range,i);
+ }
+}
+
+double check_gain_chan(int ad_chan,int range,int cdac)
+{
+ linear_fit_t l;
+
+ return check_gain_chan_x(&l,ad_chan,range,cdac);
+}
+
+double check_gain_chan_x(linear_fit_t *l,int ad_chan,int range,int cdac)
+{
+ int orig,i,n;
+ new_sv_t sv;
+ double sum_err;
+ int sum_err_count=0;
+
+ n=caldacs[cdac].maxdata+1;
+ memset(l,0,sizeof(*l));
+ l->y_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;i<n;i++){
+ caldacs[cdac].current=i;
+ update_caldac(cdac);
+
+ new_sv_measure(&sv);
+
+ l->y_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;i<n;i++){
+ printf("%d %g\n",i,l->y_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<<sv->order;
+
+ data=malloc(sizeof(sampl_t)*n);
+
+ for(i=0;i<n;){
+ sv->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;i<n;i++){
+ x=comedi_to_phys(data[i],sv->rng,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<<order;
+
+ data=malloc(sizeof(sampl_t)*n);
+
+ for(i=0;i<n;){
+ sv->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;i<n;i++){
+ x=comedi_to_phys(data[i],sv->rng,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;i<l->n;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;i<l->n;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);
+}
+
--- /dev/null
+
+
+
+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)
--- /dev/null
+
+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 <aref> uses analog reference <aref> (default: 0)
+-c <chan> uses channel <chan>
+-s <subd> uses subdevice <subd>
+-r <range> uses voltage range <range>
+-f <file> uses device file <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
+
+
--- /dev/null
+/*
+ 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 <stdio.h>
+#include <comedilib.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <errno.h>
+#include <getopt.h>
+#include <ctype.h>
+#include <math.h>
+
+#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;i<N_CHANS;i++){
+ chan[i] = CR_PACK(channels[i], range, aref);
+ }
+
+ dds_init();
+
+ dds_output(data,BUF_LEN);
+ dds_output(data,BUF_LEN);
+
+ lseek(comedi_fileno(dev),subdevice,SEEK_SET);
+ m=write(comedi_fileno(dev),data,BUF_LEN*sizeof(sampl_t));
+ perror("write");
+ printf("m=%d\n",m);
+
+
+ if ((err = comedi_trigger(dev, &it)) < 0) {
+ perror("ioctl");
+ exit(1);
+ }
+ while(1){
+ dds_output(data,BUF_LEN);
+ n=BUF_LEN*sizeof(sampl_t);
+ while(n>0){
+ m=write(comedi_fileno(dev),(void *)data+(BUF_LEN*sizeof(sampl_t)-n),n);
+ if(m<0){
+ perror("write");
+ exit(0);
+ }
+ //printf("m=%d\n",m);
+ n-=m;
+ }
+ total+=BUF_LEN;
+ //printf("%d\n",total);
+ }
+
+ return 0;
+}
+
+
+
+#define WAVEFORM_SHIFT 16
+#define WAVEFORM_LEN (1<<WAVEFORM_SHIFT)
+#define WAVEFORM_MASK (WAVEFORM_LEN-1)
+
+
+sampl_t waveform[WAVEFORM_LEN];
+
+unsigned int acc;
+unsigned int adder;
+
+void dds_init(void)
+{
+ int i;
+
+ adder=waveform_frequency/update_frequency*(1<<16)*(1<<WAVEFORM_SHIFT);
+
+ dds_init_sine();
+
+ /* this is due to a bug in the NI-E driver */
+ if(range){
+ for(i=0;i<WAVEFORM_LEN;i++){
+ waveform[i]^=0x800;
+ }
+ }
+}
+
+void dds_init_sine(void)
+{
+ int i;
+
+ for(i=0;i<WAVEFORM_LEN;i++){
+ waveform[i]=rint(offset+0.5*amplitude*cos(i*2*M_PI/WAVEFORM_LEN));
+ }
+}
+
+/* Yes, I know this is not the proper equation for a
+ cycloid. Fix it. */
+void dds_init_cycloid(void)
+{
+ int i;
+ double t;
+
+ for(i=0;i<WAVEFORM_LEN/2;i++){
+ t=2*((double)i)/WAVEFORM_LEN;
+ waveform[i]=rint(offset+amplitude*sqrt(1-4*t*t));
+ }
+ for(i=WAVEFORM_LEN/2;i<WAVEFORM_LEN;i++){
+ t=2*(1-((double)i)/WAVEFORM_LEN);
+ waveform[i]=rint(offset+amplitude*sqrt(1-t*t));
+ }
+}
+
+void dds_init_sawtooth(void)
+{
+ int i;
+
+ for(i=0;i<WAVEFORM_LEN;i++){
+ waveform[i]=rint(offset+amplitude*((double)i)/WAVEFORM_LEN);
+ }
+}
+
+void dds_output(sampl_t *buf,int n)
+{
+ int i;
+ sampl_t *p=buf;
+
+ for(i=0;i<n;i++){
+ *p=waveform[(acc>>16)&WAVEFORM_MASK];
+ p++;
+ acc+=adder;
+ }
+}
+
--- /dev/null
+/*
+ A little input demo for commands
+
+ */
+
+#include <stdio.h>
+#include <comedilib.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <errno.h>
+#include <getopt.h>
+#include <ctype.h>
+
+#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);
+}
+
--- /dev/null
+/*
+ Dumps eeproms
+ */
+
+#include <stdio.h>
+#include <comedilib.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <errno.h>
+#include <getopt.h>
+#include <ctype.h>
+#include <stdlib.h>
+
+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<n;i++){
+ ret=comedi_data_read(it,subd,i,0,0,&data);
+ ptr[i]=data;
+ if(ret<0){
+ comedi_perror("comedi_data_read");
+ return 0;
+ }
+ }
+
+ *eeprom=ptr;
+ return n;
+
+}
+
+void dump_eeprom(unsigned int *eeprom,int len)
+{
+ int i, j, c;
+
+ for (i = 0; i < len - 16; i+=16) {
+ printf("%04x: ",i);
+ for (j = 0; j < 16; j++) {
+ printf("%02x", eeprom[i + j] & 0xff);
+ }
+ printf(" ");
+ for (j = 0; j < 16; j++) {
+ c = eeprom[i + j] & 0xff;
+ printf("%c", isprint(c) ? c : '.');
+ }
+ printf("\n");
+ }
+ if(i==len)return;
+ printf("%04x: ",i);
+ for (j = 0; j < len-i; j++) {
+ printf("%02x", eeprom[i + j] & 0xff);
+ }
+ for(;j<16;j++){
+ printf(" ");
+ }
+ printf(" ");
+ for (j = 0; j < len-i; j++) {
+ c = eeprom[i + j] & 0xff;
+ printf("%c", isprint(c) ? c : '.');
+ }
+ for(;j<16;j++){
+ printf(" ");
+ }
+ printf("\n");
+}
+
--- /dev/null
+/*
+ This demo reads information about a comedi device and
+ displays the information in a human-readable form.
+ */
+
+#include <stdio.h>
+#include <comedilib.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <errno.h>
+
+void help(void)
+{
+ fprintf(stderr,"info </dev/comediN>\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<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));
+ }
+
+ return 0;
+}
+
+char *tobinary(char *s,int bits,int n)
+{
+ int bit=1<<n;
+ char *t=s;
+
+ for(;bit;bit>>=1)
+ *t++=(bits&bit)?'1':'0';
+ *t=0;
+
+ return s;
+}
+
--- /dev/null
+/*
+ A little input demo
+ */
+
+#include <stdio.h>
+#include <comedilib.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <errno.h>
+#include <getopt.h>
+#include <ctype.h>
+
+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;
+}
+
--- /dev/null
+/*
+ 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 <stdio.h>
+#include <comedilib.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <errno.h>
+#include <getopt.h>
+#include <ctype.h>
+
+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<n_chans;chan++){
+ printf("%d: ",chan);
+
+ //n_ranges=comedi_get_n_ranges(device,subdevice,chan);
+ rangetype=comedi_get_rangetype(device,subdevice,chan);
+ n_ranges=RANGE_LENGTH(rangetype);
+
+ maxdata=comedi_get_maxdata(device,subdevice,chan);
+ for(range=0;range<n_ranges;range++){
+ comedi_data_read(device,subdevice,chan,range,aref,&data);
+ voltage=comedi_to_phys(data,comedi_get_range(device,subdevice,chan,range),maxdata);
+ printf("%g ",voltage);
+ }
+ printf("\n");
+ }
+
+ exit(0);
+}
+
--- /dev/null
+/*
+ */
+
+#include <stdio.h>
+#include <comedilib.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <errno.h>
+#include <getopt.h>
+#include <ctype.h>
+#include <malloc.h>
+
+
+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<argc) {
+ c = getopt(argc, argv, "acsrfvdgom");
+ switch (c) {
+ case -1:
+ /* data value */
+ sscanf(argv[optind],"%d",&value);
+ optind++;
+ break;
+ case 'f':
+ filename = argv[optind];
+ optind++;
+ break;
+ case 's':
+ sscanf(argv[optind],"%d",&subdevice);
+ optind++;
+ break;
+ case 'c':
+ sscanf(argv[optind],"%d",&channel);
+ optind++;
+ break;
+ case 'a':
+ sscanf(argv[optind],"%d",&aref);
+ optind++;
+ break;
+ case 'r':
+ sscanf(argv[optind],"%d",&range);
+ optind++;
+ 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;
+}
+
+
+
--- /dev/null
+/*
+ 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 <stdio.h>
+#include <comedilib.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <errno.h>
+#include <getopt.h>
+#include <ctype.h>
+
+#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;i<N_CHANS;i++){
+ chan[i] = CR_PACK(channels[i], range, aref);
+ }
+
+ if ((err = comedi_trigger(dev, &it)) < 0) {
+ perror("ioctl");
+ exit(1);
+ }
+
+ data_ptr=data;
+ n_left=N_SAMPLES*sizeof(sampl_t);
+ while(n_left>0){
+ 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<N_SAMPLES;i++){
+ fprintf(stderr,"%d\n",data[i]);
+ }
+
+ return 0;
+}
+
--- /dev/null
+/*
+ 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 <stdio.h>
+#include <comedilib.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <errno.h>
+#include <getopt.h>
+#include <ctype.h>
+
+#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<N_SAMPLES;i++){
+ printf("%g\n",data[i]);
+ }
+
+ return 0;
+}
+
--- /dev/null
+/*
+ A little input demo for mode 3
+
+ Mode 3 uses an external trigger to repeatedly trigger
+ acquisition of samples. If you select n_chan>1, 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 <stdio.h>
+#include <comedilib.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <errno.h>
+#include <getopt.h>
+#include <ctype.h>
+
+#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<N_CHANS;i++){
+ chan[i] = CR_PACK(channels[i], range, aref);
+ }
+
+ if ((err = comedi_trigger(dev, &it)) < 0) {
+ perror("ioctl");
+ exit(1);
+ }
+
+ if((n=read(comedi_fileno(dev),data,N_SAMPLES*sizeof(sampl_t)))<0){
+ perror("read");
+ exit(1);
+ }
+ printf("number of samples read=%d\ndata[0]=%d\ndata[N-1]=%d\n",
+ n/sizeof(sampl_t),data[0],data[N_SAMPLES-1]);
+
+ return 0;
+}
+
--- /dev/null
+/*
+ A little input demo for mode 4
+
+ Mode 4 uses an external trigger to repeatedly trigger a
+ scan of samples. (This is different from mode 3, which
+ triggers an individual sample.) Thus, for each external
+ trigger, n_chan samples are converted.
+
+ If you have multiple external trigger lines, the
+ particular trigger line is selected by trigval.
+
+ The time between samples in a scan is selected
+ by trigval1. Conversion from seconds or Hz is done
+ using the standard timer routines.
+
+ */
+
+#include <stdio.h>
+#include <comedilib.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <errno.h>
+#include <getopt.h>
+#include <ctype.h>
+
+#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<N_CHANS;i++){
+ chan[i] = CR_PACK(channels[i], range, aref);
+ }
+
+ if ((err = comedi_trigger(dev, &it)) < 0) {
+ perror("ioctl");
+ exit(1);
+ }
+
+ if((n=read(comedi_fileno(dev),data,N_SAMPLES*sizeof(sampl_t)))<0){
+ perror("read");
+ exit(1);
+ }
+ printf("number of samples read=%d\ndata[0]=%d\ndata[N-1]=%d\n",
+ n/sizeof(sampl_t),data[0],data[N_SAMPLES-1]);
+
+ return 0;
+}
+
--- /dev/null
+/*
+ A little output demo
+ */
+
+#include <stdio.h>
+#include <comedilib.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <errno.h>
+#include <getopt.h>
+#include <ctype.h>
+
+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;
+}
+
--- /dev/null
+
+
+# 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
+
--- /dev/null
+
+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.
+
+
+
--- /dev/null
+
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/version.h>
+#include <rtl_sched.h>
+#include <asm/rt_time.h>
+#include <comedi.h>
+
+
+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);
+}
+
+
--- /dev/null
+
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/version.h>
+#include <rtl_sched.h>
+#include <asm/rt_time.h>
+#include <comedi.h>
+
+
+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);
+}
+
+
--- /dev/null
+/*
+ Demo of the comedi_sv_*() functions
+ */
+
+#include <stdio.h>
+#include <comedilib.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <errno.h>
+#include <getopt.h>
+#include <ctype.h>
+
+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;
+}
+
--- /dev/null
+
+#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;
+}
+
--- /dev/null
+
+#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 = 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;
+}
--- /dev/null
+
+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.
+
+
--- /dev/null
+<!doctype linuxdoc system>
+
+<article>
+<title>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>
+
--- /dev/null
+<!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</TITLE>
+ <LINK HREF="comedilib-2.html" REL=next>
+
+ <LINK HREF="comedilib.html#toc1" REL=contents>
+</HEAD>
+<BODY>
+<A HREF="comedilib-2.html">Next</A>
+Previous
+<A HREF="comedilib.html#toc1">Contents</A>
+<HR>
+<H2><A NAME="s1">1. Introduction</A></H2>
+
+<P>This is preliminary documentation for Comedi and Comedilib.
+<P>
+<HR>
+<A HREF="comedilib-2.html">Next</A>
+Previous
+<A HREF="comedilib.html#toc1">Contents</A>
+</BODY>
+</HTML>
--- /dev/null
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<HTML>
+<HEAD>
+ <META NAME="GENERATOR" CONTENT="SGML-Tools 1.0.9">
+ <TITLE>Comedi Documentation: Installation and configuration</TITLE>
+ <LINK HREF="comedilib-3.html" REL=next>
+ <LINK HREF="comedilib-1.html" REL=previous>
+ <LINK HREF="comedilib.html#toc2" REL=contents>
+</HEAD>
+<BODY>
+<A HREF="comedilib-3.html">Next</A>
+<A HREF="comedilib-1.html">Previous</A>
+<A HREF="comedilib.html#toc2">Contents</A>
+<HR>
+<H2><A NAME="s2">2. Installation and configuration</A></H2>
+
+<P>
+<P>This section covers compiling, installing, and configuring
+comedi and comedlib.
+<P>
+<P>
+<H2><A NAME="ss2.1">2.1 Compiling and Installing</A>
+</H2>
+
+<P>
+<P>This section has not been written.
+<P>
+<P>
+<H2><A NAME="ss2.2">2.2 Insmod'ding the kernel module</A>
+</H2>
+
+<P>
+<P>This section has not been written.
+<P>
+<P>
+<H2><A NAME="ss2.3">2.3 Configuring comedi for your hardware</A>
+</H2>
+
+<P>
+<P>
+<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.
+<P>To tell the comedi kernel module that you have a particular device, and
+some information about it, you will be running the <CODE>comedi_config</CODE>
+command. Perhaps you should read the man page now.
+<P>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.
+<P>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:
+<P>
+<P>
+<BLOCKQUOTE><CODE>
+<PRE>
+# 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)
+))
+</PRE>
+</CODE></BLOCKQUOTE>
+<P>
+<P>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.
+<P>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
+<P>
+<BLOCKQUOTE><CODE>
+<PRE>
+/usr/sbin/comedi_config /dev/comedi0 atmio-E 0x260,3
+</PRE>
+</CODE></BLOCKQUOTE>
+<P>into <CODE>/etc/rc.d/rc.local</CODE>. 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.
+<P>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:
+<P>
+<UL>
+<LI>I/O base</LI>
+<LI>IRQ</LI>
+<LI>1=differential, 0=single ended</LI>
+<LI>ai 0=unipolar, 1=bipolar</LI>
+<LI>ao0 0=unipolar, 1=bipolar</LI>
+<LI>ao1 0=unipolar, 1=bipolar</LI>
+<LI>dma1</LI>
+<LI>dma2</LI>
+</UL>
+<P>(ai=analog input, ao=analog output.) From this, I decide that
+the appropriate options list is
+<P>
+<BLOCKQUOTE><CODE>
+<PRE>
+0x200,4,,1,1,1
+</PRE>
+</CODE></BLOCKQUOTE>
+<P>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.
+<P>
+<BLOCKQUOTE><CODE>
+<PRE>
+/usr/sbin/comedi_config /dev/comedi1 dt2821-f-8di 0x200,4,,1,1,1
+</PRE>
+</CODE></BLOCKQUOTE>
+<P>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):
+<P>
+<BLOCKQUOTE><CODE>
+<PRE>
+comedi0: ni_E: 0x0200 can't find board
+</PRE>
+</CODE></BLOCKQUOTE>
+<P>When it does work, I get:
+<P>
+<BLOCKQUOTE><CODE>
+<PRE>
+comedi0: ni_E: 0x0260 at-mio-16e-10 ( irq = 3 )
+</PRE>
+</CODE></BLOCKQUOTE>
+<P>Note that it also correctly identified my board.
+<P>
+<P>
+<P>
+<H2><A NAME="ss2.4">2.4 Getting information from comedi</A>
+</H2>
+
+<P>
+<P>
+<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>
+<P>
+<P>
+<BLOCKQUOTE><CODE>
+<PRE>
+cat /proc/comedi
+</PRE>
+</CODE></BLOCKQUOTE>
+<P>Right now, on my computer, this command gives:
+<P>
+<BLOCKQUOTE><CODE>
+<PRE>
+comedi version 0.6.4
+format string
+ 0: atmio-E at-mio-16e-10 7
+ 1: dt282x dt2821-f-8di 4
+</PRE>
+</CODE></BLOCKQUOTE>
+<P>This is a feature that is not well-developed yet. Basically, it
+currently tells you driver name, device name, and number of
+subdevices.
+<P>In the <CODE>demo/</CODE> directory, there is a command called
+<CODE>info</CODE>, 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 <CODE>/dev/comedi0</CODE>.) ('demo/info /dev/comedi0')
+<P>
+<BLOCKQUOTE><CODE>
+<PRE>
+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
+</PRE>
+
+...
+</CODE></BLOCKQUOTE>
+<P>The overall info gives information about the device -- basically
+the same information as /proc/comedi.
+<P>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.
+<P>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>
+<P>
+<HR>
+<A HREF="comedilib-3.html">Next</A>
+<A HREF="comedilib-1.html">Previous</A>
+<A HREF="comedilib.html#toc2">Contents</A>
+</BODY>
+</HTML>
--- /dev/null
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<HTML>
+<HEAD>
+ <META NAME="GENERATOR" CONTENT="SGML-Tools 1.0.9">
+ <TITLE>Comedi Documentation: Individual drivers</TITLE>
+ <LINK HREF="comedilib-4.html" REL=next>
+ <LINK HREF="comedilib-2.html" REL=previous>
+ <LINK HREF="comedilib.html#toc3" REL=contents>
+</HEAD>
+<BODY>
+<A HREF="comedilib-4.html">Next</A>
+<A HREF="comedilib-2.html">Previous</A>
+<A HREF="comedilib.html#toc3">Contents</A>
+<HR>
+<H2><A NAME="s3">3. Individual drivers</A></H2>
+
+<P>
+<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.
+<P>
+<H2><A NAME="ss3.1">3.1 National Instruments AT-MIO E series</A>
+</H2>
+
+<P>
+<P>
+<P>
+<H2><A NAME="ss3.2">3.2 Data Translation</A>
+</H2>
+
+<P>
+<P>
+<P>
+<P>
+<HR>
+<A HREF="comedilib-4.html">Next</A>
+<A HREF="comedilib-2.html">Previous</A>
+<A HREF="comedilib.html#toc3">Contents</A>
+</BODY>
+</HTML>
--- /dev/null
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<HTML>
+<HEAD>
+ <META NAME="GENERATOR" CONTENT="SGML-Tools 1.0.9">
+ <TITLE>Comedi Documentation: Writing programs that use comedi and comedilib</TITLE>
+ <LINK HREF="comedilib-5.html" REL=next>
+ <LINK HREF="comedilib-3.html" REL=previous>
+ <LINK HREF="comedilib.html#toc4" REL=contents>
+</HEAD>
+<BODY>
+<A HREF="comedilib-5.html">Next</A>
+<A HREF="comedilib-3.html">Previous</A>
+<A HREF="comedilib.html#toc4">Contents</A>
+<HR>
+<H2><A NAME="s4">4. Writing programs that use comedi and comedilib</A></H2>
+
+<P>
+<P>
+<H2><A NAME="ss4.1">4.1 Your first comedi program</A>
+</H2>
+
+<P>
+<P>This example requires a card that has analog or
+digital input. Right to the source:
+<P>
+<BLOCKQUOTE><CODE>
+<PRE>
+#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;
+}
+</PRE>
+</CODE></BLOCKQUOTE>
+<P>
+<P>Should be understandable. Open the device, get the data,
+print it out. This is basically the guts of <CODE>demo/inp.c</CODE>,
+without error checking or fancy options. Including all
+the appropriate headers is sometimes a little tricky.
+Compile it using
+<P>
+<BLOCKQUOTE><CODE>
+<PRE>
+cc tut1.c -lcomedi -o tut1
+</PRE>
+</CODE></BLOCKQUOTE>
+<P>Hopefully it works.
+<P>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>
+<P>
+<P>
+<H2><A NAME="ss4.2">4.2 Converting samples to voltages</A>
+</H2>
+
+<P>
+<P>If you selected an analog input subdevice, you should notice
+that the output of <CODE>tut1</CODE> 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 <B>always</B> 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?"
+<P>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:
+<P>RANGE_OFFSET(range_type)
+RANGE_LENGTH(range_type)
+<P>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
+<P>ptr=comedi_get_range(comedi_file,subdevice,channel,
+range)
+<P>which returns a pointer to a comedi_range structure.
+The comedi_range structure looks like
+<P>
+<P>
+<BLOCKQUOTE><CODE>
+<PRE>
+typedef struct{
+ double min;
+ double max;
+ unsigned int unit;
+}comedi_range;
+</PRE>
+</CODE></BLOCKQUOTE>
+<P>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.
+<P>"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
+<P>
+<BLOCKQUOTE><CODE>
+<PRE>
+volts=comedi_to_phys(it,data,range,maxdata);
+</PRE>
+</CODE></BLOCKQUOTE>
+<P>and the opposite
+<P>
+<BLOCKQUOTE><CODE>
+<PRE>
+data=comedi_from_phys(it,volts,range,maxdata);
+</PRE>
+</CODE></BLOCKQUOTE>
+<P>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>
+<P>
+<P>
+<H2><A NAME="ss4.3">4.3 Another section</A>
+</H2>
+
+<P>
+<P>
+<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>
+<P>
+<BLOCKQUOTE><CODE>
+<PRE>
+file=comedi_open("/dev/comedi0");
+</PRE>
+</CODE></BLOCKQUOTE>
+<P>where file is of type <CODE>(comedi_t *)</CODE>. This function
+calls <CODE>open()</CODE>, like we did explicitly in a previous
+section, but also fills the <CODE>comedi_t</CODE> structure with
+lots of goodies -- information that we will need to use
+soon.
+<P>Specifically, we needed to know maxdata for a specific
+subdevice/channel. How about:
+<P>
+<BLOCKQUOTE><CODE>
+<PRE>
+maxdata=comedi_get_maxdata(file,subdevice,channel);
+</PRE>
+</CODE></BLOCKQUOTE>
+<P>Wow. How easy. And the range type?
+<P>
+<BLOCKQUOTE><CODE>
+<PRE>
+range_type=comedi_get_rangetype(file,subdevice,channel);
+</PRE>
+</CODE></BLOCKQUOTE>
+<P>Cool. Other information you need to know about a channel
+can be gotten in a similar way.
+<P>
+<P>
+<P>
+<H2><A NAME="ss4.4">4.4 Your second comedi program</A>
+</H2>
+
+<P>
+<P>
+<P>Actually, this is the first comedi program again, just
+that we've added what we've learned.
+<P>
+<P>
+<BLOCKQUOTE><CODE>
+<PRE>
+#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;
+}
+</PRE>
+</CODE></BLOCKQUOTE>
+<P>
+<P>By now, the <CODE>comedi_read_data()</CODE> line looks a little archaic, using
+the UNIX file descriptor cf->fd instead of just cf. (By the
+way, somewhere in the heart of <CODE>comedi_open()</CODE> is the line
+<CODE>cf->fd=open(filename,O_RDWR)</CODE>.) Well, there isn't one good
+replacement, since it highly depends on your application
+what additional features you might want in a <CODE>comedi_get()</CODE>
+replacement. But this is the topic of a different section.
+<P>
+<P>
+<P>
+<HR>
+<A HREF="comedilib-5.html">Next</A>
+<A HREF="comedilib-3.html">Previous</A>
+<A HREF="comedilib.html#toc4">Contents</A>
+</BODY>
+</HTML>
--- /dev/null
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<HTML>
+<HEAD>
+ <META NAME="GENERATOR" CONTENT="SGML-Tools 1.0.9">
+ <TITLE>Comedi Documentation: Application-specific functions</TITLE>
+ <LINK HREF="comedilib-6.html" REL=next>
+ <LINK HREF="comedilib-4.html" REL=previous>
+ <LINK HREF="comedilib.html#toc5" REL=contents>
+</HEAD>
+<BODY>
+<A HREF="comedilib-6.html">Next</A>
+<A HREF="comedilib-4.html">Previous</A>
+<A HREF="comedilib.html#toc5">Contents</A>
+<HR>
+<H2><A NAME="s5">5. Application-specific functions</A></H2>
+
+<P>
+<P>
+<H2><A NAME="ss5.1">5.1 Digital Input/Output</A>
+</H2>
+
+<P>
+<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.
+<P>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.
+<P>
+<P>
+<H2><A NAME="ss5.2">5.2 Timed Input/Output</A>
+</H2>
+
+<P>
+<P>
+<P>
+<H2><A NAME="ss5.3">5.3 Slowly-varying inputs</A>
+</H2>
+
+<P>
+<P>
+<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>
+<P>
+<UL>
+<LI>you are ultimately limited by "spurious free dynamic range"
+</LI>
+<LI>you need to have _some_ noise on the input channel,
+otherwise you will be averaging the same number N times.
+</LI>
+<LI>the more noise you have, the greater your SFDR, but it
+takes many more samples to compensate for the increased
+noise
+</LI>
+<LI>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.
+</LI>
+</UL>
+<P>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.
+<P>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>
+<P>
+<P>
+<P>
+<P>
+<HR>
+<A HREF="comedilib-6.html">Next</A>
+<A HREF="comedilib-4.html">Previous</A>
+<A HREF="comedilib.html#toc5">Contents</A>
+</BODY>
+</HTML>
--- /dev/null
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<HTML>
+<HEAD>
+ <META NAME="GENERATOR" CONTENT="SGML-Tools 1.0.9">
+ <TITLE>Comedi Documentation: Comedilib reference</TITLE>
+ <LINK HREF="comedilib-5.html" REL=previous>
+ <LINK HREF="comedilib.html#toc6" REL=contents>
+</HEAD>
+<BODY>
+Next
+<A HREF="comedilib-5.html">Previous</A>
+<A HREF="comedilib.html#toc6">Contents</A>
+<HR>
+<H2><A NAME="s6">6. Comedilib reference</A></H2>
+
+<P>Reference of structures:
+<P>
+<BLOCKQUOTE><CODE>
+<PRE>
+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;
+</PRE>
+</CODE></BLOCKQUOTE>
+<P>
+<H2><A NAME="ss6.1">6.1 comedi_loglevel()</A>
+</H2>
+
+<P>
+<P>int comedi_loglevel(int loglevel);
+<P>
+<P>
+<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:
+<P>COMEDILIB_LOGLEVEL=0
+<P>Comedilib prints nothing.
+<P>COMEDILIB_LOGLEVEL=1 (default)
+<P>Comedilib only prints error messages when there is a
+self-consistency error.
+<P>COMEDILIB_LOGLEVEL=2
+<P>Comedilib prints an error message whenever an invalid
+parameter is passed to comedilib.
+<P>COMEDILIB_LOGLEVEL=3
+<P>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.
+<P>COMEDILIB_LOGLEVEL=4
+<P>Comedilib prints a lot of debugging messages.
+<P>
+<P>
+<P>
+<H2><A NAME="ss6.2">6.2 comedi_open</A>
+</H2>
+
+<P>
+<P>comedi_t *comedi_open(char *fn);
+<P>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.
+<P>You are not supposed to have access to the structure comedi_t.
+<P>void comedi_close(comedi_t *it);
+<P>Closes a device previously opened by comedi_open().
+<P>void comedi_perror(const char *s);
+char *comedi_strerror(int errnum);
+int comedi_errno(void);
+<P>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
+<CODE>comedi_errno()</CODE>. This error number can be
+converted to a human-readable form by the functions
+<CODE>comedi_perror()</CODE> and <CODE>comedi_strerror()</CODE>.
+<P>These functions are intended to mimic the behavior of the
+standard C library functions <CODE>perror()</CODE>,
+<CODE>strerror</CODE>, and <CODE>errno()</CODE>. 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.
+<P>The function <CODE>comedi_perror()</CODE> 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.
+<P>The function <CODE>comedi_strerror()</CODE> returns a pointer to a
+character string
+describing the comedilib error <CODE>errnum</CODE>. 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.
+<P>The function <CODE>comedi_errno()</CODE>
+returns an integer describing the most recent comedilib error. This
+integer may be used as the <CODE>errnum</CODE> parameter for
+<CODE>comedi_strerror()</CODE>.
+<P>
+<P>
+<H2><A NAME="ss6.3">6.3 comedi_fileno()</A>
+</H2>
+
+<P>
+<P>int comedi_fileno(comedi_t *it);
+<P>The function <CODE>comedi_fileno</CODE>
+returns the integer descriptor for the handle <CODE>it</CODE>. If
+<CODE>it</CODE> is an invalid <CODE>comedi_t</CODE> pointer, the function
+returns -1 and sets the appropriate comedilib error value.
+<P>
+<P>
+<P>
+<H2><A NAME="ss6.4">6.4 comedi_get_n_subdevices()</A>
+</H2>
+
+<P>
+<P>int comedi_get_n_subdevices(comedi_t *it);
+<P>The function <CODE>comedi_get_n_subdevices</CODE> returns the
+number of subdevices associated with the comedi descriptor
+<CODE>it</CODE>, or -1 if there is an error.
+<P>
+<P>
+<H2><A NAME="ss6.5">6.5 comedi_get_version_code()</A>
+</H2>
+
+<P>
+<P>int comedi_get_version_code(comedi_t *it);
+<P>The function <CODE>comedi_get_version_code()</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.
+<P>
+<P>
+<H2><A NAME="ss6.6">6.6 comedi_get_driver_name()</A>
+</H2>
+
+<P>
+<P>char *comedi_get_driver_name(comedi_t *it);
+<P>The function <CODE>comedi_get_driver_name</CODE> returns a pointer
+to a string containing the name of the driver being used by comedi
+for the comedi device represented by <CODE>it</CODE>. This pointer is
+valid until the comedi descriptor <CODE>it</CODE> is closed. This
+function returns NULL if there is an error.
+<P>
+<P>
+<H2><A NAME="ss6.7">6.7 comedi_get_board_name()</A>
+</H2>
+
+<P>
+<P>char *comedi_get_board_name(comedi_t *it);
+<P>The function <CODE>comedi_get_board_name</CODE> returns a pointer
+to a string containing the name of the device. This pointer is
+valid until the comedi descriptor <CODE>it</CODE> is closed. This
+function returns NULL if there is an error.
+<P>
+<P>
+<H2><A NAME="ss6.8">6.8 comedi_get_subdevice_type()</A>
+</H2>
+
+<P>
+<P>int comedi_get_subdevice_type(comedi_t *it,unsigned int subdevice);
+<P>The function <CODE>comedi_get_subdevice_type()</CODE> returns an
+integer describing the type of subdevice that belongs to the comedi
+device <CODE>it</CODE> and has the index <CODE>subdevice</CODE>. The
+function returns -1 is there is an error.
+<P>Valid subdevice types are:
+<P>
+<UL>
+<LI><CODE>COMEDI_SUBD_UNUSED</CODE>
+Subdevice has no functionality, i.e., a place-holder.</LI>
+<LI><CODE>COMEDI_SUBD_AI</CODE> Analog input</LI>
+<LI><CODE>COMEDI_SUBD_AO</CODE> Analog output</LI>
+<LI><CODE>COMEDI_SUBD_DI</CODE> Digital input</LI>
+<LI><CODE>COMEDI_SUBD_DO</CODE> Digital output</LI>
+<LI><CODE>COMEDI_SUBD_DIO</CODE>
+Digital input/output. Channels are configurable as to whether they
+are inputs or outputs.</LI>
+<LI><CODE>COMEDI_SUBD_COUNTER</CODE> Counter</LI>
+<LI><CODE>COMEDI_SUBD_TIMER</CODE> Timer</LI>
+<LI><CODE>COMEDI_SUBD_MEMORY</CODE>
+Memory, e.g., EEPROM or dual-ported RAM</LI>
+<LI><CODE>COMEDI_SUBD_CALIB</CODE>
+Calibration DACs</LI>
+<LI><CODE>COMEDI_SUBD_PROC</CODE>
+Processor or DSP</LI>
+</UL>
+<P>
+<P>
+<P>
+<H2><A NAME="ss6.9">6.9 comedi_find_subdevice_by_type()</A>
+</H2>
+
+<P>
+<P>int comedi_find_subdevice_by_type(comedi_t *it,int type,unsigned int start_subdevice)
+<P>The function <CODE>comedi_find_subdevice_by_type</CODE> tries to
+locate a subdevice belonging to comedi device <CODE>it</CODE>,
+having type <CODE>type</CODE>, starting with the subdevice
+<CODE>start_subdevice</CODE>. 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.
+<P>For subdevice types, see the manual page for the function
+<CODE>comedi_get_subdevice_type()</CODE>.
+<P>
+<P>
+<H2><A NAME="ss6.10">6.10 comedi_get_n_channels()</A>
+</H2>
+
+<P>
+<P>
+<P>int comedi_get_n_channels(comedi_t *it,unsigned int subdevice);
+<P>The function <CODE>comedi_get_n_channels()</CODE> returns the number
+of channels of the subdevice belonging to the comedi device <CODE>it</CODE>
+and having index <CODE>subdevice</CODE>. This function returns -1 on error.
+<P>
+<P>
+<H2><A NAME="ss6.11">6.11 comedi_get_maxdata()</A>
+</H2>
+
+<P>
+<P>lsampl_t comedi_get_maxdata(comedi_t *it,unsigned int subdevice,unsigned int chan);
+<P>The function <CODE>comedi_get_maxdata()</CODE> returns the maximum
+valid data value for channel <CODE>chan</CODE> of subdevice
+<CODE>subdevice</CODE> belonging to the comedi device <CODE>it</CODE>
+This function returns 0 on error.
+<P>
+<P>
+<H2><A NAME="ss6.12">6.12 comedi_get_rangetype()</A>
+</H2>
+
+<P>
+<P>int comedi_get_rangetype(comedi_t *it,unsigned int subdevice,unsigned int chan);
+<P>The function <CODE>comedi_get_rangetype()</CODE> 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
+<CODE>RANGE_LENGTH(rangetype)</CODE>
+can be used to determine the number of range specifications for a given
+range type.
+<P>
+<P>
+<H2><A NAME="ss6.13">6.13 comedi_get_range()</A>
+</H2>
+
+<P>
+<P>comedi_range * comedi_get_range(comedi_t *it,unsigned int subdevice,unsigned int chan,unsigned int range);
+<P>The function <CODE>comedi_get_range</CODE> 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 <CODE>it</CODE> is closed. If there is an
+error, NULL is returned.
+<P>
+<P>
+<H2><A NAME="ss6.14">6.14 comedi_trigger()</A>
+</H2>
+
+<P>
+<P>int comedi_trigger(comedi_t *it,comedi_trig *trig);
+<P>The function <CODE>comedi_trigger()</CODE> instructs comedi to
+perform the command specified by the trigger structure
+<CODE>trig</CODE>. Results depend on the particular command
+being issued. If there is an error, -1 is returned.
+<P>Complete information about comedi commands is given in the
+manual page comedi(8).
+<P>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);
+<P>The functions <CODE>comedi_to_phys()</CODE> and
+<CODE>comedi_from_phys()</CODE> convert sample values to/from physical
+units. The parameter <CODE>rng</CODE> represents the conversion
+information to use, and the parameter <CODE>maxdata</CODE> represents
+the maximum possible data value for the channel that the data was read/
+will be written to.
+<P>
+<P>
+<P>
+<H2><A NAME="ss6.15">6.15 comedi_data_read()</A>
+</H2>
+
+<P>
+<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);
+<P>These functions read or write a single sample on the channel that
+is specified by the comedi device <CODE>it</CODE>, the
+subdevice <CODE>subd</CODE>, and the channel <CODE>chan</CODE>.
+For the operation,
+the device is configured to use range specification
+<CODE>range</CODE> and (if appropriate) analog reference type
+<CODE>aref</CODE>. Analog reference types that are not supported
+by the device are silently ignored.
+<P>The function <CODE>comedi_data_read()</CODE> reads one data value from
+the specified channel and places the
+data value that is read in the location pointed to by
+<CODE>data</CODE>.
+<P>The function <CODE>comedi_data_write()</CODE> writes the data value
+specified by the argument <CODE>data</CODE> to
+the specified channel.
+<P>On sucess, these functions return 0. If there is an error, -1 is
+returned.
+<P>Valid analog reference numbers are:
+<P>
+<UL>
+<LI>AREF_GROUND Reference to analog ground</LI>
+<LI>AREF_COMMON Reference to analog common</LI>
+<LI>AREF_DIFF Differential reference</LI>
+<LI>AREF_OTHER Board-specific meaning</LI>
+</UL>
+<P>Valid data values used by these functions is an unsigned integer
+less than or equal to <CODE>maxdata</CODE>, which is channel-dependent.
+Conversion of these data values to physical units can be performed
+by <CODE>comedi_to_phys()</CODE> and <CODE>comedi_from_phys()</CODE>.
+<P>
+<P>
+<P>
+<H2><A NAME="ss6.16">6.16 comedi_sv_init()</A>
+</H2>
+
+<P>
+<P>
+<BLOCKQUOTE><CODE>
+<PRE>
+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);
+</PRE>
+</CODE></BLOCKQUOTE>
+<P>The special functions <CODE>comedi_sv_*()</CODE> 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).
+<P>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>
+<P>
+<P>
+<H2><A NAME="ss6.17">6.17 comedi_get_timer()</A>
+</H2>
+
+<P>
+<P>
+<BLOCKQUOTE><CODE>
+<PRE>
+int comedi_get_timer(comedi_t *it,unsigned int subdev,double freq,unsigned int *trigvar,
+ double *actual_freq);
+</PRE>
+</CODE></BLOCKQUOTE>
+<P>
+<P>
+<P>
+<P>
+<HR>
+Next
+<A HREF="comedilib-5.html">Previous</A>
+<A HREF="comedilib.html#toc6">Contents</A>
+</BODY>
+</HTML>
--- /dev/null
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<HTML>
+<HEAD>
+ <META NAME="GENERATOR" CONTENT="SGML-Tools 1.0.9">
+ <TITLE>Comedi Documentation</TITLE>
+ <LINK HREF="comedilib-1.html" REL=next>
+
+
+</HEAD>
+<BODY>
+<A HREF="comedilib-1.html">Next</A>
+Previous
+Contents
+<HR>
+<H1>Comedi Documentation</H1>
+
+<H2>David Schleef, <CODE>ds@stm.lbl.gov</CODE></H2>
+<P>
+<H2><A NAME="toc1">1.</A> <A HREF="comedilib-1.html">Introduction</A></H2>
+
+<P>
+<H2><A NAME="toc2">2.</A> <A HREF="comedilib-2.html">Installation and configuration</A></H2>
+
+<UL>
+<LI><A HREF="comedilib-2.html#ss2.1">2.1 Compiling and Installing</A>
+<LI><A HREF="comedilib-2.html#ss2.2">2.2 Insmod'ding the kernel module</A>
+<LI><A HREF="comedilib-2.html#ss2.3">2.3 Configuring comedi for your hardware</A>
+<LI><A HREF="comedilib-2.html#ss2.4">2.4 Getting information from comedi</A>
+</UL>
+<P>
+<H2><A NAME="toc3">3.</A> <A HREF="comedilib-3.html">Individual drivers</A></H2>
+
+<UL>
+<LI><A HREF="comedilib-3.html#ss3.1">3.1 National Instruments AT-MIO E series</A>
+<LI><A HREF="comedilib-3.html#ss3.2">3.2 Data Translation</A>
+</UL>
+<P>
+<H2><A NAME="toc4">4.</A> <A HREF="comedilib-4.html">Writing programs that use comedi and comedilib</A></H2>
+
+<UL>
+<LI><A HREF="comedilib-4.html#ss4.1">4.1 Your first comedi program</A>
+<LI><A HREF="comedilib-4.html#ss4.2">4.2 Converting samples to voltages</A>
+<LI><A HREF="comedilib-4.html#ss4.3">4.3 Another section</A>
+<LI><A HREF="comedilib-4.html#ss4.4">4.4 Your second comedi program</A>
+</UL>
+<P>
+<H2><A NAME="toc5">5.</A> <A HREF="comedilib-5.html">Application-specific functions</A></H2>
+
+<UL>
+<LI><A HREF="comedilib-5.html#ss5.1">5.1 Digital Input/Output</A>
+<LI><A HREF="comedilib-5.html#ss5.2">5.2 Timed Input/Output</A>
+<LI><A HREF="comedilib-5.html#ss5.3">5.3 Slowly-varying inputs</A>
+</UL>
+<P>
+<H2><A NAME="toc6">6.</A> <A HREF="comedilib-6.html">Comedilib reference</A></H2>
+
+<UL>
+<LI><A HREF="comedilib-6.html#ss6.1">6.1 comedi_loglevel()</A>
+<LI><A HREF="comedilib-6.html#ss6.2">6.2 comedi_open</A>
+<LI><A HREF="comedilib-6.html#ss6.3">6.3 comedi_fileno()</A>
+<LI><A HREF="comedilib-6.html#ss6.4">6.4 comedi_get_n_subdevices()</A>
+<LI><A HREF="comedilib-6.html#ss6.5">6.5 comedi_get_version_code()</A>
+<LI><A HREF="comedilib-6.html#ss6.6">6.6 comedi_get_driver_name()</A>
+<LI><A HREF="comedilib-6.html#ss6.7">6.7 comedi_get_board_name()</A>
+<LI><A HREF="comedilib-6.html#ss6.8">6.8 comedi_get_subdevice_type()</A>
+<LI><A HREF="comedilib-6.html#ss6.9">6.9 comedi_find_subdevice_by_type()</A>
+<LI><A HREF="comedilib-6.html#ss6.10">6.10 comedi_get_n_channels()</A>
+<LI><A HREF="comedilib-6.html#ss6.11">6.11 comedi_get_maxdata()</A>
+<LI><A HREF="comedilib-6.html#ss6.12">6.12 comedi_get_rangetype()</A>
+<LI><A HREF="comedilib-6.html#ss6.13">6.13 comedi_get_range()</A>
+<LI><A HREF="comedilib-6.html#ss6.14">6.14 comedi_trigger()</A>
+<LI><A HREF="comedilib-6.html#ss6.15">6.15 comedi_data_read()</A>
+<LI><A HREF="comedilib-6.html#ss6.16">6.16 comedi_sv_init()</A>
+<LI><A HREF="comedilib-6.html#ss6.17">6.17 comedi_get_timer()</A>
+</UL>
+<HR>
+<A HREF="comedilib-1.html">Next</A>
+Previous
+Contents
+</BODY>
+</HTML>
--- /dev/null
+
+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 "<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.
+
+
+
+
+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 <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.
+
+
+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 <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.
+
+
+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.
+
+
+
+
+
--- /dev/null
+/*
+ include/comedilib.h
+ header file for the comedi library routines
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 1998 David A. Schleef <ds@stm.lbl.gov>
+
+ 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 <comedi.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#include <stdio.h>
+
+#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
+
--- /dev/null
+
+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
+
+
--- /dev/null
+/*
+ lib/comedi.c
+ comedi library routines
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 1997-8 David A. Schleef <ds@stm.lbl.gov>
+
+ 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 <stdio.h>
+#include <math.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <errno.h>
+#include <comedi.h>
+#include <string.h>
+
+#include <libinternal.h>
+
+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
+}
+
+
+
--- /dev/null
+/*
+ lib/data.c
+ comedi library routines
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 1997-8 David A. Schleef <ds@stm.lbl.gov>
+
+ 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 <stdio.h>
+#include <math.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <errno.h>
+#include <comedi.h>
+#include <string.h>
+
+#include <libinternal.h>
+
+
+
+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(i<n){
+ cmd.data=(void *)(data+i);
+ cmd.n=n-i;
+
+ ret=ioctl_trigger(it->fd,&cmd);
+ if(ret<0)
+ goto out;
+
+ i+=ret;
+ }
+out:
+ if(i==0)return -1;
+
+ return (int)i;
+}
+#endif
+
+
+
--- /dev/null
+/*
+ lib/dio.c
+ comedi library routines
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 1997-8 David A. Schleef <ds@stm.lbl.gov>
+
+ 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 <stdio.h>
+#include <math.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <errno.h>
+#include <comedi.h>
+#include <string.h>
+
+#include <libinternal.h>
+
+
+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<n_chan;i++,m<<=1){
+ if(mask&m){
+ bit=(*bits&m)?1:0;
+ ret=comedi_dio_write(it,subdev,i,bit);
+ }else{
+ ret=comedi_dio_read(it,subdev,i,&bit);
+ if(bit) *bits|=m;
+ else (*bits)&=~m;
+ }
+ if(ret<0)return ret;
+ }
+
+ return (int)n_chan;
+}
+
--- /dev/null
+/*
+ lib/error.c
+ error handling routines
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 1997-8 David A. Schleef <ds@stm.lbl.gov>
+
+ 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 <libinternal.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+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 ||
+ 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;
+}
+
+
--- /dev/null
+/*
+ lib/filler.c
+ comedi library routines
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 1997-8 David A. Schleef <ds@stm.lbl.gov>
+
+ 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 <stdio.h>
+#include <math.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <errno.h>
+#include <comedi.h>
+#include <string.h>
+
+#include <libinternal.h>
+
+
+/* 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;i<it->n_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;j<r[i].n_chan;j++){
+ r[i].rangeinfo_list[j]=get_rangeinfo(it->fd,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<RANGE_LENGTH(range_type);i++){
+ r[i].min=kr[i].min*1e-6;
+ r[i].max=kr[i].max*1e-6;
+ r[i].unit=RF_UNIT(kr[i].flags);
+ }
+ free(kr);
+
+ return r;
+}
+
+
--- /dev/null
+/*
+ lib/get.c
+ comedi library routines
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 1997-8 David A. Schleef <ds@stm.lbl.gov>
+
+ 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 <stdio.h>
+#include <math.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <errno.h>
+#include <comedi.h>
+#include <string.h>
+
+#include <libinternal.h>
+
+
+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(;subd<it->n_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;
+}
+
+
+
--- /dev/null
+/*
+ lib/ioctl.c
+ comedi library routines
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 1997-8 David A. Schleef <ds@stm.lbl.gov>
+
+ 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 <stdio.h>
+#include <math.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <errno.h>
+#include <comedi.h>
+#include <string.h>
+
+#include <libinternal.h>
+
+
+/* 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);
+}
+
+
--- /dev/null
+/*
+ lib/libinternal.h
+ header file for comedilib internals
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 1998 David A. Schleef <ds@stm.lbl.gov>
+
+ 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 <comedilib.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#include <stdio.h>
+
+
+#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
+
--- /dev/null
+/*
+ lib/range.c
+ comedi library routines for voltage ranges
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 1997-8 David A. Schleef <ds@stm.lbl.gov>
+
+ 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 <stdio.h>
+
+#define __USE_GNU
+
+#include <math.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <errno.h>
+#include <comedi.h>
+#include <string.h>
+
+#include <libinternal.h>
+
+
+/* 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;i<RANGE_LENGTH(range_type);i++){
+ range_ptr=comedi_get_range(it,subd,chan,i);
+ if(range_ptr->min<=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);
+}
+
--- /dev/null
+/*
+ lib/sv.c
+ comedi library routines - sv section
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 1997-8 David A. Schleef <ds@stm.lbl.gov>
+
+ 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 <stdio.h>
+#include <math.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <errno.h>
+#include <comedi.h>
+#include <string.h>
+
+#include <libinternal.h>
+
+
+
+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;n<it->n;){
+ 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;i<it->n;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;n<it->n;){
+ 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;i<it->n;i++){
+ sum+=comedi_to_phys(val[i],rng,it->maxdata);
+ }
+ *data=sum/it->n;
+
+out:
+ free(val);
+
+ return ret;
+}
+
+
+
+
+
+
+
--- /dev/null
+/*
+ lib/sv.c
+ comedi library routines - sv section
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 1997-8 David A. Schleef <ds@stm.lbl.gov>
+
+ 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 <libinternal.h>
+
+#include <stdio.h>
+#include <math.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <errno.h>
+#include <comedi.h>
+#include <string.h>
+
+
+#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(n<n_samples){
+ m=n_samples-n;
+ if(m>BUFSZ)m=BUFSZ;
+ if((m=read(dev->fd,buffer,m*sizeof(sampl_t)))<0){
+ /* ack! */
+ return -1;
+ }
+ m/=sizeof(sampl_t);
+ for(i=0;i<m;i++){
+ data[n+i]=comedi_to_phys(buffer[i],the_range,maxdata);
+ }
+ n+=m;
+ }
+
+ free(buffer);
+
+ return 0;
+}
+
--- /dev/null
+/*
+ lib/comedi.c
+ comedi library routines
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 1997-8 David A. Schleef <ds@stm.lbl.gov>
+
+ 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 <stdio.h>
+#include <math.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <errno.h>
+#include <comedi.h>
+#include <string.h>
+
+#include <libinternal.h>
+
+
+
+/* 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-freq<freq-f/10){
+ *trigvar=i;
+ *actual_freq=f;
+ return 0;
+ }
+ f/=10;
+ }
+ *trigvar=i;
+ *actual_freq=f;
+
+ return 0;
+}
+
+/* atmio/pcimio timer */
+static int atmio_timer(double freq,unsigned int *trigvar,double *actual_freq)
+{
+ unsigned int divider;
+
+ divider=floor(20e6/(freq));
+ *actual_freq=20e6/divider;
+ *trigvar=divider-1;
+
+ return 0;
+}
+
+/* acl8112 timer */
+static int acl8112_timer(double freq,unsigned int *trigvar,double *actual_freq)
+{
+ int divider,prescaler;
+ double basefreq=2e6;
+
+ /* XXX my notes say that the prescaler and divider cannot
+ be 1. This needs to be checked. --ds */
+
+ /* Force at least one division to get something in CTR2. */
+ prescaler=1;
+ divider = basefreq/(freq);
+
+ while(divider>32767){
+ 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);
+}
+
--- /dev/null
+
+VERSION{
+
+v0.7.0{
+ global:
+ comedi_*;
+ local:
+ *;
+};
+
+}
--- /dev/null
+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)
--- /dev/null
+# Copyright (c) 1999 Joseph E. Smith <jes@presto.med.upenn.edu>
+#
+# 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<Comedi> module provides constants and data types for using the comedi data acquisition module.
+
+By itself, this module will allow you to access the B<comedi> module using standard I/O functions and ioctls.
+For more robust access to B<comedi>, see the B<Comedi::Lib> module.
+
+=head1 Exported constants
+
+The large number of constants defined in C<comedi.h> 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 <F<jes@presto.med.upenn.edu>>.
+
+=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
--- /dev/null
+/*
+ * Copyright (c) 1999 Joseph E. Smith <jes@presto.med.upenn.edu>
+ *
+ * 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 <comedi.h>
+
+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);
--- /dev/null
+
+ 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.
+
--- /dev/null
+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)
--- /dev/null
+# Copyright (c) 1999 Joseph E. Smith <jes@presto.med.upenn.edu>
+#
+# 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<comedilib>
+
+=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<comedi_cmd> 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 <F<jes@presto.med.upenn.edu>>.
+
+=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
--- /dev/null
+/*
+ * Copyright (c) 1999 Joseph E. Smith <jes@presto.med.upenn.edu>
+ *
+ * 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 <comedi.h>
+#include <comedilib.h>
+
+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
+
--- /dev/null
+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'],
+);
--- /dev/null
+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
+
--- /dev/null
+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" },
+);
--- /dev/null
+
+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 <jes@presto.med.upenn.edu>
--- /dev/null
+Revision history for Perl extension Comedi::Trigger.
+
+0.01 Tue Nov 9 10:18:34 1999
+ - original version; created by h2xs 1.18
+
--- /dev/null
+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',
+);
--- /dev/null
+# Copyright (c) 1999 Joseph E. Smith <jes@presto.med.upenn.edu>
+#
+# 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<comedi> trigger
+structures. It provides some convenience over C<pack>ing 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<comedi> 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<Comedi::CR_PACK> (see the example above).
+
+If the number of channels (B<n_chan>)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<comedi_trig_struct>. This method
+is normally called automatically by B<Comedi::Lib> functions that take
+trigger arguments.
+
+=head1 VERSION
+
+Version 0.01 09-Nov-1999
+
+=head1 AUTHOR
+
+Joe Smith <F<jes@presto.med.upenn.edu>>.
+
+=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
--- /dev/null
+# 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):
+
--- /dev/null
+
+# 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", $_);
+}
+
+
--- /dev/null
+
+use Comedi qw( :SubdeviceTypes );
+use Comedi::Lib;
+
+sub help {
+ print STDERR "info </dev/comediN>\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));
+}
--- /dev/null
+#!/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 "$@"
--- /dev/null
+
+# 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", $_);
+}
+
+
--- /dev/null
+
+# 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", $_);
+}
--- /dev/null
+
+# 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", $_);
+}
+
+
--- /dev/null
+# 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):
+
--- /dev/null
+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
+
--- /dev/null
+
+
+
+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
--- /dev/null
+/*
+ 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 <stdio.h>
+#include <comedilib.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <errno.h>
+#include <getopt.h>
+#include <ctype.h>
+
+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<n_chans;chan++){
+ printf("%d: ",chan);
+
+ //n_ranges=comedi_get_n_ranges(device,subdevice,chan);
+ rangetype=comedi_get_rangetype(device,subdevice,chan);
+ n_ranges=RANGE_LENGTH(rangetype);
+
+ maxdata=comedi_get_maxdata(device,subdevice,chan);
+ for(range=0;range<n_ranges;range++){
+ comedi_data_read(device,subdevice,chan,range,aref,&data);
+ voltage=comedi_to_phys(data,comedi_get_range(device,subdevice,chan,range),maxdata);
+ printf("%g ",voltage);
+ }
+ printf("\n");
+ }
+
+ exit(0);
+}
+
--- /dev/null
+/*
+ */
+
+#include <stdio.h>
+#include <comedilib.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <errno.h>
+#include <getopt.h>
+#include <ctype.h>
+#include <malloc.h>
+
+
+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;
+}
+
+
+
--- /dev/null
+/*
+ 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 <stdio.h>
+#include <comedilib.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <errno.h>
+#include <getopt.h>
+#include <ctype.h>
+#include <math.h>
+#include <sys/time.h>
+#include <string.h>
+
+#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;i<n_chans;i++){
+ chanlist[i] = CR_PACK(i%maxchan, range, aref);
+ }
+
+ comedi_get_timer(device,subdevice,freq,&it.trigvar,&actual_freq);
+ printf("primary actual frequency=%g timer value=%d\n",actual_freq,it.trigvar);
+
+ gettimeofday(&start,NULL);
+
+ if ((err = comedi_trigger(device, &it)) < 0) {
+ perror("ioctl");
+ }
+
+ data_ptr=data;
+ n_left=n_scans*n_chans*sizeof(sampl_t);
+ while(n_left>0){
+ 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;
+}
+
--- /dev/null
+/*
+ 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 <stdio.h>
+#include <comedilib.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <errno.h>
+#include <getopt.h>
+#include <ctype.h>
+
+#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<N_SAMPLES;i++){
+ printf("%g\n",data[i]);
+ }
+
+ return 0;
+}
+