I've updated the manual in the section configuration and
authorBernd Porr <berndporr@f2s.com>
Fri, 27 Apr 2012 00:37:33 +0000 (01:37 +0100)
committerBernd Porr <berndporr@f2s.com>
Fri, 27 Apr 2012 00:37:33 +0000 (01:37 +0100)
also updated the tutorial. I've simplified the tut2.c
by using comedi2phys instead of comedi2physical. The use of
polynomials as a 2nd comedi program is a bit too difficult
for a novice and might deter the user to dig deeper into
the coding. Also, I've added tut3.c which is a stripped
down version of cmd.c and of that again the relevant lines
for the handbook to save space.

demo/Makefile.am
demo/tut2.c
demo/tut3.c [new file with mode: 0644]
demo/tut3_part.c [new file with mode: 0644]
doc/install.xml
doc/tutorial.xml

index 676542081034a88b523384c393768f77cde01a13..523d3eb702ae6edebdf220d08c4c2f99e8c03c83 100644 (file)
@@ -5,7 +5,7 @@ noinst_PROGRAMS = \
        gpct_encoder gpct_pulse_generator \
        gpct_simple_counting inp inpn insn ledclock \
        mmap outp poll receiver select \
-       sender sigio sv tut1 tut2
+       sender sigio sv tut1 tut2 tut3
 
 noinst_HEADERS = examples.h
 
@@ -130,3 +130,6 @@ tut2_CFLAGS = $(COMEDILIB_CFLAGS)
 tut2_LDADD = $(COMEDILIB_LIBS)
 
 
+tut3_SOURCES = tut3.c
+tut3_CFLAGS = $(COMEDILIB_CFLAGS)
+tut3_LDADD = $(COMEDILIB_LIBS)
index 4e9f8ea8b4046d85d1b6867babf5ae51af9fb9d1..23a03a66e81f73d5e31ebfc2924855bd498428cf 100644 (file)
@@ -13,6 +13,8 @@
 #include <stdio.h>             /* for printf() */
 #include <stdlib.h>
 #include <comedilib.h>
+#include <ctype.h>
+#include <math.h>
 
 int subdev = 0;                        /* change this to your input subdevice */
 int chan = 0;                  /* change this to your channel */
@@ -20,90 +22,48 @@ int range = 0;                      /* more on this later */
 int aref = AREF_GROUND;                /* more on this later */
 const char filename[] = "/dev/comedi0";
 
