--- /dev/null
+<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.
+
+
+
+
+
+
+
+
+<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;
+ 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.
+Compile it using
+
+<tscreen><verb>
+cc tut1.c -lcomedi -o tut1
+</verb></tscreen>
+
+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 probably noticed
+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?"
+
+Most devices give you a choice of gain and unipolar/bipolar
+input, and Comedi allows you to select which of these to
+use. This parameter is called the "range parameter", since
+it specifies the "input range" for analog input (or "output range"
+for analog output.) The range parameter represents both the gain
+and the unipolar/bipolar aspects.
+
+Comedi keeps the number of available ranges and the largest
+sample value for each subdevice/channel combination. (Some
+devices allow different input/output ranges for different
+channels in a subdevice.)
+
+The largest sample value can be found using the function:
+
+ comedi_get_maxdata()
+
+The number of available ranges can be found using the function:
+
+ comedi_get_n_ranges()
+
+For each value of the range parameter for a particular
+subdevice/channel, you can get range information using 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>
+
+The structure element 'min' represents
+the voltage corresponding to comedi_data_read() returning 0,
+and 'max' represents comedi_data_read() returning 'maxdata',
+(i.e., 4095 for 12 bit A/C converters, 65535 for 16 bit,
+or, 1 for digital input -- more on this in a bit.) The
+'unit' entry tells you if min and
+max refer to voltage, current, 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>
+
+
+<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> /* 'cuz we're using comedilib */
+
+int subdev = 0; /* change this to your input subdevice */
+int chan = 0; /* change this to your channel */
+int range = 0; /* more on this later */
+int aref = 0; /* more on this later */
+
+int main(int argc,char *argv[])
+{
+ comedi_t *cf;
+ int chan=0;
+ lsampl_t data;
+ int maxdata,rangetype;
+ double volts;
+
+ cf=comedi_open("/dev/comedi0");
+
+ maxdata=comedi_get_maxdata(cf,subdev,chan);
+
+ rangetype=comedi_get_rangetype(cf,subdev,chan);
+
+ comedi_data_read(cf->fd,subdev,chan,range,aref,&data);
+
+ volts=comedi_to_phys(data,rangetype,range,maxdata);
+
+ printf("%d %g\n",data,volts);
+
+ return 0;
+}
+</verb></tscreen>
+
+
+
+
+
+
+<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.
+
+Individual digital lines can be read and written using the
+functions
+
+ <tt/comedi_dio_read(device,subdevice,channel,unsigned int *bit);/
+ <tt/comedi_dio_write(device,subdevice,channel,unsigned int bit);/
+
+The direction of bidirectional lines can be configured using
+the function
+
+ <tt/comedi_dio_config(device,subdevice,channel,unsigned int dir);/
+
+The parameter <tt/dir/ should be either COMEDI_INPUT or COMEDI_OUTPUT.
+Many digital I/O subdevices group channels into blocks for
+configuring direction. Changing one channel in a block changes
+the entire block.
+
+Multiple channels can be read and written simultaneously using the
+function
+
+ <tt/comedi_dio_bitfield(device,subdevice,unsigned int write_mask,unsigned int *bits);/
+
+Each channel is assigned to a bit in the <tt/write_mask/ and <tt/bits/
+bitfield. If a bit in <tt/write_mask/ is set, the corresponding bit
+in <tt/*bits/ will be written to the corresponding digital output line.
+Each digital line is then read and placed into <tt/*bits/. The value
+of bits in <tt/*bits/ corresponding to digital output lines is
+undefined and device-specific. Channel 0 is the least significant
+bit in the bitfield; channel 31 is the most significant bit. Channels
+higher than 31 cannot be accessed using this method.
+
+
+<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>
+<sect1>Commands
+<label id="command_section">
+<p>
+
+
+Many data acquisition devices have the capability to directly
+control acquisition using either an on-board timer or an external
+triggering input. Comedi commands are used to control this kind
+of acquisition. The <ref id="comedi_cmd" name="comedi_cmd"> structure is
+used to control acquisition and query the capabilities of a device
+(see also <ref id="comedi_command" name="comedi_command()">,
+<ref id="comedi_command_test" name="comedi_command_test()">, and
+<ref id="comedi_get_cmd_src_mask" name="comedi_get_cmd_src_mask()">).
+
+Commands specify a particular data acquisition sequence, which
+is comprised of a number of scans. Each scan is comprised of
+a number of conversions, which usually corresponds to a single
+A/D or D/A conversion. The start and end of the sequence, and
+the start and end of each scan, and each conversion is called an
+event.
+
+Each of these 5 types of events are caused by a triggering
+source, specified through the <tt/*_src/ members of the
+<ref id="comedi_cmd" name="comedi_cmd"> structure. The source types are:
+
+<itemize>
+<item>TRIG_NONE: don't ever cause an event
+<item>TRIG_NOW: cause event to occur immediately
+<item>TRIG_FOLLOW: see notes below
+<item>TRIG_TIME: cause event to occur at a particular time
+<item>TRIG_TIMER: cause event to occur repeatedly at a specific rate
+<item>TRIG_COUNT: cause event when count reaches specific value
+<item>TRIG_EXT: external signal causes event
+<item>TRIG_INT: internal signal causes event
+<item>TRIG_OTHER: driver-specific meaning
+</itemize>
+
+Not all triggers are applicable to all events. Supported triggers
+for specific events depend significantly on your particular
+device. The <ref id="comedi_get_cmd_src_mask" name="comedi_get_cmd_src_mask()">
+function is useful for determining what triggers a subdevice supports.
+
+For every trigger, there is a corresponding
+argument (the <tt/*_arg/ members of the <ref id="comedi_cmd" name="comedi_cmd">
+structure) whose meaning depends on the type of trigger. The meanings
+of the arguments are as follows:
+
+TRIG_NONE is typically used only as a <tt/stop_src/. The argument for TRIG_NONE
+is reserved and should be set to 0.
+
+TRIG_NOW is most often used as a <tt/start_src/. The argument for TRIG_NOW is
+the number of nanoseconds between when the command is issued and when
+the event should occur. In the case of using TRIG now as a <tt/start_src/,
+it indicates a delay between issuing the command and the start of
+acquisition. Most drivers only support a delay of 0.
+
+TRIG_FOLLOW is a special type of trigger for events that trigger on
+the completion of some other, logically connected event. The argument
+is reserved and should be set to 0. When used
+as a <tt/scan_begin_src/, it indicates that a trigger should occur as a
+logical continuation of convert events. This is done in order to
+properly describe boards that do not have separate timers for
+convert and scan_begin events. When used as a <tt/start_src/ for analog
+output subdevices, it indicates that conversion of output samples
+should begin when samples are written to the buffer.
+
+TRIG_TIME is reserved for future use.
+
+TRIG_TIMER is most often used as a <tt/convert_src/, a <tt/scan_begin_src/, or
+both. It indicates that triggers should occur at a specific rate.
+The argument specifies the interval between triggers in nanoseconds.
+
+TRIG_COUNT is used for <tt/scan_end_src/ and <tt/stop_src/. It indicates that
+a trigger should occur when the specified number of corresponding
+lower-level triggers (convert and scan_begin, respectively) occur.
+The argument is the count of lower-level triggers.
+
+TRIG_EXT can be useful as any of the trigger sources. It indicates
+that an external digital line should be used to trigger the event.
+The exact meaning of digital line is device-dependent. Some devices
+have one dedicated line, others may allow generic digital input
+lines to be used. The argument indicates the particular external
+line to use as the trigger.
+
+TRIG_INT is typically used as a <tt/start_src/. This trigger occurs when
+the application performs an INSN_INTTRIG instruction. Using TRIG_INT
+is a method by which the application can accurately record the time of
+the start of acquisition, since the parsing and setup time of a
+particular command may be significant. The argument associated with
+TRIG_INT is reserved and should be set to 0.
+
+TRIG_OTHER can be useful as any of the trigger sources. The exact
+meaning of TRIG_OTHER is driver-specific, and implements a feature
+that otherwise does not fit into the command interface. Configuration
+of TRIG_OTHER features are done by INSN_CONFIG insns. The argument
+is reserved and should be set to 0.
+
+Ths <tt/subdev/ member of the <ref id="comedi_cmd" name="comedi_cmd">
+structure is the index of the subdevice the command is intended for. The
+<ref id="comedi_find_subdevice_by_type" name="comedi_find_subdevice_by_type()">
+function can be useful in discovering the index of your desired subdevice.
+
+The <tt/chanlist/ member of the <ref id="comedi_cmd" name="comedi_cmd">
+structure should point to an array whose number of elements is specificed by <tt/chanlist_len/
+(this will generally be the same as the scan_end_arg).
+The chanlist specifies the sequence of channels and gains (and analog references)
+that should be stepped through for each scan. The elements of the chanlist array
+should be initialized by packing the channel, range and reference information
+together with the <ref id="CR_PACK" name="CR_PACK()"> macro.
+
+The <tt/data/ and <tt/data_len/ members can be safely ignored when issueing commands
+from a user-space program. They only have meaning when a command is sent from a kernel
+module using the kcomedilib interface, in which case they specify the buffer where
+the driver should write/read its data to/from.
+
+The final member of the <ref id="comedi_cmd" name="comedi_cmd"> structure is <tt/flags/.
+The following flags are valid, and can be bitwise-or'd together.
+
+<itemize>
+<item>TRIG_BOGUS: do the motions??
+<item>TRIG_DITHER: enable dithering??
+<item>TRIG_DEGLITCH: enable deglitching??
+<item>TRIG_RT: ask driver to use a hard real-time interrupt handler. This will
+reduce latency in handling interrupts from your data aquisition hardware. It can
+be useful if you are sampling at high frequency, or if your hardware has a small onboard
+fifo. You must have a real-time kernel (RTAI or RTLinux) and must compile
+comedi with real-time support or this flag will do nothing.
+<item>TRIG_CONFIG: perform configuration, not triggering. This is a legacy of the
+deprecated comedi_trig_struct, and has no function at present.
+<item>TRIG_WAKE_EOS: some drivers will change their behaviour when this flag is set,
+trying to transfer data at the end of every scan (instead of, for example, passing
+data in chunks whenever the board's onboard fifo is half full). This flag
+may degrade a driver's performance at high frequencies.
+<item>TRIG_WRITE: write to bidirectional devices. Could be useful in principle, if someone
+wrote a driver that supported commands for a digital i/o device that could do either
+input or output.
+</itemize>
+There are also a few flags that indicate how timing arguments should be rounded
+if the hardware cannot achieve the exact timing requested.
+<itemize>
+<item>TRIG_ROUND_NEAREST: round to nearest supported timing period, the default.
+<item>TRIG_ROUND_DOWN: round period down.
+<item>TRIG_ROUND_UP: round period up.
+<item>TRIG_ROUND_UP_NEXT: this one doesn't do anything, and I don't know what it was intended
+to do??
+</itemize>
+
+<p>
+
+The typical sequence for executing a command is to first send
+the command through
+<ref id="comedi_command_test" name="comedi_command_test()">
+once or twice. The test will check that the command is valid for the particular
+device, and often makes some adjustments to the command arguments, which
+can then be read back by the user to see the actual values used. The
+command is executed with
+<ref id="comedi_command" name="comedi_command()">. For input/output commands, data
+is read from or written to the device file /dev/comedi[0..3] you are using.
+
-<!DOCTYPE article PUBLIC "-//OASIS//DTD DocBook V3.1//EN" [
+<!DOCTYPE article PUBLIC "-//OASIS//DTD DocBook V3.1//EN"
+ "docbook/dtd/3.1/docbook.dtd" [
<!ENTITY intro SYSTEM "intro.sgml">
<!ENTITY install SYSTEM "install.sgml">
<!ENTITY tutorial SYSTEM "tutorial.sgml">
<!ENTITY other SYSTEM "other.sgml">
+<!ENTITY driverwriting SYSTEM "driverwriting.sgml">
<!ENTITY drivers SYSTEM "drivers.sgml">
<!ENTITY reference SYSTEM "reference.sgml">
<!ENTITY funcref SYSTEM "funcref.sgml">
<!ENTITY glossary SYSTEM "glossary.sgml">
+<!ENTITY comedi "<acronym>Comedi</acronym>">
]>
<article>
<artheader>
<title>
- Comedi Documentation
+ Comedi
</title>
+ <subtitle>
+The <emphasis>Control and Measurement Device Interface</emphasis>
+handbook
+ </subtitle>
<author>
<firstname>David</firstname>
<surname>Schleef</surname>
</address>
</affiliation>
</author>
+ <copyright>
+ <year>1998-2003</year>
+ <holder>David Schleef</holder>
+ </copyright>
+
+ <abstract>
+ <para>
+ <emphasis role="strong">Abstract</emphasis>
+ </para>
+ <para>
+&comedi; is a free software project to interface
+<emphasis>digital acquisition</emphasis> (DAQ) cards. It is the
+combination of three complementary software items: (i) a generic,
+device-independent API, (ii) a collection of Linux kernel modules that
+implement this API for a wide range of cards, and (iii) a Linux user
+space library with a developer-oriented programming interface to
+configure and use the cards.
+ </para>
+ </abstract>
+
+ <legalnotice>
+ <para>
+This document is part of Comedilib. In the context of this
+document, the term "source code" as defined by the license is
+interpreted as the SGML source.
+
+COMEDILIB - Linux Control and Measurement Device Interface Library
+Copyright (C) 1997-2003 David A. Schleef <ds@schleef.org>
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation, version 2.1
+of the License.
+
+This library 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
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+USA.
+ </legalnotice>
+
</artheader>
-&intro
+&intro;
-&install
+&install;
-&tutorial
+&tutorial;
-&other
+&other;
-&drivers
+&driverwriting;
-<section>
+<section id="lowleveldrivers">
<title>
- Comedi Reference
+ Low-level drivers
+ </title>
+
+&drivers;
+
+</section>
+
+<section id="comedireference">
+ <title>
+ &comedi; Reference
</title>
<para>
- Reference for functions, macros, and constants.
+ Reference for
+ <link linkend="constantsmacros">constants and macros</link>,
+ <link linkend="datatypesstructures">data types and structures</link>,
+ and <link linkend="functionreference">functions</link>.
</para>
- &reference
+ &reference;
- &funcref
+<!-- </section> -->
- &glossary
+ &funcref;
</section>
-
+ &glossary;
</article>
--- /dev/null
+<section id="driverwriting">
+<title>
+Writing a &comedi; driver
+</title>
+
+<para>
+This Section explains the most important implementations aspects of
+the &comedi; device drivers. It tries to give the interested device
+driver writer an overview of the different steps required to write a
+new device driver.
+</para>
+<para>
+This Section does <emphasis>not</emphasis> explain all implementation
+details of the &comedi; software itself: &comedi; has once and for
+all solved lots of boring but indispensable infrastructural things,
+such as: timers, management of which drivers
+are active, memory management for drivers and buffers, wrapping
+of RTOS-specific interfaces, interrupt handler management, general
+error handling, the <filename role=directory>/proc</filename>
+interface, etc. So,
+the device driver writers can concentrate on the interesting stuff:
+implementing their specific interface card's DAQ functionalities.
+</para>
+<para>
+In order to make a decent &comedi; device driver, you must
+know the answers to the following questions:
+<itemizedlist>
+
+<listitem>
+<para>
+How does the
+<link linkend="userkernelhow">communication</link> between user space
+and kernel space work?
+</para>
+</listitem>
+
+<listitem>
+<para>
+What functionality is provided by the
+<link linkend="comedikernelgeneric">generic</link> kernel-space
+&comedi; functions, and what must be provided for each
+<link linkend="boardspecific">specific new driver</link>?
+</para>
+</listitem>
+
+<listitem>
+<para>
+How to use <link linkend="drivercallbacks">DMA and interrupts</link>?
+</para>
+</listitem>
+
+<listitem>
+<para>
+What are the addresses and meanings of all the card's registers?
+</para>
+<para>
+This information is to be found in the so-called “register level
+manual” of the card. Without it, coding a device driver is close
+to hopeless. It is also something that &comedi; (and hence also this
+handbook) cannot give any support or information for: board
+manufacturers all use their own design and nomenclature.
+</para>
+</listitem>
+
+</itemizedlist>
+</para>
+
+<section id="userkernelhow">
+<title>
+Communication user space-kernel space
+</title>
+
+<para>
+In user space, you interact with the functions implemented in the
+<filename role=directory>/usr/src/comedilib</filename> directory. Most
+of the device driver core of the Comedilib library is found in
+<filename role=directory>lib</filename> subdirectory.
+</para>
+<para>
+All user-space &comedi;
+<link linkend="instructions">instructions</link> and
+<link linkend="commandsstreaming">commands</link>
+are transmitted to kernel space through a traditional
+<function>ioctl</function> system call.
+(See <filename>/usr/src/comedilib/lib/ioctl.c</filename>.)
+The user space information command is <emphasis>encoded</emphasis> as
+a number in the <function>ioctl</function> call, and decoded in the
+kernel space library. There, they are executed by their kernel-space
+counterparts. This is done in the
+<filename>/usr/src/comedi/comedi/comedi_fops.c</filename> file: the
+<function>comedi_ioctl()</function> function processes the results of
+the <function>ioctl</function> system call, interprets its contents,
+and then calls the corresponding kernel space
+<function>do_…_ioctl</function> function(s).
+For example, a &comedi;
+<link linkend="instructions">instruction</link> is further processed
+by the <function>do_insn_ioctl()</function>function. (Which, in turn,
+uses <function>parse_insn()</function> for further detailed processing.)
+</para>
+<para>
+The data corresponding to instructions and commands is transmitted
+with the <function>copy_from_user()</function> system call;
+acquisition data captured by the interface card passes the kernel-user
+space boundary with the help of a <function>copy_to_user()</function>
+system call.
+</para>
+
+</section>
+
+<section id="comedikernelgeneric">
+<title>
+Generic functionality
+</title>
+
+<para>
+The major include files of the kernel-space part of &comedi; are:
+<itemizedlist>
+
+<listitem>
+<para>
+<filename>include/linux/comedidev.h</filename>: the
+header file for kernel-only structures (device, subdevice, async
+(i.e., buffer/event/interrupt/callback functionality for asynchronous
+DAQ in a &comedi; command), driver, lrange), variables, inline functions
+and constants.
+</para>
+</listitem>
+
+<listitem>
+<para>
+<filename>include/linux/comedi_rt.h</filename>:
+all the real-time stuff, such as management of ISR in RTAI and
+RTLinux/Free, and spinlocks for atomic sections.
+</para>
+</listitem>
+
+<listitem>
+<para>
+<filename>include/linux/comedilib.h</filename>: the header file for
+the kernel library of &comedi;.
+</para>
+</listitem>
+
+</itemizedlist>
+</para>
+<para>
+From all the relevant &comedi; device driver code that is found in the
+<filename role=directory>/usr/src/comedi/comedi</filename> directory
+(<emphasis>if</emphasis> the &comedi; source has been installed in its
+normal <filename role=directory>/usr/src/comedi</filename> location),
+the <emphasis role="strong">generic</emphasis> functionality is
+contained in two parts:
+ <itemizedlist>
+
+ <listitem>
+ <para>
+A couple of <filename>C</filename> files contain the <emphasis
+role="strong">infrastructural support</emphasis>.
+From these <filename>C</filename> files, it's especially the
+<filename>comedi_fops.c</filename> file that implements what makes
+&comedi; into what people want to use it for: a library that has
+solved 90% of the DAQ device driver efforts, once and for all.
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+For <emphasis role="strong">real-time</emphasis> applications,
+the subdirectory <filename role=directory>kcomedilib</filename>
+implements an interface in the kernel that is similar to the &comedi;
+interface accessible through the
+<link linkend="functionreference">user-space Comedi library</link>.
+</para>
+<para>
+There are some differences in what is possible and/or needed
+in kernel space and in user space, so the functionalities offered in
+<filename role=directory>kcomedilib</filename> are not an exact copy
+of the user-space library. For example, locking, interrupt handling,
+real-time execution, callback handling, etc., are only available in
+kernel space.
+ </para>
+ <para>
+Most drivers don't make use (yet) of these real-time functionalities.
+ </para>
+ </listitem>
+
+ </itemizedlist>
+</para>
+
+
+<section id="driverdatastructures">
+<title>
+Data structures
+</title>
+
+<para>
+This Section explains the generic data structures that a device driver
+interacts with:
+<programlisting>
+typedef struct comedi_lrange_struct <link linkend="comedilrange">comedi_lrange</link>;
+typedef struct comedi_subdevice_struct <link linkend="comedisubdevice">comedi_subdevice</link>;
+typedef struct comedi_device_struct <link linkend="comedidevice">comedi_device</link>:
+typedef struct comedi_async_struct <link linkend="comediasync">comedi_async</link>
+typedef struct comedi_driver_struct <link linkend="comedidriver">comedi_driver</link>;
+</programlisting>
+They can be found in
+<filename>/usr/src/comedi/include/linux/comedidev.h</filename>.
+Most of the fields are filled in by the &comedi; infrastructure, but
+there are still quite a handful that your driver must provide or use.
+As for the user-level &comedi;, each of the hierarchical layers has
+its own data structures: channel (<function>comedi_lrange</function>),
+subdevice, and device.
+</para>
+<para>
+Note that these kernel-space data structures have similar names as
+their
+<link linkend="datatypesstructures">user-space equivalents</link>, but
+they have a different (kernel-side) view on the DAQ problem and a
+different meaning: they encode the interaction with the
+<emphasis>hardware</emphasis>, not with the <emphasis>user</emphasis>.
+</para>
+<para>
+However, the <link linkend="ref-type-comedi-insn">comedi_insn</link>
+and <link linkend="ref-type-comedi-cmd">comedi_cmd</link>
+data structures are shared between user space and kernel space: this
+should come as no surprise, since these data structures contain all
+information that the user-space program must transfer to the
+kernel-space driver for each acquisition.
+</para>
+<para>
+In addition to these data entities that are also known at the user
+level (device, sub-device, channel), the device driver level provides
+two more data structures which the application programmer doesn't get
+in touch with: the data structure
+<link linkend="comedidriver">comedi_driver</link>
+that stores the device driver information that is relevant at the
+operating system level, and the data structure
+<link linkend="comediasync">comedi_async</link> that stores the
+information about all <emphasis>asynchronous</emphasis> activities
+(interrupts, callbacks and events).
+</para>
+
+<section id="comedilrange">
+<title>
+<function>comedi_lrange</function>
+</title>
+<para>
+The channel information is simple, since it contains only the signal
+range information:
+<programlisting>
+struct comedi_lrange_struct{
+ int length;
+ <link linkend="ref-type-comedi-krange">comedi_krange</link> range[GCC_ZERO_LENGTH_ARRAY];
+};
+</programlisting>
+</para>
+
+</section>
+
+
+<section id="comedisubdevice">
+<title>
+<function>comedi_subdevice</function>
+<para>
+The subdevice is the smallest &comedi; entity that can be used for
+“stand-alone” DAQ, so it is no surprise that it is
+quite big:
+<programlisting>
+struct comedi_subdevice_struct{
+ int type;
+ int n_chan;
+ int subdev_flags;
+ int len_chanlist; /* maximum length of channel/gain list */
+
+ void *private;
+
+ <link linkend="comediasync">comedi_async</link> *async;
+
+ void *lock;
+ void *busy;
+ unsigned int runflags;
+
+ int io_bits;
+
+ <link linkend="ref-type-lsampl-t">lsampl_t</link> maxdata; /* if maxdata==0, use list */
+ <link linkend="ref-type-lsampl-t">lsampl_t</link> *maxdata_list; /* list is channel specific */
+
+ unsigned int flags;
+ unsigned int *flaglist;
+
+ <link linkend="comedilrange">comedi_lrange</link> *range_table;
+ <link linkend="comedilrange">comedi_lrange</link> **range_table_list;
+
+ unsigned int *chanlist; /* driver-owned chanlist (not used) */
+
+ int (*insn_read)(<link linkend="comedidevice">comedi_device</link> *,<link linkend="comedisubdevice">comedi_subdevice</link> *,<link linkend="ref-type-comedi-insn">comedi_insn</link> *,<link linkend="ref-type-lsampl-t">lsampl_t</link> *);
+ int (*insn_write)(<link linkend="comedidevice">comedi_device</link> *,<link linkend="comedisubdevice">comedi_subdevice</link> *,<link linkend="ref-type-comedi-insn">comedi_insn</link> *,<link linkend="ref-type-lsampl-t">lsampl_t</link> *);
+ int (*insn_bits)(<link linkend="comedidevice">comedi_device</link> *,<link linkend="comedisubdevice">comedi_subdevice</link> *,<link linkend="ref-type-comedi-insn">comedi_insn</link> *,<link linkend="ref-type-lsampl-t">lsampl_t</link> *);
+ int (*insn_config)(<link linkend="comedidevice">comedi_device</link> *,<link linkend="comedisubdevice">comedi_subdevice</link> *,<link linkend="ref-type-comedi-insn">comedi_insn</link> *,<link linkend="ref-type-lsampl-t">lsampl_t</link> *);
+
+ int (*do_cmd)(<link linkend="comedidevice">comedi_device</link> *,<link linkend="comedisubdevice">comedi_subdevice</link> *);
+ int (*do_cmdtest)(<link linkend="comedidevice">comedi_device</link> *,<link linkend="comedisubdevice">comedi_subdevice</link> *,<link linkend="ref-type-comedi-cmd">comedi_cmd</link> *);
+ int (*poll)(<link linkend="comedidevice">comedi_device</link> *,<link linkend="comedisubdevice">comedi_subdevice</link> *);
+ int (*cancel)(<link linkend="comedidevice">comedi_device</link> *,<link linkend="comedisubdevice">comedi_subdevice</link> *);
+
+ int (*buf_change)(<link linkend="comedidevice">comedi_device</link> *,<link linkend="comedisubdevice">comedi_subdevice</link> *s,unsigned long new_size);
+ void (*munge)(<link linkend="comedidevice">comedi_device</link> *, <link linkend="comedisubdevice">comedi_subdevice</link> *s, void *data, unsigned int num_bytes, unsigned int start_chan_index );
+
+ unsigned int state;
+};
+</programlisting>
+The function pointers <function>(*insn_read)</function> …
+<function>(*cancel)</function> .
+offer (pointers to) the standardized
+<link linkend="functionreference">user-visible API</link>
+that every subdevice should offer; every device driver has to fill
+in these functions with their board-specific implementations.
+(Functionality for which &comedi; provides generic functions will, by
+definition, not show up in the device driver data structures.)
+</para>
+<para>
+The <function>buf_change()</function> and <function>munge()</function>
+functions offer functionality that is not visible to the user and for
+which the device driver writer must provide a board-specific
+implementation:
+<function>buf_change()</function> is called when a change in the
+data buffer requires handling; <function>munge()</function> transforms
+different bit-representations of DAQ values, for example from
+<emphasis>unsigned</emphasis> to <emphasis>2's complement</emphasis>.
+</para>
+
+</section>
+
+<section id="comedidevice">
+<title>
+<function>comedi_device</function>
+</title>
+
+<para>
+The last data structure stores the information at the
+<emphasis>device</emphasis> level:
+<programlisting>
+struct comedi_device_struct{
+ int use_count;
+ <link linkend="comedidriver">comedi_driver</link> *driver;
+ void *private;
+ kdev_t minor;
+ char *board_name;
+ const void *board_ptr;
+ int attached;
+ int rt;
+ spinlock_t spinlock;
+ int in_request_module;
+
+ int n_subdevices;
+ <link linkend="comedisubdevice">comedi_subdevice</link> *subdevices;
+ int options[COMEDI_NDEVCONFOPTS];
+
+ /* dumb */
+ int iobase;
+ int irq;
+
+ <link linkend="comedisubdevice">comedi_subdevice</link> *read_subdev;
+ wait_queue_head_t read_wait;
+
+ <link linkend="comedisubdevice">comedi_subdevice</link> *write_subdev;
+ wait_queue_head_t write_wait;
+
+ struct fasync_struct *async_queue;
+
+ void (*open)(<link linkend="comedidevice">comedi_device</link> *dev);
+ void (*close)(<link linkend="comedidevice">comedi_device</link> *dev);
+};
+</programlisting>
+</para>
+
+</section>
+
+<section id="comediasync">
+<title>
+<function>comedi_async</function>
+</title>
+
+<para>
+The following data structure contains all relevant information:
+addresses and sizes of buffers, pointers to the actual data, and the
+information needed for
+<link linkend="drivercallbacks">event handling</link>:
+<programlisting>
+struct comedi_async_struct{
+ void *prealloc_buf; /* pre-allocated buffer */
+ unsigned int prealloc_bufsz; /* buffer size, in bytes */
+ unsigned long *buf_page_list; /* physical address of each page */
+ unsigned int max_bufsize; /* maximum buffer size, bytes */
+ unsigned int mmap_count; /* current number of mmaps of prealloc_buf */
+
+ volatile unsigned int buf_write_count; /* byte count for writer (write completed) */
+ volatile unsigned int buf_write_alloc_count; /* byte count for writer (allocated for writing) */
+ volatile unsigned int buf_read_count; /* byte count for reader (read completed)*/
+
+ unsigned int buf_write_ptr; /* buffer marker for writer */
+ unsigned int buf_read_ptr; /* buffer marker for reader */
+
+ unsigned int cur_chan; /* useless channel marker for interrupt */
+ /* number of bytes that have been received for current scan */
+ unsigned int scan_progress;
+ /* keeps track of where we are in chanlist as for munging */
+ unsigned int munge_chan;
+
+ unsigned int events; /* events that have occurred */
+
+ <link linkend="ref-type-comedi-cmd">comedi_cmd</link> cmd;
+
+ // callback stuff
+ unsigned int cb_mask;
+ int (*cb_func)(unsigned int flags,void *);
+ void *cb_arg;
+
+ int (*inttrig)(<link linkend="comedidevice">comedi_device</link> *dev,<link linkend="comedisubdevice">comedi_subdevice</link> *s,unsigned int x);
+};
+</programlisting>
+</para>
+
+</section>
+
+<section id="comedidriver">
+<title>
+<function>comedi_driver</function>
+</title>
+
+<para>
+<programlisting>
+struct comedi_driver_struct{
+ struct comedi_driver_struct *next;
+
+ char *driver_name;
+ struct module *module;
+ int (*attach)(<link linkend="comedidevice">comedi_device</link> *,comedi_devconfig *);
+ int (*detach)(<link linkend="comedidevice">comedi_device</link> *);
+
+ /* number of elements in board_name and board_id arrays */
+ unsigned int num_names;
+ void *board_name;
+ /* offset in bytes from one board name pointer to the next */
+ int offset;
+};
+</programlisting>
+</para>
+
+</section>
+
+</section>
+
+
+<section id="driversupportfunctions">
+<title>
+Generic driver support functions
+</title>
+
+<para>
+The directory
+<filename role=directory>comedi</filename> contains a large set of
+support functions. Some of the most important ones are given below.
+</para>
+<para>
+From <filename>comedi/comedi_fops.c</filename>, functions to handle the
+hardware events (which also runs the registered callback function), to
+get data in and out of the software data buffer, and to parse the
+incoming functional requests:
+<programlisting>
+ void comedi_event(<link linkend="comedidevice">comedi_device</link> *dev,<link linkend="comedisubdevice">comedi_subdevice</link> *s,unsigned int mask);
+
+ int comedi_buf_put(<link linkend="comediasync">comedi_async</link> *async, <link linkend="ref-type-sampl-t">sampl_t</link> x);
+ int comedi_buf_get(<link linkend="comediasync">comedi_async</link> *async, <link linkend="ref-type-sampl-t">sampl_t</link> *x);
+
+ static int parse_insn(<link linkend="comedidevice">comedi_device</link> *dev,<link linkend="ref-type-comedi-insn">comedi_insn</link> *insn,<link linkend="ref-type-lsampl-t">lsampl_t</link> *data,void *file);
+</programlisting>
+The file <filename>comedi/kcomedilib/kcomedilib_main.c</filename> provides
+functions to register a callback, to poll an ongoing data acquisition,
+and to print an error message:
+<programlisting>
+ int comedi_register_callback(<link linkend="ref-type-comedi-t">comedi_t</link> *d,unsigned int subdevice, unsigned int mask,int (*cb)(unsigned int,void *),void *arg);
+
+ int comedi_poll(<link linkend="ref-type-comedi-t">comedi_t</link> *d, unsigned int subdevice);
+
+ void comedi_perror(const char *message);
+</programlisting>
+The file <filename>comedi/rt.c</filename> provides interrupt handling
+for real-time tasks (one interrupt per <emphasis>device</emphasis>!):
+<programlisting>
+ int comedi_request_irq(unsigned irq,void (*handler)(int, void *,struct pt_regs *), unsigned long flags,const char *device,<link linkend="comedidevice">comedi_device</link> *dev_id);
+ void comedi_free_irq(unsigned int irq,<link linkend="comedidevice">comedi_device</link> *dev_id)
+</programlisting>
+</para>
+
+</section>
+
+</section>
+
+
+<section id="boardspecific">
+<title>
+Board-specific functionality
+</title>
+
+<para>
+The <filename role=directory>/usr/src/comedi/comedi/drivers</filename>
+subdirectory contains
+the <emphasis role="strong">board-specific</emphasis> device driver
+code. Each new card must get an entry in this directory.
+<emphasis role="strong">Or</emphasis>
+extend the functionality of an already existing driver file if the new
+card is quite similar to that implemented in an already existing
+driver. For example, many of the National Instruments DAQ cards use
+the same driver files.
+</para>
+<para>
+To help device driver writers,
+&comedi; provides the “skeleton” of a new device driver,
+in the <filename>comedi/drivers/skel.c</filename> file. Before
+starting to write a new driver, make sure you understand this file,
+and compare it to what you find in the other already available
+board-specific files in the same directory.
+</para>
+<para>
+The first thing you notice in <filename>skel.c</filename> is the
+documentation section: the &comedi; documentation is partially
+generated automatically, from the information that is given in this
+section. So, please comply with the structure and the keywords
+provided as &comedi; standards.
+</para>
+<para>
+The second part of the device driver contains board-specific static
+data structure and defines: addresses of hardware registers; defines and
+function prototypes for functionality that is only used inside of the
+device driver for this board; the encoding of the types and number of
+available channels; PCI information; etc.
+</para>
+<para>
+Each driver has to register two functions which are called when you
+load and unload your board's device driver (typically via a kernel
+module):
+<programlisting>
+ mydriver_attach();
+ mydriver_detach();
+</programlisting>
+In the “attach” function, memory is allocated for the
+necessary <link linkend="driverdatastructures">data structures</link>,
+all properties of a device and its subdevices are defined, and filled
+in in the generic &comedi; data structures. As part of this, pointers
+to the low level instructions being supported by the subdevice have to
+be set, which define the basic functionality. In somewhat more detail,
+the <function>mydriver_attach()</function> function must:
+<itemizedlist>
+
+<listitem>
+<para>
+check and request the I/O port region, IRQ, DMA, and other hardware
+resources. It is convenient here if you verify the existence of the
+hardware and the correctness of the other information given.
+Sometimes, unfortunately, this cannot be done.
+</para>
+</listitem>
+
+<listitem>
+<para>
+allocate memory for the private data structures.
+</para>
+</listitem>
+
+<listitem>
+<para>
+initialize the board registers and possible subdevices (timer, DMA, PCI,
+hardware FIFO, etc.).
+</para>
+</listitem>
+
+<listitem>
+<para>
+return 1, indicating success. If there were any errors along the way,
+you should return the appropriate error number. If an error is
+returned, the <function>mydriver_detach()</function> function is
+called. The <function>mydriver_detach()</function> function should
+check any resources that may have been allocated and release them as
+necessary. The &comedi; core frees
+<function>dev->subdevices</function> and
+<function>dev->private</function>, so this does not need to be done in
+<function>detach</function>.
+</para>
+</listitem>
+
+<listitem>
+<para>
+If the driver has the possibility to offer asynchronous data
+acquisition, you have to code an interrupt service routine, event
+handling routines, and/or callback routines.
+</para>
+</listitem>
+
+</itemizedlist>
+Typically, you will be able to implement most of
+the above-mentioned functionality by
+<emphasis>cut-and-paste</emphasis> from already existing drivers. The
+<function>mydriver_attach()</function> function needs most of your
+attention, because it must correctly define and allocate the (private
+and generic) data structures that are needed for this device. That is,
+each sub-device and each channel must get appropriate data fields, and
+an appropriate initialization. The good news, of course, is that
+&comedi; provides the data structures and the defines that fit very
+well with almost all DAQ functionalities found on interface cards.
+These can be found in the
+<link linkend="comedikernelgeneric">header files</link> of the
+<filename role=directory>/usr/src/comedi/include/linux/</filename>
+directory.
+</para>
+<para>
+Drivers for digital IOs should implement the following functions:
+<itemizedlist>
+
+<listitem>
+<para>
+<function>insn_bits()</function>: drivers set this if they have a
+function that supports reading and writing multiple bits in a digital
+I/O subdevice at the same time. Most (if not all) of the drivers use
+this interface instead of insn_read and insn_write for DIO subdevices.
+</para>
+</listitem>
+
+<listitem>
+<para>
+<function>insn_config()</function>: implements INSN_CONFIG
+instructions. Currently used for configuring the direction of digital
+I/O lines, although will eventually be used for generic configuration
+of drivers that is outside the scope of the currently defined &comedi;
+interface.
+</para>
+</listitem>
+
+</itemizedlist>
+Finally, the device driver writer must implement the
+<function>read</function> and <function>write</function> functions for
+the analog channels on the card:
+<itemizedlist>
+
+<listitem>
+<para>
+<function>insn_read()</function>: acquire the inputs on the board and
+transfer them to the software buffer of the driver.
+</para>
+</listitem>
+
+<listitem>
+<para>
+<function>insn_write()</function>: transfer data from the software
+buffer to the card, and execute the appropriate output conversions.
+</para>
+</listitem>
+
+</itemizedlist>
+In some drivers, you want to catch interrupts, and/or want to use the
+<link linkend="insn-inttrig">INSN_INTTRIG</link> instruction. In this
+case, you must provide and register these
+<link linkend="drivercallbacks">callback</link> functions.
+</para>
+<para>
+Implementation of all of the above-mentioned functions requires
+perfect knowledge about the hardware registers and addresses of the
+interface card. In general, you can find
+<emphasis>some</emphasis> inspiration in the already available device
+drivers, but don't trust that blind
+<emphasis>cut-and-paste</emphasis> will bring you far…
+</para>
+
+</section>
+
+<section id="drivercallbacks">
+<title>
+Callbacks, events and interrupts
+</title>
+
+<para>
+Continuous acquisition is tyically an
+<emphasis>asynchronous</emphasis> activity: the function call that
+has set the acquisition in motion has returned before the acquisition
+has finished (or even started). So, not only the acquired data must be
+sent back to the user's buffer “in the background”, but
+various types of asynchronous <emphasis>event handling</emphasis> can
+be needed during the acquisition:
+<itemizedlist>
+
+<listitem>
+<para>
+The <emphasis>hardware</emphasis> can generate some error or
+warning events.
+</para>
+</listitem>
+
+<listitem>
+<para>
+Normal functional interrupts are generated by the hardware, e.g.,
+signalling the filling-up of the card's hardware buffer, or the end of
+an acquisition <link linkend="scan">scan</link>, etc.
+</para>
+</listitem>
+
+<listitem>
+<para>
+The device driver writer can register a driver-supplied
+”callback” function, that is called at the end of each
+hardware interrupt routine.
+</para>
+</listitem>
+
+<listitem>
+<para>
+Another driver-supplied callback function is executed when the user
+program launches an <link linkend="insn-inttrig">INSN_INTTRIG</link>
+instruction. This event handling is executed
+<emphasis>synchronously</emphasis> with the execution of the
+triggering instruction.
+</para>
+</listitem>
+
+</itemizedlist>
+</para>
+<para>
+The interrupt handlers are registered through the functions mentioned
+<link linkend="driversupportfunctions">before</link>
+The event handling is done in the existing &comedi; drivers in
+statements such as this one:
+<programlisting>
+<anchor id="async-events">
+ s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR
+</programlisting>
+It fills in the bits corresponding to particular events in the
+<link linkend="comediasync">comedi_async</link> data structure.
+The possible event bits are:
+<itemizedlist>
+
+<listitem>
+<para>
+<anchor id="comedi-cb-eoa">
+<parameter>COMEDI_CB_EOA</parameter>: execute the callback at the
+“End Of-Acquisition”.
+</para>
+</listitem>
+
+<listitem>
+<para>
+<anchor id="comedi-cb-eos">
+<parameter>COMEDI_CB_EOS</parameter>: execute the callback at the
+“End-Of-Scan”.
+</para>
+</listitem>
+
+<listitem>
+<para>
+<anchor id="comedi-cb-overflow">
+<parameter>COMEDI_CB_OVERFLOW</parameter>: execute the callback when a
+buffer overflow has occurred.
+</para>
+</listitem>
+
+<listitem>
+<para>
+<anchor id="comedi-cb-error">
+<parameter>COMEDI_CB_ERROR</parameter>: execute the callback at the
+occurrence of an (undetermined) error.
+</para>
+</listitem>
+
+</itemizedlist>
+</para>
+
+</section>
+
+
+<section id="drivercaveats">
+<title>
+Device driver caveats
+</title>
+
+<para>
+A few things to strive for when writing a new driver:
+<itemizedlist>
+
+<listitem>
+<para>
+Some DAQ cards consist of different “layers” of hardware,
+which can each be given their own device driver. Examples are:
+some of the National Instruments cards, that all share the same
+<emphasis>Mite</emphasis> PCI driver chip; the ubiquitous parallel
+port, that can be used for simple digital IO acquisitions. If your
+new card has such a multi-layer design too, please take the effort to
+provide drivers for each layer separately.
+</para>
+</listitem>
+
+<listitem>
+<para>
+Your hardware driver should be functional appropriate to the resources
+allocated. I.e., if the driver is fully functional when configured
+with an IRQ and DMA, it should still function moderately well with
+just an IRQ, or still do minor tasks without IRQ or DMA. Does your
+driver really require an IRQ to do digital I/O? Maybe someone will
+want to use your driver <emphasis>just</emphasis> to do digital I/O
+and has no interrupts available.
+</para>
+</listitem>
+
+<listitem>
+<para>
+Drivers are to have absolutely <emphasis role="strong">no</emphasis>
+global variables, mainly because the existence of global variables
+immediately negates any possibility of using the driver for two
+devices. The pointer <function>dev->private</function> should be used
+to point to a structure containing any additional variables needed by
+a driver/device combination.
+</para>
+</listitem>
+
+<listitem>
+<para>
+Drivers should report errors and warnings via the
+<function>comedi_error()</function> function.
+(This is <emphasis>not</emphasis> the same function as the user-space
+<link linkend="func-ref-comedi-perror">comedi_perror()</link> function.)
+
+</para>
+</listitem>
+
+</itemizedlist>
+
+</section>
+
+<section id="integratingdriver">
+<title>
+Integrating the driver in the &comedi; library
+</title>
+
+<para>
+For integrating new drivers in the &comedi;'s source tree the following
+things have to be done:
+<itemizedlist>
+
+<listitem>
+<para>
+Choose a senseful name for the source code file. Let's assume here
+that you call it “mydriver.c”
+</para>
+</listitem>
+
+<listitem>
+<para>
+Put your new driver into “comedi/drivers/mydriver.c”.
+</para>
+</listitem>
+
+<listitem>
+<para>
+Edit “comedi/Config.in” and add a new
+“dep_tristate” line (look at the other examples). Invent a
+senseful name for the driver's variable. For example:
+<programlisting>
+ dep_tristate 'MYDRIVER' CONFIG_COMEDI_MYDRIVER $CONFIG_COMEDI
+</programlisting>
+</para>
+</listitem>
+
+<listitem>
+<para>
+Add a line to “comedi/drivers/Makefile.in”, using your
+freshly defined variable, i.e., CONFIG_COMEDI_MYDRIVER.
+</para>
+</listitem>
+
+<listitem>
+<para>
+Now <command>make distclean</command>, reconfigure &comedi; with a new
+<command>make</command>, rebuild and be happy.
+</para>
+<para>
+If you want to have your driver included in the &comedi; distribution
+(you <emphasis>definitely</emphasis> want to :-) ) send it to David
+Schleef <address><email>ds@schleef.org</email></address> for
+review and integration.
+</para>
+</listitem>
+</itemizedlist>
+</para>
+
+</section>
+
+</section>
If sucessful, comedi_close returns 0. On failure, -1 is returned.
Function: comedi_open -- open a Comedi device
-Retval: comedi_t *
+Retval: comedi_t
Param: const char * filename
Description:
Open a Comedi device specified by the file filename.
For the A/D conversion (if appropriate),
the device is configured to use range specification
range and (if appropriate) analog reference type
- aref. Analog reference types that are not supported
+ aref. Analog reference types that are not supported
by the device are silently ignored.
The function comedi_data_read() reads one data value from
the specified channel and places the data value in the
location pointed to by data.
- WARNING: comedi_data_read() does not do any pausing to
- allow multiplexed analog inputs to settle before
- performing an analog to digital conversion. If you are
- switching between different channels and need to allow
- your analog input to settle for an accurate reading,
- use comedi_data_read_delayed(), or set the
- input channel at an earlier time with
- comedi_data_read_hint().
-
- On sucess, comedi_data_read() returns 1 (the number of samples
- read). If there is an error, -1 is returned.
-
+ On sucess, comedi_data_read() returns 0. If there is an
+ error, -1 is returned.
+
Data values returned by this function are unsigned integers
less than or equal to the maximum sample value of the channel,
which can be determined using the function comedi_get_maxdata().
Conversion of data values to physical units can be performed
by the function comedi_to_phys().
-Function: comedi_data_read_delayed -- read single sample from channel after delaying for specified settling time
-Retval: int
-Param: comedi_t * device
-Param: unsigned int subdevice
-Param: unsigned int channel
-Param: unsigned int range
-Param: unsigned int aref
-Param: lsampl_t * data
-Param: unsigned int nanosec
-Description:
- Similar to comedi_data_read() except it will wait for the
- specified number of nanoseconds between setting the input
- channel and taking a sample. For analog inputs, most
- boards have a single
- analog to digital converter which is multiplexed to be
- able to read multiple channels. If the input is not allowed
- to settle after the multiplexer switches channels, the
- reading will be inaccurate. This function is useful
- for allowing a multiplexed analog input to settle
- when switching channels.
-
- Although the settling time is specified in nanoseconds, the
- actual settling time will be rounded up to the nearest
- microsecond.
-
-Function: comedi_data_read_hint -- tell driver which channel/range/aref you are going to read from next
-Retval: int
-Param: comedi_t * device
-Param: unsigned int subdevice
-Param: unsigned int channel
-Param: unsigned int range
-Param: unsigned int aref
-Description:
- Used to prepare an analog input for a subsequent call to
- comedi_data_read(). It is not necessary to use this
- function, but it can be useful for eliminating inaccuaracies
- caused by insufficient settling times when switching the
- channel
- or gain on an analog input. This function sets an analog input
- to the channel, range, and aref specified but does not
- perform an actual analog to digital conversion.
-
- Alternatively, one can simply use comedi_data_read_delayed(),
- which sets up the
- input, pauses to allow settling, then performs a conversion.
-
Function: comedi_data_write -- write single sample to channel
Retval: int
Param: comedi_t * device
The function comedi_data_write() writes the data value specified
by the parameter data to the specified channel.
- On sucess, comedi_data_write() returns 1 (the number of samples
- written). If there is an error, -1 is returned.
+ On sucess, comedi_data_write() returns 0. If there is an error, -1 is
+ returned.
Function: comedi_dio_config -- change input/output properties of channel
Retval: int
case, a single call to comedi_dio_config() for any channel in the
group will affect the entire group.
- If sucessful, 1 is returned, otherwise -1.
+ If sucessful, 0 is returned, otherwise -1.
Function: comedi_dio_read -- read single bit from digital channel
Retval: int
Param: comedi_t * device
Param: unsigned int subdevice
Description:
- The function comedi_get_buffer_offset() is used on a subdevice
+ The function comedi_mark_buffer_read() is used on a subdevice
that has a Comedi command in progress. This function returns
- the offset in bytes of the read pointer in the streaming buffer.
- This offset is only useful for memory mapped buffers.
+ the offset of the read pointer in the streaming buffer. This
+ offset is only useful for memory mapped buffers.
If there is an error, -1 is returned.
Function: comedi_get_timer -- timer information (deprecated)
Param: double frequency
Param: unsigned int * trigvar
Param: double * actual_frequency
-Status: deprecated
Description:
+Status: deprecated
The function comedi_get_timer converts the frequency frequency
to a number suitable to send to the driver in a comedi_trig
structure. This function remains for compatibility with very
Param: double frequency
Param: unsigned int num_samples
Param: double * data
-Status: deprecated
Description:
+Status: deprecated
Not documented.
Function: comedi_set_global_oor_behavior -- out-of-range behavior
Retval: int
Param: enum comedi_oor_behavior behavior
-Status: alpha
Description:
+Status: alpha
This function changes the Comedilib out-of-range behavior.
This currently affects the behavior of comedi_to_phys() when
converting endpoint sample values, that is, sample values
The previous out-of-range behavior is returned.
-Function: comedi_apply_calibration -- set calibration from file
-Retval: int
-Param: comedi_t *device
-Param: unsigned int subdevice
-Param: unsigned int channel
-Param: unsigned int range
-Param: unsigned int aref
-Param: const char *file_path
-Status: alpha
-Description:
- This function sets the calibration of the specified subdevice
- so that it is in proper calibration when using the specified
- channel, range and aref. Depending on the hardware, the
- calibration settings used may or may not depend on the channel,
- range, or aref. The file_path parameter can be used
- to specify the file which contains the calibration information.
- If <parameter>file_path</parameter> is NULL, then comedilib
- will use a default
- file location. The calibration information used by this function
- is generated by the comedi_calibrate program (see its man page).
-
- The functions comedi_parse_calibration_file(),
- comedi_apply_parsed_calibration(), and comedi_cleanup_calibration()
- provide the same functionality at a slightly lower level.
-Returns:
- Zero on success, a negative number on failure.
-
-Function: comedi_apply_parsed_calibration -- set calibration from memory
-Retval: int
-Param: comedi_t * device
-Param: unsigned int subdevice
-Param: unsigned int channel
-Param: unsigned int range
-Param: unsigned int aref
-Param: const comedi_calibration_t *calibration
-Status: alpha
-Description:
- This function is similar to comedi_apply_calibration()
- except the calibration information is read from memory
- instead of a file. This function can be more
- efficient than comedi_apply_calibration() since the
- calibration file does not need to be reparsed with
- every call. The <parameter>calibration</parameter> is
- obtained by a call to comedi_parse_calibration_file().
-
-Returns:
- Zero on success, a negative number on failure.
-
-Function: comedi_cleanup_calibration_file -- free calibration resources
-Retval: void
-Param: comedi_calibration_t *calibration
-Status: alpha
-Description:
- This function frees the resources associated with a
- <parameter>calibration</parameter> obtained from
- comedi_parse_calibration_file(). <parameter>calibration</parameter>
- can not be used again after calling this function.
-
-Function: comedi_get_default_calibration_path -- get default calibration file path
-Retval: char*
-Param: comedi_t *dev
-Status: alpha
-Description:
- This function returns a string containing a default calibration file
- path appropriate for <parameter>dev</parameter>. Memory for the
- string is allocated by the function, and should be freed when
- the string is no longer needed.
-Returns:
- A string which contains a file path useable by
- comedi_parse_calibration_file(). On error, NULL is returned.
-
-Function: comedi_parse_calibration_file -- set calibration
-Retval: comedi_calibration_t*
-Param: const char *file_path
-Status: alpha
-Description:
- This function parses a calibration file (produced by the
- comedi_calibrate program) and returns a pointer to a
- comedi_calibration_t which can be passed to the
- comedi_apply_parsed_calibration() function. When you are
- finished using the comedi_calibration_t, you should
- call comedi_cleanup_calibration() to free the resources
- associated with the comedi_calibration_t.
-
- The comedi_get_default_calibration_path() function may
- be useful in conjunction with this function.
-Returns:
- A pointer to parsed calibration information on success, or NULL on failure.
<title>
Glossary
</title>
- <glossentry>
+
+ <glossentry id="api">
<glossterm>
-buffer
+ Application Program Interface
</glossterm>
+ <acronym>API</acronym>
<glossdef>
<para>
-Comedi uses permanently allocated kernel memory for streaming input
-and output to store data that has been measured by a device, but has
-not been read by an application. These buffers can be resized by the
-Comedilib function comedi_buffer_XXX() or the coemdi_config
-utility.
+The (documented) set of function calls supported by a particular
+application, by which programmers can access the functionality
+available in the application.
</para>
</glossdef>
</glossentry>
- <glossentry>
+
+ <glossentry id="buffer">
<glossterm>
-buffer overflow
+buffer
</glossterm>
<glossdef>
<para>
-This is an error message that indicates that the driver ran out of
-space in a Comedi buffer to put samples. It means that the application
-is not copying data out of the buffer quickly enough. Often, this
-problem can be fixed by making the Comedi buffer larger. See
-comedi_buffer_XXX for more information.
+&comedi; uses permanently allocated kernel memory for streaming input
+and output to store data that has been measured by a device, but has
+not been read by an application. These buffers can be resized by the
+Comedilib function <function>comedi_buffer_XXX()</function> or the
+<function>comedi_config</function>
+utility.
</para>
</glossdef>
</glossentry>
- <glossentry>
+ <glossentry id="bufferoverflow">
<glossterm>
-overrun
+buffer overflow
</glossterm>
<glossdef>
<para>
-This is an error message that indicates that there was device-level
-problem, typically with trigger pulses occurring faster than the
-board can handle.
+This is an error message that indicates that the driver ran out of
+space in a &comedi; buffer to put samples. It means that the application
+is not copying data out of the buffer quickly enough. Often, this
+problem can be fixed by making the &comedi; buffer larger. See
+<function>comedi_buffer_XXX</function> for more information.
</para>
</glossdef>
</glossentry>
- <glossentry>
+ <glossentry id="differentialIO">
<glossterm>
-command
+ Differential IO
</glossterm>
<glossdef>
<para>
-Comedi commands are the mechanism that applications configure
-subdevices for streaming input and output.
- (also: cmd, comedi_command)
+…
</para>
</glossdef>
</glossentry>
- <glossentry>
+ <glossentry id="dma">
<glossterm>
-DMA
+ Direct Memory Access
</glossterm>
+ <acronym>DMA</acronym>
<glossdef>
<para>
-Direct memory access. DMA is a method of transferring data between
+DMA is a method of transferring data between
a device and the main memory of a computer. DMA operates differently
on ISA and PCI cards. ISA DMA is handled by a controller on the
motherboard and is limited to transfers to/from the lowest 16 MB of
</glossdef>
</glossentry>
- <glossentry>
+ <glossentry id="fifo">
<glossterm>
-FIFO
+ First In, First Out
</glossterm>
+ <acronym>FIFO</acronym>
<glossdef>
<para>
Most devices have a limited amount of on-board space to store samples
</glossdef>
</glossentry>
- <glossentry>
+
+ <glossentry id="comedicommand">
+ <glossterm>
+&comedi; command
+ </glossterm>
+ <glossdef>
+ <para>
+&comedi; commands are the mechanism that applications configure
+subdevices for streaming input and output.
+ </para>
+ </glossdef>
+ </glossentry>
+
+ <glossentry id="command">
+ <glossterm>
+command
+ </glossterm>
+ <glosssee otherterm="comedicommand">
+ </glossentry>
+
+ <glossentry id="configoption">
+ <glossterm>
+configuration option
+ </glossterm>
+ <glossdef>
+ <para>
+ </para>
+ </glossdef>
+ </glossentry>
+
+ <glossentry id="instruction">
<glossterm>
instruction
</glossterm>
<glossdef>
<para>
-Comedi instructions are the mechanism used by applications to do
+&comedi; instructions are the mechanism used by applications to do
immediate input from channels, output to channels, and configuration
of subdevices and channels.
- (also: insn)
</para>
</glossdef>
</glossentry>
- <glossentry>
+ <glossentry id="instructionlist">
<glossterm>
instruction list
</glossterm>
<glossdef>
<para>
-Instruction lists allow the application to perform multiple Comedi
+Instruction lists allow the application to perform multiple &comedi;
instructions in the same system call.
</para>
</glossdef>
</glossentry>
- <glossentry>
+ <glossentry id="option">
+ <glossterm>
+option
+ </glossterm>
+ <glossdef>
+ <para>
+ </para>
+ <glossseealso otherterm="optionlist">
+ </glossdef>
+ </glossentry>
+
+
+ <glossentry id="optionlist">
<glossterm>
option list
</glossterm>
<glossdef>
<para>
-Option lists are used with comedi_config to perform driver
-configuration.
- (also: configuration options, options)
+Option lists are used with <function>comedi_config</function> to
+perform driver configuration.
</para>
+ <glossseealso otherterm="configoption">
+ <glossseealso otherterm="option">
</glossdef>
</glossentry>
- <glossentry>
+ <glossentry id="overrun">
+ <glossterm>
+overrun
+ </glossterm>
+ <glossdef>
+ <para>
+This is an error message that indicates that there was device-level
+problem, typically with trigger pulses occurring faster than the
+board can handle.
+ </para>
+ </glossdef>
+ </glossentry>
+
+ <glossentry id="poll">
<glossterm>
poll
</glossterm>
<glossdef>
<para>
The term poll (and polling) is used for several different related
-concepts in Comedi. Comedi implements the poll() system call for
-Comedi devices, which is similar to select(), and returns information
+concepts in &comedi;. &comedi; implements the
+<function>poll()</function> system call for Comedi devices, which is
+similar to <function>select()</function>, and returns information
about file descriptors that can be read or written. Comedilib also
-has a function called comedi_poll(), which causes the driver to
-copy all available data from the device to the Comedi buffer. In
-addition, some drivers may use a polling technique in place of
-interrupts.
+has a function called <function>comedi_poll()</function>, which causes
+the driver to copy all available data from the device to the &comedi;
+buffer. In addition, some drivers may use a polling technique in
+place of interrupts.
</para>
</glossdef>
</glossentry>
<!-- <!DOCTYPE section PUBLIC "-//OASIS//DTD DocBook V3.1//EN"> -->
-<section>
+<section id="install">
<title>
-Installation and Configuration
+Configuration
</title>
<para>
-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
+This section assumes that you have successfully compiled and installed
+the &comedi; software, 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.
</para>
+
+<section id="cardconfiguration">
+<title>
+Configuration
+</title>
<para>
-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.
+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 of course.)
+Here is an example of how to use the command (perhaps you should read
+its <command>man</command> page now):
+<screen>
+/usr/sbin/comedi_config /dev/comedi0 ni_atmio 0x260,3
+</screen>
+This command says that the “file”
+<filename>/dev/comedi0</filename> can be used to access the &comedi;
+device that uses the <parameter>ni_atmio</parameter> driver, and that
+you give it two run-time parameters (<literal>0x260</literal> and
+<literal>3</literal>). More parameters are possible, for example to
+discriminate between two or more identical cards in your system.
</para>
-
<para>
-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.
+If you want to have the board configured in this way every time you
+boot, put the line above into a start-up script file of your Linux
+system (for example, the
+<filename>/etc/rc.d/rc.local</filename> file), or in the system-wide
+&comedi; configuration file <filename>/etc/comedi.conf</filename>.
+You can, of course, also run this command at a command prompt.
</para>
<para>
-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:
+This tutorial goes through the process of configuring &comedi;
+for two devices, a
+<literal>National Instruments AT-MIO-16E-10</literal> (which has the
+driver mentioned above), and a
+<literal>Data Translation DT2821-F-8DI</literal>.
</para>
+<para>
+The NI board is plug-and-play, and the <command>man</command> page
+tells you that you need to configure the PnP part of the board with
+<command>isapnptools</command>. The <command>isapnptools</command>
+package is a little cryptic, but the concepts are simple. Once you've
+learned how to use it, you can settle on a
+<filename>/etc/isapnp.conf</filename> file such as this:
+</para>
<screen>
# ANSI string -->National Instruments, AT-MIO-16E-10<--
))
</screen>
-
<para>
-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.
+(This file also contains a few lines about overall configuration and
+about the sound card that happens to be in the same computer.)
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.
+the DMA lines are commented out. It has been reported that the
+National Instruments board does not always work with interrupts other
+than IRQ 3, and that the device ignores the IRQ and DMA information
+given here. However, keep the information here to remind yourself that
+the numbers aren't arbitrary.
</para>
<para>
-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 lines
+The <command>man</command> page
+explains that the option list is supposed to be
+“<literal>(I/O base),(IRQ)</literal>”, so use the same
+numbers as in <filename>/etc/isapnp.conf</filename>, i.e.,
+<literal>0x260,3</literal>.
</para>
-<screen>
-export PATH=/sbin:/usr/sbin:/usr/local/sbin:$PATH
-comedi_config /dev/comedi0 ni_atmio 0x260,3
-</screen>
-
<para>
-into <filename>/etc/rc.d/rc.local</filename>. 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 <literal>Data Translation</literal> board, you need to have a
+list of the jumper settings; these are given in the &comedi; manual
+section about this card. (Check first to see whether they are still
+correct!)
+The card discussed her is a <literal>DT2821-f-8di</literal>. The
+<command>man</command> page of <command>comedi_config</command> tells
+you that you need to know the I/O base, IRQ, DMA 1, DMA 2. However,
+the &comedi; driver also recognizes the
+differential/single-ended and unipolar/bipolar jumpers. As always,
+the source is the final authority, and looking in
+<filename>module/dt282x.c</filename>
+tells us that the options list is interpreted as:
</para>
-
<para>
-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:
+(... TO BE FILLED IN ...)
</para>
<!-- XXX
<item>dma1
<item>dma2
</itemize>
+(ai=analog input, ao=analog output.)
-->
<para>
-(ai=analog input, ao=analog output.) From this, I decide that
-the appropriate options list is
-</para>
-
+So, the appropriate options list is:
<screen>
0x200,4,,1,1,1
</screen>
-
-<para>
-I left the differential/single-ended number blank, since the
+and the full configuration command is:
+<screen>
+/usr/sbin/comedi_config /dev/comedi1 dt2821-f-8di 0x200,4,,1,1,1
+</screen>
+The differential/single-ended number is left 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.)
+differential. Also the DMA numbers are left blank, since we
+don't want the driver to use DMA. (Which could interfere
+with the sound card...)
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 lines when I put
-it in rc.local.
+so put good comments next to the following line when you put
+it in a start-up file.
</para>
-<screen>
-export PATH=/sbin:/usr/sbin:/usr/local/sbin:$PATH
-comedi_config /dev/comedi1 dt2821-f-8di 0x200,4,,1,1,1
-</screen>
-
<para>
-So now I think that I have my boards configured correctly.
+So now you have your boards configured correctly.
Since data acquisition boards are not typically well-engineered,
-Comedi sometimes can't figure out if the board is actually there.
+&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
+are well-made, so &comedi; will give 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):
+can access through the command <command>dmesg</command> or the file
+<filename>/var/log/messages</filename>.
+Here is a configuration failure (from <command>dmesg</command>):
</para>
<screen>
</screen>
<para>
-When it does work, I get:
+When it does work, you get:
</para>
<screen>
</screen>
<para>
-Note that it also correctly identified my board.
+Note that it also correctly identified the board.
</para>
+</section>
-<section>
+<section id="gettinginformation">
<title>
-Getting information from comedi
+Getting information about a card
</title>
<para>
-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:
+So now that you have &comedi; talking to the hardware, try to
+talk to &comedi;. Here's some pretty low-level information, which can
+sometimes be useful for debugging:
</para>
<screen>
</screen>
<para>
-Right now, on my computer, this command gives:
+On the particular system this demonstration was carried out, this
+command gives:
</para>
<screen>
</screen>
<para>
-This is a feature that is not well-developed yet. Basically, it
-currently tells you driver name, device name, and number of
+This documentation feature is not well-developed yet. Basically, it
+currently returns the driver name, the device name, and the number of
subdevices.
</para>
<para>
-In the <filename>demo/</filename> directory, there is a command called
-<command>info</command>, 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 <filename>/dev/comedi0</filename>.) ('demo/info /dev/comedi0')
+In the <filename role=directory>demo/</filename> directory, there is a
+command called <command>info</command>, which provides information
+about each subdevice on the board. Its output can be rather long,
+if the board has several subdevices.
+Here's part of the output of the <literal>National Instruments</literal>
+board (which is on <filename>/dev/comedi0</filename>), as a result of
+the command <command>demo/info /dev/comedi0</command>:
</para>
<screen>
board name: at-mio-16e-10
number of subdevices: 7
subdevice 0:
- type: 1 (unknown)
+ type: 1 (analog input)
number of channels: 16
max data value: 4095
...
</screen>
<para>
-The overall info gives information about the device -- basically
-the same information as /proc/comedi.
+The overall info gives information about the device; basically
+the same information as <filename>/proc/comedi</filename>.
</para>
<para>
-This board has 7 subdevices. Devices are separated into
-subdevices that each have a distinct purpose -- e.g., analog
+This board has seven 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.
</para>
<para>
-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.
+&comedi; has more information about the device than what is displayed
+above, but <command>demo/info</command> doesn't currently display
+this.
</para>
-
</section>
+
</section>
<!-- <!DOCTYPE section PUBLIC "-//OASIS//DTD DocBook V3.1//EN"> -->
-<section>
+<section id="introduction">
<title>
-Introduction
+Overview
</title>
<para>
-This section gives high-level explanation about which functionality
-you can expect from the software. Details are given in the following
-sections of this document.
+&comedi; is a <emphasis>free software</emphasis> project that develops
+drivers, tools, and libraries for various forms of
+<emphasis>data acquisition</emphasis>: reading and writing of analog
+signals; reading and writing of digital inputs/outputs; pulse and
+frequency counting; pulse generation; reading encoders; etc. The
+project's source code is distributed in two packages,
+<literal>
+<ulink url="http://www.comedi.org/download.php">comedi</ulink>
+</literal> and
+<literal>
+<ulink url="http://www.comedi.org/download.php">comedilib</ulink>
+</literal>, and provides several Linux
+<emphasis>kernel modules</emphasis> and a
+<emphasis>user space</emphasis> library:
+<itemizedlist>
+
+<listitem>
+<para>
+<emphasis role="strong">Comedi</emphasis> is a collection of drivers for a variety
+of common data acquisition plug-in boards (which are called
+“devices” in &comedi; terminology). The drivers are
+implemented as the combination of (i) one single core Linux kernel module
+(called “<literal>comedi</literal>”) providing common
+functionality, and (ii) individual low-level driver modules for
+each device.
+</para>
+</listitem>
+
+<listitem>
+<para>
+<emphasis role="strong">Comedilib</emphasis> is a separately distributed package
+containing a user-space library that provides a
+developer-friendly interface to the &comedi; devices. Included in the
+<emphasis>Comedilib</emphasis> package are documentation,
+configuration and calibration utilities, and demonstration programs.
</para>
+</listitem>
-<section>
+<listitem>
+<para>
+<emphasis role="strong">Kcomedilib</emphasis> is a Linux kernel module
+(distributed with the <literal>comedi</literal> package) that provides
+the same interface as <emphasis>comedilib</emphasis> in kernel space,
+and suitable for <emphasis>real-time</emphasis> tasks. It is
+effectively a “kernel library” for using &comedi; from
+real-time tasks.
+</para>
+</listitem>
+
+</itemizedlist>
+&comedi; works with standard Linux kernels, but also with its
+real-time extensions <ulink url="http://www.rtai.org">RTAI</ulink> and
+<ulink url="http://www.fsmlabs.com/products/openrtlinux/">RTLinux/Free</ulink>.
+</para>
+<para>
+This section gives a high-level introduction to which functionality
+you can expect from the software. More technical details and
+programming examples are given in the following sections of this
+document.
+</para>
+
+<section id="whatisdevicedriver">
<title>
-What is a "device driver"?
+What is a “device driver”?
</title>
<para>
A device driver is a piece of software that interfaces a particular
piece of hardware: a printer, a sound card, a motor drive, etc. It
translates the primitive, device-dependent commands with which the
-hardware manufacturer want you to configure, read and write the
+hardware manufacturer allows you to configure, read and write the
electronics of the hardware interface into more abstract and generic
function calls and data structures for the application programmer.
</para>
<para>
-David Schleef started the Comedi project to interface
+David Schleef started the &comedi; project to put a generic interface
+on top of
lots of different cards for measurement and control purposes. This
-type of cards are often called Data AcQuisition cards, or DAQ cards.
+type of cards are often called <emphasis>data acquisition</emphasis>
+(or <emphasis role="strong">DAQ</emphasis>) cards.
+</para>
+<para>
+<emphasis>Analog input and output</emphasis> cards were the first goal
+of the project, but now &comedi; also provides a device
+independent interface to digital <emphasis>input and output</emphasis>
+cards, and <emphasis>counter and timer</emphasis> cards (including
+encoders, pulse generators, frequency and pulse timers, etc.).
+</para>
+<para>
Schleef designed a structure which is a balance between
-modularity (i.e., it's fairly easy to integrate a new card because
-most of the infrastructure part of the driver can be reused) and
-complexity (i.e., the structure doesn't present so much overhead that
-new contributors are scared away from writing their new drivers within
-the Comedi framework). The Comedi project consists of two
-complementary packages: "comedi" (which implements the kernel space
-functionality) and "comedilib" (which implements the user space access
-to the device driver functionality). Comedi works with a standard
-Linux kernel, but also with its real-time extensions RTAI and
-Real-Time Linux.
+<emphasis>modularity</emphasis> and <emphasis>complexity</emphasis>:
+it's fairly easy to integrate a new card because most of the
+infrastructure part of other, similar drivers can be reused, and
+learning the generic and hence somewhat “heavier” &comedi;
+API doesn't scare away new contributors from integrating their drivers
+into the &comedi; framework.
</para>
</section>
-<section>
+
+<section id="policymechanism">
<title>
-A general DAQ device driver package.
+Policy vs. mechanism
</title>
<para>
-From the point of view of system developers, it is worthwhile to
-standardize the structure and API (Application Programming Interface)
-for device drivers as much as possible:
+Device drivers are often written by application programmers, that have
+only their particular application in mind; especially in real-time
+applications. For example, one writes a
+driver for the parallel port, because one wants to use it to generate
+pulses that drive a stepper motor. This approach often leads to device
+drivers that depend too much on that particular application, and are
+not general enough to be re-used for other applications. One golden
+rule for the device driver writer is to separate mechanism and policy:
+<itemizedlist>
+
+<listitem>
+<para>
+ <emphasis role="strong">Mechanism.</emphasis>
+ The mechanism part of the device interface is a faithful
+ representation of the bare functionality of the device, independent of
+ what part of the functionality an application will use.
+</para>
+</listitem>
+
+<listitem>
+<para>
+ <emphasis role="strong">Policy.</emphasis>
+ Once a device driver offers a software interface to the mechanism of
+ the device, an application writer can use this mechanism interface to
+ use the device in one particular fashion. That is, some of the data
+ stuctures offered by the mechanism are interpreted in specific
+ physical units, or some of them are taken together because this
+ composition is relevant for the application. For example, a analog
+ output card can be used to generate voltages that are the inputs for
+ the electronic drivers of the motors of a robot; these voltages can be
+ interpreted as setpoints for the desired velocity of these motors, and
+ six of them are taken together to steer one particular robot with
+ six-degrees of freedom. Some of the other outputs of the same physical
+ device can be used by another application program, for example to
+ generate a sine wave that drives a vibration shaker.
</para>
+</listitem>
+
+</itemizedlist>
+So, &comedi; focuses only on the <emphasis>mechanism</emphasis> part
+of DAQ interfacing. The project does not provide the policy parts,
+such as Graphical User Interfaces to program and display acquisitions,
+signal processing libraries, or control algorithms.
+</para>
+
+</section>
+
+<section id="generaldaqpackage">
+<title>
+A general DAQ device driver package
+</title>
+<para>
+From the point of view of application developers, there are many
+reasons to welcome the standardization of the API and the
+architectural structure of DAQ software:
+<itemizedlist>
+
+<listitem>
<para>
- API: devices that offer similar functionalities, should have the same
+ <emphasis role="strong">API</emphasis>: devices that offer similar functionalities, should have the same
software interface, and their differences should be coped with by
parameterizing the interfaces, not by changing the interface for
- each new device in the family.
+ each new device in the family. However, the DAQ manufacturers
+have never been able (or willing) to come up with such a
+standardization effort themselves.
+</para>
+</listitem>
+<listitem>
<para>
- Structure: many electronic interfaces have more than one layer of
+ <emphasis role="strong">Architectural structure</emphasis>: many electronic interfaces have more than one layer of
functionality between the hardware and the operating system, and
the device driver code should reflect this fact. For example, many
different interface cards use the same PCI driver chips, or use the
- parallel port to connect to the hardware device. Hence, providing
- "low-level" device drivers for these PCI chips and parallel ports
- allows for an increased modularity and re-useability of the software.
+ parallel port as an intermediate means to connect to the hardware
+device. Hence, “lower-level” device drivers for
+these PCI chips and parallel ports allow for an increased modularity
+and re-useability of the software. Finding the generic
+similarities and structure among different cards helps in developing
+device drivers faster and with better documentation.
</para>
+</listitem>
-<para>
+</itemizedlist>
In the case of Linux as the host operating system, device driver
writers must keep the following Linux-specific issues in mind:
-</para>
-
+<itemizedlist>
+<listitem>
<para>
- Kernel space vs. User space.
- The Linux operating system has two levels: only privileged processes
+ <emphasis role="strong">Kernel space vs. User space.</emphasis>
+ The Linux operating system has two levels that require
+basically different programming approaches. Only privileged processes
can run in the kernel, where they have access to all hardware and to
- all kernel data structures and system calls; normal application
+ all kernel data structures. Normal application
programs can run their processes only in user space, where these
processes are shielded from each other, and from direct access to
- hardware and to critical data of the operating system. Device drivers
- typically must access specific addresses on the bus, and hence use
- privileged system calls. Therefore, a device driver has a component
- in kernel space. One can write a user space driver for, for example,
- a device on the parallel port, but in this case, the basic parallel
- port device driver runs already in the kernel by default; the
- interaction with the hardware then takes place via the method
- explained below.
+ hardware and to critical data of the operating system; these user
+space programs execute much of the operating system's functionality
+through <emphasis>system calls</emphasis>.
+</para>
+<para>
+Device drivers typically must access specific addresses on the bus,
+and hence must (at least partially) run in kernel space. Normal users
+program against the API of <emphasis>Comedi</emphasis>, while
+&comedi; device driver writers use the API offered by
+<emphasis>Kcomedilib</emphasis>. Typical examples of the latter are
+the registration of interrupt handler routines, and the handling of
+events.
+</para>
+</listitem>
+
+<listitem>
+<para>
+ <emphasis role="strong">Device files or device file system.</emphasis>
+ Users who write an application for a particular device,
+ must link their application to that device's device driver. Part of
+this device driver, however, runs in kernel space, and the user
+application in user space. So, the operating system provides an
+interface between both. In Linux or Unix, these interfaces are in the
+form of “files”
+ in the <filename class=directory>/dev</filename> directory (2.2.x kernels or
+earlier) or <filename class=directory>/devfs</filename> directory
+ (2.4.x kernels and later). Each device supported in the kernel has a
+representative as such a user space device file, and its functionality can
+ be accessed by classical Unix file I/O:
+<function>open</function>,
+<function>close</function>, <function>read</function>,
+ <function>write</function>, and <function>ioctl</function>.
+</para>
+</listitem>
+
+<listitem>
+<para>
+ <emphasis role="strong"><filename class=directory>/proc</filename> interface.</emphasis>
+ Linux (and some other UNIX operating systems) offer a file-like
+interface to attached devices (and other OS-related information) via
+the <filename class=directory>/proc</filename> directories. These
+“files” do not really exist, but it gives a familiar
+interface to users, with which they can inspect the current status of
+each device.
+</para>
+</listitem>
+
+<listitem>
+<para>
+ <emphasis role="strong">Direct Memory Access (DMA) vs. Programmed
+Input/Output (PIO).</emphasis>
+ Almost all devices can be interfaced in PIO mode: the processor is
+ responsible for directly accessing the bus addresses allocated to
+the device whenever it needs
+ to read or write data. Some devices also allow DMA: the device and the
+ memory “talk” to each other directly, without needing the processor.
+ DMA is a feature of the bus, not of the operating system (which, of
+course, has
+ to support its processes to use the feature).
</para>
+</listitem>
+<listitem>
<para>
- Device files or device file system.
- The users that want to write an application for a particular device,
- must link their application to the device's device driver. This device
- driver, however, runs in kernel space, and the user application in
- user space. So, the operating system provides an interface between
- both. In Linux or Unix, these interfaces are in the form of "files"
- in the /dev directory (2.2.x kernels or earlier) or /devfs directory
- (2.4.x kernels and later). Each device has a representative, and can
- be accessed by the classical Unix file I/O calls: open, close, read,
- write, and ioctl.
+<emphasis role="strong">Real-time vs. non real-time.</emphasis>
+If the device is to be used in a
+<ulink
+ url="http://www.fsmlabs.com/products/openrtlinux/">RTLinux/Free
+</ulink>
+or <ulink url="http://www.rtai.org">RTAI</ulink> application,
+there are a few extra requirements, because not all system calls are
+available in the kernel of the real-time operating systems
+<ulink
+ url="http://www.fsmlabs.com/products/openrtlinux/">RTLinux/Free
+</ulink>
+or <ulink url="http://www.rtai.org">RTAI</ulink>.
+The APIs of RTAI and RTLinux/Free differ in
+different ways, so the &comedi; developers have spent a lot of efforts
+to make generic wrappers to the required RTOS primitives: timers,
+memory allocation, registration of interrupt handlers, etc.
</para>
+</listitem>
+
+</itemizedlist>
+
+</section>
+
+<section id="comediosignals">
+<title>
+DAQ signals
+</title>
+<para>
+The cards supported in &comedi; have one or more of the following
+<emphasis role="strong">signals</emphasis>: analog input, analog
+output, digital input, digital output, counter input, counter output,
+pulse input, pulse output:
+<itemizedlist>
+<listitem>
<para>
- /proc interface.
- Linux offers a file-like interface to attached devices (and other
- OS-related information) via the /proc directories. This interface
- allows to inspect the current status of each device.
+<emphasis role="strong">Digital</emphasis> signals are conceptually quite simple, and don't need
+much configuration: the number of channels, their addresses on the
+bus, and their input or output direction.
</para>
+</listitem>
+<listitem>
<para>
- Direct Memory Access (DMA) vs. Programmed Input/Output (PIO).
- Almost all devices can be interfaced in PIO mode: the processor is
- responsible for accessing bus addresses allocated to the device, and
- to read or write data. Some devices also allow DMA: the device and the
- memory "talk" to each other directly, without needing the processor.
- DMA is a feature of the bus, not of the operating system (which has
- to support its processes to use the feature, of course).
+<emphasis role="strong">Analog</emphasis> signals are a bit more complicated. Typically, an analog
+acquisition channel can be programmed to generate or read a voltage between a
+lower and an upper threshold (e.g., <literal>-10V</literal> and
+<literal>+10V</literal>); the card's electronics can be programmed to
+automatically sample a set of channels, in a prescribed order, to
+<emphasis>buffer</emphasis> sequences of data on the board; or to use
+DMA or an interrupt routine to dump the data in a prescribed part of
+memory.
</para>
+</listitem>
+<listitem>
<para>
-If the device is to be used in a Real-Time Linux or RTAI application,
-there are a few extra requirements, because not all system calls are
-available in the RTOS kernel of Real-Time Linux or RTAI.
+<emphasis role="strong">Pulse</emphasis>-based signals (counters,
+timers, encoders, etc.) are conceptually
+only a bit more complex than digital inputs and outputs, in that
+they only add some <emphasis>timing specifications</emphasis> to the
+signal. &comedi; has still only a limited number of drivers for this
+kind of signals, although most of the necessary API and support
+functionality is available.
</para>
-</section>
+</listitem>
+</itemizedlist>
+In addition to these “real” DAQ functions, &comedi; also
+offers basic timer access.
+</para>
+</section>
-<section>
+<section id="comedidevices">
<title>
-Policy vs. mechanism.
+Device hierarchy
</title>
<para>
-Device drivers are often written by application programmers, that have
-a particular application in mind. For example, one writes a driver for
-the parallel port, because one wants to use it to generate pulses that
-drive a stepper motor. This approach often leads to device drivers
-that depend too much on the application, and are not general enough to
-be re-used for other applications. One golden rule for the device
-driver writer is to separate mechanism and policy:
-</para>
+&comedi; organizes all hardware according to the following generic
+hierarchy:
+<itemizedlist>
+<listitem>
<para>
- Mechanism.
- The mechanism part of the device interface is a faithful
- representation of the bare functionality of the device, independent of
- what part of the functionality an application will use.
+<emphasis role="strong">Channel</emphasis>: the lowest-level hardware
+component, that represents the properties of one single data channel;
+for example, an analog input, or a digital output.
+Each channel has several parameters, such as: the voltage range; the
+reference voltage; the channel polarity (unipolar, bipolar); a
+conversion factor between voltages and physical units; the binary
+values “0” and “1”; etc.
</para>
+</listitem>
+<listitem>
<para>
- Policy.
- Once a device driver offers a software interface to the mechanism of
- the device, an application writer can use this mechanism interface to
- use the device in one particular fashion. That is, some of the data
- stuctures offered by the mechanism are interpreted in specific
- physical units, or some of them are taken together because this
- composition is relevant for the application. For example, a analog
- output card can be used to generate voltages that are the inputs for
- the electronic drivers of the motors of a robot; these voltages can be
- interpreted as setpoints for the desired velocity of these motors, and
- six of them are taken together to steer one particular robot with
- six-degrees of freedom. Some of the other outputs of the same physical
- device can be used by another application program, for example to
- generate a sine wave that drives a vibration shaker.
+<emphasis role="strong">Sub-device</emphasis>: a set of functionally
+identical channels that are physically implemented on the same (chip
+on an) interface card. For example, a set of 16 identical analog
+outputs. Each sub-device has parameters for: the number of channel
+and the type of the channels.
+</para>
+</listitem>
+
+<listitem>
+<para>
+<emphasis role="strong">Device</emphasis>: a set of sub-devices that are physically implemented on the
+ same interface card; in other words, the interface card itself.
+ For example, the <literal>National Instruments 6024E</literal>
+device has a sub-device with 16 analog input channels, another
+sub-device with two analog output channels, and a
+ third sub-device with eight digital inputs/outputs.
+ Each device has parameters for: the device identification tag from
+ the manufacturer, the identification tag given by the operating system
+ (in order to discriminate between multiple interface cards of the same
+ type), the number of sub-devices, etc.
+</para>
+</listitem>
+
+</itemizedlist>
+Some interface cards have extra components that don't fit in the
+above-mentioned classification, such as an EEPROM to store
+configuration and board parameters, or calibration inputs. These
+special components are also classified as “sub-devices” in
+&comedi;.
</para>
+
</section>
-<section>
+<section id="acquisitionterminology">
<title>
-Overview of Comedi.
+Acquisition terminology
</title>
-<para>
-The supported cards in Comedi have one or more of the following
-features: analog input channels, analog output channels, digital input
-channels, and digital output channels. The digital channels are
-conceptually quite simple, and don't need much configuration: the
-number of channels, their addresses on the bus, and their direction
-(input/output).
-</para>
<para>
-The analog channels are a bit more complicated. Typically, an analog
-channel can be programmed to generate or read a voltage between a
-lower and an upper threshold (e.g., -10V and +10V); the card's
-electronics can be programmed to automatically sample a set of
-channels, in a prescribed order; top buffer sequences of data on the
-board; or to use DMA to dump the data in an available part of memory,
-without intervention from the processor.
-</para>
+This Section introduces the terminology that this document uses when
+talking about “acquisitions.” <xref linkend="fig-acq-seq">
+depicts a typical acquisition <emphasis role="strong">sequence</emphasis>:
+<itemizedlist>
+<listitem>
<para>
-Many interface cards have extra functionality, besides the analog and
-digital channels. For example, an EEPROM for configuration and board
-parameters, calibration inputs, counters and timers, encoders (=
-quadrature counter on two channels), etc. Therefore, Comedi offers
-more than just analog and digital data acquisition.
+The sequence has a <emphasis role="strong">start</emphasis> and an
+<emphasis role="strong">end</emphasis>. At both sides, the software
+and the hardware need some finite
+<emphasis role="strong">initialization or settling time</emphasis>.
</para>
+</listitem>
+<listitem>
<para>
-The kernel space structures that Comedi uses have the following
-hierarchy:
+<anchor id="scan">
+The sequence consists of a number of identically repeated
+<emphasis role="strong">scans</emphasis>. This is where the actual
+data acquisitions are taking place: data is read from the card, or
+written to it. Each scan also has a
+<emphasis role="strong">begin</emphasis>, an
+<emphasis role="strong">end</emphasis>, and a finite
+<emphasis role="strong">setup time</emphasis>. Possibly, there is also
+a settling time
+(“<emphasis role="strong">scan delay</emphasis>”) at the
+end of a scan.
</para>
<para>
-- channel: the lowest-level component, that represents the properties
- of one single data channel (analog in or out; digital in or out).
- Each channel has parameters for: the voltage range, the reference
- voltage, the channel polarity (unipolar, bipolar), a conversion
- factor between voltages and physical units.
+So, the hardware puts a
+lower boundary (the <emphasis role="strong">scan interval</emphasis>)
+on the minimum time needed to complete a full scan.
</para>
+</listitem>
+
+<listitem>
<para>
-- sub-device: a set of functionally identical channels that are
- physically implemented on the same (chip on an) interface card. For
- example, a set of 16 identical analog outputs.
- Each sub-device has parameters for: the number of channels, and the type
- of the channels.
+Each scan contains one or more
+<anchor id="conversion">
+<emphasis role="strong">conversions</emphasis> on particular channels,
+i.e., the AD/DA converter is activated on each of the programmed
+channels, and produces a sample, again in a finite
+<emphasis role="strong">conversion time</emphasis>, starting from the
+moment in time called the
+<emphasis role="strong">sample time</emphasis>
+in <xref linkend="fig-acq-seq">
+(sometimes also called the “timestamp”),
+and caused by a
+triggering event, called <emphasis role="strong">convert</emphasis>.
+In addition, each hardware has limits on the minimum
+<emphasis role="strong">conversion interval</emphasis> it can achieve,
+i.e., the minimum time it needs between
+<emphasis>subsequent</emphasis> conversions.
</para>
<para>
-- device: a set of sub-devices that are physically implemented on the
- same interface card; in other words, the interface card itself.
- For example, the NI 6024E device has a sub-device with 16 analog input
- channels, another sub-device with two analog output channels, and a
- third sub-device with eight digital inputs/outputs.
- Each device has parameters for: the device identification tag from
- the manufacturer, the identification tag given by the operating system
- (in order to discriminate between multiple interface cards of the same
- type), the number of sub-devices, etc.
+Some hardware must <emphasis>multiplex</emphasis> the conversions onto
+one single AD/DA hardware, such that the conversions are done serially
+in time (as shown on the <link linkend="fig-acq-seq">Figure</link>);
+other cards have the hardware to do two or more acquisitions in
+parallel. The begin of each conversion is “triggered” by
+some internally or externally generated pulse, e.g., a timer.
</para>
+</listitem>
-<para>
-The basic functionalities offered by Comedi are:
-</para>
-<para>
-- instruction: to synchronously perform one single data acquisition on a
- specified channel, or to perform a configuration on the channel.
- "Synchronous" means that the calling process blocks until the data
- acquisition has finished.
+</itemizedlist>
+In general, not only the begin of a <emphasis>conversion</emphasis> is
+triggered, but also the begin of a <emphasis>scan</emphasis> and of a
+<emphasis>sequence</emphasis>. &comedi; provides the API to configure
+what <link linkend="comedicmdsources">triggering source</link>
+one wants to use in each case. The API also
+allows to specify the <emphasis role="strong">channel list</emphasis>,
+i.e., the sequence of channels that needs to be acquired during each
+scan.
</para>
+
<para>
-- scan: repeated instructions on a number of different channels, with a
- programmed sequence and timing.
+<figure id="fig-acq-seq" float="1" pgwide="0">
+<title>
+ Acquisition sequence. (Figure courtesy of
+ <ulink url="mailto:Kurt.Mueller@sfwte.ch">Kurt Müller</ulink>.)
+</title>
+<mediaobject>
+<imageobject>
+<imagedata fileref="figures/acq-seq.gif" format="GIF">
+</imageobject>
+<!--
+<imageobject>
+<imagedata fileref="/prior-inv.eps" format="EPS">
+</imageobject>
+-->
+</mediaobject>
+</figure>
+
</para>
+
+</section>
+
+
+<section id="comedifunctions">
+<title>
+DAQ functions
+</title>
+
<para>
-- command: start or stop an autonomous (and hence aynchronous) data
- acquisition (i.e., a number of scans) on a specified set of
- channels. "Autonomous" means: without interaction from the software,
- i.e., by means of on-board timers or possibly external triggers.
+The basic data acquisition functionalities that &comedi; offers work
+on channels, or sets of channels:
+<itemizedlist>
+
+<listitem>
+<para>
+<emphasis role="strong">Single acquisition</emphasis>: &comedi; has
+function calls to synchronously perform
+<emphasis>one single</emphasis> data acquisition on a specified
+channel: <function>comedi_data_read()</function>,
+<function>comedi_data_write()</function>,
+<function>comedi_dio_read()</function>,
+<function>comedi_dio_write()</function>.
+“Synchronous” means that the calling process
+blocks until the data acquisition has finished.
+</para>
+</listitem>
+
+<listitem>
+<para>
+<emphasis role="strong">Instruction</emphasis>: a
+<function>comedi_do_insn()</function> instruction
+performs (possibly multiple) data acquisitions on a specified channel,
+in a <emphasis role="strong">synchronous</emphasis> way. So, the
+function call blocks until the whole acquisition has finished.
+</para>
+<para>
+In addition, <function>comedi_do_insnlist()</function> executes a
+<emphasis>list</emphasis> of instructions (on different channels) in
+one single (blocking, synchronous) call, such that the overhead
+involved in configuring each individual acquisition is reduced.
+</para>
+</listitem>
+
+<listitem>
+<para>
+<emphasis role="strong">Scan</emphasis>: a scan is an acquisition on a
+set of different channels, with a <emphasis>specified sequence and
+timing</emphasis>.
+</para>
+<para>
+Scans are not directly available as stand-alone function calls in the
+&comedi; API. They are the internal building blocks of a &comedi;
+<emphasis>command</emphasis> (see below).
+</para>
+</listitem>
+
+<listitem>
+<para>
+<emphasis role="strong">Command</emphasis>: a command is
+<emphasis>sequence</emphasis> of
+<emphasis>scans</emphasis>, for which conditions have been specified
+that determine when the acquisition will start and stop. A
+<function>comedi_command()</function> function call generates
+<emphasis role="strong">aynchronous</emphasis> data acquisition:
+as soon as the command information has been filled in, the
+<function>comedi_command()</function> function call returns,
+the hardware of the card takes care of the sequencing and the timing of
+the data acquisition,
+ and makes sure that the acquired data is delivered
+in a software buffer provided by the calling process. Asynchronous
+operation requires some form of “callback” functionality
+to prevent buffer overflow: after the calling process has launched the
+acquisition command, it goes off doing other things, but not after it
+has configured the “handler” that the interface card can
+use when it needs to put data in the calling process's buffer.
+Interrupt routines or DMA are typical techniques to allow such
+asynchronous operation. Their handlers are configured at driver load
+time, and can typically not be altered from user space.
+</para>
+<para>
+Buffer management is not the only asynchronous activity: a running
+acquisition must eventually be stopped too, or it must be started
+after the <function>comedi_command()</function> function call has
+prepared (but not started) the hardware for the acquisition.
+The command functionality is very configurable with respect to
+choosing which <emphasis role="strong">events</emphasis> will signal
+the starting or stopping of the programmed acquisition: external triggers,
+internal triggers, end of scan interrupts, timers, etc.
+The user of the driver can execute a &comedi;
+<emphasis>instruction</emphasis> that sends a
+trigger signal to the device driver. What the driver does exactly with
+this trigger signal is determined in the specific driver. For example,
+it starts or stops the ongoing acquisition. The execution of the event
+associated with this trigger instruction is
+<emphasis role="strong">synchronous</emphasis> with the execution of
+the trigger instruction in the device driver, but it is
+<emphasis role="strong">asynchronous</emphasis> with respect to the
+instruction or command that initiated the current acquisition.
+</para>
+<para>
+Typically, there is one synchronous triggering instruction for each
+<emphasis>subdevice</emphasis>.
+</para>
+</listitem>
+
+</itemizedlist>
+Note that software triggering is only relevant for commands, and not
+for instructions: instructions are executed
+<emphasis>synchronously</emphasis> in the sense that the instruction
+call blocks until the whole instruction has finished. The command call, on
+the other hand, activates an acquisition and returns before this
+acquisition has finished. So, the software trigger works
+asynchronously for the ongoing acquisition.
</para>
+
+</section>
+
+<section id="comedisupporting">
+<title>
+Supporting functionality
+</title>
+
<para>
-This command functionality is not offered by all DAQ cards. When
-using RTAI or Real-Time Linux, the command functionality is emulated
-through the "comedi_rt_timer" virtual driver.
-The command functionality is very configurable, with respect to the
-choice of events with which to signal the progress of the programmed
-scans: external triggers, end of instruction, etc.
+The full command functionality cannot be offered by DAQ cards that
+lack the hardware to autonomously sequence a series of
+scans, and/or to support interrupt or DMA callback functionality.
+For these cards, the command functionality must be provided in
+software. And because of the quite strict real-time requirements for a
+command acquisition, a real-time operating system should be used to
+translate the command specification into a correctly timed sequence of
+instructions. Such a correct translation is the responsibility of the
+device driver developer for the card. However,
+&comedi; provides the <function>comedi_rt_timer</function> kernel
+module to support such a
+<emphasis role="strong">virtual command execution</emphasis> under
+<acronym>RTAI</acronym> or <acronym>RTLinux/Free</acronym>.
</para>
<para>
-Comedi not only offers the API to access the functionality of the
-cards, but also to query the capabilities of the installed Comedi
-devices. That is, a user process can find out on-line what channels
-are available, and what their physical parameters are (range,
-direction of input/output, etc.).
+&comedi; not only offers the API
+<emphasis role="strong">to access</emphasis> the functionality of the
+cards, but also <emphasis role="strong">to query</emphasis> the
+capabilities of the installed devices. That is, a user process can
+find out <emphasis>on-line</emphasis> what channels are available, and
+what their physical parameters are (range, direction of input/output,
+etc.).
</para>
<para>
-Buffers are an important aspect of device drivers: the data has
-to be stored in such buffers, if the application program cannot
-guarantee to read or write the data as soon as the interface board
-wants to do so. Therefore, Comedi offers functionality to configure
-and manage data buffers.
+<emphasis role="strong">Buffering</emphasis> is another important
+aspect of device drivers: the acquired data has to be stored in such
+buffers, because, in general, the application program cannot guarantee
+to always be ready to provide or accept data as soon as the interface
+board wants to do a read or write operation. Therefore, &comedi;
+offers all functionality to configure and manage data buffers,
+abstracting away the intricacies of buffer management at the bare
+operating system level.
</para>
<para>
-Comedi contains more than just procedural function calls: it also
-offers event-driven functionality. The data acquisition can signal
-its completion by means of an interrupt or a callback function call.
+As already mentioned before, &comedi; contains more than just
+procedural function calls, since it also offers
+<emphasis role="strong">event-driven</emphasis>
+(“asynchronous”) functionality:
+the data acquisition can signal
+its completion by means of an interrupt or a
+<emphasis>callback</emphasis> function call.
Callbacks are also used to signal errors during the data
acquisition or when writing to buffers, or at the end of a scan or
acquisition that has been launched previously to take place
asynchronously (i.e., the card fills up som shared memory buffer
autonomously, and only warns the user program after it has finished).
-</para>
-
-<para>
The mechanisms for synchronization and interrupt handling are a bit
-different when used in a real-time context (i.e., with either RTAI or
-Real-Time Linux), but both are encapsulated behind the same Comedi calls.
+different when used in real-time
+(<application>RTAI</application> or
+<application>RTLinux/Free</application>) or non real-time, but both
+contexts are encapsulated wihting the same &comedi; calls.
</para>
<para>
-Because multiple devices can all be active at the same time, Comedi
-provides (non-SMP!) locking primitives to ensure atomic operations on
-critical sections of the code or data structures.
+Because multiple devices can all be active at the same time, &comedi;
+provides <emphasis role="strong">locking</emphasis> primitives to
+ensure atomic operations on critical sections of the code or data
+structures.
</para>
<para>
-Finally, Comedi offers the above-mentioned "high-level" interaction,
-i.e., at the level of user space device drivers, through file
-operations on entries in the /dev directory (for access to the
+Finally, &comedi; offers the previously mentioned
+“high-level” interaction, i.e., at the level of user space
+device drivers, through file operations on entries in the
+<filename class=directory>/dev</filename> directory (for access to the
device's functionality), or interactively from the command line
-through the "files" in the /proc directory (which allow to inspect
-the status of a Comedi device). This high-level interface resides in
-the "comedilib" tarball, which is the user space library, with
-facilities to connect to the kernel space drivers residing in the
-"comedi" tarball.
+through the “files” in the
+<filename class=directory>/proc</filename> directory (which allow to
+inspect the status of a &comedi; device).
</para>
+
</section>
print
"<!--This file is autogenerated. Do not edit-->
-<section>
+<section id="functionreference">
<title>
Comedi Function Reference
</title>
<!-- <!DOCTYPE section PUBLIC "-//OASIS//DTD DocBook V3.1//EN"> -->
-<section>
+<section id="acquisitionfunctions">
<title>
-Application-specific functions
+Acquisition and configuration functions
</title>
+<para>
+This Section gives an overview of all &comedi; functions with which
+application programmers can implement their data acquisition. (With
+“acquisition” we mean all possible kinds of interfacing
+with the cards: input, output, configuration, streaming, etc.)
+<xref linkend="comedireference"> explains the function calls in full
+detail.
+</para>
+
+<section id="singleacquisition">
+<title>
+Functions for single acquisition
+</title>
+
+<para>
+The simplest form of using &comedi; is to get one single sample to or
+from an interface card. This sections explains how to do such simple
+<link linkend="dio">digital</link> and
+<link linkend="singleanalog">analog</link> acquisitions.
+</para>
+
+<section id="dio">
+<title>
+Single digital acquisition
+</title>
+
+<para>
+Many boards supported by &comedi; have digital input and output
+channels; i.e., channels that can only produce a <literal>0</literal>
+or a <literal>1</literal>.
+Some boards allow the <emphasis>direction</emphasis> (input or output)
+of each channel to be specified independently in software.
+</para>
+
+<para>
+&comedi; groups digital channels into a
+<emphasis>subdevice</emphasis>, 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.
+</para>
+
+<para>
+Individual bits on a digital I/O device can be read and written using
+the functions
+<programlisting>
+ int <link linkend="func-ref-comedi-dio-read">comedi_dio_read</link>(device,subdevice,channel,unsigned int *bit);
+ int <link linkend="func-ref-comedi-dio-write">comedi_dio_write</link>(device,subdevice,channel,unsigned int bit);
+</programlisting>
+The <parameter class=function>device</parameter> parameter is a
+<link linkend="ref-type-comedi-t">pointer</link>
+to a successfully opened &comedi; device.
+The <parameter class=function>subdevice</parameter> and
+<parameter class=function>channel</parameter> parameters are positive
+integers that indicate which subdevice and channel is used in the
+acquisition. The integer <parameter class=function>bit</parameter>
+contains the value of the acquired bit.
+<para>
+The direction of bidirectional lines can be configured using
+the function
+<programlisting>
+ <link linkend="func-ref-comedi-dio-config">comedi_dio_config</link>(device,subdevice,channel,unsigned int dir);
+</programlisting>
+The parameter <parameter class=function>dir</parameter> should be
+either <literal>COMEDI_INPUT</literal> or
+<literal>COMEDI_OUTPUT</literal>.
+Many digital I/O subdevices group channels into blocks for
+configuring direction. Changing one channel in a block changes
+the entire block.
+</para>
+
+<para>
+Multiple channels can be read and written simultaneously using the
+function
+<programlisting>
+ <link linkend="func-ref-comedi-dio-bitfield">comedi_dio_bitfield</link>(device,subdevice,unsigned int write_mask,unsigned int *bits);
+</programlisting>
+Each channel is assigned to a bit in the
+<parameter class=function>write_mask</parameter> and
+<parameter class=function>bits</parameter>
+bitfield. If a bit in
+<parameter class=function>write_mask</parameter> is set, the
+corresponding bit in <parameter class=function>*bits</parameter> will
+be written to the corresponding digital output line.
+Each digital line is then read and placed into
+<parameter class=function>*bits</parameter>. The value
+of bits in <parameter class=function>*bits</parameter> corresponding
+to digital output lines is undefined and device-specific. Channel
+<literal>0</literal> is the least significant bit in the bitfield;
+channel <literal>31</literal> is the most significant bit. Channels
+higher than <literal>31</literal> cannot be accessed using this method.
+</para>
+
+<para>
+The digital acquisition functions seem to be very simple, but, behind
+the implementation screens of the &comedi; kernel module, they are
+executed as special cases of the general
+<link linkend="instructions">instruction</link> command.
+</para>
+
+
+</section>
+
+
+<section id="singleanalog">
+<title>
+Single analog acquisition
+</title>
+<para>
+Analog &comedi; channels can produce data values that are
+<emphasis>samples</emphasis> from continuous analog signals.
+These samples are integers with a significant content in
+the range of, typically, <literal>8</literal>, <literal>10</literal>,
+<literal>12</literal>, or <literal>16</literal> bits.
+</para>
+<para>
+The
+<programlisting>
+ int <link linkend="func-ref-comedi-data-read">comedi_data_read</link>(<link linkend="ref-type-comedi-t">comedi_t</link> * device, unsigned int subdevice, unsigned int channel,
+ unsigned int range, unsigned int aref, <link linkend="ref-type-lsampl-t">lsampl_t</link> * data);
+</programlisting>
+function reads one such data value from a &comedi; channel, and puts it in
+the user-specified <parameter>data</parameter> buffer. The
+<programlisting>
+ int <link linkend="func-ref-comedi-data-write">comedi_data_write</link>(<link linkend="ref-type-comedi-t">comedi_t</link> * device, unsigned int subdevice, unsigned int channel,
+ unsigned int range, unsigned int aref, <link linkend="ref-type-lsampl-t">lsampl_t</link> data);
+</programlisting>
+works in the opposite direction. Data values returned by this function
+are unsigned integers less than, or equal to, the maximum sample value
+of the channel, which can be determined using the function
+<programlisting>
+ <link linkend="ref-type-lsampl-t">lsampl_t</link> <link linkend="func-ref-comedi-get-maxdata">comedi_get_maxdata</link>(<link linkend="ref-type-comedi-t">comedi_t</link> * device, unsigned int subdevice, unsigned int channel);
+</programlisting>
+Conversion of data values to physical units can be performed by the
+function
+<programlisting>
+ double <link linkend="func-ref-comedi-to-phys">comedi_to_phys</link>(<link linkend="ref-type-lsampl-t">lsampl_t</link> data, comedi_range * range, <link linkend="ref-type-lsampl-t">lsampl_t</link> maxdata);
+</programlisting>
+There are two data structures in these commands that are not fully
+self-explanatory:
+<itemizedlist>
+
+<listitem>
+<para>
+<link linkend="ref-type-comedi-t">comedi_t</link>: this data structure
+contains all information that a user program has to know about an
+<emphasis>open</emphasis> &comedi; device. The programmer doesn't have
+to fill in this data structure manually: it gets filled in by opening
+the device.
+</para>
+</listitem>
+
+<listitem>
+<para>
+<link linkend="ref-type-lsampl-t">lsampl_t</link>: this
+“data structure” represents one single sample. On most
+architectures, it's nothing more than a 32 bits value. Internally,
+&comedi; does some conversion from raw sample data to
+“correct” integers. This is called “data
+munging”.
+</para>
+</listitem>
+
+</itemizedlist>
+</para>
+<para>
+Each single acquisition by, for example,
+<function>
+ <link linkend="func-ref-comedi-data-read">comedi_data_read()</link>
+</function>
+requires quite some overhead, because all the arguments of the
+function call are checked. If multiple acquisitions must be done on
+the same channel, this overhead can be avoided by using a function
+that can read more than one sample:
+<programlisting>
+ int <link linkend="func-ref-comedi-dio-read">comedi_data_read_n</link>(<link linkend="ref-type-comedi-t">comedi_t</link> *it, unsigned int subdev, unsigned int chan, unsigned int range,
+ unsigned int aref, <link linkend="ref-type-lsampl-t">lsampl_t</link> *data, unsigned int n)
+</programlisting>
+The number of samples, <parameter class=function>n</parameter>, is
+limited by the &comedi; implementation (to a maximum of 100 samples),
+because the call is blocking.
+</para>
+<para>
+The start of the data acquisition can also be delayed by a specified
+number of nano-seconds:
+<programlisting>
+int <link linkend="func-ref-comedi-data-read-delayed">comedi_data_read_delayed</link>(<link linkend="ref-type-comedi-t">comedi_t</link> *it, unsigned int subdev, unsigned int chan, unsigned int range,
+ unsigned int aref, <link linkend="ref-type-lsampl-t">lsampl_t</link> *data, unsigned int nano_sec)
+</programlisting>
+All these read and write acquisition functions are implemented on top
+of the generic <link linkend="instructions">instruction</link>
+command.
+</para>
+
+</section>
+
+</section>
+
+
+<section id="instructions">
+<title>
+Instructions for multiple acquisitions
+</title>
+<para>
+The <emphasis>instruction</emphasis> is one of the most generic,
+overloaden and flexible functions in the &comedi; API. It is used to
+execute a multiple of identical acquisitions on the same channel, but
+also to perform a
+<link linkend="instructionsconfiguration">configuration</link> of a
+channel.
+<anchor id="anchor.instruction.list">
+An <emphasis>instruction list</emphasis> is a list of instructions,
+possibly on different channels. Both instructions and instructions
+lists are executed <emphasis>synchronously</emphasis>, i.e., while
+<emphasis role="strong">blocking</emphasis> the calling process.
+This is one of the limitations of instructions; the other one is that
+they cannot code an acquisition involving timers or external events.
+These limits are eliminated by the
+<link linkend="commandsstreaming">command</link> acquisition
+primitive.
+</para>
+
+
+<section id="comediinsnstructure">
+<title>
+The instruction data structure
+</title>
+<para>
+All the information needed to execute an instruction is stored in the
+<link linkend="ref-type-comedi-insn">comedi_insn</link>
+data structure:
+<programlisting>
+struct <anchor id="insn-data-structure">comedi_insn_struct{
+ <anchor id="insn-data-structure-insn">unsigned int insn; // integer encoding the type of acquisition
+ // (or configuration)
+ unsigned int n; // number of samples
+ <link linkend="ref-type-lsampl-t">lsampl_t</link> <anchor id="insn-data-structure-data">*data; // pointer to data buffer
+ unsigned int subdev; // subdevice
+ unsigned int <anchor id="insn-data-structure-chanspec"><link linkend="ref-macro-CR-PACK">chanspec</link>; // encoded channel specification
+ unsigned int unused[3];
+} comedi_insn;
+</programlisting>
+Because of the large flexibility of the instruction function, many
+types of instruction do not need to fill in all fields, or attach
+different meanings to the same field. But the current implementation
+of &comedi; requires the
+<link linkend="insn-data-structure-data">data</link> field to be at
+least one byte long.
+</para>
+
+<para>
+The <link linkend="insn-data-structure-insn">insn</link> flag of the
+<link linkend="insn-data-structure">instruction data structure</link>
+determines the type of acquisition executed in the corresponding
+instruction:
+<itemizedlist>
+
+<listitem>
+<para>
+INSN_READ: the instruction executes a read on an analog channel.
+</para>
+</listitem>
+
+<listitem>
+<para>
+INSN_WRITE: the instruction executes a write on an analog channel.
+</para>
+</listitem>
+
+<listitem>
+<para>
+INSN_BITS: indicates that the instruction must
+read or write values on multiple digital I/O channels.
+</para>
+</listitem>
+
+<listitem>
+<para>
+INSN_GTOD: the instruction performs a “Get Time Of Day”
+acquisition.
+</para>
+</listitem>
+
+<listitem>
+<para>
+INSN_WAIT: the instruction blocks for a specified number of
+nanoseconds.
+</para>
+</listitem>
+
+</itemizedlist>
+</para>
+
+</section>
+
+
+<section id="instructionexecution">
+<title>
+Instruction execution
+</title>
+<para>
+Once an instruction data structure has been filled in, the
+corresponding instruction is executed as follows:
+<programlisting>
+ int <link linkend="func-ref-comedi-do-insn">comedi_do_insn</link>(<link linkend="ref-type-comedi-t">comedi_t</link> *it, <link linkend="ref-type-comedi-insn">comedi_insn</link> * instruction);
+</programlisting>
+Many &comedi; instructions are shortcuts that relieve the programmer
+from explicitly filling in the data structure and calling the
+<link linkend="func-ref-comedi-do-insn">comedi_do_insn</link>
+function.
+</para>
+<para>
+The
+<programlisting>
+ int <link linkend="func-ref-comedi-do-insnlist">comedi_do_insnlist</link><link linkend="ref-type-comedi-t">comedi_t</link> *it, <link linkend="ref-type-comedi-insnlist">comedi_insnlist</link> * list)
+</programlisting>
+instruction allows to perform a list of instructions in one function
+call. The number of instructions in the list is limited in the
+implementation, because instructions are executed
+<emphasis>synchronously</emphasis>, i.e., the call blocks until the
+whole instruction (list) has finished.
+</para>
+
+</section>
+
+</section>
+
+
+<section id="instructionsconfiguration">
+<title>
+Instructions for configuration
+</title>
+<para>
+<xref linkend="instructions"> explains how instructions are used to do
+<emphasis>acquisition</emphasis> on channels. This section explains
+how they are used to <emphasis>configure</emphasis> a device.
+There are various sorts of configurations, and the
+specific information for each different configuration possibility is
+to be specified via the
+<link linkend="insn-data-structure-data">data</link> buffer of the
+<link linkend="insn-data-structure">instruction data structure</link>.
+(So, the pointer to a
+<link linkend="ref-type-lsampl-t">lsampl_t</link>
+is misused as a pointer to an array with board-specific information.)
+</para>
+
+<para>
+Using INSN_CONFIG as the
+<link linkend="insn-data-structure-insn">insn</link> flag in an
+<link linkend="insn-data-structure">instruction data structure</link>
+indicates that the instruction will
+<emphasis>not perform acquisition</emphasis> on a
+channel, but will <emphasis>configure</emphasis> that channel.
+For example, the configuration of digital I/O channels is done as
+follows. The
+<link linkend="ref-macro-CR-PACK">chanspec</link> field in the
+<link linkend="insn-data-structure-chanspec">comedi_insn</link>
+data structure, contains the channel to be configured. And
+<link linkend="insn-data-structure-data">data</link>[0] contains
+either COMEDI_INPUT or COMEDI_OUTPUT, depending on the desired
+direction of the digital I/O lines.
+On typical devices, multiple channels are grouped together in blocks
+for determining their direction. And configuring one channel in a
+block configures the entire block.
+</para>
+
+<para>
+Another example of an INSN_CONFIG instruction is the configuration of
+the <link linkend="trigother-event">TRIG_OTHER</link> event source.
+</para>
+
+</section>
+
+
+<section id="inttrigconfiguration">
+<title>
+Instruction for internal triggering
+</title>
+<para>
+This special instruction has
+<anchor id="insn-inttrig">INSN_INTTRIG as the
+<link linkend="insn-data-structure-insn">insn</link> flag in its
+<link linkend="insn-data-structure">instruction data structure</link>.
+Its execution causes an
+<link linkend="trig-int-start-src">internal triggering event</link>. This
+event can, for example, cause the device driver to start a conversion,
+or to stop an ongoing acquisition. The exact meaning of the triggering
+depends on the card and its particular driver.
+</para>
+<para>
+The
+<link linkend="insn-data-structure-data">data</link>[0] field of the
+INSN_INTTRIG instruction is reserved for future use, and should be set
+to “0”.
+</para>
+
+</section>
+
+
+<section id="commandsstreaming">
+<title>
+Commands for streaming acquisition
+</title>
+
+<para>
+The most powerful &comedi; acquisition primitive is the
+<emphasis>command</emphasis>. It's powerful because, with one single
+command, the programmer launches:
+<itemizedlist>
+
+<listitem>
+<para>
+a possibly infinite <emphasis>sequence of acquisitions</emphasis>,
+</para>
+</listitem>
+
+<listitem>
+<para>
+accompanied with various <emphasis>callback</emphasis> functionalities
+(DMA, interrupts, driver-specific callback functions),
+</para>
+</listitem>
+
+<listitem>
+<para>
+for <emphasis>any number of channels</emphasis>,
+</para>
+</listitem>
+
+<listitem>
+<para>
+with an <emphasis>arbitrary order</emphasis> of channels in each scan
+(possibly even with repeated channels per scan),
+</para>
+</listitem>
+
+<listitem>
+<para>
+and with various scan <emphasis>triggering sources</emphasis>,
+external (i.e., hardware pulses) as well as internal (i.e., pulses
+generated on the DAQ card itself, or generated by a
+<link linkend="inttrigconfiguration">software trigger instruction</link>).
+</para>
+</listitem>
+
+</itemizedlist>
+This command functionality exists in the &comedi; API, because various
+data acquisition devices have the capability to perform this kind of
+complex acquisition, driven by either on-board or
+off-board timers and triggers.
+</para>
+
+<para>
+A command specifies a particular data
+<link linkend="fig-acq-seq">acquisition sequence</link>, which
+consists of a number of <emphasis>scans</emphasis>, and each scan is
+comprised of a number of <emphasis>conversions</emphasis>, which
+usually corresponds to a single A/D or D/A conversion. So, for
+example, a scan could consist of sampling channels 1, 2 and 3 of a
+particular device, and this scan should be repeated 1000 times, at
+intervals of 1 millisecond apart.
+</para>
+<para>
+The command function is complementary to the
+<link linkend="instructionsconfiguration">configuration instruction</link>
+function: each channel in the command's
+<link linkend="command-data-struct-chanlist">chanlist</link>
+should first be configured by an appropriate instruction.
+</para>
+
+
+<section id="executingcommand">
+<title>
+Executing a command
+</title>
+
+<para>
+A commands is executed by the following &comedi; function:
+<programlisting>
+ int <link linkend="func-ref-comedi-command">comedi_command</link>(<link linkend="ref-type-comedi-t">comedi_t</link> * device, <link linkend="ref-type-comedi-cmd">comedi_cmd</link> * command);
+</programlisting>
+The following sections explain the meaning of the
+<link linkend="ref-type-comedi-cmd">comedi_cmd</link> data structure.
+Filling in this structure can be quite complicated, and
+requires good knowledge about the exact functionalities of the DAQ
+card. So, before launching a command, the application programmer is
+adviced to check whether this complex command data structure can be
+successfully parsed. So, the typical sequence for executing a command is
+to first send the command through
+<link linkend="func-ref-comedi-command-test">comedi_command_test()</link>
+once or twice. The test will check that the command is valid for the
+particular device, and often makes some adjustments to the command
+arguments, which can then be read back by the user to see the actual
+values used.
+</para>
+<para>
+A &comedi; program can find out on-line what the command capabilities
+of a specific device are, by means of the
+<link linkend="func-ref-comedi-get-cmd-src-mask">comedi_get_cmd_src_mask()</link>
+function.
+</para>
+
+</section>
+
+
+<section id="comedicmdstructure">
+<title>
+The command data structure
+</title>
+<para>
+The command executes according to the information about the requested
+acquisition, which is stored in the
+<link linkend="ref-type-comedi-cmd">comedi_cmd</link>
+<anchor id="command-data-struct">data structure:
+<programlisting>
+typedef struct comedi_cmd_struct comedi_cmd;
+
+struct comedi_cmd_struct{
+ unsigned int subdev; // which subdevice to sample
+ unsigned int <anchor id="command-data-struct-flags">flags; // encode some configuration possibilities
+ // of the command execution; e.g.,
+ // whether a callback routine is to be
+ // called at the end of the command
+
+ unsigned int <anchor id="command-data-struct-start-src">start_src; // event to make the acquisition start
+ unsigned int <anchor id="command-data-struct-start-arg">start_arg; // parameters that influence this start
+
+ unsigned int <anchor id="command-data-struct-scan-begin-src">scan_begin_src; // event to make a particular scan start
+ unsigned int <anchor id="command-data-struct-scan-begin-arg">scan_begin_arg; // parameters that influence this start`
+
+ unsigned int <anchor id="command-data-struct-convert-src">convert_src; // event to make a particular conversion start
+ unsigned int <anchor id="command-data-struct-convert-arg">convert_arg; // parameters that influence this start
+
+ unsigned int <anchor id="command-data-struct-scan-end-src">scan_end_src; // event to make a particular scan terminate
+ unsigned int <anchor id="command-data-struct-scan-end-arg">scan_end_arg; // parameters that influence this termination
+
+ unsigned int <anchor id="command-data-struct-stop-src">stop_src; // what make the acquisition terminate
+ unsigned int <anchor id="command-data-struct-stop-arg">stop_arg; // parameters that influence this termination
+
+ unsigned int <anchor id="command-data-struct-chanlist">*chanlist; // pointer to list of channels to be sampled
+ unsigned int <anchor id="command-data-struct-chanlist-len">chanlist_len; // number of channels to be sampled
+
+ sampl_t *<anchor id="command-data-struct-data">data; // address of buffer
+ unsigned int <anchor id="command-data-struct-data-len">data_len; // number of samples to acquire
+};
+</programlisting>
+The start and end of the whole command acquisition sequence, and the
+start and end of each scan and of each conversion, is triggered by a
+so-called <emphasis>event</emphasis>. More on these in
+<xref linkend="comedicmdsources">.
+</para>
+
+<para>
+The <parameter class=function>subdev</parameter> member of the
+<link linkend="ref-type-comedi-cmd">comedi_cmd()</link> structure is
+the index of the subdevice the command is intended for. The
+<link linkend="func-ref-comedi-find-subdevice-by-type">comedi_find_subdevice_by_type()</link>
+function can be useful in discovering the index of your desired subdevice.
+</para>
+
+<para>
+The <link linkend="command-data-struct-chanlist">chanlist</link>
+member of the
+<link linkend="ref-type-comedi-cmd">comedi_cmd</link> data
+structure should point to an array whose number of elements is
+specificed by
+<link linkend="command-data-struct-chanlist-len">chanlist_len</link>
+(this will generally be the same as the
+<link linkend="command-data-struct-scan-end-arg">scan_end_arg</link>).
+The
+<link linkend="command-data-struct-chanlist">chanlist</link>
+specifies the sequence of channels and gains (and analog references)
+that should be stepped through for each scan. The elements of the
+<link linkend="command-data-struct-chanlist">chanlist</link> array should be
+initialized by “packing” the channel, range and reference
+information together with the
+<parameter class=function>
+ <link linkend="ref-macro-CR-PACK">CR_PACK()</link>
+</parameter>
+macro.
+</para>
+
+<para>
+The <link linkend="command-data-struct-data">data</link> and
+<link linkend="command-data-struct-data-len">data_len</link>
+members can be safely ignored when issueing commands from a user-space
+program. They only have meaning when a command is sent from a
+<emphasis role="strong">kernel</emphasis> module using the
+<function>kcomedilib</function> interface, in which case they specify
+the buffer where the driver should write/read its data to/from.
+</para>
+
+<para>
+The final member of the
+<link linkend="command-data-struct">comedi_cmd</link> structure is the
+<link linkend="command-data-struct-flags">flags</link> field,
+i.e., bits in a word that can be bitwise-or'd together. The meaning of
+these bits are explained in a
+<link linkend="source.flags.anchor">later section</link>.
+</para>
+
+</section>
+
+
+<section id="comedicmdsources">
+<title>
+The command trigger events
+<anchor id="source.trigger.anchor">
+</title>
+<para>
+A command is a very versatile acquisition instruction, in the sense
+that it offers lots of possibilities to let different hardware and
+software sources determine when acquisitions are started, performed,
+and stopped. More specifically, the command
+<link linkend="command-data-struct">data structure</link>
+has <emphasis>five</emphasis> types of events: start the
+<link linkend="acquisitionterminology">acquisition</link>,
+start a <link linkend="scan">scan</link>, start a
+<link linkend="conversion">conversion</link>, stop a scan, and stop
+the acquisition. Each event can be given its own
+<emphasis><link linkend="source.trigger.anchor">source</link></emphasis>
+(the <parameter class=function>*_src</parameter> members in the
+<link linkend="ref-type-comedi-cmd">comedi_cmd</link> data
+structure). And each event source can have a corresponding
+argument (the <parameter class=function>*_arg</parameter> members of
+the <link linkend="ref-type-comedi-cmd">comedi_cmd</link> data
+structure) whose meaning depends on the type of source trigger.
+For example, to specify an external digital line “3” as a
+source (in general, <emphasis>any</emphasis> of the five event
+sources), you would use
+<parameter>src</parameter>=<link linkend="trig-ext">TRIG_EXT</link> and
+<parameter>arg</parameter>=3.
+</para>
+<para>
+The following paragraphs discuss in somewhat more detail the trigger
+event sources(<parameter class=function>*_src</parameter>), and the
+corresponding arguments (<parameter class=function>*_arg</parameter>).
+</para>
+<para>
+The start of an acquisition is controlled by the
+<link linkend="command-data-struct-start-src">start_src</link> events.
+The available options are:
+<itemizedlist>
+
+<listitem>
+<para>
+<anchor id="trig-now-start-src">
+TRIG_NOW: the
+<link linkend="command-data-struct-start-src">start_src</link>
+event occurs
+<link linkend="command-data-struct-start-arg">start_arg</link>
+nanoseconds after the
+<link linkend="ref-type-comedi-cmd">comedi_cmd</link>
+is called. Currently, only
+<link linkend="command-data-struct-start-arg">start_arg</link>=0 is
+supported.
+</para>
+</listitem>
+
+<listitem>
+<para>
+<anchor id="trig-follow-start-src">
+TRIG_FOLLOW: (For an output device.) The
+<link linkend="command-data-struct-start-src">start_src</link>
+event occurs when data is written to the buffer.
+</para>
+</listitem>
+
+<listitem>
+<para>
+<anchor id="trig-ext-start-src">
+TRIG_EXT: the start event occurs when an external trigger signal
+occurs; e.g., a rising edge of a digital line.
+<link linkend="command-data-struct-start-arg">start_arg</link>
+chooses the particular digital line.
+</para>
+</listitem>
+
+<listitem>
+<para>
+<anchor id="trig-int-start-src">
+TRIG_INT: the start event occurs on a &comedi; internal signal, which
+is typically caused by an
+<link linkend="insn-inttrig">INSN_INTTRIG instruction</link>.
+</para>
+</listitem>
+
+</itemizedlist>
+The start of the beginning of each
+<link linkend="scan">scan</link> is controlled by the
+<link linkend="command-data-struct-scan-begin-src">scan_begin</link> events.
+The available options are:
+<itemizedlist>
+
+<listitem>
+<para>
+<anchor id="trig-timer-start-scan">
+TRIG_TIMER:
+<link linkend="command-data-struct-scan-begin-src">scan_begin</link>
+events occur periodically. The time between
+<link linkend="command-data-struct-scan-begin-src">scan_begin</link>
+events is
+<link linkend="command-data-struct-convert-arg">convert_arg</link>
+nanoseconds.
+</para>
+</listitem>
+
+<listitem>
+<para>
+<anchor id="trig-follow-start-scan">
+TRIG_FOLLOW: The
+<link linkend="command-data-struct-scan-begin-src">scan_begin</link>
+event occurs immediately after a
+<link linkend="command-data-struct-scan-end-src">scan_end</link>
+event occurs.
+</para>
+</listitem>
+
+<listitem>
+<para>
+<anchor id="trig-ext-start-scan">
+TRIG_EXT: the
+<link linkend="command-data-struct-scan-begin-src">scan_begin</link>
+event occurs when an external trigger signal
+occurs; e.g., a rising edge of a digital line.
+<link linkend="command-data-struct-scan-begin-arg">scan_begin_arg</link>
+chooses the particular digital line.
+</para>
+</listitem>
+
+</itemizedlist>
+The
+<link linkend="command-data-struct-scan-begin-arg">scan_begin_arg</link>
+used here may not be supported exactly by the device, but it
+will be adjusted to the nearest supported value by
+<link linkend="func-ref-comedi-command-test">comedi_command_test()</link>.
+</para>
+<para>
+The timing between each sample in a
+<link linkend="scan">scan</link> is controlled by the
+<link linkend="command-data-struct-convert-src">convert_*</link>
+fields:
+<itemizedlist>
+
+<listitem>
+<para>
+<anchor id="convert-trig-timer">
+<anchor id="trig-timer">
+TRIG_TIMER: the conversion events occur periodically. The time
+between convert events is
+<link linkend="command-data-struct-convert-arg">convert_arg</link>
+nanoseconds.
+</para>
+</listitem>
+
+<listitem>
+<para>
+<anchor id="convert-trig-ext">
+<anchor id="trig-ext">
+TRIG_EXT: the conversion events occur when an external trigger signal
+occurs, e.g., a rising edge of a digital line.
+<link linkend="command-data-struct-convert-arg">convert_arg</link>
+chooses the particular digital line.
+</para>
+</listitem>
+
+<listitem>
+<para>
+<anchor id="convert-trig-now">
+<anchor id="trig-now">
+TRIG_NOW: All conversion events in a
+<link linkend="scan">scan</link> occur simultaneously.
+</para>
+</listitem>
+
+</itemizedlist>
+The <emphasis>end</emphasis> of each scan is almost always specified
+using
+<link linkend="trig-count">TRIG_COUNT</link>, with the argument being
+the same as the number of channels in the
+<link linkend="command-data-struct-chanlist">chanlist</link>. You
+could probably find a device that allows something else, but it would
+be strange.
+</para>
+<para>
+The end of an
+<link linkend="acquisitionterminology">acquisition</link> is
+controlled by
+<link linkend="command-data-struct-stop-src">stop_src</link>
+and <link linkend="command-data-struct-stop-arg">stop_arg</link>:
+<itemizedlist>
+
+<listitem>
+<para>
+<anchor id="acquisition-end-trig-count">
+<anchor id="trig-count">
+TRIG_COUNT: stop the acquisition after
+<link linkend="command-data-struct-stop-arg">stop_arg</link>
+scans.
+</para>
+</listitem>
+
+<listitem>
+<para>
+<anchor id="acquisition-end-trig-none">
+<anchor id="trig-none">
+TRIG_NONE: perform continuous acquisition, until stopped using
+<link linkend="func-ref-comedi-cancel">comedi_cancel()</link>.
+</para>
+<para>
+Its argument is reserved and should be set to 0.
+(“Reserved”
+means that unspecified things could happen if it is set to something
+else but 0.)
+</para>
+</listitem>
+
+</itemizedlist>
+There are a couple of less usual or not yet implemented events:
+<itemizedlist>
+
+<listitem>
+<para>
+<anchor id="trig-time">
+TRIG_TIME:
+cause an event to occur at a particular time.
+</para>
+<para>
+(This event source is reserved for future use.)
+</para>
+</listitem>
+
+<listitem>
+<para>
+<anchor id="trigother-event">
+TRIG_OTHER: driver specific event trigger.
+</para>
+<para>
+This event can be useful as any of the trigger sources. Its exact
+meaning is driver specific, because it implements a feature that
+otherwise does not fit into the generic &comedi; command interface.
+Configuration of TRIG_OTHER features are done by
+<link linkend="instructionsconfiguration">INSN_CONFIG</link>
+instructions.
+</para>
+<para>
+The argument is reserved and should be set to 0.
+</para>
+</listitem>
+
+</itemizedlist>
+Not all event sources are applicable to all events. Supported
+trigger sources for specific events depend significantly on your
+particular device, and even more on the current state of its device
+driver. The
+<link linkend="func-ref-comedi-get-cmd-src-mask">comedi_get_cmd_src_mask()</link>
+function is useful for determining what trigger sources a subdevice
+supports.
+</para>
+
+</section>
+
+
+<section id="comedicmdflags">
+<title>
+The command flags
+<anchor id="source.flags.anchor">
+</title>
+
+<para>
+The
+<link linkend="command-data-struct-flags">flags</link> field in the
+<link linkend="ref-type-comedi-cmd">command data structure</link>
+is used to specify some “behaviour” of the acquisitions in
+a command.
+The meaning of the field is as follows:
+<itemizedlist>
+
+<listitem>
+<para>
+<anchor id="trig-rt">
+TRIG_RT: ask the driver to use a
+<emphasis role="strong">hard real-time</emphasis> interrupt handler.
+This will reduce latency in handling interrupts from your data
+aquisition
+hardware. It can be useful if you are sampling at high frequency, or
+if your hardware has a small onboard data buffer. You must have a
+real-time kernel (<ulink url="http://www.rtai.org">RTAI</ulink> or
+<ulink url="http://fsmlabs.com/community/">RTLinux/Free</ulink>)
+and must compile &comedi; with real-time support, or this flag will do
+nothing.
+</para>
+</listitem>
+
+<listitem>
+<para>
+<anchor id="trig-wake-eos">
+TRIG_WAKE_EOS:
+where “EOS” stands for “End of Scan”. Some
+drivers will change their behaviour when this flag is set, trying to
+transfer data at the end of every scan (instead of, for example,
+passing data in chunks whenever the board's hardware data buffer is
+half full). This flag may degrade a driver's performance at high
+frequencies, because the end of a scan is, in general, a much more
+frequent event than the filling up of the data buffer.
+</para>
+</listitem>
+
+<listitem>
+<para>
+<anchor id="trig-round-nearest">
+TRIG_ROUND_NEAREST:
+round to nearest supported timing period, the default.
+This flag (as well as the following three), indicates how timing
+arguments should be rounded if the hardware cannot achieve the exact
+timing requested.
+</para>
+</listitem>
+
+<listitem>
+<para>
+<anchor id="trig-round-down">
+TRIG_ROUND_DOWN: round period down.
+</para>
+</listitem>
+
+<listitem>
+<para>
+<anchor id="trig-round-up">
+TRIG_ROUND_UP: round period up.
+</para>
+</listitem>
+
+<listitem>
+<para>
+<anchor id="trig-round-up-next">
+TRIG_ROUND_UP_NEXT:
+this one doesn't do anything, and I don't know what it was intended
+to do…?
+</para>
+</listitem>
+
+<listitem>
+<para>
+<anchor id="trig-dither">
+TRIG_DITHER: enable dithering? Dithering is a software technique to
+smooth the influence of discretization “noise”.
+</para>
+</listitem>
+
+<listitem>
+<para>
+<anchor id="trig-deglitch">
+TRIG_DEGLITCH: enable deglitching? Another “noise”
+smoothing technique.
+</para>
+</listitem>
+
+<listitem>
+<para>
+<anchor id="trig-write">
+TRIG_WRITE:
+write to bidirectional devices. Could be useful, in principle, if
+someone wrote a driver that supported commands for a digital I/O
+device that could do either input or output.
+</para>
+</listitem>
+
+<listitem>
+<para>
+<anchor id="trig-bogus">
+TRIG_BOGUS: do the motions?
+</para>
+</listitem>
+
+<listitem>
+<para>
+<anchor id="trig-other">
+TRIG_CONFIG: perform configuration, not triggering. This is a legacy
+of the deprecated
+<link linkend="ref-type-comedi-cmd">comedi_trig_struct</link>
+data structure, and has no function at present.
+</para>
+</listitem>
+
+</itemizedlist>
+</para>
+
+</section>
+
-<section>
+</section>
+
+
+<section id="slowlyvarying">
<title>
-Digital Input/Output
+Slowly-varying inputs
</title>
<para>
-Many boards supported by comedi have digital input and output
-channels. Some boards allow the direction of a channel to be
-specified in software.
+Sometimes, your input channels change slowly enough that
+you are able to average many successive 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:
</para>
+<itemizedlist>
+
+<listitem>
<para>
-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.
+you are ultimately limited by “Spurious Free Dynamic
+Range”. This SFDR is one of the popular measures to quantify how
+much noise a signal carries. If you take a Fourier transform of your
+signal, you will see several “peaks” in the transform: one
+or more of the fundamental harmonics of the measured signal, and lots
+of little “peaks” (called “spurs”) caused by
+noise. The SFDR is then the difference between the amplitude of the
+fundamental harmonic and of the largest spur (at frequencies below
+half of the Nyquist frequency of the DAQ sampler!).
</para>
+</listitem>
+<listitem>
<para>
-Individual digital lines can be read and written using the
-functions
+you need to have <emphasis>some</emphasis> noise on the input channel,
+otherwise you will be averaging the same number <literal>N</literal>
+times. (Of course, this only holds if the noise is large enough to
+cause at least a one-bit discretization.)
</para>
+</listitem>
-<programlisting>
- comedi_dio_read(device,subdevice,channel,unsigned int *bit);
- comedi_dio_write(device,subdevice,channel,unsigned int bit);
-</programlisting>
+<listitem>
+<para>
+the more noise you have, the greater your SFDR, but it
+takes many more samples to compensate for the increased
+noise.
+</para>
+</listitem>
+<listitem>
<para>
-The direction of bidirectional lines can be configured using
-the function
+if you feel the need to average samples for, for example, two seconds,
+your signal will need to be <emphasis>very</emphasis> slowly-varying,
+i.e., not varying more than your target uncertainty for the entire two
+seconds.
</para>
+</listitem>
+
+</itemizedlist>
+
+<para>
+As you might have guessed, the &comedi; library has functions
+to help you in your quest to accurately measure slowly varying
+inputs:
+<programlisting>
+ int <link linkend="func-ref-comedi-sv-init">comedi_sv_init</link>(<link linkend="ref-type-comedi-sv-t">comedi_sv_t</link> * sv, <link linkend="ref-type-comedi-t">comedi_t</link> * device, unsigned int subdevice, unsigned int channel);
+</programlisting>
+This function initializes the
+<link linkend="ref-type-comedi-sv-t">comedi_sv_t</link> data structure, used
+to do the averaging acquisition:
+<programlisting>
+struct comedi_sv_struct{
+ <link linkend="ref-type-comedi-t">comedi_t</link> *dev;
+ unsigned int subdevice;
+ unsigned int chan;
+ /* range policy */
+ int range;
+ int aref;
+
+ /* number of measurements to average (for analog inputs) */
+ int n;
+
+ lsampl_t maxdata;
+};
+</programlisting>
+The actual acquisition is done with:
<programlisting>
- comedi_dio_config(device,subdevice,channel,unsigned int dir);
+ int <link linkend="func-ref-comedi-sv-measure">comedi_sv_measure</link>(<link linkend="ref-type-comedi-sv-t">comedi_sv_t</link> * sv, double * data);
</programlisting>
+The number of samples over which the
+<function>comedi_sv_measure()</function> averages is limited by the
+implementation (currently the limit is 100 samples).
+</para>
<para>
-The parameter dir should be either COMEDI_INPUT or COMEDI_OUTPUT.
-Many digital I/O subdevices group channels into blocks for
-configuring direction. Changing one channel in a block changes
-the entire block.
+One typical use for this function is the measurement of thermocouple
+voltages.
+And 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 a part-per-thousand.
</para>
+</section>
+
+<section id="experimentalfunctionality">
+<title>
+Experimental functionality
+</title>
+
<para>
-Multiple channels can be read and written simultaneously using the
-function
+The following subsections document functionality that has not yet
+matured. Most of this functionality has even not been implemented yet
+in any single device driver. This information is included here, in
+order to stimulate discussion about their API, and to encourage
+pioneering implementations.
</para>
-<programlisting>
- comedi_dio_bitfield(device,subdevice,unsigned int write_mask,unsigned int *bits);
-</programlisting>
+<section id="digitalinputcombining">
+<title>
+Digital input combining machines
+</title>
+
+<para>
+(<emphasis role="strong">Status: experimental (i.e., no driver implements
+this yet)</emphasis>)
+</para>
+<para>
+When one or several digital inputs are used to modify an output
+value, either an accumulator or a single digital line or bit,
+a bitfield structure is typically used in the &comedi; interface.
+The digital inputs have two properties, “sensitive” inputs
+and “modifier” inputs. Edge transitions on sensitive
+inputs cause changes in the output signal, whereas modifier inputs
+change the effect of edge transitions on sensitive inputs. Note that
+inputs can be both modifier inputs and sensitive inputs.
+</para>
+
+<para>
+For simplification purposes, it is assumed that multiple digital
+inputs do not change simultaneously.
+</para>
+
+<para>
+The combined state of the modifier inputs determine a modifier
+state. For each combination of modifier state and sensitive
+input, there is a set of bits that determine the effect on the
+output value due to positive or negative transitions of the
+sensitive input. For each transition direction, there are two
+bits defined as follows:
+<simplelist>
+
+<member>
+00: transition is ignored.
+</member>
+
+<member>
+01: accumulator is incremented, or output is set.
+</member>
+
+<member>
+10: accumulator is decremented, or output is cleared.
+</member>
+
+<member>
+11: reserved.
+</member>
+
+</simplelist>
+For example, a simple digital follower is specified by the bit
+pattern 01 10, because it sets the output on positive transitions
+of the input, and clears the output on negative transitions. A
+digital inverter is similarily 10 01. These systems have only
+one sensitive input.
+</para>
+
+<para>
+As another example, a simple up counter, which increments on
+positive transitions of one input, is specified by 01 00. This
+system has only one sensitive input.
+</para>
<para>
-Each channel is assigned to a bit in the write_mask and bits
-bitfield. If a bit in write_mask is set, the corresponding bit
-in *bits will be written to the corresponding digital output line.
-Each digital line is then read and placed into *bits. The value
-of bits in *bits corresponding to digital output lines is
-undefined and device-specific. Channel 0 is the least significant
-bit in the bitfield; channel 31 is the most significant bit. Channels
-higher than 31 cannot be accessed using this method.
+When multiple digital inputs are used, the inputs are divided
+into two types, inputs which cause changes in the accumulator, and
+those that only modify the meaning of transitions on other inputs.
+Modifier inputs do not require bitfields, but there needs to be
+a bitfield of length 4*(2^(N-1)) for each edge sensitive input,
+where N is the total number of inputs. Since N is usually 2 or
+3, with only one edge sensitive input, the scaling issues are
+not significant.
</para>
</section>
-<section>
+
+<section id="analogconversion">
<title>
-Slowly-varying inputs
+Analog filtering configuration
</title>
<para>
-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:
+<emphasis role="strong">(Status: design (i.e., no driver implements
+this yet).)</emphasis>
+</para>
+
+<para>
+The <link linkend="insn-data-structure-insn">insn</link> field of the
+<link linkend="insn-data-structure">instruction data structure</link>
+has not been assigned yet.
+</para>
+<para>
+The <link linkend="insn-data-structure-chanspec">chanspec</link> field
+of the <link linkend="insn-data-structure">instruction data
+structure</link> is ignored.
+</para>
+
+<para>
+Some devices have the capability to add white noise (dithering) to
+analog input measurement. This additional noise can then be averaged
+out, to get a more accurate measurement of the input signal. It
+should not be assumed that channels can be separately configured.
+A simple design can use 1 bit to turn this feature on/off.
+</para>
+
+<para>
+Some devices have the capability of changing the glitch characteristics
+of analog output subsytems. The default (off) case should be where
+the average settling time is lowest. A simple design can use 1 bit
+to turn this feature on/off.
+</para>
+
+<para>
+Some devices have a configurable analog filters as part of the analog
+input stage. A simple design can use 1 bit to enable/disable the
+filter. Default is disabled, i.e., the filter being bypassed, or if
+the choice is between two filters, the filter with the largest
+bandwidth.
+</para>
+</section>
+
+<section id="waveformgeneration">
+<title>
+Analog Output Waveform Generation
+</title>
+
+<para>
+<emphasis role="strong">(Status: design (i.e., no driver implements
+this yet).)</emphasis>
+</para>
+<para>
+The <link linkend="insn-data-structure-insn">insn</link> field of the
+<link linkend="insn-data-structure">instruction data structure</link>
+has not been assigned yet.
+</para>
+<para>
+The <link linkend="insn-data-structure-chanspec">chanspec</link> field
+of the <link linkend="insn-data-structure">instruction data
+structure</link> is ignored.
+</para>
+
+<para>
+Some devices have the ability to cyclicly loop through samples kept in
+an on-board analog output FIFO. This config should allow the user to
+enable/disable this mode.
+</para>
+
+<para>
+This config should allow the user to configure the number of samples
+to loop through. It may be necessary to configure the channels used.
+</para>
+
+</section>
+
+<section id="extendedtriggering">
+<title>
+Extended Triggering
+</title>
+<para>
+<emphasis role="strong">(Status: alpha.)</emphasis>
+</para>
+
+<para>
+The <link linkend="insn-data-structure-insn">insn</link> field of the
+<link linkend="insn-data-structure">instruction data structure</link>
+has not been assigned yet.
+</para>
+<para>
+The <link linkend="insn-data-structure-chanspec">chanspec</link> field
+of the <link linkend="insn-data-structure">instruction data
+structure</link> is ignored.
+</para>
+
+<para>
+This section covers common information for all extended
+triggering configuration, and doesn't describe a particular
+type of extended trigger.
+</para>
+
+<para>
+Extended triggering is used to configure triggering engines that
+do not fit into commands. In a typical programming sequence, the
+application will use
+<link linkend="instructionsconfiguration">configuration instructions</link>
+to configure an extended trigger, and a
+<link linkend="commandsstreaming">command</link>,
+specifying
+<link linkend="trig-other">TRIG_OTHER</link> as one of the trigger
+sources.
+</para>
+
+<para>
+Extended trigger configuration should be designed in such a way
+that the user can probe for valid parameters, similar to how
+command testing works. An extended trigger configuration instruction
+should not configure the hardware directly, rather, the configuration
+should be saved until the subsequent command is issued. This
+allows more flexibility for future interface changes.
+</para>
+
+<para>
+It has not been decided whether the configuration stage should return a
+token that is then used as the trigger argument in the command.
+Using tokens is one method to satisfy the problem that extended
+trigger configurations may have subtle compatiblity issues with
+other trigger sources/arguments that can only be determined at
+command test time. Passing all stages of a command test should
+only be allowed with a properly configured extended trigger.
+</para>
+
+<para>
+Extended triggers must use
+<link linkend="insn-data-structure-data">data[1]</link> as flags. The
+upper 16 bits are reserved and used only for flags that are common to
+all extended triggers. The lower 16 bits may be defined by the
+particular type of extended trigger.
+</para>
+
+<para>
+Various types of extended triggers must use
+<link linkend="insn-data-structure-data">data[1]</link> to know which
+event the extended trigger will be assigned to in the command
+structure. The possible values are an OR'd mask of the following:
+</para>
+
+<itemizedlist>
+ <listitem>
+ <para>
+COMEDI_EV_START
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+COMEDI_EV_SCAN_BEGIN
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+COMEDI_EV_CONVERT
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+COMEDI_EV_SCAN_END
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+COMEDI_EV_STOP
+ </para>
+ </listitem>
+</itemizedlist>
+
+</section>
+
+<section id="analogtriggering">
+<title>
+Analog Triggering
+</title>
+<para>
+<emphasis role="strong">
+(Status: alpha. The <function>ni_mio_common.c</function> driver
+implements this feature.)
+</emphasis>
</para>
+<para>
+The <link linkend="insn-data-structure-insn">insn</link> field of the
+<link linkend="insn-data-structure">instruction data structure</link>
+has not been assigned yet.
+</para>
+<para>
+The <link linkend="insn-data-structure-chanspec">chanspec</link> field
+of the <link linkend="insn-data-structure">instruction data
+structure</link> is ignored.
+</para>
+
+<para>
+The <link linkend="insn-data-structure-data">data</link> field
+of the <link linkend="insn-data-structure">instruction data
+structure</link> is used as follows:
<simplelist>
<member>
-you are ultimately limited by "spurious free dynamic range"
+data[1]: trigger and combining machine configuration.
</member>
<member>
-you need to have _some_ noise on the input channel,
-otherwise you will be averaging the same number N times.
+data[2]: analog triggering signal chanspec.
</member>
<member>
-the more noise you have, the greater your SFDR, but it
-takes many more samples to compensate for the increased
-noise
+data[3]: primary analog level.
</member>
<member>
-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.
+data[4]: secondary analog level.
</member>
</simplelist>
<para>
-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.
+Analog triggering is described by a digital combining machine that
+has two sensitive digital inputs. The sensitive digital inputs are
+generated by configurable analog comparators. The analog comparators
+generate a digital 1 when the analog triggering signal is greater
+than the comparator level. The digital inputs are not modifier
+inputs. Note, however, there is an effective modifier due to the
+restriction that the primary analog comparator level must be less
+than the secondary analog comparator level.
</para>
<para>
-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.
+If only one analog comparator signal is used, the combining machine
+for the secondary input should be set to ignored, and the secondary
+analog level should be set to 0.
+</para>
+
+<para>
+The interpretation of the chanspec and voltage levels is device
+dependent, but should correspond to similar values of the analog
+input subdevice, if possible.
+</para>
+
+<para>
+Notes: Reading range information is not addressed. This makes it
+difficult to convert comparator voltages to data values.
+</para>
+
+<para>
+Possible extensions: A parameter that specifies the necessary time
+that the set condition has to be true before the trigger is generated.
+A parameter that specifies the necessary time that the reset condition
+has to be true before the state machine is reset.
</para>
</section>
-<section>
+<section id="bitfieldmatching">
<title>
-Commands and streaming input/output
+Bitfield Pattern Matching Extended Trigger
</title>
+<para>
+<emphasis role="strong">
+(Status: design. No driver implements this feature yet.)
+</emphasis>
+</para>
<para>
-Many data acquisition devices have the capability to directly
-control acquisition using either an on-board timer or an external
-triggering input. Comedi commands are used to control this kind
-of acquisition. The comedi_cmd structure is
-used to control acquisition and query the capabilities of a device
-(see also comedi_command(), comedi_command_test(), and
-comedi_get_cmd_src_mask()).
-</para>
-
-<para>
-Commands specify a particular data acquisition sequence, which
-is comprised of a number of scans. Each scan is comprised of
-a number of conversions, which usually corresponds to a single
-A/D or D/A conversion. The start and end of the sequence, and
-the start and end of each scan, and each conversion is called an
-event.
-</para>
-
-<para>
-Each of these 5 types of events are caused by a triggering
-source, specified through the *_src members of the
-comedi_cmd structure. The source types are:
-</para>
-
-<informaltable>
- <tgroup cols='2'>
- <tbody>
- <row>
- <entry>
-TRIG_NONE
- </entry>
- <entry>
-don't ever cause an event
- </entry>
- </row>
- <row>
- <entry>
-TRIG_NOW
- </entry>
- <entry>
-cause event to occur immediately
- </entry>
- </row>
- <row>
- <entry>
-TRIG_FOLLOW
- </entry>
- <entry>
-see notes below
- </entry>
- </row>
- <row>
- <entry>
-TRIG_TIME
- </entry>
- <entry>
-cause event to occur at a particular time
- </entry>
- </row>
- <row>
- <entry>
-TRIG_TIMER
- </entry>
- <entry>
-cause event to occur repeatedly at a specific rate
- </entry>
- </row>
- <row>
- <entry>
-TRIG_COUNT
- </entry>
- <entry>
-cause event when count reaches specific value
- </entry>
- </row>
- <row>
- <entry>
-TRIG_EXT
- </entry>
- <entry>
-external signal causes event
- </entry>
- </row>
- <row>
- <entry>
-TRIG_INT
- </entry>
- <entry>
-internal signal causes event
- </entry>
- </row>
- <row>
- <entry>
-TRIG_OTHER
- </entry>
- <entry>
-driver-specific meaning
- </entry>
- </row>
- </tbody>
- </tgroup>
-</informaltable>
-
-<para>
-Not all triggers are applicable to all events. Supported triggers
-for specific events depend significantly on your particular
-device. The comedi_get_cmd_src_mask()
-function is useful for determining what triggers a subdevice supports.
-</para>
-
-<para>
-For every trigger, there is a corresponding
-argument (the *_arg members of the comedi_cmd
-structure) whose meaning depends on the type of trigger. The meanings
-of the arguments are as follows:
-</para>
-
-<para>
-TRIG_NONE is typically used only as a stop_src. The argument for TRIG_NONE
-is reserved and should be set to 0.
-</para>
-
-<para>
-TRIG_NOW is most often used as a start_src. The argument for TRIG_NOW is
-the number of nanoseconds between when the command is issued and when
-the event should occur. In the case of using TRIG now as a start_src,
-it indicates a delay between issuing the command and the start of
-acquisition. Most drivers only support a delay of 0.
-</para>
-
-<para>
-TRIG_FOLLOW is a special type of trigger for events that trigger on
-the completion of some other, logically connected event. The argument
-is reserved and should be set to 0. When used
-as a scan_begin_src, it indicates that a trigger should occur as a
-logical continuation of convert events. This is done in order to
-properly describe boards that do not have separate timers for
-convert and scan_begin events. When used as a start_src for analog
-output subdevices, it indicates that conversion of output samples
-should begin when samples are written to the buffer.
-</para>
-
-<para>
-TRIG_TIME is reserved for future use.
-</para>
-
+The <link linkend="insn-data-structure-insn">insn</link> field of the
+<link linkend="insn-data-structure">instruction data structure</link>
+has not been assigned yet.
+</para>
+<para>
+The <link linkend="insn-data-structure-chanspec">chanspec</link> field
+of the <link linkend="insn-data-structure">instruction data
+structure</link> is ignored.
+</para>
+
<para>
-TRIG_TIMER is most often used as a convert_src, a scan_begin_src, or
-both. It indicates that triggers should occur at a specific rate.
-The argument specifies the interval between triggers in nanoseconds.
+The <link linkend="insn-data-structure-data">data</link> field
+of the <link linkend="insn-data-structure">instruction data
+structure</link> is used as follows:
</para>
+<simplelist>
+ <member>
+data[1]: trigger flags.
+ </member>
+ <member>
+data[2]: mask.
+ </member>
+ <member>
+data[3]: pattern.
+ </member>
+</simplelist>
<para>
-TRIG_COUNT is used for scan_end_src and stop_src. It indicates that
-a trigger should occur when the specified number of corresponding
-lower-level triggers (convert and scan_begin, respectively) occur.
-The argument is the count of lower-level triggers.
+The pattern matching trigger issues a trigger when all of a specifed
+set of input lines match a specified pattern. If the device allows,
+the input lines should correspond to the input lines of a digital input
+subdevice, however, this will necessarily be device dependent. Each
+possible digital line that can be matched is assigned a bit in the
+mask and pattern. A bit set in the mask indicates that the
+input line must match the corresponding bit in the pattern.
+A bit cleared in the mask indicates that the input line is ignored.
</para>
<para>
-TRIG_EXT can be useful as any of the trigger sources. It indicates
-that an external digital line should be used to trigger the event.
-The exact meaning of digital line is device-dependent. Some devices
-have one dedicated line, others may allow generic digital input
-lines to be used. The argument indicates the particular external
-line to use as the trigger.
+Notes: This only allows 32 bits in the pattern/mask, which may be
+too few. Devices may support selecting different sets of lines from
+which to match a pattern.
</para>
<para>
-TRIG_INT is typically used as a start_src. This trigger occurs when
-the application performs an INSN_INTTRIG instruction. Using TRIG_INT
-is a method by which the application can accurately record the time of
-the start of acquisition, since the parsing and setup time of a
-particular command may be significant. The argument associated with
-TRIG_INT is reserved and should be set to 0.
+Discovery: The number of bits can be discovered by setting the mask
+to all 1's. The driver must modify this value and return -EAGAIN.
</para>
+</section>
+
+<section id="countertimer">
+<title>
+Counter configuration
+</title>
<para>
-TRIG_OTHER can be useful as any of the trigger sources. The exact
-meaning of TRIG_OTHER is driver-specific, and implements a feature
-that otherwise does not fit into the command interface. Configuration
-of TRIG_OTHER features are done by INSN_CONFIG insns. The argument
-is reserved and should be set to 0.
+<emphasis role="strong">
+(Status: design. No driver implements this feature yet.)
+</emphasis>
</para>
<para>
-Ths subdev member of the comedi_cmd
-structure is the index of the subdevice the command is intended for. The
-comedi_find_subdevice_by_type()
-function can be useful in discovering the index of your desired subdevice.
+The <link linkend="insn-data-structure-insn">insn</link> field of the
+<link linkend="insn-data-structure">instruction data structure</link>
+has not been assigned yet.
+</para>
+<para>
+The <link linkend="insn-data-structure-chanspec">chanspec</link> field
+of the <link linkend="insn-data-structure">instruction data
+structure</link> is used to specify which counter to use. (I.e., the
+counter is a &comedi; channel.)
</para>
<para>
-The chanlist member of the comedi_cmd
-structure should point to an array whose number of elements is specificed
-by chanlist_len (this will generally be the same as the scan_end_arg).
-The chanlist specifies the sequence of channels and gains (and analog
-references) that should be stepped through for each scan. The
-elements of the chanlist array should be initialized by packing
-the channel, range and reference information together with the
-CR_PACK() macro.
-</para>
-
-<para>
-The data and data_len members can be safely ignored when issueing commands
-from a user-space program. They only have meaning when a command
-is sent from a kernel module using the kcomedilib interface, in
-which case they specify the buffer where the driver should
-write/read its data to/from.
-</para>
-
-<para>
-The final member of the comedi_cmd structure is flags.
-The following flags are valid, and can be bitwise-or'd together.
-</para>
-
-<variablelist>
- <varlistentry>
- <term>
-TRIG_BOGUS
- </term>
- <listitem>
- <para>
-do the motions??
- </para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term>
-TRIG_DITHER
- </term>
- <listitem>
- <para>
-enable dithering??
- </para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term>
-TRIG_DEGLITCH
- </term>
- <listitem>
- <para>
-enable deglitching??
- </para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term>
-TRIG_RT
- </term>
- <listitem>
- <para>
-ask driver to use a hard real-time interrupt handler. This will
-reduce latency in handling interrupts from your data aquisition hardware. It can
-be useful if you are sampling at high frequency, or if your hardware has a small onboard
-fifo. You must have a real-time kernel (RTAI or RTLinux) and must compile
-comedi with real-time support or this flag will do nothing.
- </para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term>
-TRIG_CONFIG
- </term>
- <listitem>
- <para>
-perform configuration, not triggering. This is a legacy of the
-deprecated comedi_trig_struct, and has no function at present.
- </para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term>
-TRIG_WAKE_EOS
- </term>
- <listitem>
- <para>
-some drivers will change their behaviour when this flag is set,
-trying to transfer data at the end of every scan (instead of, for example, passing
-data in chunks whenever the board's onboard fifo is half full). This flag
-may degrade a driver's performance at high frequencies.
- </para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term>
-TRIG_WRITE
- </term>
- <listitem>
- <para>
-write to bidirectional devices. Could be useful in principle, if someone
-wrote a driver that supported commands for a digital i/o device that could do either
-input or output.
- </para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term>
-TRIG_ROUND_NEAREST
- </term>
- <listitem>
- <para>
-round to nearest supported timing period, the default.
- </para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term>
-TRIG_ROUND_DOWN
- </term>
- <listitem>
- <para>
-round period down.
- </para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term>
-TRIG_ROUND_UP
- </term>
- <listitem>
- <para>
-round period up.
- </para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term>
-TRIG_ROUND_UP_NEXT
- </term>
- <listitem>
- <para>
-this one doesn't do anything, and I don't know what it was intended
-to do?? XXX
- </para>
- </listitem>
- </varlistentry>
-</variablelist>
+The <link linkend="insn-data-structure-data">data</link> field
+of the <link linkend="insn-data-structure">instruction data
+structure</link> is used as follows:
+</para>
+<simplelist>
+ <member>
+data[1]: trigger configuration.
+ </member>
+ <member>
+data[2]: primary input chanspec.
+ </member>
+ <member>
+data[3]: primary combining machine configuration.
+ </member>
+ <member>
+data[4]: secondary input chanspec.
+ </member>
+ <member>
+data[5]: secondary combining machine configuration.
+ </member>
+ <member>
+data[6]: latch configuration.
+ </member>
+</simplelist>
+
+<para>
+Note that this configuration is only useful if the counting has to be
+done in <emphasis>software</emphasis>. Many cards offer configurable
+counters in hardware; e.g., general purpose timer cards can be
+configured to act as pulse generators, frequency counters, timers,
+encoders, etc.
+</para>
+<para>
+Counters can be operated either in synchronous mode (using
+<link linkend="comediinsnstructure">INSN_READ</link>)
+or asynchronous mode (using
+<link linkend="commandsstreaming">commands</link>), similar to analog
+input subdevices.
+The input signal for both modes is the accumulator.
+Commands on counter subdevices are almost always specified using
+<link linkend="command-data-struct-scan-begin-src">scan_begin_src</link>
+= <link linkend="trigother-event">TRIG_OTHER</link>, with the
+counter configuration also serving as the extended configuration for
+the scan begin source.
+</para>
<para>
-The last four flags indicate how timing arguments should be rounded
-if the hardware cannot achieve the exact timing requested.
+Counters are made up of an accumulator and a combining machine that
+determines when the accumulator should be incremented or decremented
+based on the values of the input signals. The combining machine
+optionally determines when the accumulator should be latched and
+put into a buffer. This feature is used in asynchronous mode.
</para>
<para>
-The typical sequence for executing a command is to first send
-the command through comedi_command_test() once or twice. The
-test will check that the command is valid for the particular
-device, and often makes some adjustments to the command arguments, which
-can then be read back by the user to see the actual values used. The
-command is executed with
-comedi_command(). For input/output commands, data
-is read from or written to the device file /dev/comedi[0..3] you are using.
+Note: How to access multiple pieces of data acquired at each event?
</para>
</section>
-<section>
+<section id="auxcounter">
<title>
-Anti-aliasing
+One source plus auxiliary counter configuration
</title>
+<para>
+<emphasis role="strong">
+(Status: design. No driver implements this feature yet.)
+</emphasis>
+</para>
+
+<para>
+The <link linkend="insn-data-structure-insn">insn</link> field of the
+<link linkend="insn-data-structure">instruction data structure</link>
+has not been assigned yet.
+</para>
+<para>
+The <link linkend="insn-data-structure-chanspec">chanspec</link> field
+of the <link linkend="insn-data-structure">instruction data
+structure</link> is used to …
+</para>
<para>
-If you wish to aquire accurate waveforms, it is vital that you use an
-anti-alias filter. An anti-alias filter is a low-pass filter used to
-remove all
-frequencies higher than the Nyquist frequency (half your sampling rate)
- from your analog input signal
-before you convert it to digital. If you fail to filter your input signal,
-any high frequency components in the original analog signal will create
-artifacts in your recorded
-digital waveform that cannot be corrected.
+The <link linkend="insn-data-structure-data">data</link> field
+of the <link linkend="insn-data-structure">instruction data
+structure</link> is used as follows:
</para>
<para>
-For example, suppose you are sampling an analog input channel at a rate of
-1000 Hz. If you were to apply a 900 Hz sine wave to the input, you
-would find that your
-sampling rate is not high enough to faithfully record the 900 Hz input,
-since it is above your Nyquist frequency of 500 Hz. Instead, what you
-will see in your recorded digital waveform is a 100 Hz sine wave! If you
-don't use an anti-alias filter, it is impossible to tell whether the 100
-Hz sine wave you see in your digital signal was really produced by a
-100 Hz input signal, or a 900 Hz signal aliased to 100 Hz, or a 1100 Hz
-signal, etc.
+<simplelist>
+ <member>
+data[1]: is flags, including the flags for the command triggering
+configuration. If a command is not subsequently issued on the
+subdevice, the command triggering portion of the flags are ignored.
+ </member>
+ <member>
+data[2]: determines the mode of operation. The mode of operation
+is actually a bitfield that encodes what to do for various
+transitions of the source signals.
+</member>
+
+<member>
+data[3], data[4]: determine the primary source for the counter,
+similar to the
+<link linkend="command-data-struct-scan-begin-src">_src</link> and the
+<link linkend="command-data-struct-scan-begin-arg">_arg</link> fields
+used in the
+<link linkend="command-data-struct">command data structure</link>.
+</member>
+
+</simplelist>
</para>
<para>
-In practice, the cutoff frequency for the anti-alias filter is usually
-set 10% to 20% below the Nyquist frequency due to fact that real filters
-do not have infinitely sharp cutoffs.
+Notes: How to specify which events cause a latch and push, and what
+should get latched?
</para>
</section>
</section>
-
+</section>
-<!-- <!DOCTYPE article PUBLIC "-//OASIS//DTD DocBook V3.1//EN"> -->
+<!-- <!DOCTYPE article PUBLIC "-//OASIS//DTD DocBook V3.1//EN" "docbook/dtd/3.1/docbook.dtd"> -->
-<section>
+<section id="comedi-comedilib-h">
+<title>
+Headerfiles: <filename>comedi.h</filename> and <filename>comedilib.h</filename>
+</title>
+
+<para>
+All <link linkend="writingprograms">application programs</link> must
+include the header file <filename>comedilib.h</filename>. (This file
+itself includes <filename>comedi.h</filename>.) They contain the full
+interface of &comedi;: defines, function prototypes, data structures.
+</para>
+<para>
+The following Sections give more details.
+</para>
+
+</section>
+
+<section id="constantsmacros">
<title>
Constants and Macros
</title>
</title>
<para>
-CR_PACK is used to initialize the elements of the chanlist array in the
-comedi_cmd structure, and the chanspec member
-of the comedi_insn structure.
+CR_PACK is used to initialize the elements of the
+<parameter>chanlist</parameter> array in the
+<link linkend="ref-type-comedi-cmd">comedi_cmd</link> data structure,
+and the <parameter>chanspec</parameter> member of the
+<link linkend="ref-type-comedi-insn">comedi_insn</link> structure.
</para>
-
+<programlisting>
+<![CDATA[
+#define CR_PACK(chan,rng,aref) ( (((aref)&0x3)<<24) | (((rng)&0xff)<<16) | (chan) )
+]]>
+</programlisting>
<para>
-The channel argument is the channel you wish to use, with the channel
-numbering starting at zero.
+The <parameter>chan</parameter> argument is the channel you wish to
+use, with the channel numbering starting at zero.
</para>
<para>
-The range is an index, starting at zero, whose meaning
-is device dependent. The
-comedi_get_n_ranges() and
-comedi_get_range functions
-are useful in discovering information about the available ranges.
+The range <parameter>rng</parameter> is an index, starting at zero,
+whose meaning is device dependent. The
+<link linkend="func-ref-comedi-get-n-ranges">comedi_get_n_ranges()</link>
+and
+<link linkend="func-ref-comedi-get-range">comedi_get_range()</link>
+functions are useful in discovering information about the available
+ranges.
</para>
<para>
-The aref argument indicates what reference you want the device to use. It
-can be any of the following:
-</para>
-
+The <parameter>aref</parameter> argument indicates what reference you
+want the device to use. It can be any of the following:
<variablelist>
<varlistentry>
- <term>AREF_GROUND</term>
+ <term>AREF_GROUND <anchor id="aref-ground"> </term>
<listitem>
<para>
- is for inputs/outputs referenced to ground
+ is for inputs/outputs referenced to ground.
</para>
</listitem>
</varlistentry>
<varlistentry>
- <term>AREF_COMMON</term>
+ <term>AREF_COMMON <anchor id="aref-common"> </term>
<listitem>
<para>
- is for a `common' reference (the low inputs of all the channels are tied
-together, but are isolated from ground)
+ is for a “common” reference (the low inputs of all the
+channels are tied together, but are isolated from ground).
</para>
</listitem>
</varlistentry>
<varlistentry>
- <term>AREF_DIFF</term>
+ <term>AREF_DIFF <anchor id="aref-diff"> </term>
<listitem>
<para>
- is for differential inputs/outputs
+ is for differential inputs/outputs.
</para>
</listitem>
</varlistentry>
<varlistentry>
- <term>AREF_OTHER</term>
+ <term>AREF_OTHER <anchor id="aref-other"> </term>
<listitem>
<para>
- is for any reference that does not fit into the above categories
+ is for any reference that does not fit into the above categories.
</para>
</listitem>
</varlistentry>
</variablelist>
-
-<para>
Particular drivers may or may not use the AREF flags. If they are
not supported, they are silently ignored.
</para>
</section>
-<section>
+<section id="datatypesstructures">
<title>
Data Types and Structures
</title>
+<para>
+This Section explains the data structures that users of the &comedi;
+API are confronted with:
+<programlisting>
+typedef struct subdevice_struct <link linkend="ref-type-subdevice-struct">subdevice_struct</link>:
+typedef struct comedi_devinfo_struct <link linkend="ref-type-comedi-devinfo">comedi_devinfo</link>;
+typedef struct comedi_t_struct <link linkend="ref-type-comedi-t">comedi_t</link>;
+typedef struct sampl_t_struct <link linkend="ref-type-sampl-t">sampl_t</link>;
+typedef struct lsampl_t_struct <link linkend="ref-type-lsampl-t">lsampl_t</link>;
+typedef struct comedi_sv_t_struct <link linkend="ref-type-comedi-sv-t">comedi_sv_t</link>;
+typedef struct comedi_cmd_struct <link linkend="ref-type-comedi-cmd">comedi_cmd</link>;
+typedef struct comedi_insn_struct <link linkend="ref-type-comedi-insn">comedi_insn</link>;
+typedef struct comedi_range_struct <link linkend="ref-type-comedi-range">comedi_range</link>;
+typedef struct comedi_krange_struct <link linkend="ref-type-comedi-krange">comedi_krange</link>;
+typedef struct comedi_insnlist_struct <link linkend="ref-type-comedi-insnlist">comedi_insnlist</link>;
+</programlisting>
+The data structures used in the implementation of the &comedi; drivers
+are treated <link linkend="driverdatastructures">elsewhere</link>.
+</para>
+
+<section id="ref-type-subdevice-struct">
+<title>
+subdevice_struct
+</title>
+
+<para>
+The data type <parameter>subdevice_struct</parameter> is used to store
+information about a subdevice. This structure is usually filled in
+automatically when the driver is loaded (“attached”), so
+programmers need not access this data structure directly.
+<programlisting>
+typedef struct subdevice_struct <anchor id="ref-type-subdevice">subdevice;
+
+struct subdevice_struct{
+ unsigned int type;
+ unsigned int n_chan;
+ unsigned int subd_flags;
+ unsigned int timer_type;
+ unsigned int len_chanlist;
+ <link linkend="ref-type-lsampl-t">lsampl_t</link> maxdata;
+ unsigned int flags;
+ unsigned int range_type;
+
+ <link linkend="ref-type-lsampl-t">lsampl_t</link> *maxdata_list;
+ unsigned int *range_type_list;
+ unsigned int *flags_list;
+
+ <link linkend="ref-type-comedi-range">comedi_range</link> *rangeinfo;
+ <link linkend="ref-type-comedi-range">ccomedi_range</link> **rangeinfo_list;
+
+ unsigned int has_cmd;
+ unsigned int has_insn_bits;
+
+ int cmd_mask_errno;
+ <link linkend="ref-type-comedi-cmd">comedi_cmd</link> *cmd_mask;
+ int cmd_timed_errno;
+ <link linkend="ref-type-comedi-cmd">comedi_cmd</link> *cmd_timed;
+};
+</programlisting>
+
+</para>
+
+</section>
+
+
+<section id="ref-type-comedi-devinfo">
+<title>
+comedi_devinfo
+</title>
+
+<para>
+The data type <parameter>comedi_devinfo</parameter> is used to store
+information about a device. This structure is usually filled in
+automatically when the driver is loaded (“attached”), so
+programmers need not access this data structure directly.
+<programlisting>
+typedef struct comedi_devinfo_struct comedi_devinfo;
+
+struct comedi_devinfo_struct{
+ unsigned int version_code; // version number of the Comedi code
+ unsigned int n_subdevs; // number of subdevices on this device
+ char driver_name[COMEDI_NAMELEN];
+ char board_name[COMEDI_NAMELEN];
+ int read_subdevice; // number of read devices
+ int write_subdevice; // number of write devices
+ int unused[30];
+};
+
+</programlisting>
+</para>
+
+</section>
+
<section id="ref-type-comedi-t">
<title>
comedi_t
</title>
+<para>
+The data type <parameter>comedi_t</parameter> is used to represent an
+open &comedi; device:
<programlisting>
typedef struct comedi_t_struct comedi_t;
-</programlisting>
-<para>
-The data type comedi_t is used to represent an open Comedi
-device. A valid comedi_t pointer is returned by a successful
-call to comedi_open(), and should be used for subsequent
-access to the device.
-It is a transparent type, and pointers to type comedi_t
+struct comedi_t_struct{
+ int magic; // driver-specific magic number, for identification
+ int fd; // file descriptor, for open() and close()
+ int n_subdevices; // number of subdevices on this device
+ <link linkend="ref-type-comedi-devinfo">comedi_devinfo</link> devinfo;
+ <link linkend="ref-type-subdevice">subdevice</link> *subdevices; // pointer to subdevice list
+ // filled in automatically at load time
+ unsigned int has_insnlist_ioctl; // can process <link linkend="anchor.instruction.list">instruction lists</link>
+ unsigned int has_insn_ioctl; // can process <link linkend="instructions">instructions</link>
+};
+</programlisting>
+A valid <parameter>comedi_t</parameter> pointer is returned by a
+successful call to
+<link linkend="func-ref-comedi-open">comedi_open()</link>,
+and should be used for subsequent access to the device.
+It is a transparent type, and pointers to type
+<parameter>comedi_t</parameter>
should not be dereferenced by the application.
</para>
</programlisting>
<para>
-The data type sampl_t is one of the generic types used to represent
-data values in Comedilib. It is used in a few places where a data type
-shorter than lsampl_t is useful. On most architectures, sampl_t
-is defined to be uint16.
+The data type <link linkend="ref-type-sampl-t">sampl_t</link> is one
+of the generic
+types used to represent data values in Comedilib. It is used in a few
+places where a data type
+shorter than <link linkend="ref-type-lsampl-t">lsampl_t</link> is
+useful. On most architectures,
+<link linkend="ref-type-sampl-t">sampl_t</link>
+is defined to be <parameter>uint16</parameter>.
</para>
<para>
-Most drivers represent data trasferred by read() and write()
-using sampl_t. Applications should check the subdevice flag
-SDF_LSAMPL to determine if the subdevice uses sampl_t or
-lsampl_t.
+Most drivers represent data transferred by <function>read()</function> and
+<function>write()</function> using
+<link linkend="ref-type-sampl-t">sampl_t</link>.
+Applications should check the subdevice flag
+SDF_LSAMPL to determine if the subdevice uses
+<link linkend="ref-type-sampl-t">sampl_t</link> or
+<link linkend="ref-type-lsampl-t">lsampl_t</link>.
</para>
</section>
</programlisting>
<para>
-The data type lsampl_t is the data type typically used to represent
-data values in libcomedi. On most architectures, lsampl_t is
-defined to be uint32.
+The data type
+<link linkend="ref-type-lsampl-t">lsampl_t</link>
+is the data type typically used to represent
+data values in libcomedi. On most architectures,
+<link linkend="ref-type-lsampl-t">lsampl_t</link>
+is defined to be uint32.
</para>
</section>
typedef struct comedi_trig_struct comedi_trig;
struct comedi_trig_struct{
- unsigned int subdev; /* subdevice */
- unsigned int mode; /* mode */
- unsigned int flags;
- unsigned int n_chan; /* number of channels */
- unsigned int *chanlist; /* channel/range list */
- sampl_t *data; /* data list, size depends on subd flags */
- unsigned int n; /* number of scans */
- unsigned int trigsrc;
- unsigned int trigvar;
- unsigned int trigvar1;
- unsigned int data_len;
- unsigned int unused[3];
+ unsigned int subdev; /* subdevice */
+ unsigned int mode; /* mode */
+ unsigned int flags;
+ unsigned int n_chan; /* number of channels */
+ unsigned int *chanlist; /* channel/range list */
+ <link linkend="ref-type-sampl-t">sampl_t</link> *data; /* data list, size depends on subd flags */
+ unsigned int n; /* number of scans */
+ unsigned int trigsrc;
+ unsigned int trigvar;
+ unsigned int trigvar1;
+ unsigned int data_len;
+ unsigned int unused[3];
};
</programlisting>
typedef struct comedi_sv_struct comedi_sv_t;
struct comedi_sv_struct{
- comedi_t *dev;
- unsigned int subdevice;
- unsigned int chan;
+ comedi_t *dev;
+ unsigned int subdevice;
+ unsigned int chan;
- /* range policy */
- int range;
- int aref;
+ /* range policy */
+ int range;
+ int aref;
- /* number of measurements to average (for ai) */
- int n;
+ /* number of measurements to average (for ai) */
+ int n;
- lsampl_t maxdata;
+ <link linkend="ref-type-lsampl-t">lsampl_t</link> maxdata;
};
</programlisting>
typedef struct comedi_cmd_struct comedi_cmd;
struct comedi_cmd_struct{
- unsigned int subdev;
- unsigned int flags;
+ unsigned int subdev;
+ unsigned int flags;
- unsigned int start_src;
- unsigned int start_arg;
+ unsigned int start_src;
+ unsigned int start_arg;
- unsigned int scan_begin_src;
- unsigned int scan_begin_arg;
+ unsigned int scan_begin_src;
+ unsigned int scan_begin_arg;
- unsigned int convert_src;
- unsigned int convert_arg;
+ unsigned int convert_src;
+ unsigned int convert_arg;
- unsigned int scan_end_src;
- unsigned int scan_end_arg;
+ unsigned int scan_end_src;
+ unsigned int scan_end_arg;
- unsigned int stop_src;
- unsigned int stop_arg;
+ unsigned int stop_src;
+ unsigned int stop_arg;
- unsigned int *chanlist;
- unsigned int chanlist_len;
+ unsigned int *chanlist;
+ unsigned int chanlist_len;
- sampl_t *data;
- unsigned int data_len;
+ <link linkend="ref-type-sampl-t">sampl_t</link> *data;
+ unsigned int data_len;
};
</programlisting>
typedef struct comedi_insn_struct comedi_insn;
struct comedi_insn_struct{
- unsigned int insn;
- unsigned int n;
- lsampl_t *data;
- unsigned int subdev;
- unsigned int chanspec;
- unsigned int unused[3];
+ unsigned int insn;
+ unsigned int n;
+ <link linkend="ref-type-lsampl-t">lsampl_t</link>*data;
+ unsigned int subdev;
+ unsigned int chanspec;
+ unsigned int unused[3];
};
</programlisting>
<para>
Comedi instructions are described by the comedi_insn structure.
-Applications send instructions to the driver in order to preform
+Applications send instructions to the driver in order to perform
control and measurement operations that are done immediately or
synchronously, i.e., the operations complete before program control
returns to the application. In particular, instructions cannot
<para>
The field insn determines the type of instruction that is sent
-to the driver. Valid instruction types are
+to the driver. Valid instruction types are:
</para>
<variablelist>
<varlistentry>
<term>
+<anchor id="insn-read">
INSN_READ
</term>
<listitem>
</varlistentry>
<varlistentry>
<term>
+<anchor id="insn-write">
INSN_WRITE
</term>
<listitem>
</varlistentry>
<varlistentry>
<term>
+<anchor id="insn-bits">
INSN_BITS
</term>
<listitem>
</varlistentry>
<varlistentry>
<term>
+<anchor id="insn-config">
INSN_CONFIG
</term>
<listitem>
</varlistentry>
<varlistentry>
<term>
+<anchor id="insn-gtod">
INSN_GTOD
</term>
<listitem>
</varlistentry>
<varlistentry>
<term>
+<anchor id="insn-wait">
INSN_WAIT
</term>
<listitem>
typedef struct comedi_range_struct comedi_range;
struct comedi_range_struct{
- double min;
- double max;
- unsigned int unit;
+ double min;
+ double max;
+ unsigned int unit;
}comedi_range;
</programlisting>
typedef struct comedi_krange_struct comedi_krange;
struct comedi_krange_struct{
- int min;
- int max;
- unsigned int flags;
+ int min;
+ int max;
+ unsigned int flags;
};
</programlisting>
typedef struct comedi_insnlist_struct comedi_insnlist;
struct comedi_insnlist_struct{
- unsigned int n_insns;
- comedi_insn *insns;
+ unsigned int n_insns;
+ comedi_insn *insns;
};
</programlisting>
</section>
</section>
-
-<section>
-<title>
-Interface reference
-</title>
-
-<para>
-This chapter is meant to be a reference for some of the advanced
-features of Comedi.
-</para>
-
-<section>
-<title>
-Digital input combining machines
-</title>
-
-<para>
-When one or several digital inputs are used to modify an output
-value, either an accumulator or a single digital line or bit,
-a bitfield structure is typically used in the Comedi interface.
-The digital inputs have two properties, "sensitive" inputs and
-"modifier" inputs. Edge transitions on sensitive inputs cause
-changes in the output signal, whereas modifier inputs change the
-effect of edge transitions on sensitive inputs. Note that inputs
-can be both modifier inputs and sensitive inputs.
-</para>
-
-<para>
-For simplification purposes, it is assumed that multiple digital
-inputs do not change simultaneously.
-</para>
-
-<para>
-The combined state of the modifier inputs determine a modifier
-state. For each combination of modifier state and sensitive
-input, there is a set of bits that determine the effect on the
-output value due to positive or negative transitions of the
-sensitive input. For each transition direction, there are two
-bits defined as follows:
-</para>
-
-<variablelist>
- <varlistentry>
- <term>
-00
- </term>
- <listitem>
- <para>
-transition is ignored
- </para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term>
-01
- </term>
- <listitem>
- <para>
-accumulator is incremented, or output is set
- </para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term>
-10
- </term>
- <listitem>
- <para>
-accumulator is decremented, or output is cleared
- </para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term>
-11
- </term>
- <listitem>
- <para>
-reserved
- </para>
- </listitem>
- </varlistentry>
-</variablelist>
-
-<para>
-For example, a simple digital follower is specified by the bit
-pattern 01 10, because it sets the output on positive transitions
-of the input, and clears the output on negative transitions. A
-digital inverter is similarily 10 01. These systems have only
-one sensitive input.
-</para>
-
-<para>
-As another example, a simple up counter, which increments on
-positive transitions of one input, is specified by 01 00. This
-system has only one sensitive input.
-</para>
-
-<para>
-When multiple digital inputs are used, the inputs are divided
-into two types, inputs which cause changes in the accumulator, and
-those that only modify the meaning of transitions on other inputs.
-Modifier inputs do not require bitfields, but there needs to be
-a bitfield of length 4*(2^(N-1)) for each edge sensitive input,
-where N is the total number of inputs. Since N is usually 2 or
-3, with only one edge sensitive input, the scaling issues are
-not significant.
-</para>
-</section>
-
-
-<section>
-<title>
-INSN_CONFIG
-</title>
-
-<para>
-Configuration instructions are used to access device and driver features
-that do not fit well into other parts of the Comedi interface. This
-includes changing the direction of configurable digital I/O lines,
-configuring complex triggering engines, and counter/timer configuration.
-</para>
-
-<para>
-If a specified ID is not supported, the driver must return -EINVAL.
-</para>
-
-
-<section>
-<title>
-Digital I/O configuration
-</title>
-
-<para></para>
-<simplelist>
- <member>
- Status: Implemented
- </member>
- <member>
- ID: COMEDI_INPUT, COMEDI_OUTPUT, COMEDI_OPENDRAIN
- </member>
- <member>
- Length: 1
- </member>
- <member>
- Chanspec: used to specify channel
- </member>
-</simplelist>
-
-<para>
-These IDs are used to configure direction of digital I/O lines.
-Direction is chosen by the ID. On typical devices, multiple
-channels are grouped together in blocks for determining direction.
-Configuring one channel in a block configures the entire block.
-</para>
-
-<para>
-There should also be a method to read the configuration.
-</para>
-
-<para>
-Errors: Should return -EINVAL if the ID is not supported.
-</para>
-
-</section>
-
-<section>
-<title>
-Analog conversion configuration
-</title>
-
-<simplelist>
- <member>
-Status: design
- </member>
- <member>
-ID: not assigned
- </member>
- <member>
-Length:
- </member>
- <member>
-Chanspec: used to specify channel
- </member>
-</simplelist>
-
-<para>
-Some devices have the capability to add white noise (dithering) to
-analog input measurement. This additional noise can then be averaged
-out, to get a more accurate measurement of the input signal. It
-should not be assumed that channels can be separately configured.
-A simple design can use 1 bit to turn this feature on/off.
-</para>
-
-<para>
-Some devices have the capability of changing the glitch characteristics
-of analog output subsytems. The default (off) case should be where
-the average settling time is lowest. A simple design can use 1 bit
-to turn this feature on/off.
-</para>
-
-<para>
-Some devices have a configurable analog filters as part of the analog
-input stage. A simple designe can use 1 bit to enable/disable the
-filter. Default is disabled, i.e., the filter being bypassed, or if
-the choice is between two filters, the filter with the largest
-bandwidth.
-</para>
-</section>
-
-<section>
-<title>
-Analog Output Waveform Generation
-</title>
-
-<simplelist>
- <member>
-Status: design
- </member>
- <member>
-ID: not assigned
- </member>
- <member>
-Length:
- </member>
- <member>
-Chanspec: ignored
- </member>
-</simplelist>
-
-<para>
-Some devices have the ability to cyclicly loop through samples kept in
-an on-board analog output FIFO. This config should allow the user to
-enable/disable this mode.
-</para>
-
-<para>
-This config should allow the user to configure the number of samples
-to loop through. It may be necessary to configure the channels used.
-</para>
-
-</section>
-
-<section>
-<title>
-Extended Triggering
-</title>
-
-<simplelist>
- <member>
-Status: alpha
- </member>
- <member>
-ID: not assigned
- </member>
- <member>
-Chanspec: ignored
- </member>
-</simplelist>
-
-<para>
-This section covers common information for all extended
-triggering configuration, and doesn't describe a particular
-type of extended trigger.
-</para>
-
-<para>
-Extended triggering is used to configure triggering engines that
-do not fit into commands. In a typical programming sequence, the
-application will use configuration instructions to configure an
-extended trigger, and the issue a command, specifying TRIG_OTHER
-as one of the trigger sources.
-</para>
-
-<para>
-Extended trigger configuration should be designed in such a way
-that the user can probe for valid parameters, similar to how
-command testing works. An extended trigger config instruction
-should not configure the hardware directly, rather, the configuration
-should be saved until the subsequent command is issued. This
-allows more flexibility for future interface changes.
-</para>
-
-<para>
-It has not been decided whether the config stage should return a
-token that is then used as the trigger argument in the command.
-Using tokens is one method to satisfy the problem that extended
-trigger configurations may have subtle compatiblity issues with
-other trigger sources/arguments that can only be determined at
-command test time. Passing all stages of a command test should
-only be allowed with a properly configured extended trigger.
-</para>
-
-<para>
-Extended triggers must use data[1] as flags. The upper 16 bits
-are reserved and used only for flags that are common to
-all extended triggers. The lower 16 bits may be defined by the
-particular type of extended trigger.
-</para>
-
-<para>
-Various types of extended triggers must use data[1] to know which
-event the extended trigger will be assigned to in the command
-structure. The possible values are an OR'd mask of the following:
-</para>
-
-<itemizedlist>
- <listitem>
- <para>
-COMEDI_EV_START
- </para>
- </listitem>
- <listitem>
- <para>
-COMEDI_EV_SCAN_BEGIN
- </para>
- </listitem>
- <listitem>
- <para>
-COMEDI_EV_CONVERT
- </para>
- </listitem>
- <listitem>
- <para>
-COMEDI_EV_SCAN_END
- </para>
- </listitem>
- <listitem>
- <para>
-COMEDI_EV_STOP
- </para>
- </listitem>
-</itemizedlist>
-
-</section>
-
-<section>
-<title>
-Analog Triggering
-</title>
-
-<simplelist>
- <member>
-Status: alpha
- </member>
- <member>
-ID: not assigned
- </member>
- <member>
-Implementation: ni_mio_common
- </member>
- <member>
-Chanspec: ignored
- </member>
-</simplelist>
-
-<simplelist>
- <member>
-data 1 - trigger and combining machine configuration
- </member>
- <member>
-data 2 - analog triggering signal chanspec
- </member>
- <member>
-data 3 - primary analog level
- </member>
- <member>
-data 4 - secondary analog level
- </member>
-</simplelist>
-
-<para>
-Analog triggering is described by a digital combining machine that
-has two sensitive digital inputs. The sensitive digital inputs are
-generated by configurable analog comparators. The analog comparators
-generate a digital 1 when the analog triggering signal is greater
-than the comparator level. The digital inputs are not modifier
-inputs. Note, however, there is an effective modifier due to the
-restriction that the primary analog comparator level must be less
-than the secondary analog comparator level.
-</para>
-
-<para>
-If only one analog comparator signal is used, the combining machine
-for the secondary input should be set to ignored, and the secondary
-analog level should be set to 0.
-</para>
-
-<para>
-The interpretation of the chanspec and voltage levels is device
-dependent, but should correspond to similar values of the analog
-input subdevice, if possible.
-</para>
-
-<para>
-Notes: Reading range information is not addressed. This makes it
-difficult to convert comparator voltages to data values.
-</para>
-
-<para>
-Possible extensions: A parameter that specifies the necessary time
-that the set condition has to be true before the trigger is generated.
-A parameter that specifies the necessary time that the reset condition
-has to be true before the state machine is reset.
-</para>
-
-</section>
-
-<section>
-<title>
-Bitfield Pattern Matching Extended Trigger
-</title>
-
-<simplelist>
- <member>
-Status: design
- </member>
- <member>
-ID: not assigned
- </member>
- <member>
-Chanspec: ignored
- </member>
-</simplelist>
-
-<simplelist>
- <member>
-data 1 - trigger flags
- </member>
- <member>
-data 2 - mask
- </member>
- <member>
-data 3 - pattern
- </member>
-</simplelist>
-
-<para>
-The pattern matching trigger issues a trigger when all of a specifed
-set of input lines match a specified pattern. If the device allows,
-the input lines should correspond to the input lines of a digital input
-subdevice, however, this will necessarily be device dependent. Each
-possible digital line that can be matched is assigned a bit in the
-mask and pattern. A bit set in the mask indicates that the
-input line must match the corresponding bit in the pattern.
-A bit cleared in the mask indicates that the input line is ignored.
-</para>
-
-<para>
-Notes: This only allows 32 bits in the pattern/mask, which may be
-too few. Devices may support selecting different sets of lines from
-which to match a pattern.
-</para>
-
-<para>
-Discovery: The number of bits can be discovered by setting the mask
-to all 1's. The driver must modify this value and return -EAGAIN.
-</para>
-
-</section>
-
-<section>
-<title>
-Counter configuration
-</title>
-
-<simplelist>
- <member>
-Status: design
- </member>
- <member>
-ID: not assigned
- </member>
- <member>
-Chanspec: used to specify counter
- </member>
-</simplelist>
-
-<simplelist>
- <member>
-data 1 - trigger configuration
- </member>
- <member>
-data 2 - primary input chanspec
- </member>
- <member>
-data 3 - primary combining machine configuration
- </member>
- <member>
-data 4 - secondary input chanspec
- </member>
- <member>
-data 5 - secondary combining machine configuration
- </member>
- <member>
-data 6 - latch configuration
- </member>
-</simplelist>
-
-<para>
-Counters can be operated either in synchronous mode (using insn_read)
-or asynchronous mode (using commands), similar to analog input subdevices.
-The input signal for both modes is the accumulator.
-Commands on counter subdevices are almost always specified using
-scan_begin_src=TRIG_OTHER, with the counter configuration also serving
-as the extended configuration for the scan begin source.
-</para>
-
-<para>
-Counters are made up of an accumulator and a combining machine that
-determines when the accumulator should be incremented or decremented
-based on the values of the input signals. The combining machine
-optionally determines when the accumulator should be latched and
-put into a buffer. This feature is used in asynchronous mode.
-</para>
-
-<para>
-Notes: How to access multiple pieces of data acquired at each event?
-</para>
-
-</section>
-
-<section>
-<title>
-One source plus auxiliary counter configuration
-</title>
-
-<simplelist>
- <member>
-Status: design
- </member>
- <member>
-ID: not assigned
- </member>
- <member>
-Chanspec: ?
- </member>
-</simplelist>
-
-<para>
-data[1] is flags, including the flags for the command triggering
-configuration. If a command is not subsequently issued on the
-subdevice, the command triggering portion of the flags are ignored.
-</para>
-
-<para>
-data[2] determines the mode of operation. The mode of operation
-is actually a bitfield that encodes what to do for various
-transitions of the source signals.
-</para>
-
-<para>
-data[3] and data[4] determine the primary source for the counter,
-similar to _src and _arg used in commands.
-</para>
-
-
-<para>
-Notes: How to specify which events cause a latch and push, and what
-should get latched.
-</para>
-</section>
-
-</section>
-</section>
-
-
-<!-- <!DOCTYPE article PUBLIC "-//OASIS//DTD DocBook V3.1//EN"> -->
+<!-- <!DOCTYPE article PUBLIC "-//OASIS//DTD DocBook V3.1//EN" "docbook/dtd/3.1/docbook.dtd"> -->
-<section>
+<section id="writingprograms">
<title>
-Writing programs that use comedi and comedilib
+Writing &comedi; programs
</title>
+<para>
+This Section describes how a well-installed and configured &comedi;
+package 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>
+<section id="firstprogram">
<title>
-Your first comedi program
+Your first &comedi; program
</title>
<para>
-This example requires a card that has analog or
-digital input. Right to the source:
-</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>
-#include <stdio.h> /* for printf() */
-#include <comedilib.h>
+#include <![CDATA[<stdio.h>]]> /* for printf() */
+#include <![CDATA[<]]><link linkend="comedi-comedilib-h">comedilib.h</link><![CDATA[>]]>
-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 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 = <link linkend="aref-ground">AREF_GROUND</link>; /* more on this later */
int main(int argc,char *argv[])
{
- comedi_t *it;
- lsampl_t data;
-
- it=comedi_open("/dev/comedi0");
-
- comedi_data_read(it,subdev,chan,range,aref,&data);
-
- printf("%d\n",data);
-
- return 0;
+ <link linkend="ref-type-comedi-t">comedi_t</link> *it;
+ <link linkend="ref-type-lsampl-t">lsampl_t</link> data;
+
+ it=<link linkend="func-ref-comedi-open">comedi_open</link>("/dev/comedi0");
+
+ <link linkend="func-ref-comedi-data-read">comedi_data_read</link>(it,subdev,chan,range,aref, & data);
+
+ printf("%d\n",data);
+
+ return 0;
}
</programlisting>
-
+The
+<function>
+ <link linkend="func-ref-comedi-open">comedi_open()</link>
+</function> can only be successful if the
+<filename>comedi0</filename> device file is configured to point to a
+valid &comedi; driver. <xref linkend="cardconfiguration"> explains
+how this driver is linked to the “device file”.
<para>
-Should be understandable: open the device, get the data,
-print it out. This is basically the guts of <filename>demo/inp.c</filename>,
-without error checking or fancy options.
-Compile it using
+The code above is basically the guts of
+<filename>demo/inp.c</filename>, without error checking or fancy
+options. Compile the program using
</para>
<screen>
cc tut1.c -lcomedi -o tut1
</screen>
+<para>
+(Replace <literal>cc</literal> by your favourite C compiler command.)
+</para>
<para>
-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.
+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>
+<section id="convertingsamples">
<title>
Converting samples to voltages
</title>
<para>
If you selected an analog input subdevice, you probably noticed
that the output of <command>tut1</command> 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 <emphasis>always</emphasis>
-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?"
+<literal>0</literal> and <literal>4095</literal>, or
+<literal>0</literal> and <literal>65535</literal>, depending on the
+number of bits in the A/D converter. &comedi; samples are
+<emphasis>always</emphasis> unsigned,
+with <literal>0</literal> representing the lowest voltage of the ADC,
+and <literal>4095</literal>
+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?”
</para>
<para>
Most devices give you a choice of gain and unipolar/bipolar
-input, and Comedi allows you to select which of these to
-use. This parameter is called the "range parameter", since
-it specifies the "input range" for analog input (or "output range"
-for analog output.) The range parameter represents both the gain
-and the unipolar/bipolar aspects.
+input, and &comedi; allows you to select which of these to use. This
+parameter is called the “range parameter,” since it
+specifies the “input range” for analog input (or
+“output range” for analog output.) The range parameter
+represents both the gain and the unipolar/bipolar aspects.
</para>
<para>
-Comedi keeps the number of available ranges and the largest
+&comedi; keeps the number of available ranges and the largest
sample value for each subdevice/channel combination. (Some
devices allow different input/output ranges for different
channels in a subdevice.)
</para>
<para>
-The largest sample value can be found using the function:
-</para>
-
+The largest sample value can be found using the function
<programlisting>
-comedi_get_maxdata()
+ <link linkend="ref-type-lsampl-t">lsampl_t</link> <link linkend="func-ref-comedi-get-maxdata">comedi_get_maxdata</link>(<link linkend="ref-type-comedi-t">comedi_t</link> * device, unsigned int subdevice, unsigned int channel))
</programlisting>
-
-<para>
The number of available ranges can be found using the function:
-</para>
-
<programlisting>
-comedi_get_n_ranges()
+ int <link linkend="func-ref-comedi-get-n-ranges">comedi_get_n_ranges</link>(<link linkend="ref-type-comedi-t">comedi_t</link> * device, unsigned int subdevice, unsigned int channel);
</programlisting>
+</para>
<para>
For each value of the range parameter for a particular
-subdevice/channel, you can get range information using the
-function:
-</para>
-
+subdevice/channel, you can get range information using:
<programlisting>
- ptr=comedi_get_range(comedi_file,subdevice,channel,
- range);
+ <link linkend="ref-type-comedi-range">comedi_range</link> * <link linkend="func-ref-comedi-get-range">comedi_get_range</link>(<link linkend="ref-type-comedi-t">comedi_t</link> * device,
+ unsigned int subdevice, unsigned int channel, unsigned int range);
</programlisting>
-
-<para>
-which returns a pointer to a comedi_range structure.
-The comedi_range structure looks like
-</para>
-
+which returns a pointer to a
+<link linkend="ref-type-comedi-range">comedi_range</link>
+structure, which has the following contents:
<programlisting>
typedef struct{
double min;
unsigned int unit;
}comedi_range;
</programlisting>
-
-<para>
-The structure element 'min' represents
-the voltage corresponding to comedi_data_read() returning 0,
-and 'max' represents comedi_data_read() returning 'maxdata',
-(i.e., 4095 for 12 bit A/C converters, 65535 for 16 bit,
-or, 1 for digital input -- more on this in a bit.) The
-'unit' entry tells you if min and
-max refer to voltage, current, etc.
+The structure element <parameter class=function>min</parameter>
+represents the voltage corresponding to
+<link linkend="func-ref-comedi-data-read">comedi_data_read()</link>
+returning <literal>0</literal>,
+and <parameter class=function>max</parameter> represents
+<link linkend="func-ref-comedi-data-read">comedi_data_read()</link>
+returning <parameter class=function>maxdata</parameter>,
+(i.e., <literal>4095</literal> for <literal>12</literal> bit A/C
+converters, <literal>65535</literal> for <literal>16</literal> bit,
+or, <literal>1</literal> for digital input; more on this in a bit.)
+The <parameter class=function>unit</parameter> entry tells you if
+<parameter class=function>min</parameter> and
+<parameter class=function>max</parameter> refer to voltage, current,
+or are dimensionless (e.g., for digital I/O).
</para>
<para>
-"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
+“Could it get easier?” you say. Well, yes. Use
+the function <function>comedi_to_phys()</function>
+<link linkend="func-ref-comedi-to-phys">comedi_to_phys()</link>, which
+converts data values to physical units. Call it using something like
</para>
<programlisting>
-volts=comedi_to_phys(it,data,range,maxdata);
+volts=<link linkend="func-ref-comedi-to-phys">comedi_to_phys</link>(it,data,range,maxdata);
</programlisting>
<para>
</para>
<programlisting>
-data=comedi_from_phys(it,volts,range,maxdata);
+data=<link linkend="func-ref-comedi-from-phys">comedi_from_phy</link>s(it,volts,range,maxdata);
</programlisting>
</section>
-<section>
+<section id="usingfileinterface">
<title>
-Another section
+Using the file interface
</title>
<para>
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():
+access, the &comedi; library provides higher-level access,
+much like the standard <acronym>C</acronym> library provides
+<function>fopen()</function>, etc. as a high-level (and portable)
+alternative to the direct <acronym>UNIX</acronym> system calls
+<function>open()</function>, etc. Similarily to
+<function>fopen()</function>, we have
+<link linkend="func-ref-comedi-open">comedi_open()</link>:
</para>
<programlisting>
-file=comedi_open("/dev/comedi0");
+file=<link linkend="func-ref-comedi-open">comedi_open</link>("/dev/comedi0");
</programlisting>
<para>
-where file is of type (comedi_t *). This function
-calls <function>open()</function>, 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.
+where <parameter class=function>file</parameter> is of type
+<parameter>(<link linkend="ref-type-comedi-t">comedi_t</link> *)</parameter>.
+This function calls <function>open()</function>, as done explicitly in
+a previous section, but also fills the
+<link linkend="ref-type-comedi-t">comedi_t</link>
+structure with lots of goodies; this information will be useful soon.
</para>
<para>
-Specifically, we needed to know maxdata for a specific
-subdevice/channel. How about:
-</para>
+Specifically, you need to know
+<parameter class=function>maxdata</parameter> for a specific
+subdevice/channel. How about:
<programlisting>
-maxdata=comedi_get_maxdata(file,subdevice,channel);
+maxdata=<link linkend="func-ref-comedi-get-maxdata">comedi_get_maxdata</link>(file,subdevice,channel);
</programlisting>
-<para>
-Wow. How easy. And the range type?
-</para>
+Wow! How easy. And the range information?
<programlisting>
-range_type=comedi_get_rangetype(file,subdevice,channel);
+<link linkend="ref-type-comedi-range">comedi_range</link> * <link linkend="func-ref-comedi-get-range">comedi_get_range(<link linkend="ref-type-comedi-t">comedi_t</link>comedi_t</link> *it,unsigned int subdevice,unsigned int chan,unsigned int range);
</programlisting>
-<para>
-Cool. Other information you need to know about a channel
-can be gotten in a similar way.
</para>
</section>
-<section>
+<section id="secondprogram">
<title>
-Your second comedi program
+Your second &comedi; program: simple acquisition
</title>
<para>
-Actually, this is the first comedi program again, just
+Actually, this is the first &comedi; program again, just
that we've added what we've learned.
</para>
<programlisting>
#include <stdio.h> /* for printf() */
-#include <comedi.h> /* also included by comedilib.h */
-#include <comedilib.h> /* 'cuz we're using comedilib */
+#include <![CDATA[<]]><link linkend="comedi-comedilib-h">comedilib.h</link><![CDATA[>]]>
int subdev = 0; /* change this to your input subdevice */
int chan = 0; /* change this to your channel */
int main(int argc,char *argv[])
{
- comedi_t *cf;
- int chan=0;
- lsampl_t data;
- int maxdata,rangetype;
- double volts;
+ <link linkend="ref-type-comedi-t">comedi_t</link> *cf;
+ int chan=0;
+ <link linkend="ref-type-lsampl-t">lsampl_t</link> data;
+ int maxdata,rangetype;
+ double volts;
+
+ cf=<link linkend="func-ref-comedi-open">comedi_open</link>("/dev/comedi0");
+
+ maxdata=<link linkend="func-ref-comedi-get-maxdata">comedi_get_maxdata</link>(cf,subdev,chan);
- cf=comedi_open("/dev/comedi0");
+ rangetype=comedi_get_rangetype(cf,subdev,chan);
- maxdata=comedi_get_maxdata(cf,subdev,chan);
+ <link linkend="func-ref-comedi-data-read">comedi_data_read</link>(cf->fd,subdev,chan,range,aref,&data);
- rangetype=comedi_get_rangetype(cf,subdev,chan);
+ volts=<link linkend="func-ref-comedi-to-phys">comedi_to_phys</link>(data,rangetype,range,maxdata);
- comedi_data_read(cf->fd,subdev,chan,range,aref,&data);
+ printf("%d %g\n",data,volts);
- volts=comedi_to_phys(data,rangetype,range,maxdata);
+ return 0;
+}
+</programlisting>
- printf("%d %g\n",data,volts);
+</section>
- return 0;
+<section id="thirdprogram">
+<title>
+Your third &comedi; program: instructions
+</title>
+<para>
+This program (taken from the set of demonstration examples that come
+with &comedi;) shows how to use a somewhat more flexible acquisition
+function, the so-called <link linkend="instructions">instruction</link>.
+<programlisting>
+<![CDATA[
+#include <stdio.h>
+#include <]]><link linkend="comedi-comedilib-h">comedilib.h</link><![CDATA[>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/time.h>
+#include <unistd.h>
+#include "examples.h"
+]]>
+
+/*
+ * This example does 3 instructions in one system call. It does
+ * a gettimeofday() call, then reads N_SAMPLES samples from an
+ * analog input, and the another gettimeofday() call.
+ */
+
+#define MAX_SAMPLES 128
+
+<link linkend="ref-type-comedi-t">comedi_t</link> *device;
+
+int main(int argc, char *argv[])
+{
+ int ret,i;
+ <link linkend="ref-type-comedi-insn">comedi_insn</link> insn[3];
+ <link linkend="ref-type-comedi-insnlist">comedi_insnlist</link> il;
+ struct timeval t1,t2;
+ <link linkend="ref-type-lsampl-t">lsampl_t</link> data[MAX_SAMPLES];
+
+ parse_options(argc,argv);
+
+
+ device=<link linkend="func-ref-comedi-open">comedi_open</link>(filename);
+ if(!device){
+ <link linkend="func-ref-comedi-perror">comedi_perror</link>(filename);
+ exit(0);
+ }
+
+ if(verbose){
+ printf("measuring device=%s subdevice=%d channel=%d range=%d analog reference=%d\n",
+ filename,subdevice,channel,range,aref);
+ }
+
+ /* Set up a the "instruction list", which is just a pointer
+ * to the array of instructions and the number of instructions.
+ */
+ il.n_insns=3;
+ il.insns=insn;
+
+ /* Instruction 0: perform a gettimeofday() */
+ insn[0].insn=<link linkend="insn-gtod">INSN_GTOD</link>;
+ insn[0].n=2;
+ insn[0].data=(void *)&t1;
+
+ /* Instruction 1: do 10 analog input reads */
+ insn[1].insn=<link linkend="insn-read">INSN_READ</link>;
+ insn[1].n=n_scan;
+ insn[1].data=data;
+ insn[1].subdev=subdevice;
+ insn[1].chanspec=<link linkend="ref-macro-CR-PACK">CR_PACK</link>(channel,range,aref);
+
+ /* Instruction 2: perform a gettimeofday() */
+ insn[2].insn=<link linkend="insn-gtod">INSN_GTOD</link>;
+ insn[2].n=2;
+ insn[2].data=(void *)&t2;
+
+ ret=<link linkend="func-ref-comedi-do-insnlist">comedi_do_insnlist</link>(device,&il);
+ if(ret<![CDATA[<]]>0){
+ <link linkend="func-ref-comedi-perror">comedi_perror</link>(filename);
+ exit(0);
+ }
+
+ printf("initial time: %ld.%06ld\n",t1.tv_sec,t1.tv_usec);
+ for(i=0;i<![CDATA[<]]>n_scan;i++){
+ printf("%d\n",data[i]);
+ }
+ printf("final time: %ld.%06ld\n",t2.tv_sec,t2.tv_usec);
+
+ printf("difference (us): %ld\n",(t2.tv_sec-t1.tv_sec)*1000000+
+ (t2.tv_usec-t1.tv_usec));
+
+ return 0;
}
</programlisting>
+</para>
</section>
+
+<section id="fourthprogram">
+<title>
+Your fourth &comedi; program: commands
+</title>
+
+<para>
+This example programs an analog output subdevice with &comedi;'s most
+powerful acquisition function, the asynchronous
+<link linkend="commandsstreaming">command</link>, to generate a waveform.
+</para>
+
+<para>
+The waveform in this example is a sine wave, but this can be easily
+changed to make a generic function generator.
+</para>
+
+<para>
+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.
+</para>
+
+<para>
+Once you have
+issued the command, &comedi; expects you to keep the buffer full of
+data to output to the acquisition card. This is done by
+<function>write()</function>. Since there may be a delay between the
+<link linkend="func-ref-comedi-command">comedi_command()</link>
+and a subsequent <function>write()</function>, you
+should fill the buffer using <function>write()</function> before you call
+<link linkend="func-ref-comedi-command">comedi_command()</link>,
+as is done here.
+<programlisting>
+<![CDATA[
+#include <stdio.h>
+#include <]]><link linkend="comedi-comedilib-h">comedilib.h</link><![CDATA[>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <getopt.h>
+#include <ctype.h>
+#include <math.h>
+#include "examples.h"
+]]>
+
+double waveform_frequency = 10.0; /* frequency of the sine wave to output */
+double amplitude = 4000; /* peak-to-peak amplitude, in DAC units (i.e., 0-4095) */
+double offset = 2048; /* offset, in DAC units */
+
+/* This is the size of chunks we deal with when creating and
+ outputting data. This *could* be 1, but that would be
+ inefficient */
+#define BUF_LEN 4096
+
+int subdevice;
+int external_trigger_number = 0;
+
+sampl_t data[BUF_LEN];
+
+void <link linkend="dds-output">dds_output</link>(sampl_t *buf,int n);
+void <link linkend="dds-init">dds_init</link>(void);
+
+/* This define determines which waveform to use. */
+#define <anchor id="dds-init-function">dds_init_function <link linkend="dds-init-sine">dds_init_sine</link>
+
+void <link linkend="dds-init-sine">dds_init_sine</link>(void);
+void <link linkend="dds-init-pseudocycloid">dds_init_pseudocycloid</link>(void);
+void <link linkend="dds-init-sawtooth">dds_init_sawtooth</link>(void);
+
+int <anchor id="comedi-internal-trigger">comedi_internal_trigger(<link linkend="ref-type-comedi-t">comedi_t</link> *dev, unsigned int subd, unsigned int trignum)
+{
+ <link linkend="ref-type-comedi-insn">comedi_insn</link> insn;
+ <link linkend="ref-type-lsampl-t">lsampl_t</link> data[1];
+
+ memset(<![CDATA[&insn]]>, 0, sizeof(<link linkend="ref-type-comedi-insn">comedi_insn</link>));
+ insn.insn = <link linkend="insn-inttrig">INSN_INTTRIG</link>;
+ insn.subdev = subd;
+ insn.data = data;
+ insn.n = 1;
+
+ data[0] = trignum;
+
+ return <link linkend="func-ref-comedi-do-insn">comedi_do_insn</link>(dev, <![CDATA[&insn]]>);
+}
+
+
+int main(int argc, char *argv[])
+{
+ <link linkend="ref-type-comedi-cmd">comedi_cmd</link> cmd;
+ int err;
+ int n,m;
+ int total=0;
+ <link linkend="ref-type-comedi-t">comedi_t</link> *dev;
+ unsigned int chanlist[16];
+ unsigned int maxdata;
+ <link linkend="ref-type-comedi-range">comedi_range</link> *rng;
+ int ret;
+ <link linkend="ref-type-lsampl-t">lsampl_t</link> insn_data = 0;
+
+ parse_options(argc,argv);
+
+ /* Force n_chan to be 1 */
+ n_chan = 2;
+
+ if(value){ waveform_frequency = value; }
+
+ dev = <link linkend="func-ref-comedi-open">comedi_open</link>(filename);
+ if(dev == NULL){
+ fprintf(stderr, "error opening %s\n", filename);
+ return -1;
+ }
+ subdevice = <link linkend="func-ref-comedi-find-subdevice-by-type">comedi_find_subdevice_by_type</link>(dev,COMEDI_SUBD_AO,0);
+
+ maxdata = <link linkend="func-ref-comedi-get-maxdata">comedi_get_maxdata</link>(dev,subdevice,0);
+ rng = <link linkend="func-ref-comedi-get-range">comedi_get_range</link>(dev,subdevice,0,0);
+ offset = (double)<link linkend="func-ref-comedi-from-phys">comedi_from_phys</link>(0.0,rng,maxdata);
+ amplitude = (double)<link linkend="func-ref-comedi-from-phys">comedi_from_phys</link>(1.0,rng,maxdata) - offset;
+
+ memset(<![CDATA[&cmd]]>,0,sizeof(cmd));
+ /* fill in the <link linkend="ref-type-comedi-cmd">command data structure</link>: */
+ cmd.subdev = subdevice;
+ cmd.flags = 0;
+ cmd.start_src = <link linkend="trig-int-start-src">TRIG_INT</link>;
+ cmd.start_arg = 0;
+ cmd.scan_begin_src = <link linkend="trig-timer">TRIG_TIMER</link>;
+ cmd.scan_begin_arg = 1e9/freq;
+ cmd.convert_src = <link linkend="trig-now">TRIG_NOW</link>;
+ cmd.convert_arg = 0;
+ cmd.scan_end_src = <link linkend="trig-count">TRIG_COUNT</link>;
+ cmd.scan_end_arg = n_chan;
+ cmd.stop_src = <link linkend="trig-none">TRIG_NONE</link>;
+ cmd.stop_arg = 0;
+
+ cmd.chanlist = chanlist;
+ cmd.chanlist_len = n_chan;
+
+ chanlist[0] = <link linkend="ref-macro-CR-PACK">CR_PACK</link>(channel,range,aref);
+ chanlist[1] = <link linkend="ref-macro-CR-PACK">CR_PACK</link>(channel+1,range,aref);
+
+ <link linkend="dds-init">dds_init</link>();
+
+ <link linkend="dds-output">dds_output</link>(data,BUF_LEN);
+ <link linkend="dds-output">dds_output</link>(data,BUF_LEN);
+
+ dump_cmd(stdout,<![CDATA[&cmd]]>);
+
+ if ((err = <link linkend="func-ref-comedi-command">comedi_command</link>(dev, <![CDATA[&cmd]]>)) < 0) {
+ <link linkend="func-ref-comedi-perror">comedi_perror</link>("comedi_command");
+ exit(1);
+ }
+
+ m=write(comedi_fileno(dev),data,BUF_LEN*sizeof(sampl_t));
+ if(<![CDATA[m<0]]>){
+ perror("write");
+ exit(1);
+ }
+ printf("m=%d\n",m);
+
+ ret = <link linkend="comedi-internal-trigger">comedi_internal_trigger</link>(dev, subdevice, 0);
+<![CDATA[
+ if(ret<0){
+]]>
+ perror("comedi_internal_trigger\n");
+ exit(1);
+ }
+
+ while(1){
+ <link linkend="dds-output">dds_output</link>(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);
+<![CDATA[
+ if(m<0){
+]]>
+ perror("write");
+ exit(0);
+ }
+ printf("m=%d\n",m);
+ n-=m;
+ }
+ total+=BUF_LEN;
+ }
+
+ return 0;
+}
+
+#define WAVEFORM_SHIFT 16
+<![CDATA[
+#define WAVEFORM_LEN (1<<WAVEFORM_SHIFT)
+]]>
+#define WAVEFORM_MASK (WAVEFORM_LEN-1)
+
+sampl_t waveform[WAVEFORM_LEN];
+
+unsigned int acc;
+unsigned int adder;
+
+void <anchor id="dds-init">dds_init(void)
+{
+<![CDATA[
+ adder=waveform_frequency/freq*(1<<16)*(1<<WAVEFORM_SHIFT);
+]]>
+
+ <link linkend="dds-init-function">dds_init_function</link>();
+}
+
+void <anchor id="dds-output">dds_output(sampl_t *buf,int n)
+{
+ int i;
+ sampl_t *p=buf;
+
+ <![CDATA[
+ for(i=0;i<n;i++){
+ *p=waveform[(acc>>16)&WAVEFORM_MASK];
+ ]]>
+ p++;
+ acc+=adder;
+ }
+}
+
+
+void <anchor id="dds-init-sine">dds_init_sine(void)
+{
+ int i;
+
+ <![CDATA[
+ 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 <anchor id="dds-init-pseudocycloid">dds_init_pseudocycloid(void)
+{
+ int i;
+ double t;
+
+ <![CDATA[
+ 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 <anchor id="dds-init-sawtooth">dds_init_sawtooth(void)
+{
+ int i;
+
+ <![CDATA[
+ for(i=0;i<WAVEFORM_LEN;i++){
+ waveform[i]=rint(offset+amplitude*((double)i)/WAVEFORM_LEN);
+ ]]>
+ }
+}
+</programlisting>
+</para>
+
+</section>
+
</section>