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
tut2_LDADD = $(COMEDILIB_LIBS)
+tut3_SOURCES = tut3.c
+tut3_CFLAGS = $(COMEDILIB_CFLAGS)
+tut3_LDADD = $(COMEDILIB_LIBS)
#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 */
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;
}
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+ /* 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;
+}
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,
</screen>
<para>
- Note that it also correctly identified the board.
+ Note that it also correctly identified the board.
</para>
-
+
</section>
<section id="gettinginformation">
</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>
</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:
]>
<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>
-