-/* figure out if we are talking to a hardware-calibrated or software-calibrated board,
-       then obtain a comedi_polynomial_t which can be used with comedi_to_physical */
-int get_converter(comedi_t *device, unsigned subdevice, unsigned channel,
-       unsigned range, comedi_polynomial_t *converter)
-{
-       int retval;
-       int flags;
-
-       flags = comedi_get_subdevice_flags(device, subdevice);
-       if(flags < 0)
-       {
-               comedi_perror("comedi_get_subdevice_flags");
-               return -1;
-       }
-
-       if(flags & SDF_SOFT_CALIBRATED) /* board uses software calibration */
-       {
-               char *calibration_file_path = comedi_get_default_calibration_path(device);
-
-               /* parse a calibration file which was produced by the
-                       comedi_soft_calibrate program */
-               comedi_calibration_t* parsed_calibration =
-                       comedi_parse_calibration_file(calibration_file_path);
-               free(calibration_file_path);
-               if(parsed_calibration == NULL)
-               {
-                       comedi_perror("comedi_parse_calibration_file");
-                       return -1;
-               }
-
-               /* get the comedi_polynomial_t for the subdevice/channel/range
-                       we are interested in */
-               retval = comedi_get_softcal_converter(subdevice, channel, range,
-                       COMEDI_TO_PHYSICAL, parsed_calibration, converter);
-               comedi_cleanup_calibration(parsed_calibration);
-               if(retval < 0)
-               {
-                       comedi_perror("comedi_get_softcal_converter");
-                       return -1;
-               }
-       }else /* board uses hardware calibration */
-       {
-               retval = comedi_get_hardcal_converter(device, subdevice, channel, range,
-                       COMEDI_TO_PHYSICAL, converter);
-               if(retval < 0)
-               {
-                       comedi_perror("comedi_get_hardcal_converter");
-                       return -1;
-               }
-       }
-
-       return 0;
-}
-
 int main(int argc, char *argv[])
 {
-       comedi_t *it;
+       comedi_t *device;
        lsampl_t data;
        double physical_value;
        int retval;
-       comedi_polynomial_t converter;
+       comedi_range * range_info;
+       lsampl_t maxdata;
 
-       it = comedi_open(filename);
-       if(it == NULL)
+       device = comedi_open(filename);
+       if(device == NULL)
        {
                comedi_perror(filename);
                return -1;
        }
 
-       retval = comedi_data_read(it, subdev, chan, range, aref, &data);
+       retval = comedi_data_read(device, subdev, chan, range, aref, &data);
        if(retval < 0)
        {
                comedi_perror(filename);
                return -1;
        }
 
-       retval = get_converter(it, subdev, chan, range, &converter);
-       if(retval < 0)
-       {
-               return -1;
+       comedi_set_global_oor_behavior(COMEDI_OOR_NAN);
+       range_info = comedi_get_range(device, subdev, chan, range);
+       maxdata = comedi_get_maxdata(device, subdev, chan);
+       printf("[0,%d] -> [%g,%g]\n", maxdata,
+              range_info->min, range_info->max);
+       physical_value = comedi_to_phys(data, range_info, maxdata);
+       if(isnan(physical_value)) {
+               printf("Out of range [%g,%g]",
+                      range_info->min, range_info->max);
+       } else {
+               printf("%g", physical_value);
+               switch(range_info->unit) {
+               case UNIT_volt: printf(" V"); break;
+               case UNIT_mA: printf(" mA"); break;
+               case UNIT_none: break;
+               default: printf(" (unknown unit %d)",
+                               range_info->unit);
+               }
+               printf(" (%lu in raw units)\n", (unsigned long)data);
        }
-
-       physical_value = comedi_to_physical(data, &converter);
-       printf("%d %g\n", data, physical_value);
-
        return 0;
 }
diff --git a/demo/tut3.c b/demo/tut3.c
new file mode 100644 (file)
index 0000000..c67ef2e
--- /dev/null
@@ -0,0 +1,220 @@
+/*
+ * Example of using commands - asynchronous input
+ * Part of Comedilib
+ *
+ * Copyright (c) 1999,2000,2001 David A. Schleef <ds@schleef.org>
+ *               2008 Bernd Porr <berndporr@f2s.com>
+ *
+ * This file may be freely modified, distributed, and combined with
+ * other software, as long as proper attribution is given in the
+ * source code.
+ */
+
+/*
+ * The program is used to test the usbdux sigma board
+ */
+
+#include <stdio.h>
+#include <comedilib.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/time.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+extern comedi_t *device;
+
+struct parsed_options
+{
+       char *filename;
+       double value;
+       int subdevice;
+       int channel;
+       int aref;
+       int range;
+       int verbose;
+       int n_chan;
+       int n_scan;
+       double freq;
+};
+
+
+
+#define BUFSZ 10000
+char buf[BUFSZ];
+
+#define N_CHANS 256
+static unsigned int chanlist[N_CHANS];
+static comedi_range * range_info[N_CHANS];
+static lsampl_t maxdata[N_CHANS];
+
+
+int prepare_cmd_lib(comedi_t *dev, int subdevice, int n_scan, int n_chan, unsigned period_nanosec, comedi_cmd *cmd);
+
+void do_cmd(comedi_t *dev,comedi_cmd *cmd);
+
+void print_datum(lsampl_t raw, int channel_index);
+
+char *cmdtest_messages[]={
+       "success",
+       "invalid source",
+       "source conflict",
+       "invalid argument",
+       "argument conflict",
+       "invalid chanlist",
+};
+
+int main(int argc, char *argv[])
+{
+       comedi_t *dev;
+       comedi_cmd c,*cmd=&c;
+       int ret;
+       int total=0;
+       int i;
+       struct timeval start,end;
+       int subdev_flags;
+       lsampl_t raw;
+
+       struct parsed_options options;
+
+       /* The following variables used in this demo
+        * can be modified by command line
+        * options.  When modifying this demo, you may want to
+        * change them here. */
+       options.filename = "/dev/comedi0";
+       options.subdevice = 0;
+       options.channel = 0;
+       options.range = 0;
+       options.aref = AREF_GROUND;
+       options.n_chan = 2;
+       options.n_scan = 10000;
+       options.freq = 1000.0;
+
+       /* open the device */
+       dev = comedi_open(options.filename);
+       if(!dev){
+               comedi_perror(options.filename);
+               exit(1);
+       }
+
+       // Print numbers for clipped inputs
+       comedi_set_global_oor_behavior(COMEDI_OOR_NUMBER);
+
+       /* Set up channel list */
+       for(i = 0; i < options.n_chan; i++){
+               chanlist[i] = CR_PACK(options.channel + i, options.range, options.aref);
+               range_info[i] = comedi_get_range(dev, options.subdevice, options.channel, options.range);
+               maxdata[i] = comedi_get_maxdata(dev, options.subdevice, options.channel);
+       }
+
+       /* prepare_cmd_lib() uses a Comedilib routine to find a
+        * good command for the device.  prepare_cmd() explicitly
+        * creates a command, which may not work for your device. */
+       prepare_cmd_lib(dev, options.subdevice, options.n_scan, options.n_chan, 1e9 / options.freq, cmd);
+
+       /* comedi_command_test() tests a command to see if the
+        * trigger sources and arguments are valid for the subdevice.
+        * If a trigger source is invalid, it will be logically ANDed
+        * with valid values (trigger sources are actually bitmasks),
+        * which may or may not result in a valid trigger source.
+        * If an argument is invalid, it will be adjusted to the
+        * nearest valid value.  In this way, for many commands, you
+        * can test it multiple times until it passes.  Typically,
+        * if you can't get a valid command in two tests, the original
+        * command wasn't specified very well. */
+       ret = comedi_command_test(dev, cmd);
+       if(ret < 0){
+               comedi_perror("comedi_command_test");
+               if(errno == EIO){
+                       fprintf(stderr,"Ummm... this subdevice doesn't support commands\n");
+               }
+               exit(1);
+       }
+       ret = comedi_command_test(dev, cmd);
+       if(ret < 0){
+               comedi_perror("comedi_command_test");
+               exit(1);
+       }
+       fprintf(stderr,"second test returned %d (%s)\n", ret,
+                       cmdtest_messages[ret]);
+       if(ret!=0){
+               fprintf(stderr, "Error preparing command\n");
+               exit(1);
+       }
+
+       /* start the command */
+       ret = comedi_command(dev, cmd);
+       if(ret < 0){
+               comedi_perror("comedi_command");
+               exit(1);
+       }
+       subdev_flags = comedi_get_subdevice_flags(dev, options.subdevice);
+       while(1){
+               ret = read(comedi_fileno(dev),buf,BUFSZ);
+               if(ret < 0){
+                       /* some error occurred */
+                       perror("read");
+                       break;
+               }else if(ret == 0){
+                       /* reached stop condition */
+                       break;
+               }else{
+                       static int col = 0;
+                       int bytes_per_sample;
+                       total += ret;
+                       if(options.verbose)fprintf(stderr, "read %d %d\n", ret, total);
+                       if(subdev_flags & SDF_LSAMPL)
+                               bytes_per_sample = sizeof(lsampl_t);
+                       else
+                               bytes_per_sample = sizeof(sampl_t);
+                       for(i = 0; i < ret / bytes_per_sample; i++){
+                               if(subdev_flags & SDF_LSAMPL) {
+                                       raw = ((lsampl_t *)buf)[i];
+                               } else {
+                                       raw = ((sampl_t *)buf)[i];
+                               }
+                               print_datum(raw, col);
+                               col++;
+                               if(col == options.n_chan){
+                                       printf("\n");
+                                       col=0;
+                               }
+                       }
+               }
+       }
+
+}
+
+/*
+ * This prepares a command in a pretty generic way.  We ask the
+ * library to create a stock command that supports periodic
+ * sampling of data, then modify the parts we want. */
+int prepare_cmd_lib(comedi_t *dev, int subdevice, int n_scan, int n_chan, unsigned scan_period_nanosec, comedi_cmd *cmd)
+{
+       int ret;
+
+       memset(cmd,0,sizeof(*cmd));
+
+       /* This comedilib function will get us a generic timed
+        * command for a particular board.  If it returns -1,
+        * that's bad. */
+       ret = comedi_get_cmd_generic_timed(dev, subdevice, cmd, n_chan, scan_period_nanosec);
+       if(ret<0){
+               printf("comedi_get_cmd_generic_timed failed\n");
+               return ret;
+       }
+
+       /* Modify parts of the command */
+       cmd->chanlist = chanlist;
+       cmd->chanlist_len = n_chan;
+       if(cmd->stop_src == TRIG_COUNT) cmd->stop_arg = n_scan;
+
+       return 0;
+}
+
+void print_datum(lsampl_t raw, int channel_index) {
+       double physical_value;
+       physical_value = comedi_to_phys(raw, range_info[channel_index], maxdata[channel_index]);
+       printf("%#8.6g ",physical_value);
+}
diff --git a/demo/tut3_part.c b/demo/tut3_part.c
new file mode 100644 (file)
index 0000000..1daa708
--- /dev/null
@@ -0,0 +1,128 @@
+       /* open the device */
+       dev = comedi_open(options.filename);
+       if(!dev){
+               comedi_perror(options.filename);
+               exit(1);
+       }
+
+       // Print numbers for clipped inputs
+       comedi_set_global_oor_behavior(COMEDI_OOR_NUMBER);
+
+       /* Set up channel list */
+       for(i = 0; i < options.n_chan; i++){
+               chanlist[i] = CR_PACK(options.channel + i,
+                                     options.range, 
+                                     options.aref);
+               range_info[i] = comedi_get_range(dev, 
+                                                options.subdevice, 
+                                                options.channel, options.range);
+               maxdata[i] = comedi_get_maxdata(dev, 
+                                               options.subdevice, 
+                                               options.channel);
+       }
+
+       /* prepare_cmd_lib() uses a Comedilib routine to find a
+        * good command for the device.  prepare_cmd() explicitly
+        * creates a command, which may not work for your device. */
+       prepare_cmd_lib(dev, 
+                       options.subdevice, 
+                       options.n_scan, 
+                       options.n_chan, 
+                       1e9 / options.freq, cmd);
+
+       /* comedi_command_test() tests a command to see if the
+        * trigger sources and arguments are valid for the subdevice.
+        * If a trigger source is invalid, it will be logically ANDed
+        * with valid values (trigger sources are actually bitmasks),
+        * which may or may not result in a valid trigger source.
+        * If an argument is invalid, it will be adjusted to the
+        * nearest valid value.  In this way, for many commands, you
+        * can test it multiple times until it passes.  Typically,
+        * if you can't get a valid command in two tests, the original
+        * command wasn't specified very well. */
+       ret = comedi_command_test(dev, cmd);
+       if(ret < 0){
+               comedi_perror("comedi_command_test");
+               exit(1);
+       }
+       ret = comedi_command_test(dev, cmd);
+       if(ret < 0){
+               comedi_perror("comedi_command_test");
+               exit(1);
+       }
+       fprintf(stderr,"second test returned %d (%s)\n", ret,
+                       cmdtest_messages[ret]);
+       if(ret!=0){
+               fprintf(stderr, "Error preparing command\n");
+               exit(1);
+       }
+
+       /* start the command */
+       ret = comedi_command(dev, cmd);
+       if(ret < 0){
+               comedi_perror("comedi_command");
+               exit(1);
+       }
+       subdev_flags = comedi_get_subdevice_flags(dev, options.subdevice);
+       while(1){
+               ret = read(comedi_fileno(dev),buf,BUFSZ);
+               if(ret < 0){
+                       /* some error occurred */
+                       perror("read");
+                       break;
+               }else if(ret == 0){
+                       /* reached stop condition */
+                       break;
+               }else{
+                       static int col = 0;
+                       int bytes_per_sample;
+                       total += ret;
+                       if(options.verbose)fprintf(stderr, "read %d %d\n", ret, total);
+                       if(subdev_flags & SDF_LSAMPL)
+                               bytes_per_sample = sizeof(lsampl_t);
+                       else
+                               bytes_per_sample = sizeof(sampl_t);
+                       for(i = 0; i < ret / bytes_per_sample; i++){
+                               if(subdev_flags & SDF_LSAMPL) {
+                                       raw = ((lsampl_t *)buf)[i];
+                               } else {
+                                       raw = ((sampl_t *)buf)[i];
+                               }
+                               print_datum(raw, col);
+                               col++;
+                               if(col == options.n_chan){
+                                       printf("\n");
+                                       col=0;
+                               }
+                       }
+               }
+       }
+
+}
+
+/*
+ * This prepares a command in a pretty generic way.  We ask the
+ * library to create a stock command that supports periodic
+ * sampling of data, then modify the parts we want. */
+int prepare_cmd_lib(comedi_t *dev, int subdevice, int n_scan, int n_chan, unsigned scan_period_nanosec, comedi_cmd *cmd)
+{
+       int ret;
+
+       memset(cmd,0,sizeof(*cmd));
+
+       /* This comedilib function will get us a generic timed
+        * command for a particular board.  If it returns -1,
+        * that's bad. */
+       ret = comedi_get_cmd_generic_timed(dev, subdevice, cmd, n_chan, scan_period_nanosec);
+       if(ret<0){
+               printf("comedi_get_cmd_generic_timed failed\n");
+               return ret;
+       }
+
+       /* Modify parts of the command */
+       cmd->chanlist = chanlist;
+       cmd->chanlist_len = n_chan;
+       if(cmd->stop_src == TRIG_COUNT) cmd->stop_arg = n_scan;
+
+       return 0;
+}
index 7d846b0cd4f0bdd23739fce5f6bfe2f6cf65af53..681a4e6f2e73843145607437374c9ee9f6ced1a5 100644 (file)
                        Configuration
                </title>
                <para>
