From: David Schleef Date: Mon, 7 Jul 2003 22:34:18 +0000 (+0000) Subject: Much fixage and new text from Herman. X-Git-Tag: r0_7_21~60 X-Git-Url: http://git.tremily.us/?a=commitdiff_plain;h=d70d0eef2cd9cdfadb10c7571ae43083464a0619;p=comedilib.git Much fixage and new text from Herman. --- diff --git a/doc/advanced.sgml b/doc/advanced.sgml new file mode 100644 index 0000000..d5f6303 --- /dev/null +++ b/doc/advanced.sgml @@ -0,0 +1,672 @@ +

+Configuring comedi for your hardware +

+ + +I assume that your hardware device is in your computer, and that +you know the relevant details about it, i.e., what kind of card +it is, the I/O base, the IRQ, jumper settings related to input +ranges, etc. + +To tell the comedi kernel module that you have a particular device, and +some information about it, you will be running the comedi_config +command. Perhaps you should read the man page now. + +In this tutorial, I will go through the process of configuring comedi +for two devices, a National Instruments AT-MIO-16E-10 +and a Data Translation DT2821-F-8DI. + +The NI board is plug-and-play, and the man page tells me that I need +to configure the PnP part of the board with isapnptools. The isapnptools +package is a little cryptic, but the concepts are simple. Once I +learned how to use it, I settled on a /etc/isapnp.conf file that +contained the lines: + + + +# ANSI string -->National Instruments, AT-MIO-16E-10<-- +(CONFIGURE NIC2400/10725401 (LD 0 + (IO 0 (BASE 0x0260)) + (INT 0 (IRQ 3 (MODE +E))) +# (DMA 0 (CHANNEL 5)) +# (DMA 1 (CHANNEL 6)) + (ACT Y) +)) + + + +It also contains a few lines about overall configuration and about my +sound card. I found out after a bit of trial-and-error that the NI +board does not always work with interrupts other than IRQ 3. YMMV. +Currently, the driver doesn't use DMA, but it may in the future, so +I commented out the DMA lines. It is a curious fact that the device +ignores the IRQ and DMA information given here, however, I keep the +information here to remind myself that the numbers aren't arbitrary. + +When I run comedi_config (as root, of course), I provide the same +information. Since I want to have the board configured every time +I boot, I put the line + + +/usr/sbin/comedi_config /dev/comedi0 atmio-E 0x260,3 + + +into /etc/rc.d/rc.local. You can, of course, run this command at +a command prompt. The man page tells me that the option list +is supposed to be "(I/O base),(IRQ)", so I used the same numbers +as I put in /etc/isapnp.conf, i.e., 0x260,3. + +For the Data Translation board, I need to have a list of the +jumper settings. Fortunately, I wrote them all down in the +manual -- I hope they are still correct. However, I had to +open the case to figure out which board in the series I had. +It is a DT2821-f-8di. The man page of comedi_config tells +me that I need to know the I/O base, IRQ, DMA 1, DMA 2. However, +since I wrote the driver, I know that it also recognizes the +differential/single-ended and unipolar/bipolar jumpers. As always, +the source is the final authority, and looking in module/dt282x.c +tells me that the options list is interpreted as: + + +I/O base +IRQ +1=differential, 0=single ended +ai 0=unipolar, 1=bipolar +ao0 0=unipolar, 1=bipolar +ao1 0=unipolar, 1=bipolar +dma1 +dma2 + + +(ai=analog input, ao=analog output.) From this, I decide that +the appropriate options list is + + +0x200,4,,1,1,1 + + +I left the differential/single-ended number blank, since the +driver already knowns (from the board name), that it is +differential. I also left the DMA numbers blank, since I +don't want the driver to use DMA. (Don't want it to interfere +with my sound card -- life is full of difficult choices.) +Keep in mind that things commented in the source, but not in +the documentation are about as likely to change as the weather, +so I put good comments next to the following line when I put +it in rc.local. + + +/usr/sbin/comedi_config /dev/comedi1 dt2821-f-8di 0x200,4,,1,1,1 + + +So now I think that I have my boards configured correctly. +Since data acquisition boards are not typically well-engineered, +comedi sometimes can't figure out if the board is actually there. +If it can't, it assumes you are right. Both of these boards +are well-made, so comedi will give me an error message if it +can't find them. The comedi kernel module, since it is a part +of the kernel, prints messages to the kernel logs, which you +can access through the command 'dmesg' or /var/log/messages. +Here is a configuration failure (from dmesg): + + +comedi0: ni_E: 0x0200 can't find board + + +When it does work, I get: + + +comedi0: ni_E: 0x0260 at-mio-16e-10 ( irq = 3 ) + + +Note that it also correctly identified my board. + + +

+Getting information from comedi +

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

+ + +cat /proc/comedi + + +Right now, on my computer, this command gives: + + +comedi version 0.6.4 +format string + 0: atmio-E at-mio-16e-10 7 + 1: dt282x dt2821-f-8di 4 + + +This is a feature that is not well-developed yet. Basically, it +currently tells you driver name, device name, and number of +subdevices. + +In the demo/ directory, there is a command called +info, which provides information about each subdevice on the +board. The output of it is rather long, since I have 7 +subdevices (4 or fewer is common for other boards.) +Here's part of the output of the NI board (which +is on /dev/comedi0.) ('demo/info /dev/comedi0') + + +overall info: + version code: 0x000604 + driver name: atmio-E + board name: at-mio-16e-10 + number of subdevices: 7 +subdevice 0: + type: 1 (unknown) + number of channels: 16 + max data value: 4095 + +... + + +The overall info gives information about the device -- basically +the same information as /proc/comedi. + +This board has 7 subdevices. Devices are separated into +subdevices that each have a distinct purpose -- e.g., analog +input, analog output, digital input/output. This board also +has an EEPROM and calibration DACs that are also subdevices. + +Subdevice 0 is the analog input subdevice. You would have +known this from the 'type: 1 (unknown)' line, if I've updated +demo/info recently, because it would say 'type: 1 (analog input)' +instead. The other lines should be self-explanitory. Comedi +has more information about the device, but demo/info doesn't +currently display this. + + + + + + + + +Writing programs that use comedi and comedilib +

+ +Your first comedi program +

+ +This example requires a card that has analog or +digital input. Right to the source: + + +#include /* for printf() */ +#include + +int subdev = 0; /* change this to your input subdevice */ +int chan = 0; /* change this to your channel */ +int range = 0; /* more on this later */ +int aref = AREF_GROUND; /* more on this later */ + +int main(int argc,char *argv[]) +{ + comedi_t *it; + lsampl_t data; + + it=comedi_open("/dev/comedi0"); + + comedi_data_read(it,subdev,chan,range,aref,&data); + + printf("%d\n",data); + + return 0; +} + + + +Should be understandable: open the device, get the data, +print it out. This is basically the guts of demo/inp.c, +without error checking or fancy options. +Compile it using + + +cc tut1.c -lcomedi -o tut1 + + +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. + + +

+Converting samples to voltages +

+ +If you selected an analog input subdevice, you probably noticed +that the output of tut1 is a number between +0 and 4095, or 0 and 65535, depending on the number of bits +in the A/D converter. Comedi samples are always unsigned, +with 0 representing the lowest voltage of the ADC, and 4095 +the highest. Comedi compensates for +anything else the manual for your device says. However, +you probably prefer to have this number translated to +a voltage. Naturally, as a good programmer, your first +question is: "How do I do this in a device-independent +manner?" + +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 + +

+ +typedef struct{ + double min; + double max; + unsigned int unit; +}comedi_range; + + +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 + + +volts=comedi_to_phys(it,data,range,maxdata); + + +and the opposite + + +data=comedi_from_phys(it,volts,range,maxdata); + + + +

+Another section +

+ + +In addition to providing low level routines for data +access, the comedi library provides higher-level access, +much like the standard C library provides fopen(), etc. +as a high-level (and portable) alternative to the direct +UNIX system calls open(), etc. Similarily to fopen(), +we have comedi_open(): + +

+ +file=comedi_open("/dev/comedi0"); + + +where file is of type (comedi_t *). This function +calls open(), like we did explicitly in a previous +section, but also fills the comedi_t structure with +lots of goodies -- information that we will need to use +soon. + +Specifically, we needed to know maxdata for a specific +subdevice/channel. How about: + + +maxdata=comedi_get_maxdata(file,subdevice,channel); + + +Wow. How easy. And the range type? + + +range_type=comedi_get_rangetype(file,subdevice,channel); + + +Cool. Other information you need to know about a channel +can be gotten in a similar way. + + + +Your second comedi program +

+ + +Actually, this is the first comedi program again, just +that we've added what we've learned. + + + +#include /* for printf() */ +#include /* also included by comedilib.h */ +#include /* '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; +} + + + + + + + +

+Application-specific functions +

+ +Digital Input/Output +

+ +Many boards supported by comedi have digital input and output +channels. Some boards allow the direction of a channel to be +specified in software. + +Comedi groups digital channels into subdevice, which is a group +of digital channels that have the same characteristics. For +example, digital output lines will be grouped into a digital +output subdevice, bidirectional digital lines will be grouped +into a digital I/O subdevice. Thus, there can be multiple +digital subdevices on a particular board. + +Individual digital lines can be read and written using the +functions + + +Slowly-varying inputs +

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

+ + +you are ultimately limited by "spurious free dynamic range" + + +you need to have _some_ noise on the input channel, +otherwise you will be averaging the same number N times. + + +the more noise you have, the greater your SFDR, but it +takes many more samples to compensate for the increased +noise + + +if you feel the need to average samples for 2 seconds, +your signal will need to be _very_ slowly-varying, i.e., +not varying more than your target uncertainty for the +entire 2 seconds. + + + +As you might have guessed, the comedi library has functions +to help you in your quest to accurately measure slowly varying +inputs. I use these functions to measure thermocouple voltages +-- actually, the library functions came from a section of code +that was previously part of the thermocouple reading program. + +The comedi self-calibration utility also uses these functions. +On some hardware, it is possible to tell it to measure an +internal stable voltage reference, which is typically going +to be very slowly varying -- on the kilosecond time scale +or more. So it is reasonable to measure millions of samples, +to get a very accurate measurement of the A/D converter output +value that corresponds to the voltage reference. Sometimes, +however, this is overkill, since there is no need to +perform a part-per-million calibration to a standard that +is only accurate to part-per-thousand. + + +