-                       Before being able to get information from a DAQ card, you first have
-                       to tell the &comedi; core kernel module which device you have, which
-                       driver you want to attach to the card, and which run-time options
-                       you want to give to the driver. This configuration is done by running
-                       the <command>comedi_config</command> command (as root).
-                       Here is an example of how to use the command (perhaps you should read
-                       its <command>man</command> page now):
+                 The good news is: on most systems PCI and USB based boards are
+                 configured automatically. The kernel will
+                 detect your data acquisition devices, will load the appropriate
+                 kernel drivers and will create the /dev/comedi entries.
 <screen>
-comedi_config /dev/comedi0 labpc-1200 0x260,3
+bp1@bp1-x61:~/sandbox/comedilib$ ls -l /dev/comedi0*
+crw-rw---- 1 root iocard 98,  0 2012-04-26 23:41 /dev/comedi0
+crw-rw---- 1 root iocard 98, 48 2012-04-26 23:41 /dev/comedi0_subd0
+crw-rw---- 1 root iocard 98, 49 2012-04-26 23:41 /dev/comedi0_subd1
 </screen>
-                       This command says that the <quote>file</quote>
-                       <filename>/dev/comedi0</filename> can be used to access the &comedi;
-                       device that uses the <parameter>labpc-1200</parameter> board, and that
-                       you give it two run-time parameters (<literal>0x260</literal> and
-                       <literal>3</literal>). More parameters are possible, and their
-                       meaning is driver dependant.
+                 Usually these devices belong to the group "iocard" as shown here. The only
+                 action you need to take is to become member of this group and
+                 then the &comedi; device is ready to be used.
                </para>
 
                <para>
-                       This tutorial goes through the process of configuring &comedi;
-                       for two devices, a
-                       <literal>National Instruments AT-MIO-16E-10</literal>, and a
-                       <literal>Data Translation DT2821-F-8DI</literal>.
+                 Old ISA based cards need to be manually configured which is
+                 explained here. You only need to read on here 
+                 if you have one of these old cards.
+                  On embedded systems it might also be necessary to load the
+                  driver and then to configure the boards manually.
+                 In general manual configuration is done by running
+                 the <command>comedi_config</command> command (as root).
+                 Here is an example of how to use the command (perhaps you should read
+                 its <command>man</command> page now):
+                 <screen>
+                   comedi_config /dev/comedi0 labpc-1200 0x260,3
+                 </screen>
+                 This command says that the <quote>file</quote>
+                 <filename>/dev/comedi0</filename> can be used to access the &comedi;
+                 device that uses the <parameter>labpc-1200</parameter> board, and that
+                 you give it two run-time parameters (<literal>0x260</literal> and
+                 <literal>3</literal>). More parameters are possible, and their
+                 meaning is driver dependant.
                </para>
 
                <para>
-                       The NI board is plug-and-play.  The current ni_atmio driver
-                       has kernel-level ISAPNP support, which is used by default
-                       if you do not specify a base address.  So you could simply
-                       run comedi_config as
-<screen>
-comedi_config /dev/comedi0 ni_atmio
-</screen>
-               For the preceding comedi_config command to succeed, the
-               ni_atmio kernel module must
-               be loaded first.    For plug-n-play boards on
-               modern kernels, the appropriate comedi kernel modules should get loaded
-               automatically when your computer is booted.
-               The <command>modprobe</command> command can
-               be used to manually load/unload kernel modules, and <command>lsmod</command>
-               will list all the currently loaded modules.
-               </para>
-               <para>
-                       For the <literal>Data Translation</literal> board, you need to know
-                       how the board's jumpers are configured in order to specify the correct
-                       comedi_config parameters.  These parameters for the board are given in the
-                       <link endterm="lowleveldrivers">kernel drivers</link> section about the dt282x
-                       driver.
-                       The card discussed here is a <literal>DT2821-f-8di</literal>.  The
-                       entry for the dt282x driver tells you that the
-                       comedi_config parameters give the driver the I/O base,
-                       IRQ, DMA 1, DMA 2, and
-                       in addition the states of the
-                       differential/single-ended and unipolar/bipolar jumpers:
-                       <itemizedlist>
-                               <title>dt282x configuration options:</title>
-                               <listitem><para>[0] - I/O port base address</para></listitem>
-                               <listitem><para>[1] - IRQ</para></listitem>
-                               <listitem><para>[2] - DMA 1</para></listitem>
-                               <listitem><para>[3] - DMA 2</para></listitem>
-                               <listitem><para>[4] - AI jumpered for 0=single ended, 1=differential</para></listitem>
-                               <listitem><para>[5] - AI jumpered for 0=straight binary, 1=2's complement</para></listitem>
-                               <listitem><para>[6] - AO 0 jumpered for 0=straight binary, 1=2's complement</para></listitem>
-                               <listitem><para>[7] - AO 1 jumpered for 0=straight binary, 1=2's complement</para></listitem>
-                               <listitem><para>[8] - AI jumpered for 0=[-10,10]V, 1=[0,10], 2=[-5,5], 3=[0,5]</para></listitem>
-                               <listitem><para>[9] - AO 0 jumpered for 0=[-10,10]V, 1=[0,10], 2=[-5,5], 3=[0,5],
-                                       4=[-2.5,2.5]</para></listitem>
-                               <listitem><para>[10]- A0 1 jumpered for 0=[-10,10]V, 1=[0,10], 2=[-5,5], 3=[0,5],
-                                       4=[-2.5,2.5]</para></listitem>
-                       </itemizedlist>
-               </para>
-
-               <para>
-                       So, the appropriate options list might be:
-<screen>
-0x200,4,0,0,1,1,1,1,0,2,2
-</screen>
-                       and the full configuration command is:
-<screen>
-comedi_config /dev/comedi1 dt2821-f-8di 0x200,4,0,0,1,1,1,1,0,2,2
-</screen>
-                       Setting the DMA channels to 0 disables the use of DMA.
+                 This tutorial goes through the process of configuring &comedi;
+                 for two devices, a
+                 <literal>National Instruments AT-MIO-16E-10</literal>, and a
+                 <literal>Data Translation DT2821-F-8DI</literal>.
                </para>
 