+Commands +

+ + +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 structure is +used to control acquisition and query the capabilities of a device +(see also , +, and +). + +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 structure. The source types are: + + +TRIG_NONE: don't ever cause an event +TRIG_NOW: cause event to occur immediately +TRIG_FOLLOW: see notes below +TRIG_TIME: cause event to occur at a particular time +TRIG_TIMER: cause event to occur repeatedly at a specific rate +TRIG_COUNT: cause event when count reaches specific value +TRIG_EXT: external signal causes event +TRIG_INT: internal signal causes event +TRIG_OTHER: driver-specific meaning + + +Not all triggers are applicable to all events. Supported triggers +for specific events depend significantly on your particular +device. The +function is useful for determining what triggers a subdevice supports. + +For every trigger, there is a corresponding +argument (the +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 +structure is the index of the subdevice the command is intended for. The + +function can be useful in discovering the index of your desired subdevice. + +The +structure should point to an array whose number of elements is specificed by macro. + +The structure is +TRIG_BOGUS: do the motions?? +TRIG_DITHER: enable dithering?? +TRIG_DEGLITCH: enable deglitching?? +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. +TRIG_CONFIG: perform configuration, not triggering. This is a legacy of the +deprecated comedi_trig_struct, and has no function at present. +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. +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. + +There are also a few flags that indicate how timing arguments should be rounded +if the hardware cannot achieve the exact timing requested. + +TRIG_ROUND_NEAREST: round to nearest supported timing period, the default. +TRIG_ROUND_DOWN: round period down. +TRIG_ROUND_UP: round period up. +TRIG_ROUND_UP_NEXT: this one doesn't do anything, and I don't know what it was intended +to do?? + + +

+ +The typical sequence for executing a command is to first send +the command through + +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 +. For input/output commands, data +is read from or written to the device file /dev/comedi[0..3] you are using. + diff --git a/doc/comedilib.sgml b/doc/comedilib.sgml index 7148df1..fe330e1 100644 --- a/doc/comedilib.sgml +++ b/doc/comedilib.sgml @@ -1,20 +1,27 @@ - + +Comedi"> ]>