+               <para>
+                 The NI board is plug-and-play.  The current ni_atmio driver
+                 has kernel-level ISAPNP support, which is used by default
+                 if you do not specify a base address.  So you could simply
+                 run comedi_config as
+                 <screen>
+                   comedi_config /dev/comedi0 ni_atmio
+                 </screen>
+                 For the preceding comedi_config command to succeed, the
+                 ni_atmio kernel module must
+                 be loaded first.    For plug-n-play boards on
+                 modern kernels, the appropriate comedi kernel modules should get loaded
+                 automatically when your computer is booted.
+                 The <command>modprobe</command> command can
+                 be used to manually load/unload kernel modules, and <command>lsmod</command>
+                 will list all the currently loaded modules.
+               </para>
+               <para>
+                 For the <literal>Data Translation</literal> board, you need to know
+                 how the board's jumpers are configured in order to specify the correct
+                 comedi_config parameters.  These parameters for the board are given in the
+                 <link endterm="lowleveldrivers">kernel drivers</link> section about the dt282x
+                 driver.
+                 The card discussed here is a <literal>DT2821-f-8di</literal>.  The
+                 entry for the dt282x driver tells you that the
+                 comedi_config parameters give the driver the I/O base,
+                 IRQ, DMA 1, DMA 2, and
+                 in addition the states of the
+                 differential/single-ended and unipolar/bipolar jumpers:
+                 <itemizedlist>
+                   <title>dt282x configuration options:</title>
+                   <listitem><para>[0] - I/O port base address</para></listitem>
+                   <listitem><para>[1] - IRQ</para></listitem>
+                   <listitem><para>[2] - DMA 1</para></listitem>
+                   <listitem><para>[3] - DMA 2</para></listitem>
+                   <listitem><para>[4] - AI jumpered for 0=single ended, 1=differential</para></listitem>
+                   <listitem><para>[5] - AI jumpered for 0=straight binary, 1=2's complement</para></listitem>
+                   <listitem><para>[6] - AO 0 jumpered for 0=straight binary, 1=2's complement</para></listitem>
+                   <listitem><para>[7] - AO 1 jumpered for 0=straight binary, 1=2's complement</para></listitem>
+                   <listitem><para>[8] - AI jumpered for 0=[-10,10]V, 1=[0,10], 2=[-5,5], 3=[0,5]</para></listitem>
+                   <listitem><para>[9] - AO 0 jumpered for 0=[-10,10]V, 1=[0,10], 2=[-5,5], 3=[0,5],
+                       4=[-2.5,2.5]</para></listitem>
+                   <listitem><para>[10]- A0 1 jumpered for 0=[-10,10]V, 1=[0,10], 2=[-5,5], 3=[0,5],
+                       4=[-2.5,2.5]</para></listitem>
+                 </itemizedlist>
+               </para>
+               
+               <para>
+                 So, the appropriate options list might be:
+                 <screen>
+                   0x200,4,0,0,1,1,1,1,0,2,2
+                 </screen>
+                 and the full configuration command is:
+                 <screen>
+                   comedi_config /dev/comedi1 dt2821-f-8di 0x200,4,0,0,1,1,1,1,0,2,2
+                 </screen>
+                 Setting the DMA channels to 0 disables the use of DMA.
+               </para>
+               
                <para>
                        So now you have your boards configured correctly.
                        Since data acquisition boards are not typically well-engineered,
@@ -134,9 +152,9 @@ comedi0: ni_atmio: 0x0260 at-mio-16e-10 ( irq = 3 )
 </screen>
 
                <para>
-                       Note that it also correctly identified the board.
+                 Note that it also correctly identified the board.
                </para>
-
+               
        </section>
 
        <section id="gettinginformation">
@@ -145,10 +163,10 @@ comedi0: ni_atmio: 0x0260 at-mio-16e-10 ( irq = 3 )
                </title>
 
                <para>
-                       So now that you have &comedi; talking to the hardware, try to
-                       talk to &comedi;.  Here's some information from comedi's proc
-                       file, which indicates what drivers are loaded and which
-                       boards are configured:
+                 So now that you have &comedi; talking to the hardware, try to
+                 talk to &comedi;.  Here's some information from comedi's proc
+                 file, which indicates what drivers are loaded and which
+                 boards are configured:
                </para>
 
 <screen>
@@ -156,17 +174,17 @@ cat /proc/comedi
 </screen>
 
                <para>
-                       For example, on a computer with an NI pxi-6281 configured on
-                       <filename>/dev/comedi0</filename> and
-                       a pxi-6602 configured on <filename>/dev/comedi1</filename> you might
-                       see something like:
+                 For example, on a computer with an NI pxi-6281 configured on
+                 <filename>/dev/comedi0</filename> and
+                 a pxi-6602 configured on <filename>/dev/comedi1</filename> you might
+                 see something like:
                </para>
-
+               
 <screen>
 comedi version 0.7.74
 format string: "%2d: %-20s %-20s %4d",i,driver_name,board_name,n_subdevices
- 0: ni_pcimio            pxi-6281               14
- 1: ni_660x              PXI-6602               10
+0: ni_pcimio            pxi-6281               14
+1: ni_660x              PXI-6602               10
 ni_pcimio:
  ni_pcimio
 8255:
index ad1ee8d0d700a3d5bb7d5eb107d7d5127408dff8..a7fb4dc61f23342392194e21eb8adf9c5abff79a 100644 (file)
 ]>
 
 <section id="writingprograms" xmlns:xi="http://www.w3.org/2001/XInclude">