- Comedi Documentation + Comedi + +The Control and Measurement Device Interface +handbook + David Schleef @@ -42,35 +49,92 @@ + + 1998-2003 + David Schleef + + + + + Abstract + + +&comedi; is a free software project to interface +digital acquisition (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. + + + + + +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. + + -&intro +&intro; -&install +&install; -&tutorial +&tutorial; -&other +&other; -&drivers +&driverwriting; -
+
- Comedi Reference + Low-level drivers + + +&drivers; + +
+ +
+ + &comedi; Reference - Reference for functions, macros, and constants. + Reference for + constants and macros, + data types and structures, + and functions. - &reference + &reference; - &funcref + - &glossary + &funcref;
- + &glossary;
diff --git a/doc/driverwriting.sgml b/doc/driverwriting.sgml new file mode 100644 index 0000000..b8c10f6 --- /dev/null +++ b/doc/driverwriting.sgml @@ -0,0 +1,894 @@ +
+ +Writing a &comedi; driver + + + +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. + + +This Section does not 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 /proc +interface, etc. So, +the device driver writers can concentrate on the interesting stuff: +implementing their specific interface card's DAQ functionalities. + + +In order to make a decent &comedi; device driver, you must +know the answers to the following questions: + + + + +How does the +communication between user space +and kernel space work? + + + + + +What functionality is provided by the +generic kernel-space +&comedi; functions, and what must be provided for each +specific new driver? + + + + + +How to use DMA and interrupts? + + + + + +What are the addresses and meanings of all the card's registers? + + +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. + + + + + + +
+ +Communication user space-kernel space + + + +In user space, you interact with the functions implemented in the +/usr/src/comedilib directory. Most +of the device driver core of the Comedilib library is found in +lib subdirectory. + + +All user-space &comedi; +instructions and +commands +are transmitted to kernel space through a traditional +ioctl system call. +(See /usr/src/comedilib/lib/ioctl.c.) +The user space information command is encoded as +a number in the ioctl call, and decoded in the +kernel space library. There, they are executed by their kernel-space +counterparts. This is done in the +/usr/src/comedi/comedi/comedi_fops.c file: the +comedi_ioctl() function processes the results of +the ioctl system call, interprets its contents, +and then calls the corresponding kernel space +do_…_ioctl function(s). +For example, a &comedi; +instruction is further processed +by the do_insn_ioctl()function. (Which, in turn, +uses parse_insn() for further detailed processing.) + + +The data corresponding to instructions and commands is transmitted +with the copy_from_user() system call; +acquisition data captured by the interface card passes the kernel-user +space boundary with the help of a copy_to_user() +system call. + + +
+ +
+ +Generic functionality + + + +The major include files of the kernel-space part of &comedi; are: + + + + +include/linux/comedidev.h: 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. + + + + + +include/linux/comedi_rt.h: +all the real-time stuff, such as management of ISR in RTAI and +RTLinux/Free, and spinlocks for atomic sections. + + + + + +include/linux/comedilib.h: the header file for +the kernel library of &comedi;. + + + + + + +From all the relevant &comedi; device driver code that is found in the +/usr/src/comedi/comedi directory +(if the &comedi; source has been installed in its +normal /usr/src/comedi location), +the generic functionality is +contained in two parts: + + + + +A couple of C files contain the infrastructural support. +From these C files, it's especially the +comedi_fops.c 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. + + + + + +For real-time applications, +the subdirectory kcomedilib +implements an interface in the kernel that is similar to the &comedi; +interface accessible through the +user-space Comedi library. + + +There are some differences in what is possible and/or needed +in kernel space and in user space, so the functionalities offered in +kcomedilib 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. + + +Most drivers don't make use (yet) of these real-time functionalities. + + + + + + + +
+ +Data structures + + + +This Section explains the generic data structures that a device driver +interacts with: + +typedef struct comedi_lrange_struct comedi_lrange; +typedef struct comedi_subdevice_struct comedi_subdevice; +typedef struct comedi_device_struct comedi_device: +typedef struct comedi_async_struct comedi_async +typedef struct comedi_driver_struct comedi_driver; + +They can be found in +/usr/src/comedi/include/linux/comedidev.h. +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 (comedi_lrange), +subdevice, and device. + + +Note that these kernel-space data structures have similar names as +their +user-space equivalents, but +they have a different (kernel-side) view on the DAQ problem and a +different meaning: they encode the interaction with the +hardware, not with the user. + + +However, the comedi_insn +and comedi_cmd +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. + + +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 +comedi_driver +that stores the device driver information that is relevant at the +operating system level, and the data structure +comedi_async that stores the +information about all asynchronous activities +(interrupts, callbacks and events). + + +
+ +<function>comedi_lrange</function> + + +The channel information is simple, since it contains only the signal +range information: + +struct comedi_lrange_struct{ + int length; + comedi_krange range[GCC_ZERO_LENGTH_ARRAY]; +}; + + + +
+ + +
+ +<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> + + + +The last data structure stores the information at the +device level: + +struct comedi_device_struct{ + int use_count; + comedi_driver *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; + comedi_subdevice *subdevices; + int options[COMEDI_NDEVCONFOPTS]; + + /* dumb */ + int iobase; + int irq; + + comedi_subdevice *read_subdev; + wait_queue_head_t read_wait; + + comedi_subdevice *write_subdev; + wait_queue_head_t write_wait; + + struct fasync_struct *async_queue; + + void (*open)(comedi_device *dev); + void (*close)(comedi_device *dev); +}; + + + +
+ +
+ +<function>comedi_async</function> + + + +The following data structure contains all relevant information: +addresses and sizes of buffers, pointers to the actual data, and the +information needed for +event handling: + +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 */ + + comedi_cmd cmd; + + // callback stuff + unsigned int cb_mask; + int (*cb_func)(unsigned int flags,void *); + void *cb_arg; + + int (*inttrig)(comedi_device *dev,comedi_subdevice *s,unsigned int x); +}; + + + +
+ +
+ +<function>comedi_driver</function> + + + + +struct comedi_driver_struct{ + struct comedi_driver_struct *next; + + char *driver_name; + struct module *module; + int (*attach)(comedi_device *,comedi_devconfig *); + int (*detach)(comedi_device *); + + /* 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; +}; + + + +
+ +
+ + +
+ +Generic driver support functions + + + +The directory +comedi contains a large set of +support functions. Some of the most important ones are given below. + + +From comedi/comedi_fops.c, 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: + + void comedi_event(comedi_device *dev,comedi_subdevice *s,unsigned int mask); + + int comedi_buf_put(comedi_async *async, sampl_t x); + int comedi_buf_get(comedi_async *async, sampl_t *x); + + static int parse_insn(comedi_device *dev,comedi_insn *insn,lsampl_t *data,void *file); + +The file comedi/kcomedilib/kcomedilib_main.c provides +functions to register a callback, to poll an ongoing data acquisition, +and to print an error message: + + int comedi_register_callback(comedi_t *d,unsigned int subdevice, unsigned int mask,int (*cb)(unsigned int,void *),void *arg); + + int comedi_poll(comedi_t *d, unsigned int subdevice); + + void comedi_perror(const char *message); + +The file comedi/rt.c provides interrupt handling +for real-time tasks (one interrupt per device!): + + int comedi_request_irq(unsigned irq,void (*handler)(int, void *,struct pt_regs *), unsigned long flags,const char *device,comedi_device *dev_id); + void comedi_free_irq(unsigned int irq,comedi_device *dev_id) + + + +
+ +
+ + +
+ +Board-specific functionality + + + +The /usr/src/comedi/comedi/drivers +subdirectory contains +the board-specific device driver +code. Each new card must get an entry in this directory. +Or +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. + + +To help device driver writers, +&comedi; provides the “skeleton” of a new device driver, +in the comedi/drivers/skel.c 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. + + +The first thing you notice in skel.c 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. + + +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. + + +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): + + mydriver_attach(); + mydriver_detach(); + +In the “attach” function, memory is allocated for the +necessary data structures, +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 mydriver_attach() function must: + + + + +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. + + + + + +allocate memory for the private data structures. + + + + + +initialize the board registers and possible subdevices (timer, DMA, PCI, +hardware FIFO, etc.). + + + + + +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 mydriver_detach() function is +called. The mydriver_detach() function should +check any resources that may have been allocated and release them as +necessary. The &comedi; core frees +dev->subdevices and +dev->private, so this does not need to be done in +detach. + + + + + +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. + + + + +Typically, you will be able to implement most of +the above-mentioned functionality by +cut-and-paste from already existing drivers. The +mydriver_attach() 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 +header files of the +/usr/src/comedi/include/linux/ +directory. + + +Drivers for digital IOs should implement the following functions: + + + + +insn_bits(): 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. + + + + + +insn_config(): 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. + + + + +Finally, the device driver writer must implement the +read and write functions for +the analog channels on the card: + + + + +insn_read(): acquire the inputs on the board and +transfer them to the software buffer of the driver. + + + + + +insn_write(): transfer data from the software +buffer to the card, and execute the appropriate output conversions. + + + + +In some drivers, you want to catch interrupts, and/or want to use the +INSN_INTTRIG instruction. In this +case, you must provide and register these +callback functions. + + +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 +some inspiration in the already available device +drivers, but don't trust that blind +cut-and-paste will bring you far… + + +
+ +
+ +Callbacks, events and interrupts + + + +Continuous acquisition is tyically an +asynchronous 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 event handling can +be needed during the acquisition: + + + + +The hardware can generate some error or +warning events. + + + + + +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 scan, etc. + + + + + +The device driver writer can register a driver-supplied +”callback” function, that is called at the end of each +hardware interrupt routine. + + + + + +Another driver-supplied callback function is executed when the user +program launches an INSN_INTTRIG +instruction. This event handling is executed +synchronously with the execution of the +triggering instruction. + + + + + + +The interrupt handlers are registered through the functions mentioned +before +The event handling is done in the existing &comedi; drivers in +statements such as this one: + + + s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR + +It fills in the bits corresponding to particular events in the +comedi_async data structure. +The possible event bits are: + + + + + +COMEDI_CB_EOA: execute the callback at the +“End Of-Acquisition”. + + + + + + +COMEDI_CB_EOS: execute the callback at the +“End-Of-Scan”. + + + + + + +COMEDI_CB_OVERFLOW: execute the callback when a +buffer overflow has occurred. + + + + + + +COMEDI_CB_ERROR: execute the callback at the +occurrence of an (undetermined) error. + + + + + + +
+ + +
+ +Device driver caveats + + + +A few things to strive for when writing a new driver: + + + + +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 +Mite 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. + + + + + +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 just to do digital I/O +and has no interrupts available. + + + + + +Drivers are to have absolutely no +global variables, mainly because the existence of global variables +immediately negates any possibility of using the driver for two +devices. The pointer dev->private should be used +to point to a structure containing any additional variables needed by +a driver/device combination. + + + + + +Drivers should report errors and warnings via the +comedi_error() function. +(This is not the same function as the user-space +comedi_perror() function.) + + + + + + +
+ +
+ +Integrating the driver in the &comedi; library + + + +For integrating new drivers in the &comedi;'s source tree the following +things have to be done: + + + + +Choose a senseful name for the source code file. Let's assume here +that you call it “mydriver.c” + + + + + +Put your new driver into “comedi/drivers/mydriver.c”. + + + + + +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: + + dep_tristate 'MYDRIVER' CONFIG_COMEDI_MYDRIVER $CONFIG_COMEDI + + + + + + +Add a line to “comedi/drivers/Makefile.in”, using your +freshly defined variable, i.e., CONFIG_COMEDI_MYDRIVER. + + + + + +Now make distclean, reconfigure &comedi; with a new +make, rebuild and be happy. + + +If you want to have your driver included in the &comedi; distribution +(you definitely want to :-) ) send it to David +Schleef
ds@schleef.org
for +review and integration. +
+
+
+
+ +
+ +
diff --git a/doc/funcref b/doc/funcref index 5d0da93..3e03693 100644 --- a/doc/funcref +++ b/doc/funcref @@ -7,7 +7,7 @@ Returns: 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. @@ -464,77 +464,22 @@ Description: 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 @@ -554,8 +499,8 @@ Description: 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 @@ -574,7 +519,7 @@ Description: 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 @@ -779,10 +724,10 @@ 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) @@ -792,8 +737,8 @@ Param: unsigned int subdevice 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 @@ -812,15 +757,15 @@ Param: unsigned int aref 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 @@ -831,91 +776,3 @@ Description: 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 file_path 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 calibration 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 - calibration obtained from - comedi_parse_calibration_file(). calibration - 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 dev. 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. diff --git a/doc/glossary.sgml b/doc/glossary.sgml index f14d657..1922026 100644 --- a/doc/glossary.sgml +++ b/doc/glossary.sgml @@ -4,69 +4,72 @@ Glossary - + + -buffer + Application Program Interface + API -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. - + + -buffer overflow +buffer -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 comedi_buffer_XXX() or the +comedi_config +utility. - + -overrun +buffer overflow -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 +comedi_buffer_XXX for more information. - + -command + Differential IO -Comedi commands are the mechanism that applications configure -subdevices for streaming input and output. - (also: cmd, comedi_command) +… - + -DMA + Direct Memory Access + DMA -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 @@ -80,10 +83,11 @@ be the optimal transfer mechanism for a particular situation. - + -FIFO + First In, First Out + FIFO Most devices have a limited amount of on-board space to store samples @@ -96,59 +100,115 @@ interruptions in data. - + + + +&comedi; command + + + +&comedi; commands are the mechanism that applications configure +subdevices for streaming input and output. + + + + + + +command + + + + + + +configuration option + + + + + + + + instruction -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) - + instruction list -Instruction lists allow the application to perform multiple Comedi +Instruction lists allow the application to perform multiple &comedi; instructions in the same system call. - + + +option + + + + + + + + + + option list -Option lists are used with comedi_config to perform driver -configuration. - (also: configuration options, options) +Option lists are used with comedi_config to +perform driver configuration. + + - + + +overrun + + + +This is an error message that indicates that there was device-level +problem, typically with trigger pulses occurring faster than the +board can handle. + + + + + poll 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 +poll() system call for Comedi devices, which is +similar to select(), 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 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. diff --git a/doc/install.sgml b/doc/install.sgml index 70f06a8..bbcb581 100644 --- a/doc/install.sgml +++ b/doc/install.sgml @@ -1,37 +1,66 @@ -
+
-Installation and Configuration +Configuration -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. + +
+ +Configuration + -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 comedi_config command. (As root of course.) +Here is an example of how to use the command (perhaps you should read +its man page now): + +/usr/sbin/comedi_config /dev/comedi0 ni_atmio 0x260,3 + +This command says that the “file” +/dev/comedi0 can be used to access the &comedi; +device that uses the ni_atmio driver, and that +you give it two run-time parameters (0x260 and +3). More parameters are possible, for example to +discriminate between two or more identical cards in your system. - -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 +/etc/rc.d/rc.local file), or in the system-wide +&comedi; configuration file /etc/comedi.conf. +You can, of course, also run this command at a command prompt. -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 +National Instruments AT-MIO-16E-10 (which has the +driver mentioned above), and a +Data Translation DT2821-F-8DI. + +The NI board is plug-and-play, and the man page +tells you that you need to configure the PnP part of the board with +isapnptools. The isapnptools +package is a little cryptic, but the concepts are simple. Once you've +learned how to use it, you can settle on a +/etc/isapnp.conf file such as this: + # ANSI string -->National Instruments, AT-MIO-16E-10<-- @@ -44,46 +73,41 @@ contained the lines: )) - -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. -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 man page +explains that the option list is supposed to be +“(I/O base),(IRQ)”, so use the same +numbers as in /etc/isapnp.conf, i.e., +0x260,3. - -export PATH=/sbin:/usr/sbin:/usr/local/sbin:$PATH -comedi_config /dev/comedi0 ni_atmio 0x260,3 - - -into /etc/rc.d/rc.local. You can, of course, run this command at -a command prompt. The man page tells me that the option list -is supposed to be "(I/O base),(IRQ)", so I used the same numbers -as I put in /etc/isapnp.conf, i.e., 0x260,3. +For the Data Translation board, 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 DT2821-f-8di. The +man page of comedi_config 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 +module/dt282x.c +tells us that the options list is interpreted as: - -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 ...) -(ai=analog input, ao=analog output.) From this, I decide that -the appropriate options list is - - +So, the appropriate options list is: 0x200,4,,1,1,1 - - -I left the differential/single-ended number blank, since the +and the full configuration command is: + +/usr/sbin/comedi_config /dev/comedi1 dt2821-f-8di 0x200,4,,1,1,1 + +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. - -export PATH=/sbin:/usr/sbin:/usr/local/sbin:$PATH -comedi_config /dev/comedi1 dt2821-f-8di 0x200,4,,1,1,1 - - -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 dmesg or the file +/var/log/messages. +Here is a configuration failure (from dmesg): @@ -142,7 +162,7 @@ comedi0: ni_atmio: 0x0200 can't find board -When it does work, I get: +When it does work, you get: @@ -150,19 +170,20 @@ comedi0: ni_atmio: 0x0260 at-mio-16e-10 ( irq = 3 ) -Note that it also correctly identified my board. +Note that it also correctly identified the board. +
-
+
-Getting information from comedi +Getting information about a card -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: @@ -170,7 +191,8 @@ cat /proc/comedi -Right now, on my computer, this command gives: +On the particular system this demonstration was carried out, this +command gives: @@ -181,19 +203,19 @@ format string -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. -In the demo/ directory, there is a command called -info, which provides information about each -subdevice on the -board. The output of it is rather long, since I have 7 -subdevices (4 or fewer is common for other boards.) -Here's part of the output of the NI board (which -is on /dev/comedi0.) ('demo/info /dev/comedi0') +In the demo/ directory, there is a +command called info, 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 National Instruments +board (which is on /dev/comedi0), as a result of +the command demo/info /dev/comedi0: @@ -203,33 +225,30 @@ overall info: 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 ... -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 /proc/comedi. -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. -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 demo/info doesn't currently display +this. -
+
diff --git a/doc/intro.sgml b/doc/intro.sgml index a564efc..057e1af 100644 --- a/doc/intro.sgml +++ b/doc/intro.sgml @@ -1,312 +1,705 @@ -
+
-Introduction +Overview -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 free software project that develops +drivers, tools, and libraries for various forms of +data acquisition: 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, + +comedi + and + +comedilib +, and provides several Linux +kernel modules and a +user space library: + + + + +Comedi 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 “comedi”) providing common +functionality, and (ii) individual low-level driver modules for +each device. + + + + + +Comedilib is a separately distributed package +containing a user-space library that provides a +developer-friendly interface to the &comedi; devices. Included in the +Comedilib package are documentation, +configuration and calibration utilities, and demonstration programs. + -
+ + +Kcomedilib is a Linux kernel module +(distributed with the comedi package) that provides +the same interface as comedilib in kernel space, +and suitable for real-time tasks. It is +effectively a “kernel library” for using &comedi; from +real-time tasks. + + + + +&comedi; works with standard Linux kernels, but also with its +real-time extensions RTAI and +RTLinux/Free. + + +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. + + +
-What is a "device driver"? +What is a “device driver”? 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. -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 data acquisition +(or DAQ) cards. + + +Analog input and output cards were the first goal +of the project, but now &comedi; also provides a device +independent interface to digital input and output +cards, and counter and timer cards (including +encoders, pulse generators, frequency and pulse timers, etc.). + + 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. +modularity and complexity: +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.
-
+ +
-A general DAQ device driver package. +Policy vs. mechanism -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: + + + + + 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. + + + + + + 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. + + + +So, &comedi; focuses only on the mechanism 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. + + +
+ +
+ +A general DAQ device driver package + + +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: + + + - API: devices that offer similar functionalities, should have the same + API: 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. + + + - Structure: many electronic interfaces have more than one layer of + Architectural structure: 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. + - + In the case of Linux as the host operating system, device driver writers must keep the following Linux-specific issues in mind: - - + + - Kernel space vs. User space. - The Linux operating system has two levels: only privileged processes + Kernel space vs. User space. + 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 system calls. + + +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 Comedi, while +&comedi; device driver writers use the API offered by +Kcomedilib. Typical examples of the latter are +the registration of interrupt handler routines, and the handling of +events. + + + + + + Device files or device file system. + 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 /dev directory (2.2.x kernels or +earlier) or /devfs 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: +open, +close, read, + write, and ioctl. + + + + + + /proc interface. + Linux (and some other UNIX operating systems) offer a file-like +interface to attached devices (and other OS-related information) via +the /proc 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. + + + + + + Direct Memory Access (DMA) vs. Programmed +Input/Output (PIO). + 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). + + - 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. +Real-time vs. non real-time. +If the device is to be used in a +RTLinux/Free + +or RTAI application, +there are a few extra requirements, because not all system calls are +available in the kernel of the real-time operating systems +RTLinux/Free + +or RTAI. +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. + + + + +
+ +
+ +DAQ signals + + +The cards supported in &comedi; have one or more of the following +signals: analog input, analog +output, digital input, digital output, counter input, counter output, +pulse input, pulse output: + + - /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. +Digital 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. + + - 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). +Analog 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., -10V and ++10V); the card's electronics can be programmed to +automatically sample a set of channels, in a prescribed order, to +buffer sequences of data on the board; or to use +DMA or an interrupt routine to dump the data in a prescribed part of +memory. + + -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. +Pulse-based signals (counters, +timers, encoders, etc.) are conceptually +only a bit more complex than digital inputs and outputs, in that +they only add some timing specifications 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. -
+ + +In addition to these “real” DAQ functions, &comedi; also +offers basic timer access. + +
-
+
-Policy vs. mechanism. +Device hierarchy -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: - +&comedi; organizes all hardware according to the following generic +hierarchy: + + - 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. +Channel: 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. + + - 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. +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 channel +and the type of the channels. + + + + + +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 National Instruments 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 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;. +
-
+
-Overview of Comedi. +Acquisition terminology - -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). - -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. - +This Section introduces the terminology that this document uses when +talking about “acquisitions.” +depicts a typical acquisition sequence: + + -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 start and an +end. At both sides, the software +and the hardware need some finite +initialization or settling time. + + -The kernel space structures that Comedi uses have the following -hierarchy: + +The sequence consists of a number of identically repeated +scans. 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 +begin, an +end, and a finite +setup time. Possibly, there is also +a settling time +(“scan delay”) at the +end of a scan. -- 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 scan interval) +on the minimum time needed to complete a full scan. + + + -- 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 + +conversions 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 +conversion time, starting from the +moment in time called the +sample time +in +(sometimes also called the “timestamp”), +and caused by a +triggering event, called convert. +In addition, each hardware has limits on the minimum +conversion interval it can achieve, +i.e., the minimum time it needs between +subsequent conversions. -- 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 multiplex the conversions onto +one single AD/DA hardware, such that the conversions are done serially +in time (as shown on the Figure); +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. + - -The basic functionalities offered by Comedi are: - - -- 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. + +In general, not only the begin of a conversion is +triggered, but also the begin of a scan and of a +sequence. &comedi; provides the API to configure +what triggering source +one wants to use in each case. The API also +allows to specify the channel list, +i.e., the sequence of channels that needs to be acquired during each +scan. + -- scan: repeated instructions on a number of different channels, with a - programmed sequence and timing. +
+ + Acquisition sequence. (Figure courtesy of + <ulink url="mailto:Kurt.Mueller@sfwte.ch">Kurt Müller</ulink>.) + + + + + + + +
+
+ +
+ + +
+ +DAQ functions + + -- 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: + + + + +Single acquisition: &comedi; has +function calls to synchronously perform +one single data acquisition on a specified +channel: comedi_data_read(), +comedi_data_write(), +comedi_dio_read(), +comedi_dio_write(). +“Synchronous” means that the calling process +blocks until the data acquisition has finished. + + + + + +Instruction: a +comedi_do_insn() instruction +performs (possibly multiple) data acquisitions on a specified channel, +in a synchronous way. So, the +function call blocks until the whole acquisition has finished. + + +In addition, comedi_do_insnlist() executes a +list of instructions (on different channels) in +one single (blocking, synchronous) call, such that the overhead +involved in configuring each individual acquisition is reduced. + + + + + +Scan: a scan is an acquisition on a +set of different channels, with a specified sequence and +timing. + + +Scans are not directly available as stand-alone function calls in the +&comedi; API. They are the internal building blocks of a &comedi; +command (see below). + + + + + +Command: a command is +sequence of +scans, for which conditions have been specified +that determine when the acquisition will start and stop. A +comedi_command() function call generates +aynchronous data acquisition: +as soon as the command information has been filled in, the +comedi_command() 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. + + +Buffer management is not the only asynchronous activity: a running +acquisition must eventually be stopped too, or it must be started +after the comedi_command() function call has +prepared (but not started) the hardware for the acquisition. +The command functionality is very configurable with respect to +choosing which events 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; +instruction 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 +synchronous with the execution of +the trigger instruction in the device driver, but it is +asynchronous with respect to the +instruction or command that initiated the current acquisition. + + +Typically, there is one synchronous triggering instruction for each +subdevice. + + + + +Note that software triggering is only relevant for commands, and not +for instructions: instructions are executed +synchronously 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. + +
+ +
+ +Supporting functionality + + -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 comedi_rt_timer kernel +module to support such a +virtual command execution under +RTAI or RTLinux/Free. -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 +to access the functionality of the +cards, but also to query the +capabilities of the installed 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.). -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. +Buffering 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. -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 +event-driven +(“asynchronous”) functionality: +the data acquisition can signal +its completion by means of an interrupt or a +callback 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). - - - 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 +(RTAI or +RTLinux/Free) or non real-time, but both +contexts are encapsulated wihting the same &comedi; calls. -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 locking primitives to +ensure atomic operations on critical sections of the code or data +structures. -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 +/dev 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 +/proc directory (which allow to +inspect the status of a &comedi; device). +
diff --git a/doc/mkref b/doc/mkref index aa1b2e8..b3a33f9 100755 --- a/doc/mkref +++ b/doc/mkref @@ -8,7 +8,7 @@ $end = ""; print " -
+
Comedi Function Reference diff --git a/doc/other.sgml b/doc/other.sgml index 58ecdfe..9644ebd 100644 --- a/doc/other.sgml +++ b/doc/other.sgml @@ -1,541 +1,1670 @@ -
+
-Application-specific functions +Acquisition and configuration functions + +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.) + explains the function calls in full +detail. + + +
+ +Functions for single acquisition + + + +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 +digital and +analog acquisitions. + + +
+ +Single digital acquisition + + + +Many boards supported by &comedi; have digital input and output +channels; i.e., channels that can only produce a 0 +or a 1. +Some boards allow the direction (input or output) +of each channel to be specified independently in software. + + + +&comedi; groups digital channels into a +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 bits on a digital I/O device can be read and written using +the functions + + int comedi_dio_read(device,subdevice,channel,unsigned int *bit); + int comedi_dio_write(device,subdevice,channel,unsigned int bit); + +The device parameter is a +pointer +to a successfully opened &comedi; device. +The subdevice and +channel parameters are positive +integers that indicate which subdevice and channel is used in the +acquisition. The integer bit +contains the value of the acquired bit. + +The direction of bidirectional lines can be configured using +the function + + comedi_dio_config(device,subdevice,channel,unsigned int dir); + +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. + + + +Multiple channels can be read and written simultaneously using the +function + + comedi_dio_bitfield(device,subdevice,unsigned int write_mask,unsigned int *bits); + +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. + + + +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 +instruction command. + + + +
+ + +
+ +Single analog acquisition + + +Analog &comedi; channels can produce data values that are +samples from continuous analog signals. +These samples are integers with a significant content in +the range of, typically, 8, 10, +12, or 16 bits. + + +The + + int comedi_data_read(comedi_t * device, unsigned int subdevice, unsigned int channel, + unsigned int range, unsigned int aref, lsampl_t * data); + +function reads one such data value from a &comedi; channel, and puts it in +the user-specified data buffer. The + + int comedi_data_write(comedi_t * device, unsigned int subdevice, unsigned int channel, + unsigned int range, unsigned int aref, lsampl_t data); + +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 + + lsampl_t comedi_get_maxdata(comedi_t * device, unsigned int subdevice, unsigned int channel); + +Conversion of data values to physical units can be performed by the +function + + double comedi_to_phys(lsampl_t data, comedi_range * range, lsampl_t maxdata); + +There are two data structures in these commands that are not fully +self-explanatory: + + + + +comedi_t: this data structure +contains all information that a user program has to know about an +open &comedi; device. The programmer doesn't have +to fill in this data structure manually: it gets filled in by opening +the device. + + + + + +lsampl_t: 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”. + + + + + + +Each single acquisition by, for example, + + comedi_data_read() + +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: + + int comedi_data_read_n(comedi_t *it, unsigned int subdev, unsigned int chan, unsigned int range, + unsigned int aref, lsampl_t *data, unsigned int n) + +The number of samples, n, is +limited by the &comedi; implementation (to a maximum of 100 samples), +because the call is blocking. + + +The start of the data acquisition can also be delayed by a specified +number of nano-seconds: + +int comedi_data_read_delayed(comedi_t *it, unsigned int subdev, unsigned int chan, unsigned int range, + unsigned int aref, lsampl_t *data, unsigned int nano_sec) + +All these read and write acquisition functions are implemented on top +of the generic instruction +command. + + +
+ +
+ + +
+ +Instructions for multiple acquisitions + + +The instruction 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 +configuration of a +channel. + +An instruction list is a list of instructions, +possibly on different channels. Both instructions and instructions +lists are executed synchronously, i.e., while +blocking 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 +command acquisition +primitive. + + + +
+ +The instruction data structure + + +All the information needed to execute an instruction is stored in the +comedi_insn +data structure: + +struct comedi_insn_struct{ + unsigned int insn; // integer encoding the type of acquisition + // (or configuration) + unsigned int n; // number of samples + lsampl_t *data; // pointer to data buffer + unsigned int subdev; // subdevice + unsigned int chanspec; // encoded channel specification + unsigned int unused[3]; +} comedi_insn; + +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 +data field to be at +least one byte long. + + + +The insn flag of the +instruction data structure +determines the type of acquisition executed in the corresponding +instruction: + + + + +INSN_READ: the instruction executes a read on an analog channel. + + + + + +INSN_WRITE: the instruction executes a write on an analog channel. + + + + + +INSN_BITS: indicates that the instruction must +read or write values on multiple digital I/O channels. + + + + + +INSN_GTOD: the instruction performs a “Get Time Of Day” +acquisition. + + + + + +INSN_WAIT: the instruction blocks for a specified number of +nanoseconds. + + + + + + +
+ + +
+ +Instruction execution + + +Once an instruction data structure has been filled in, the +corresponding instruction is executed as follows: + + int comedi_do_insn(comedi_t *it, comedi_insn * instruction); + +Many &comedi; instructions are shortcuts that relieve the programmer +from explicitly filling in the data structure and calling the +comedi_do_insn +function. + + +The + + int comedi_do_insnlistcomedi_t *it, comedi_insnlist * list) + +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 +synchronously, i.e., the call blocks until the +whole instruction (list) has finished. + + +
+ +
+ + +
+ +Instructions for configuration + + + explains how instructions are used to do +acquisition on channels. This section explains +how they are used to configure a device. +There are various sorts of configurations, and the +specific information for each different configuration possibility is +to be specified via the +data buffer of the +instruction data structure. +(So, the pointer to a +lsampl_t +is misused as a pointer to an array with board-specific information.) + + + +Using INSN_CONFIG as the +insn flag in an +instruction data structure +indicates that the instruction will +not perform acquisition on a +channel, but will configure that channel. +For example, the configuration of digital I/O channels is done as +follows. The +chanspec field in the +comedi_insn +data structure, contains the channel to be configured. And +data[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. + + + +Another example of an INSN_CONFIG instruction is the configuration of +the TRIG_OTHER event source. + + +
+ + +
+ +Instruction for internal triggering + + +This special instruction has +INSN_INTTRIG as the +insn flag in its +instruction data structure. +Its execution causes an +internal triggering event. 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. + + +The +data[0] field of the +INSN_INTTRIG instruction is reserved for future use, and should be set +to “0”. + + +
+ + +
+ +Commands for streaming acquisition + + + +The most powerful &comedi; acquisition primitive is the +command. It's powerful because, with one single +command, the programmer launches: + + + + +a possibly infinite sequence of acquisitions, + + + + + +accompanied with various callback functionalities +(DMA, interrupts, driver-specific callback functions), + + + + + +for any number of channels, + + + + + +with an arbitrary order of channels in each scan +(possibly even with repeated channels per scan), + + + + + +and with various scan triggering sources, +external (i.e., hardware pulses) as well as internal (i.e., pulses +generated on the DAQ card itself, or generated by a +software trigger instruction). + + + + +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. + + + +A command specifies a particular data +acquisition sequence, which +consists of a number of scans, and each scan is +comprised of a number of conversions, 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. + + +The command function is complementary to the +configuration instruction +function: each channel in the command's +chanlist +should first be configured by an appropriate instruction. + + + +
+ +Executing a command + + + +A commands is executed by the following &comedi; function: + + int comedi_command(comedi_t * device, comedi_cmd * command); + +The following sections explain the meaning of the +comedi_cmd 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 +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. + + +A &comedi; program can find out on-line what the command capabilities +of a specific device are, by means of the +comedi_get_cmd_src_mask() +function. + + +
+ + +
+ +The command data structure + + +The command executes according to the information about the requested +acquisition, which is stored in the +comedi_cmd +data structure: + +typedef struct comedi_cmd_struct comedi_cmd; + +struct comedi_cmd_struct{ + unsigned int subdev; // which subdevice to sample + unsigned int 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 start_src; // event to make the acquisition start + unsigned int start_arg; // parameters that influence this start + + unsigned int scan_begin_src; // event to make a particular scan start + unsigned int scan_begin_arg; // parameters that influence this start` + + unsigned int convert_src; // event to make a particular conversion start + unsigned int convert_arg; // parameters that influence this start + + unsigned int scan_end_src; // event to make a particular scan terminate + unsigned int scan_end_arg; // parameters that influence this termination + + unsigned int stop_src; // what make the acquisition terminate + unsigned int stop_arg; // parameters that influence this termination + + unsigned int *chanlist; // pointer to list of channels to be sampled + unsigned int chanlist_len; // number of channels to be sampled + + sampl_t *data; // address of buffer + unsigned int data_len; // number of samples to acquire +}; + +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 event. More on these in +. + + + +The 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 chanlist +member of the +comedi_cmd data +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. + + + +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. + + + +The final member of the +comedi_cmd structure is the +flags field, +i.e., bits in a word that can be bitwise-or'd together. The meaning of +these bits are explained in a +later section. + + +
+ + +
+ +The command trigger events +<anchor id="source.trigger.anchor"> + + +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 +data structure +has five types of events: start the +acquisition, +start a scan, start a +conversion, stop a scan, and stop +the acquisition. Each event can be given its own +source +(the *_src members in the +comedi_cmd data +structure). And each event source can have a corresponding +argument (the *_arg members of +the comedi_cmd 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, any of the five event +sources), you would use +src=TRIG_EXT and +arg=3. + + +The following paragraphs discuss in somewhat more detail the trigger +event sources(*_src), and the +corresponding arguments (*_arg). + + +The start of an acquisition is controlled by the +start_src events. +The available options are: + + + + + +TRIG_NOW: the +start_src +event occurs +start_arg +nanoseconds after the +comedi_cmd +is called. Currently, only +start_arg=0 is +supported. + + + + + + +TRIG_FOLLOW: (For an output device.) The +start_src +event occurs when data is written to the buffer. + + + + + + +TRIG_EXT: the start event occurs when an external trigger signal +occurs; e.g., a rising edge of a digital line. +start_arg +chooses the particular digital line. + + + + + + +TRIG_INT: the start event occurs on a &comedi; internal signal, which +is typically caused by an +INSN_INTTRIG instruction. + + + + +The start of the beginning of each +scan is controlled by the +scan_begin events. +The available options are: + + + + + +TRIG_TIMER: +scan_begin +events occur periodically. The time between +scan_begin +events is +convert_arg +nanoseconds. + + + + + + +TRIG_FOLLOW: The +scan_begin +event occurs immediately after a +scan_end +event occurs. + + + + + + +TRIG_EXT: the +scan_begin +event occurs when an external trigger signal +occurs; e.g., a rising edge of a digital line. +scan_begin_arg +chooses the particular digital line. + + + + +The +scan_begin_arg +used here may not be supported exactly by the device, but it +will be adjusted to the nearest supported value by +comedi_command_test(). + + +The timing between each sample in a +scan is controlled by the +convert_* +fields: + + + + + + +TRIG_TIMER: the conversion events occur periodically. The time +between convert events is +convert_arg +nanoseconds. + + + + + + + +TRIG_EXT: the conversion events occur when an external trigger signal +occurs, e.g., a rising edge of a digital line. +convert_arg +chooses the particular digital line. + + + + + + + +TRIG_NOW: All conversion events in a +scan occur simultaneously. + + + + +The end of each scan is almost always specified +using +TRIG_COUNT, with the argument being +the same as the number of channels in the +chanlist. You +could probably find a device that allows something else, but it would +be strange. + + +The end of an +acquisition is +controlled by +stop_src +and stop_arg: + + + + + + +TRIG_COUNT: stop the acquisition after +stop_arg +scans. + + + + + + + +TRIG_NONE: perform continuous acquisition, until stopped using +comedi_cancel(). + + +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.) + + + + +There are a couple of less usual or not yet implemented events: + + + + + +TRIG_TIME: +cause an event to occur at a particular time. + + +(This event source is reserved for future use.) + + + + + + +TRIG_OTHER: driver specific event trigger. + + +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 +INSN_CONFIG +instructions. + + +The argument is reserved and should be set to 0. + + + + +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 +comedi_get_cmd_src_mask() +function is useful for determining what trigger sources a subdevice +supports. + + +
+ + +
+ +The command flags +<anchor id="source.flags.anchor"> + + + +The +flags field in the +command data structure +is used to specify some “behaviour” of the acquisitions in +a command. +The meaning of the field is as follows: + + + + + +TRIG_RT: ask the 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 data buffer. You must have a +real-time kernel (RTAI or +RTLinux/Free) +and must compile &comedi; with real-time support, or this flag will do +nothing. + + + + + + +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. + + + + + + +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. + + + + + + +TRIG_ROUND_DOWN: round period down. + + + + + + +TRIG_ROUND_UP: round period up. + + + + + + +TRIG_ROUND_UP_NEXT: +this one doesn't do anything, and I don't know what it was intended +to do…? + + + + + + +TRIG_DITHER: enable dithering? Dithering is a software technique to +smooth the influence of discretization “noise”. + + + + + + +TRIG_DEGLITCH: enable deglitching? Another “noise” +smoothing technique. + + + + + + +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. + + + + + + +TRIG_BOGUS: do the motions? + + + + + + +TRIG_CONFIG: perform configuration, not triggering. This is a legacy +of the deprecated +comedi_trig_struct +data structure, and has no function at present. + + + + + + +
+ -
+
+ + +
-Digital Input/Output +Slowly-varying inputs -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: + + + -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!). + + -Individual digital lines can be read and written using the -functions +you need to have some noise on the input channel, +otherwise you will be averaging the same number N +times. (Of course, this only holds if the noise is large enough to +cause at least a one-bit discretization.) + - - comedi_dio_read(device,subdevice,channel,unsigned int *bit); - comedi_dio_write(device,subdevice,channel,unsigned int bit); - + + +the more noise you have, the greater your SFDR, but it +takes many more samples to compensate for the increased +noise. + + + -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 very slowly-varying, +i.e., not varying more than your target uncertainty for the entire two +seconds. + + + + + +As you might have guessed, the &comedi; library has functions +to help you in your quest to accurately measure slowly varying +inputs: + + int comedi_sv_init(comedi_sv_t * sv, comedi_t * device, unsigned int subdevice, unsigned int channel); + +This function initializes the +comedi_sv_t data structure, used +to do the averaging acquisition: + +struct comedi_sv_struct{ + comedi_t *dev; + unsigned int subdevice; + unsigned int chan; + /* range policy */ + int range; + int aref; + + /* number of measurements to average (for analog inputs) */ + int n; + + lsampl_t maxdata; +}; + +The actual acquisition is done with: - comedi_dio_config(device,subdevice,channel,unsigned int dir); + int comedi_sv_measure(comedi_sv_t * sv, double * data); +The number of samples over which the +comedi_sv_measure() averages is limited by the +implementation (currently the limit is 100 samples). + -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. +
+ +
+ +Experimental functionality + + -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. - - comedi_dio_bitfield(device,subdevice,unsigned int write_mask,unsigned int *bits); - +
+ +Digital input combining machines + + + +(Status: experimental (i.e., no driver implements +this yet)) + + +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. + + + +For simplification purposes, it is assumed that multiple digital +inputs do not change simultaneously. + + + +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: + + + +00: transition is ignored. + + + +01: accumulator is incremented, or output is set. + + + +10: accumulator is decremented, or output is cleared. + + + +11: reserved. + + + +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. + + + +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. + -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.
-
+ +
-Slowly-varying inputs +Analog filtering configuration -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: +(Status: design (i.e., no driver implements +this yet).) + + + +The insn field of the +instruction data structure +has not been assigned yet. + + +The chanspec field +of the instruction data +structure is ignored. + + + +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. + + + +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. + + + +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. + +
+ +
+ +Analog Output Waveform Generation + + + +(Status: design (i.e., no driver implements +this yet).) + + +The insn field of the +instruction data structure +has not been assigned yet. + + +The chanspec field +of the instruction data +structure is ignored. + + + +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. + + + +This config should allow the user to configure the number of samples +to loop through. It may be necessary to configure the channels used. + + +
+ +
+ +Extended Triggering + + +(Status: alpha.) + + + +The insn field of the +instruction data structure +has not been assigned yet. + + +The chanspec field +of the instruction data +structure is ignored. + + + +This section covers common information for all extended +triggering configuration, and doesn't describe a particular +type of extended trigger. + + + +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 a +command, +specifying +TRIG_OTHER as one of the trigger +sources. + + + +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. + + + +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. + + + +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. + + + +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: + + + + + +COMEDI_EV_START + + + + +COMEDI_EV_SCAN_BEGIN + + + + +COMEDI_EV_CONVERT + + + + +COMEDI_EV_SCAN_END + + + + +COMEDI_EV_STOP + + + + +
+ +
+ +Analog Triggering + + + +(Status: alpha. The ni_mio_common.c driver +implements this feature.) + + +The insn field of the +instruction data structure +has not been assigned yet. + + +The chanspec field +of the instruction data +structure is ignored. + + + +The data field +of the instruction data +structure is used as follows: -you are ultimately limited by "spurious free dynamic range" +data[1]: trigger and combining machine configuration. -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. -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. -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. -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. -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. + + + +The interpretation of the chanspec and voltage levels is device +dependent, but should correspond to similar values of the analog +input subdevice, if possible. + + + +Notes: Reading range information is not addressed. This makes it +difficult to convert comparator voltages to data values. + + + +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.
-
+
-Commands and streaming input/output +Bitfield Pattern Matching Extended Trigger + + +(Status: design. No driver implements this feature yet.) + + -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()). - - - -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 *_src members of the -comedi_cmd structure. The source types are: - - - - - - - -TRIG_NONE - - -don't ever cause an event - - - - -TRIG_NOW - - -cause event to occur immediately - - - - -TRIG_FOLLOW - - -see notes below - - - - -TRIG_TIME - - -cause event to occur at a particular time - - - - -TRIG_TIMER - - -cause event to occur repeatedly at a specific rate - - - - -TRIG_COUNT - - -cause event when count reaches specific value - - - - -TRIG_EXT - - -external signal causes event - - - - -TRIG_INT - - -internal signal causes event - - - - -TRIG_OTHER - - -driver-specific meaning - - - - - - - -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. - - - -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: - - - -TRIG_NONE is typically used only as a stop_src. The argument for TRIG_NONE -is reserved and should be set to 0. - - - -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. - - - -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. - - - -TRIG_TIME is reserved for future use. - - +The insn field of the +instruction data structure +has not been assigned yet. + + +The chanspec field +of the instruction data +structure is ignored. + + -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 data field +of the instruction data +structure is used as follows: + + +data[1]: trigger flags. + + +data[2]: mask. + + +data[3]: pattern. + + -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. -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. -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. +
+ +
+ +Counter configuration + -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. + +(Status: design. No driver implements this feature yet.) + -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 insn field of the +instruction data structure +has not been assigned yet. + + +The chanspec field +of the instruction data +structure is used to specify which counter to use. (I.e., the +counter is a &comedi; channel.) -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. - - - -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. - - - -The final member of the comedi_cmd structure is flags. -The following flags are valid, and can be bitwise-or'd together. - - - - - -TRIG_BOGUS - - - -do the motions?? - - - - - -TRIG_DITHER - - - -enable dithering?? - - - - - -TRIG_DEGLITCH - - - -enable deglitching?? - - - - - -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. - - - - - -TRIG_CONFIG - - - -perform configuration, not triggering. This is a legacy of the -deprecated comedi_trig_struct, and has no function at present. - - - - - -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. - - - - - -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. - - - - - -TRIG_ROUND_NEAREST - - - -round to nearest supported timing period, the default. - - - - - -TRIG_ROUND_DOWN - - - -round period down. - - - - - -TRIG_ROUND_UP - - - -round period up. - - - - - -TRIG_ROUND_UP_NEXT - - - -this one doesn't do anything, and I don't know what it was intended -to do?? XXX - - - - +The data field +of the instruction data +structure is used as follows: + + + +data[1]: trigger configuration. + + +data[2]: primary input chanspec. + + +data[3]: primary combining machine configuration. + + +data[4]: secondary input chanspec. + + +data[5]: secondary combining machine configuration. + + +data[6]: latch configuration. + + + + +Note that this configuration is only useful if the counting has to be +done in software. 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. + + +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. + -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. -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?
-
+
-Anti-aliasing +One source plus auxiliary counter configuration + + +(Status: design. No driver implements this feature yet.) + + + + +The insn field of the +instruction data structure +has not been assigned yet. + + +The chanspec field +of the instruction data +structure is used to … + -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 data field +of the instruction data +structure is used as follows: -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. + + +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. + + +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. + + + +data[3], data[4]: determine the primary source for the counter, +similar to the +_src and the +_arg fields +used in the +command data structure. + + + -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?
- +
diff --git a/doc/reference.sgml b/doc/reference.sgml index dcb9785..653ed9a 100644 --- a/doc/reference.sgml +++ b/doc/reference.sgml @@ -1,6 +1,23 @@ - + -
+
+ +Headerfiles: <filename>comedi.h</filename> and <filename>comedilib.h</filename> + + + +All application programs must +include the header file comedilib.h. (This file +itself includes comedi.h.) They contain the full +interface of &comedi;: defines, function prototypes, data structures. + + +The following Sections give more details. + + +
+ +
Constants and Macros @@ -11,66 +28,70 @@ CR_PACK -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 +chanlist array in the +comedi_cmd data structure, +and the chanspec member of the +comedi_insn structure. - + + + -The channel argument is the channel you wish to use, with the channel -numbering starting at zero. +The chan argument is the channel you wish to +use, with the channel numbering starting at zero. -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 rng 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 aref argument indicates what reference you want the device to use. It -can be any of the following: - - +The aref argument indicates what reference you +want the device to use. It can be any of the following: - AREF_GROUND + AREF_GROUND - is for inputs/outputs referenced to ground + is for inputs/outputs referenced to ground. - AREF_COMMON + AREF_COMMON - 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). - AREF_DIFF + AREF_DIFF - is for differential inputs/outputs + is for differential inputs/outputs. - AREF_OTHER + AREF_OTHER - is for any reference that does not fit into the above categories + is for any reference that does not fit into the above categories. - - Particular drivers may or may not use the AREF flags. If they are not supported, they are silently ignored. @@ -102,26 +123,132 @@ file at version 1.0. Binary compatibility may be broken for version
-
+
Data Types and Structures + +This Section explains the data structures that users of the &comedi; +API are confronted with: + +typedef struct subdevice_struct subdevice_struct: +typedef struct comedi_devinfo_struct comedi_devinfo; +typedef struct comedi_t_struct comedi_t; +typedef struct sampl_t_struct sampl_t; +typedef struct lsampl_t_struct lsampl_t; +typedef struct comedi_sv_t_struct comedi_sv_t; +typedef struct comedi_cmd_struct comedi_cmd; +typedef struct comedi_insn_struct comedi_insn; +typedef struct comedi_range_struct comedi_range; +typedef struct comedi_krange_struct comedi_krange; +typedef struct comedi_insnlist_struct comedi_insnlist; + +The data structures used in the implementation of the &comedi; drivers +are treated elsewhere. + + +
+ +subdevice_struct + + + +The data type subdevice_struct 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. + +typedef struct subdevice_struct subdevice; + +struct subdevice_struct{ + unsigned int type; + unsigned int n_chan; + unsigned int subd_flags; + unsigned int timer_type; + unsigned int len_chanlist; + lsampl_t maxdata; + unsigned int flags; + unsigned int range_type; + + lsampl_t *maxdata_list; + unsigned int *range_type_list; + unsigned int *flags_list; + + comedi_range *rangeinfo; + ccomedi_range **rangeinfo_list; + + unsigned int has_cmd; + unsigned int has_insn_bits; + + int cmd_mask_errno; + comedi_cmd *cmd_mask; + int cmd_timed_errno; + comedi_cmd *cmd_timed; +}; + + + + +
+ + +
+ +comedi_devinfo + + + +The data type comedi_devinfo 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. + +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]; +}; + + + + +
+
comedi_t + +The data type comedi_t is used to represent an +open &comedi; device: typedef struct comedi_t_struct comedi_t; - - -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 + comedi_devinfo devinfo; + subdevice *subdevices; // pointer to subdevice list + // filled in automatically at load time + unsigned int has_insnlist_ioctl; // can process instruction lists + unsigned int has_insn_ioctl; // can process instructions +}; + +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 should not be dereferenced by the application. @@ -138,17 +265,24 @@ typedef unsigned short sampl_t; -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 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. -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 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.
@@ -163,9 +297,12 @@ typedef unsigned int lsampl_t; -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 +lsampl_t +is the data type typically used to represent +data values in libcomedi. On most architectures, +lsampl_t +is defined to be uint32.
@@ -179,18 +316,18 @@ comedi_trig (deprecated) 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 */ + 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]; }; @@ -211,18 +348,18 @@ comedi_sv_t 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; + lsampl_t maxdata; }; @@ -243,29 +380,29 @@ comedi_cmd 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; + sampl_t *data; + unsigned int data_len; }; @@ -284,18 +421,18 @@ comedi_insn 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; + lsampl_t*data; + unsigned int subdev; + unsigned int chanspec; + unsigned int unused[3]; }; 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 @@ -304,12 +441,13 @@ describe acquisition that involves timers or external events. 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: + INSN_READ @@ -320,6 +458,7 @@ read values from an input channel + INSN_WRITE @@ -330,6 +469,7 @@ write values to an output channel + INSN_BITS @@ -340,6 +480,7 @@ read/write values on multiple digital I/O channels + INSN_CONFIG @@ -350,6 +491,7 @@ configure a subdevice + INSN_GTOD @@ -360,6 +502,7 @@ read a timestamp, identical to gettimeofday() + INSN_WAIT @@ -394,9 +537,9 @@ comedi_range 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; @@ -420,9 +563,9 @@ comedi_krange 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; }; @@ -451,8 +594,8 @@ comedi_insnlist 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; }; @@ -464,570 +607,3 @@ a list of instructions.
- -
- -Interface reference - - - -This chapter is meant to be a reference for some of the advanced -features of Comedi. - - -
- -Digital input combining machines - - - -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. - - - -For simplification purposes, it is assumed that multiple digital -inputs do not change simultaneously. - - - -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: - - - - - -00 - - - -transition is ignored - - - - - -01 - - - -accumulator is incremented, or output is set - - - - - -10 - - - -accumulator is decremented, or output is cleared - - - - - -11 - - - -reserved - - - - - - -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. - - - -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. - - - -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. - -
- - -
- -INSN_CONFIG - - - -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. - - - -If a specified ID is not supported, the driver must return -EINVAL. - - - -
- -Digital I/O configuration - - - - - - Status: Implemented - - - ID: COMEDI_INPUT, COMEDI_OUTPUT, COMEDI_OPENDRAIN - - - Length: 1 - - - Chanspec: used to specify channel - - - - -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. - - - -There should also be a method to read the configuration. - - - -Errors: Should return -EINVAL if the ID is not supported. - - -
- -
- -Analog conversion configuration - - - - -Status: design - - -ID: not assigned - - -Length: - - -Chanspec: used to specify channel - - - - -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. - - - -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. - - - -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. - -
- -
- -Analog Output Waveform Generation - - - - -Status: design - - -ID: not assigned - - -Length: - - -Chanspec: ignored - - - - -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. - - - -This config should allow the user to configure the number of samples -to loop through. It may be necessary to configure the channels used. - - -
- -
- -Extended Triggering - - - - -Status: alpha - - -ID: not assigned - - -Chanspec: ignored - - - - -This section covers common information for all extended -triggering configuration, and doesn't describe a particular -type of extended trigger. - - - -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. - - - -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. - - - -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. - - - -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. - - - -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: - - - - - -COMEDI_EV_START - - - - -COMEDI_EV_SCAN_BEGIN - - - - -COMEDI_EV_CONVERT - - - - -COMEDI_EV_SCAN_END - - - - -COMEDI_EV_STOP - - - - -
- -
- -Analog Triggering - - - - -Status: alpha - - -ID: not assigned - - -Implementation: ni_mio_common - - -Chanspec: ignored - - - - - -data 1 - trigger and combining machine configuration - - -data 2 - analog triggering signal chanspec - - -data 3 - primary analog level - - -data 4 - secondary analog level - - - - -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. - - - -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. - - - -The interpretation of the chanspec and voltage levels is device -dependent, but should correspond to similar values of the analog -input subdevice, if possible. - - - -Notes: Reading range information is not addressed. This makes it -difficult to convert comparator voltages to data values. - - - -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. - - -
- -
- -Bitfield Pattern Matching Extended Trigger - - - - -Status: design - - -ID: not assigned - - -Chanspec: ignored - - - - - -data 1 - trigger flags - - -data 2 - mask - - -data 3 - pattern - - - - -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. - - - -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. - - - -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. - - -
- -
- -Counter configuration - - - - -Status: design - - -ID: not assigned - - -Chanspec: used to specify counter - - - - - -data 1 - trigger configuration - - -data 2 - primary input chanspec - - -data 3 - primary combining machine configuration - - -data 4 - secondary input chanspec - - -data 5 - secondary combining machine configuration - - -data 6 - latch configuration - - - - -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. - - - -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. - - - -Notes: How to access multiple pieces of data acquired at each event? - - -
- -
- -One source plus auxiliary counter configuration - - - - -Status: design - - -ID: not assigned - - -Chanspec: ? - - - - -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. - - - -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. - - - -data[3] and data[4] determine the primary source for the counter, -similar to _src and _arg used in commands. - - - - -Notes: How to specify which events cause a latch and push, and what -should get latched. - -
- -
-
- - diff --git a/doc/tutorial.sgml b/doc/tutorial.sgml index be6f4fb..65a8684 100644 --- a/doc/tutorial.sgml +++ b/doc/tutorial.sgml @@ -1,66 +1,87 @@ - + -
+
-Writing programs that use comedi and comedilib +Writing &comedi; programs + +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. + gives more details about +the various acquisition functions with which the application +programmer can perform data acquisition in &comedi;. + + +Also don't forget to take a good look at the +demo +directory of the Comedilib source code. It contains lots of examples +for the basic functionalities of &comedi;. + -
+
-Your first comedi program +Your first &comedi; program -This example requires a card that has analog or -digital input. Right to the source: - - +This example requires a card that has analog or digital input. This +progam opens the device, gets the data, and prints it out: -#include <stdio.h> /* for printf() */ -#include <comedilib.h> +#include ]]> /* 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 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; + 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; } - +The + + comedi_open() + can only be successful if the +comedi0 device file is configured to point to a +valid &comedi; driver. explains +how this driver is linked to the “device file”. -Should be understandable: open the device, get the data, -print it out. This is basically the guts of demo/inp.c, -without error checking or fancy options. -Compile it using +The code above is basically the guts of +demo/inp.c, without error checking or fancy +options. Compile the program using cc tut1.c -lcomedi -o tut1 + +(Replace cc by your favourite C compiler command.) + -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 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.
-
+
Converting samples to voltages @@ -68,66 +89,56 @@ Converting samples to voltages If you selected an analog input subdevice, you probably noticed that the output of tut1 is a number between -0 and 4095, or 0 and 65535, depending on the number of bits -in the A/D converter. Comedi samples are always -unsigned, -with 0 representing the lowest voltage of the ADC, and 4095 -the highest. Comedi compensates for -anything else the manual for your device says. However, -you probably prefer to have this number translated to -a voltage. Naturally, as a good programmer, your first -question is: "How do I do this in a device-independent -manner?" +0 and 4095, or +0 and 65535, depending on the +number of bits in the A/D converter. &comedi; samples are +always unsigned, +with 0 representing the lowest voltage of the ADC, +and 4095 +the highest. &comedi; compensates for anything else the manual for +your device says. However, you probably prefer to have this number +translated to a voltage. Naturally, as a good programmer, your first +question is: “How do I do this in a device-independent +manner?” 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. -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.) -The largest sample value can be found using the function: - - +The largest sample value can be found using the function -comedi_get_maxdata() + lsampl_t comedi_get_maxdata(comedi_t * device, unsigned int subdevice, unsigned int channel)) - - The number of available ranges can be found using the function: - - -comedi_get_n_ranges() + int comedi_get_n_ranges(comedi_t * device, unsigned int subdevice, unsigned int channel); + For each value of the range parameter for a particular -subdevice/channel, you can get range information using the -function: - - +subdevice/channel, you can get range information using: - ptr=comedi_get_range(comedi_file,subdevice,channel, - range); + comedi_range * comedi_get_range(comedi_t * device, + unsigned int subdevice, unsigned int channel, unsigned int range); - - -which returns a pointer to a comedi_range structure. -The comedi_range structure looks like - - +which returns a pointer to a +comedi_range +structure, which has the following contents: typedef struct{ double min; @@ -135,25 +146,31 @@ typedef struct{ unsigned int unit; }comedi_range; - - -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 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, +or are dimensionless (e.g., for digital I/O). -"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 comedi_to_phys() +comedi_to_phys(), which +converts data values to physical units. Call it using something like -volts=comedi_to_phys(it,data,range,maxdata); +volts=comedi_to_phys(it,data,range,maxdata); @@ -161,79 +178,76 @@ and the opposite -data=comedi_from_phys(it,volts,range,maxdata); +data=comedi_from_phys(it,volts,range,maxdata);
-
+
-Another section +Using the file interface 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 C library provides +fopen(), etc. as a high-level (and portable) +alternative to the direct UNIX system calls +open(), etc. Similarily to +fopen(), we have +comedi_open(): -file=comedi_open("/dev/comedi0"); +file=comedi_open("/dev/comedi0"); -where file is of type (comedi_t *). This function -calls open(), like we did explicitly in a previous -section, but also fills the comedi_t structure with -lots of goodies -- information that we will need to use -soon. +where file is of type +(comedi_t *). +This function calls open(), as done explicitly in +a previous section, but also fills the +comedi_t +structure with lots of goodies; this information will be useful soon. -Specifically, we needed to know maxdata for a specific -subdevice/channel. How about: - +Specifically, you need to know +maxdata for a specific +subdevice/channel. How about: -maxdata=comedi_get_maxdata(file,subdevice,channel); +maxdata=comedi_get_maxdata(file,subdevice,channel); - -Wow. How easy. And the range type? - +Wow! How easy. And the range information? -range_type=comedi_get_rangetype(file,subdevice,channel); +comedi_range * comedi_get_range(comedi_tcomedi_t *it,unsigned int subdevice,unsigned int chan,unsigned int range); - -Cool. Other information you need to know about a channel -can be gotten in a similar way.
-
+
-Your second comedi program +Your second &comedi; program: simple acquisition -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. #include <stdio.h> /* for printf() */ -#include <comedi.h> /* also included by comedilib.h */ -#include <comedilib.h> /* 'cuz we're using comedilib */ +#include comedilib.h]]> int subdev = 0; /* change this to your input subdevice */ int chan = 0; /* change this to your channel */ @@ -242,28 +256,396 @@ 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; + 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); - cf=comedi_open("/dev/comedi0"); + rangetype=comedi_get_rangetype(cf,subdev,chan); - maxdata=comedi_get_maxdata(cf,subdev,chan); + comedi_data_read(cf->fd,subdev,chan,range,aref,&data); - rangetype=comedi_get_rangetype(cf,subdev,chan); + volts=comedi_to_phys(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; +} + - printf("%d %g\n",data,volts); +
- return 0; +
+ +Your third &comedi; program: instructions + + +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 instruction. + + +#include <]]>comedilib.h +#include +#include +#include +#include +#include +#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 + +comedi_t *device; + +int main(int argc, char *argv[]) +{ + int ret,i; + comedi_insn insn[3]; + comedi_insnlist il; + struct timeval t1,t2; + lsampl_t data[MAX_SAMPLES]; + + parse_options(argc,argv); + + + device=comedi_open(filename); + if(!device){ + comedi_perror(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=INSN_GTOD; + insn[0].n=2; + insn[0].data=(void *)&t1; + + /* Instruction 1: do 10 analog input reads */ + insn[1].insn=INSN_READ; + insn[1].n=n_scan; + insn[1].data=data; + insn[1].subdev=subdevice; + insn[1].chanspec=CR_PACK(channel,range,aref); + + /* Instruction 2: perform a gettimeofday() */ + insn[2].insn=INSN_GTOD; + insn[2].n=2; + insn[2].data=(void *)&t2; + + ret=comedi_do_insnlist(device,&il); + if(ret0){ + comedi_perror(filename); + exit(0); + } + + printf("initial time: %ld.%06ld\n",t1.tv_sec,t1.tv_usec); + for(i=0;in_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; } +
+ +
+ +Your fourth &comedi; program: commands + + + +This example programs an analog output subdevice with &comedi;'s most +powerful acquisition function, the asynchronous +command, to generate a waveform. + + + +The waveform in this example is a sine wave, but this can be easily +changed to make a generic function generator. + + + +The function generation algorithm is the same as what is typically +used in digital function generators. A 32-bit accumulator is +incremented by a phase factor, which is the amount (in radians) that +the generator advances each time step. The accumulator is then +shifted right by 20 bits, to get a 12 bit offset into a lookup table. +The value in the lookup table at that offset is then put into a buffer +for output to the DAC. + + + +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 +write(). Since there may be a delay between the +comedi_command() +and a subsequent write(), you +should fill the buffer using write() before you call +comedi_command(), +as is done here. + + +#include <]]>comedilib.h +#include +#include +#include +#include +#include +#include +#include +#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 dds_output(sampl_t *buf,int n); +void dds_init(void); + +/* This define determines which waveform to use. */ +#define dds_init_function dds_init_sine + +void dds_init_sine(void); +void dds_init_pseudocycloid(void); +void dds_init_sawtooth(void); + +int comedi_internal_trigger(comedi_t *dev, unsigned int subd, unsigned int trignum) +{ + comedi_insn insn; + lsampl_t data[1]; + + memset(, 0, sizeof(comedi_insn)); + insn.insn = INSN_INTTRIG; + insn.subdev = subd; + insn.data = data; + insn.n = 1; + + data[0] = trignum; + + return comedi_do_insn(dev, ); +} + + +int main(int argc, char *argv[]) +{ + comedi_cmd cmd; + int err; + int n,m; + int total=0; + comedi_t *dev; + unsigned int chanlist[16]; + unsigned int maxdata; + comedi_range *rng; + int ret; + lsampl_t insn_data = 0; + + parse_options(argc,argv); + + /* Force n_chan to be 1 */ + n_chan = 2; + + if(value){ waveform_frequency = value; } + + dev = comedi_open(filename); + if(dev == NULL){ + fprintf(stderr, "error opening %s\n", filename); + return -1; + } + subdevice = comedi_find_subdevice_by_type(dev,COMEDI_SUBD_AO,0); + + maxdata = comedi_get_maxdata(dev,subdevice,0); + rng = comedi_get_range(dev,subdevice,0,0); + offset = (double)comedi_from_phys(0.0,rng,maxdata); + amplitude = (double)comedi_from_phys(1.0,rng,maxdata) - offset; + + memset(,0,sizeof(cmd)); + /* fill in the command data structure: */ + cmd.subdev = subdevice; + cmd.flags = 0; + cmd.start_src = TRIG_INT; + cmd.start_arg = 0; + cmd.scan_begin_src = TRIG_TIMER; + cmd.scan_begin_arg = 1e9/freq; + cmd.convert_src = TRIG_NOW; + cmd.convert_arg = 0; + cmd.scan_end_src = TRIG_COUNT; + cmd.scan_end_arg = n_chan; + cmd.stop_src = TRIG_NONE; + cmd.stop_arg = 0; + + cmd.chanlist = chanlist; + cmd.chanlist_len = n_chan; + + chanlist[0] = CR_PACK(channel,range,aref); + chanlist[1] = CR_PACK(channel+1,range,aref); + + dds_init(); + + dds_output(data,BUF_LEN); + dds_output(data,BUF_LEN); + + dump_cmd(stdout,); + + if ((err = comedi_command(dev, )) < 0) { + comedi_perror("comedi_command"); + exit(1); + } + + m=write(comedi_fileno(dev),data,BUF_LEN*sizeof(sampl_t)); + if(){ + perror("write"); + exit(1); + } + printf("m=%d\n",m); + + ret = comedi_internal_trigger(dev, subdevice, 0); + + perror("comedi_internal_trigger\n"); + exit(1); + } + + while(1){ + dds_output(data,BUF_LEN); + n=BUF_LEN*sizeof(sampl_t); + while(n>0){ + m=write(comedi_fileno(dev),(void *)data+(BUF_LEN*sizeof(sampl_t)-n),n); + + perror("write"); + exit(0); + } + printf("m=%d\n",m); + n-=m; + } + total+=BUF_LEN; + } + + return 0; +} + +#define WAVEFORM_SHIFT 16 + +#define WAVEFORM_MASK (WAVEFORM_LEN-1) + +sampl_t waveform[WAVEFORM_LEN]; + +unsigned int acc; +unsigned int adder; + +void dds_init(void) +{ + + + dds_init_function(); +} + +void dds_output(sampl_t *buf,int n) +{ + int i; + sampl_t *p=buf; + + >16)&WAVEFORM_MASK]; + ]]> + p++; + acc+=adder; + } +} + + +void dds_init_sine(void) +{ + int i; + + + } +} + +/* Yes, I know this is not the proper equation for a cycloid. Fix it. */ +void dds_init_pseudocycloid(void) +{ + int i; + double t; + + +} + +void dds_init_sawtooth(void) +{ + int i; + + + } +} + + + +
+