-<title>
-Writing &comedi; programs
-</title>
-<para>
-This section describes how &comedi;
-can be used in an application, to communicate data with a set
-of &comedi; devices.
-<xref linkend="acquisitionfunctions"/> gives more details about
-the various acquisition functions with which the application
-programmer can perform data acquisition in &comedi;.
-</para>
-<para>
-Also don't forget to take a good look at the
-<filename class="directory">demo</filename>
-directory of the Comedilib source code. It contains lots of examples
-for the basic functionalities of &comedi;.
-</para>
-
-<section id="firstprogram">
-<title>
-Your first &comedi; program
-</title>
-
-<para>
-This example requires a card that has analog or digital input. This
-progam opens the device, gets the data, and prints it out:
-<programlisting>
-<xi:include href="../demo/tut1.c" parse="text"/>
-</programlisting>
-</para>
-<para>
-The source code file for the above program can be found in Comedilib,
-at <filename>demo/tut1.c</filename>.  You can compile the program using
-</para>
-
-<screen>
-cc tut1.c -lcomedi -o tut1
-</screen>
-<para>
-The
-<function>
- <link linkend="func-ref-comedi-open">comedi_open</link>
-</function> call can only be successful if the
-<filename>comedi0</filename> device file is configured with a
-valid &comedi; driver. <xref linkend="cardconfiguration"/> explains
-how this driver is linked to the <quote>device file</quote>.
-</para>
-<para>
-The <parameter class="function">range</parameter> 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 <literal>0</literal>, because it won't cause errors.  Likewise
-with <parameter class="function">aref</parameter>, which determines the
-analog reference used.
-</para>
-</section>
-
-
-<section id="convertingsamples">
-<title>
-Converting between integer data and physical units
-</title>
-
-<para>
-If you selected an analog input subdevice, you probably noticed
-that the output of <command>tut1</command> is an unsigned number, for
-example between <literal>0</literal> and <literal>65535</literal>
-for a 16 bit analog input. &comedi; samples are
-unsigned,
-with <literal>0</literal>  representing the lowest voltage of the ADC,
-and a hardware-dependent maximum value representing the highest voltage.
-&comedi; compensates for anything else the manual for
-your device says (for example, many boards represent bipolar
-analog input voltages as signed integers).  However, you probably prefer to have this number
-translated to a voltage.  Naturally, as a good programmer, your first
-question is: <quote>How do I do this in a device-independent
-manner?</quote>
-</para>
-
-<para>
-The functions
-<link linkend="func-ref-comedi-to-physical"><function>comedi_to_physical</function></link> and
-<link linkend="func-ref-comedi-from-physical"><function>comedi_from_physical</function></link>
-are used to convert between &comedi;'s integer data and floating point numbers corresponding
-to physical values (voltages, etc.).  In order to use the conversion functions, you must
-first obtain a <link linkend="ref-type-comedi-polynomial-t">comedi_polynomial_t</link>
-corresponding to the subdevice, range, and possibly channel the integer data is associated
-with.  The comedi_polynomial_t object specifies the polynomial function that will be applied
-to convert between &comedi;'s integer data and physical values.
-</para>
-
-<para>
-A  <link linkend="ref-type-comedi-polynomial-t">comedi_polynomial_t</link> may be obtained
-from one of two functions:
-<link linkend="func-ref-comedi-get-hardcal-converter"><function>comedi_get_hardcal_converter</function></link> or
-<link linkend="func-ref-comedi-get-softcal-converter"><function>comedi_get_softcal_converter</function></link>.
-Which function to use depends on whether your board does calibration in hardware, or relies on
-the host computer for a software calibration.  The
-SDF_SOFT_CALIBRATED flag (queried by calling
-<link linkend="func-ref-comedi-get-subdevice-flags"><function>comedi_get_subdevice_flags</function></link>)
-will be set for boards that use software calibration.
-</para>
-
+  <title>
+    Writing &comedi; programs
+  </title>
+  <para>
+    This section describes how &comedi;
+    can be used in an application, to communicate data with a set
+    of &comedi; devices.
+    <xref linkend="acquisitionfunctions"/> gives more details about
+    the various acquisition functions with which the application
+    programmer can perform data acquisition in &comedi;.
+  </para>
+  <para>
+    Also don't forget to take a good look at the
+    <filename class="directory">demo</filename>
+    directory of the Comedilib source code. It contains lots of examples
+    for the basic functionalities of &comedi;.
+  </para>
+  
+  <section id="firstprogram">
+    <title>
+      Your first &comedi; program
+    </title>
+    
+    <para>
+      This example requires a card that has analog or digital input. This
+      progam opens the device, gets the data, and prints it out:
+      <programlisting>
+       <xi:include href="../demo/tut1.c" parse="text"/>
+      </programlisting>
+    </para>
+    <para>
+      The source code file for the above program can be found in Comedilib,
+      at <filename>demo/tut1.c</filename>.  You can compile the program using
+    </para>
+    
+    <screen>
+      cc tut1.c -lcomedi -o tut1
+    </screen>
+    <para>
+      The
+      <function>
+       <link linkend="func-ref-comedi-open">comedi_open</link>
+      </function> call can only be successful if the
+      <filename>comedi0</filename> device file is configured with a
+      valid &comedi; driver. <xref linkend="cardconfiguration"/> explains
+      how this driver is linked to the <quote>device file</quote>.
+    </para>
+    <para>
+      The <parameter class="function">range</parameter> 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 <literal>0</literal>, because it won't cause errors.  Likewise
+      with <parameter class="function">aref</parameter>, which determines the
+      analog reference used.
+    </para>
+  </section>
+
+
+  <section id="convertingsamples">
+    <title>
+      Converting between integer data and physical units
+    </title>
+    
+    <para>
+      If you selected an analog input subdevice, you probably noticed
+      that the output of <command>tut1</command> is an unsigned number, for
+      example between <literal>0</literal> and <literal>65535</literal>
+      for a 16 bit analog input. &comedi; samples are
+      unsigned,
+      with <literal>0</literal>  representing the lowest voltage of the ADC,
+      and a hardware-dependent maximum value representing the highest voltage.
+      &comedi; compensates for anything else the manual for
+      your device says (for example, many boards represent bipolar
+      analog input voltages as signed integers).  
+      However, you probably prefer to have this number
+      translated to a voltage.  Naturally, as a good programmer, your first
+      question is: <quote>How do I do this in a device-independent
+    manner?</quote>
+    </para>
+    
+    <para>
+      The functions
+      <link linkend="func-ref-comedi-to-physical"><function>comedi_to_physical</function></link>, <link linkend="func-ref-comedi-to-phys"><function>comedi_to_phys</function></link>,
+      <link linkend="func-ref-comedi-from-physical"><function>comedi_from_physical</function></link> and <link linkend="func-ref-comedi-from-phys"><function>comedi_from_phys</function></link>
+      are used to convert between &comedi;'s integer data and floating point numbers corresponding
+      to physical values (voltages, etc.).
+    </para>
+    
+  </section>
+
+  <section id="secondprogram">
+    <title>
+      Your second &comedi; program
+    </title>
+    
+    
+    <para>
+      Actually, this is the first &comedi; program again, except
+      we've added code to convert the integer data value to physical units.
+    </para>
+    
+    <programlisting>
+      <xi:include href="../demo/tut2.c" parse="text"/>
+    </programlisting>
+    <para>
+      The source code file for the above program can be found in Comedilib, at demo/tut2.c.
+    </para>
+  </section>
+
+  <section id="asyncprogram">
+    <title>
+      Asynchronous acquisition
+    </title>
+    <para>
+      Of special importance is the so called
+      "asynchronous data acquisition" where the &comedi; is sampling
+      in the background at a given sample rate. The
+      the user can retrieve the data whenever it is convenient.
+      &comedi; stores the data in a ring-buffer so that
+      programs can perform other tasks in the foreground, for example
+      plotting data or interacting with the user. 
+      This technique is used in programs such
+      as <command>ktimetrace</command> or <command>comedirecord</command>.
+    </para>
+    <para>
+      The program <command>tut3.c</command> demonstrates the
+      asynchronous acquisition. The general strategy is always
+      the same: first, we tell &comedi; all sampling parameters such as
+      the sampling rate,
+      the number of channels and anything it needs to know
+      so that it can run independently in the background.
+      Then &comedi; checks our request and it might
+      modify it. For example we might want to have a sampling rate of
+      16kHz but we only get 1kHz. Finally we can start
+      the asynchronous acquisition. Once it is started we
+      need to check periodically if data is available and
+      request it from &comedi; so that its internal buffer
+      won't overrun.
+    </para>
+    <para>
+      The program below is a stripped down version
+      of the program <command>cmd.c</command> in
+      the demo directory. To compile it run:
+    </para>
+    <screen>
+      gcc tut3.c -lcomedi -lm -o tut3
+    </screen>
+    <para>
+      It requests data from two channels at 
+      a sampling rate of 1kHz and a total of 10000 samples.
+      which are then printed to stdout. You can pipe the data
+      into a file and plot it with gnuplot. Central in this
+      program is the loop using the standard C read command
+      which receives the buffer contents. Below is an
+      extract from <filename>tut3.c</filename> showing the
+      relevant commands:
+    </para>
+    <programlisting>
+      <xi:include href="../demo/tut3_part.c" parse="text"/>
+    </programlisting>
+  </section>
+  
+  <section>
+    <title>Further examples</title>
+    <para>
+      See the demo subdirectory of Comedilib for more example programs.
+      The directory contains
+      a README file with descriptions of the various demo programs.
+    </para>
+  </section>
+  
 </section>
-
-<section id="secondprogram">
-<title>
-Your second &comedi; program
-</title>
-
-
-<para>
-Actually, this is the first &comedi; program again, except
-we've added code to convert the integer data value to physical units.
-The program handles both the case of a software-calibrated
-board and a hardware-calibrated board.
-</para>
-
-<programlisting>
-<xi:include href="../demo/tut2.c" parse="text"/>
-</programlisting>
-<para>
-The source code file for the above program can be found in Comedilib, at demo/tut2.c.
-</para>
-</section>
-
-<section>
-       <title>Further examples</title>
-       <para>
-               See the demo subdirectory of Comedilib for more example programs.  The directory contains
-               a README file with descriptions of the various demo programs.
-       </para>
-</section>
-</section>
-