Converting docs to DocBook 3.1
This commit is contained in:
parent
575761aed2
commit
f95a0c1eac
12 changed files with 5324 additions and 0 deletions
18
doc/docbook/Makefile
Normal file
18
doc/docbook/Makefile
Normal file
|
@ -0,0 +1,18 @@
|
|||
|
||||
all: drivers.sgml funcref.sgml
|
||||
-mkdir html
|
||||
-mkdir man
|
||||
-docbook2html -o html comedilib.sgml
|
||||
-docbook2man -o man comedilib.sgml
|
||||
|
||||
funcref.sgml: funcref mkref
|
||||
./mkref funcref >funcref.sgml
|
||||
|
||||
drivers.sgml: drivers.txt mkdr
|
||||
./mkdr drivers.txt >drivers.sgml
|
||||
|
||||
clean:
|
||||
-rm -rf html
|
||||
-rm -rf man
|
||||
-rm -f drivers.sgml funcref.sgml
|
||||
|
672
doc/docbook/advanced.sgml
Normal file
672
doc/docbook/advanced.sgml
Normal file
|
@ -0,0 +1,672 @@
|
|||
<p>
|
||||
<sect1>Configuring comedi for your hardware
|
||||
<p>
|
||||
|
||||
|
||||
I assume that your hardware device is in your computer, and that
|
||||
you know the relevant details about it, i.e., what kind of card
|
||||
it is, the I/O base, the IRQ, jumper settings related to input
|
||||
ranges, etc.
|
||||
|
||||
To tell the comedi kernel module that you have a particular device, and
|
||||
some information about it, you will be running the <tt>comedi_config</tt>
|
||||
command. Perhaps you should read the man page now.
|
||||
|
||||
In this tutorial, I will go through the process of configuring comedi
|
||||
for two devices, a National Instruments AT-MIO-16E-10
|
||||
and a Data Translation DT2821-F-8DI.
|
||||
|
||||
The NI board is plug-and-play, and the man page tells me that I need
|
||||
to configure the PnP part of the board with isapnptools. The isapnptools
|
||||
package is a little cryptic, but the concepts are simple. Once I
|
||||
learned how to use it, I settled on a /etc/isapnp.conf file that
|
||||
contained the lines:
|
||||
|
||||
|
||||
<tscreen><verb>
|
||||
# ANSI string -->National Instruments, AT-MIO-16E-10<--
|
||||
(CONFIGURE NIC2400/10725401 (LD 0
|
||||
(IO 0 (BASE 0x0260))
|
||||
(INT 0 (IRQ 3 (MODE +E)))
|
||||
# (DMA 0 (CHANNEL 5))
|
||||
# (DMA 1 (CHANNEL 6))
|
||||
(ACT Y)
|
||||
))
|
||||
</verb></tscreen>
|
||||
|
||||
|
||||
It also contains a few lines about overall configuration and about my
|
||||
sound card. I found out after a bit of trial-and-error that the NI
|
||||
board does not always work with interrupts other than IRQ 3. YMMV.
|
||||
Currently, the driver doesn't use DMA, but it may in the future, so
|
||||
I commented out the DMA lines. It is a curious fact that the device
|
||||
ignores the IRQ and DMA information given here, however, I keep the
|
||||
information here to remind myself that the numbers aren't arbitrary.
|
||||
|
||||
When I run comedi_config (as root, of course), I provide the same
|
||||
information. Since I want to have the board configured every time
|
||||
I boot, I put the line
|
||||
|
||||
<tscreen><verb>
|
||||
/usr/sbin/comedi_config /dev/comedi0 atmio-E 0x260,3
|
||||
</verb></tscreen>
|
||||
|
||||
into <tt>/etc/rc.d/rc.local</tt>. You can, of course, run this command at
|
||||
a command prompt. The man page tells me that the option list
|
||||
is supposed to be "(I/O base),(IRQ)", so I used the same numbers
|
||||
as I put in /etc/isapnp.conf, i.e., 0x260,3.
|
||||
|
||||
For the Data Translation board, I need to have a list of the
|
||||
jumper settings. Fortunately, I wrote them all down in the
|
||||
manual -- I hope they are still correct. However, I had to
|
||||
open the case to figure out which board in the series I had.
|
||||
It is a DT2821-f-8di. The man page of comedi_config tells
|
||||
me that I need to know the I/O base, IRQ, DMA 1, DMA 2. However,
|
||||
since I wrote the driver, I know that it also recognizes the
|
||||
differential/single-ended and unipolar/bipolar jumpers. As always,
|
||||
the source is the final authority, and looking in module/dt282x.c
|
||||
tells me that the options list is interpreted as:
|
||||
|
||||
<itemize>
|
||||
<item>I/O base
|
||||
<item>IRQ
|
||||
<item>1=differential, 0=single ended
|
||||
<item>ai 0=unipolar, 1=bipolar
|
||||
<item>ao0 0=unipolar, 1=bipolar
|
||||
<item>ao1 0=unipolar, 1=bipolar
|
||||
<item>dma1
|
||||
<item>dma2
|
||||
</itemize>
|
||||
|
||||
(ai=analog input, ao=analog output.) From this, I decide that
|
||||
the appropriate options list is
|
||||
|
||||
<tscreen><verb>
|
||||
0x200,4,,1,1,1
|
||||
</verb></tscreen>
|
||||
|
||||
I left the differential/single-ended number blank, since the
|
||||
driver already knowns (from the board name), that it is
|
||||
differential. I also left the DMA numbers blank, since I
|
||||
don't want the driver to use DMA. (Don't want it to interfere
|
||||
with my sound card -- life is full of difficult choices.)
|
||||
Keep in mind that things commented in the source, but not in
|
||||
the documentation are about as likely to change as the weather,
|
||||
so I put good comments next to the following line when I put
|
||||
it in rc.local.
|
||||
|
||||
<tscreen><verb>
|
||||
/usr/sbin/comedi_config /dev/comedi1 dt2821-f-8di 0x200,4,,1,1,1
|
||||
</verb></tscreen>
|
||||
|
||||
So now I think that I have my boards configured correctly.
|
||||
Since data acquisition boards are not typically well-engineered,
|
||||
comedi sometimes can't figure out if the board is actually there.
|
||||
If it can't, it assumes you are right. Both of these boards
|
||||
are well-made, so comedi will give me an error message if it
|
||||
can't find them. The comedi kernel module, since it is a part
|
||||
of the kernel, prints messages to the kernel logs, which you
|
||||
can access through the command 'dmesg' or /var/log/messages.
|
||||
Here is a configuration failure (from dmesg):
|
||||
|
||||
<tscreen><verb>
|
||||
comedi0: ni_E: 0x0200 can't find board
|
||||
</verb></tscreen>
|
||||
|
||||
When it does work, I get:
|
||||
|
||||
<tscreen><verb>
|
||||
comedi0: ni_E: 0x0260 at-mio-16e-10 ( irq = 3 )
|
||||
</verb></tscreen>
|
||||
|
||||
Note that it also correctly identified my board.
|
||||
|
||||
|
||||
<p>
|
||||
<sect1>Getting information from comedi
|
||||
<p>
|
||||
|
||||
|
||||
So now that we have comedi talking to the hardware, we want to
|
||||
talk to comedi. Here's some pretty low-level information --
|
||||
it's sometimes useful for debugging:
|
||||
|
||||
<p>
|
||||
|
||||
<tscreen><verb>
|
||||
cat /proc/comedi
|
||||
</verb></tscreen>
|
||||
|
||||
Right now, on my computer, this command gives:
|
||||
|
||||
<tscreen><verb>
|
||||
comedi version 0.6.4
|
||||
format string
|
||||
0: atmio-E at-mio-16e-10 7
|
||||
1: dt282x dt2821-f-8di 4
|
||||
</verb></tscreen>
|
||||
|
||||
This is a feature that is not well-developed yet. Basically, it
|
||||
currently tells you driver name, device name, and number of
|
||||
subdevices.
|
||||
|
||||
In the <tt>demo/</tt> directory, there is a command called
|
||||
<tt>info</tt>, which provides information about each subdevice on the
|
||||
board. The output of it is rather long, since I have 7
|
||||
subdevices (4 or fewer is common for other boards.)
|
||||
Here's part of the output of the NI board (which
|
||||
is on <tt>/dev/comedi0</tt>.) ('demo/info /dev/comedi0')
|
||||
|
||||
<tscreen><verb>
|
||||
overall info:
|
||||
version code: 0x000604
|
||||
driver name: atmio-E
|
||||
board name: at-mio-16e-10
|
||||
number of subdevices: 7
|
||||
subdevice 0:
|
||||
type: 1 (unknown)
|
||||
number of channels: 16
|
||||
max data value: 4095
|
||||
</verb>
|
||||
...
|
||||
</tscreen>
|
||||
|
||||
The overall info gives information about the device -- basically
|
||||
the same information as /proc/comedi.
|
||||
|
||||
This board has 7 subdevices. Devices are separated into
|
||||
subdevices that each have a distinct purpose -- e.g., analog
|
||||
input, analog output, digital input/output. This board also
|
||||
has an EEPROM and calibration DACs that are also subdevices.
|
||||
|
||||
Subdevice 0 is the analog input subdevice. You would have
|
||||
known this from the 'type: 1 (unknown)' line, if I've updated
|
||||
demo/info recently, because it would say 'type: 1 (analog input)'
|
||||
instead. The other lines should be self-explanitory. Comedi
|
||||
has more information about the device, but demo/info doesn't
|
||||
currently display this.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<sect>Writing programs that use comedi and comedilib
|
||||
<p>
|
||||
|
||||
<sect1>Your first comedi program
|
||||
<p>
|
||||
|
||||
This example requires a card that has analog or
|
||||
digital input. Right to the source:
|
||||
|
||||
<tscreen><verb>
|
||||
#include <stdio.h> /* for printf() */
|
||||
#include <comedilib.h>
|
||||
|
||||
int subdev = 0; /* change this to your input subdevice */
|
||||
int chan = 0; /* change this to your channel */
|
||||
int range = 0; /* more on this later */
|
||||
int aref = AREF_GROUND; /* more on this later */
|
||||
|
||||
int main(int argc,char *argv[])
|
||||
{
|
||||
comedi_t *it;
|
||||
lsampl_t data;
|
||||
|
||||
it=comedi_open("/dev/comedi0");
|
||||
|
||||
comedi_data_read(it,subdev,chan,range,aref,&data);
|
||||
|
||||
printf("%d\n",data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
</verb></tscreen>
|
||||
|
||||
|
||||
Should be understandable: open the device, get the data,
|
||||
print it out. This is basically the guts of <tt>demo/inp.c</tt>,
|
||||
without error checking or fancy options.
|
||||
Compile it using
|
||||
|
||||
<tscreen><verb>
|
||||
cc tut1.c -lcomedi -o tut1
|
||||
</verb></tscreen>
|
||||
|
||||
A few notes: The range variable tells comedi which gain
|
||||
to use when measuring an analog voltage. Since we don't
|
||||
know (yet) which numbers are valid, or what each means,
|
||||
we'll use 0, because it won't cause errors. Likewise with
|
||||
aref, which determines the analog reference used.
|
||||
|
||||
|
||||
<p>
|
||||
<sect1>Converting samples to voltages
|
||||
<p>
|
||||
|
||||
If you selected an analog input subdevice, you probably noticed
|
||||
that the output of <tt>tut1</tt> is a number between
|
||||
0 and 4095, or 0 and 65535, depending on the number of bits
|
||||
in the A/D converter. Comedi samples are <bf>always</bf> unsigned,
|
||||
with 0 representing the lowest voltage of the ADC, and 4095
|
||||
the highest. Comedi compensates for
|
||||
anything else the manual for your device says. However,
|
||||
you probably prefer to have this number translated to
|
||||
a voltage. Naturally, as a good programmer, your first
|
||||
question is: "How do I do this in a device-independent
|
||||
manner?"
|
||||
|
||||
Most devices give you a choice of gain and unipolar/bipolar
|
||||
input, and Comedi allows you to select which of these to
|
||||
use. This parameter is called the "range parameter", since
|
||||
it specifies the "input range" for analog input (or "output range"
|
||||
for analog output.) The range parameter represents both the gain
|
||||
and the unipolar/bipolar aspects.
|
||||
|
||||
Comedi keeps the number of available ranges and the largest
|
||||
sample value for each subdevice/channel combination. (Some
|
||||
devices allow different input/output ranges for different
|
||||
channels in a subdevice.)
|
||||
|
||||
The largest sample value can be found using the function:
|
||||
|
||||
comedi_get_maxdata()
|
||||
|
||||
The number of available ranges can be found using the function:
|
||||
|
||||
comedi_get_n_ranges()
|
||||
|
||||
For each value of the range parameter for a particular
|
||||
subdevice/channel, you can get range information using the
|
||||
function:
|
||||
|
||||
ptr=comedi_get_range(comedi_file,subdevice,channel,
|
||||
range)
|
||||
|
||||
which returns a pointer to a comedi_range structure.
|
||||
The comedi_range structure looks like
|
||||
|
||||
<p>
|
||||
<tscreen><verb>
|
||||
typedef struct{
|
||||
double min;
|
||||
double max;
|
||||
unsigned int unit;
|
||||
}comedi_range;
|
||||
</verb></tscreen>
|
||||
|
||||
The structure element 'min' represents
|
||||
the voltage corresponding to comedi_data_read() returning 0,
|
||||
and 'max' represents comedi_data_read() returning 'maxdata',
|
||||
(i.e., 4095 for 12 bit A/C converters, 65535 for 16 bit,
|
||||
or, 1 for digital input -- more on this in a bit.) The
|
||||
'unit' entry tells you if min and
|
||||
max refer to voltage, current, etc.
|
||||
|
||||
"Could it get easier?", you say. Well, yes. Use
|
||||
the function comedi_to_phys(), which converts data
|
||||
values to physical units. Call it using something like
|
||||
|
||||
<tscreen><verb>
|
||||
volts=comedi_to_phys(it,data,range,maxdata);
|
||||
</verb></tscreen>
|
||||
|
||||
and the opposite
|
||||
|
||||
<tscreen><verb>
|
||||
data=comedi_from_phys(it,volts,range,maxdata);
|
||||
</verb></tscreen>
|
||||
|
||||
|
||||
<p>
|
||||
<sect1>Another section
|
||||
<p>
|
||||
|
||||
|
||||
In addition to providing low level routines for data
|
||||
access, the comedi library provides higher-level access,
|
||||
much like the standard C library provides fopen(), etc.
|
||||
as a high-level (and portable) alternative to the direct
|
||||
UNIX system calls open(), etc. Similarily to fopen(),
|
||||
we have comedi_open():
|
||||
|
||||
<p>
|
||||
<tscreen><verb>
|
||||
file=comedi_open("/dev/comedi0");
|
||||
</verb></tscreen>
|
||||
|
||||
where file is of type <tt>(comedi_t *)</tt>. This function
|
||||
calls <tt>open()</tt>, like we did explicitly in a previous
|
||||
section, but also fills the <tt>comedi_t</tt> structure with
|
||||
lots of goodies -- information that we will need to use
|
||||
soon.
|
||||
|
||||
Specifically, we needed to know maxdata for a specific
|
||||
subdevice/channel. How about:
|
||||
|
||||
<tscreen><verb>
|
||||
maxdata=comedi_get_maxdata(file,subdevice,channel);
|
||||
</verb></tscreen>
|
||||
|
||||
Wow. How easy. And the range type?
|
||||
|
||||
<tscreen><verb>
|
||||
range_type=comedi_get_rangetype(file,subdevice,channel);
|
||||
</verb></tscreen>
|
||||
|
||||
Cool. Other information you need to know about a channel
|
||||
can be gotten in a similar way.
|
||||
|
||||
|
||||
|
||||
<sect1>Your second comedi program
|
||||
<p>
|
||||
|
||||
|
||||
Actually, this is the first comedi program again, just
|
||||
that we've added what we've learned.
|
||||
|
||||
|
||||
<tscreen><verb>
|
||||
#include <stdio.h> /* for printf() */
|
||||
#include <comedi.h> /* also included by comedilib.h */
|
||||
#include <comedilib.h> /* 'cuz we're using comedilib */
|
||||
|
||||
int subdev = 0; /* change this to your input subdevice */
|
||||
int chan = 0; /* change this to your channel */
|
||||
int range = 0; /* more on this later */
|
||||
int aref = 0; /* more on this later */
|
||||
|
||||
int main(int argc,char *argv[])
|
||||
{
|
||||
comedi_t *cf;
|
||||
int chan=0;
|
||||
lsampl_t data;
|
||||
int maxdata,rangetype;
|
||||
double volts;
|
||||
|
||||
cf=comedi_open("/dev/comedi0");
|
||||
|
||||
maxdata=comedi_get_maxdata(cf,subdev,chan);
|
||||
|
||||
rangetype=comedi_get_rangetype(cf,subdev,chan);
|
||||
|
||||
comedi_data_read(cf->fd,subdev,chan,range,aref,&data);
|
||||
|
||||
volts=comedi_to_phys(data,rangetype,range,maxdata);
|
||||
|
||||
printf("%d %g\n",data,volts);
|
||||
|
||||
return 0;
|
||||
}
|
||||
</verb></tscreen>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<p>
|
||||
<sect>Application-specific functions
|
||||
<p>
|
||||
|
||||
<sect1>Digital Input/Output
|
||||
<p>
|
||||
|
||||
Many boards supported by comedi have digital input and output
|
||||
channels. Some boards allow the direction of a channel to be
|
||||
specified in software.
|
||||
|
||||
Comedi groups digital channels into subdevice, which is a group
|
||||
of digital channels that have the same characteristics. For
|
||||
example, digital output lines will be grouped into a digital
|
||||
output subdevice, bidirectional digital lines will be grouped
|
||||
into a digital I/O subdevice. Thus, there can be multiple
|
||||
digital subdevices on a particular board.
|
||||
|
||||
Individual digital lines can be read and written using the
|
||||
functions
|
||||
|
||||
<tt/comedi_dio_read(device,subdevice,channel,unsigned int *bit);/
|
||||
<tt/comedi_dio_write(device,subdevice,channel,unsigned int bit);/
|
||||
|
||||
The direction of bidirectional lines can be configured using
|
||||
the function
|
||||
|
||||
<tt/comedi_dio_config(device,subdevice,channel,unsigned int dir);/
|
||||
|
||||
The parameter <tt/dir/ should be either COMEDI_INPUT or COMEDI_OUTPUT.
|
||||
Many digital I/O subdevices group channels into blocks for
|
||||
configuring direction. Changing one channel in a block changes
|
||||
the entire block.
|
||||
|
||||
Multiple channels can be read and written simultaneously using the
|
||||
function
|
||||
|
||||
<tt/comedi_dio_bitfield(device,subdevice,unsigned int write_mask,unsigned int *bits);/
|
||||
|
||||
Each channel is assigned to a bit in the <tt/write_mask/ and <tt/bits/
|
||||
bitfield. If a bit in <tt/write_mask/ is set, the corresponding bit
|
||||
in <tt/*bits/ will be written to the corresponding digital output line.
|
||||
Each digital line is then read and placed into <tt/*bits/. The value
|
||||
of bits in <tt/*bits/ corresponding to digital output lines is
|
||||
undefined and device-specific. Channel 0 is the least significant
|
||||
bit in the bitfield; channel 31 is the most significant bit. Channels
|
||||
higher than 31 cannot be accessed using this method.
|
||||
|
||||
|
||||
<p>
|
||||
<sect1>Slowly-varying inputs
|
||||
<p>
|
||||
|
||||
|
||||
Sometimes, your input channels change slowly enough that
|
||||
you are able to average many sucessive input values to get a
|
||||
more accurate measurement of the actual value. In general,
|
||||
the more samples you average, the better your estimate
|
||||
gets, roughly by a factor of sqrt(number_of_samples).
|
||||
Obviously, there are limitations to this:
|
||||
|
||||
<p>
|
||||
<itemize>
|
||||
<item>
|
||||
you are ultimately limited by "spurious free dynamic range"
|
||||
|
||||
<item>
|
||||
you need to have _some_ noise on the input channel,
|
||||
otherwise you will be averaging the same number N times.
|
||||
|
||||
<item>
|
||||
the more noise you have, the greater your SFDR, but it
|
||||
takes many more samples to compensate for the increased
|
||||
noise
|
||||
|
||||
<item>
|
||||
if you feel the need to average samples for 2 seconds,
|
||||
your signal will need to be _very_ slowly-varying, i.e.,
|
||||
not varying more than your target uncertainty for the
|
||||
entire 2 seconds.
|
||||
|
||||
</itemize>
|
||||
|
||||
As you might have guessed, the comedi library has functions
|
||||
to help you in your quest to accurately measure slowly varying
|
||||
inputs. I use these functions to measure thermocouple voltages
|
||||
-- actually, the library functions came from a section of code
|
||||
that was previously part of the thermocouple reading program.
|
||||
|
||||
The comedi self-calibration utility also uses these functions.
|
||||
On some hardware, it is possible to tell it to measure an
|
||||
internal stable voltage reference, which is typically going
|
||||
to be very slowly varying -- on the kilosecond time scale
|
||||
or more. So it is reasonable to measure millions of samples,
|
||||
to get a very accurate measurement of the A/D converter output
|
||||
value that corresponds to the voltage reference. Sometimes,
|
||||
however, this is overkill, since there is no need to
|
||||
perform a part-per-million calibration to a standard that
|
||||
is only accurate to part-per-thousand.
|
||||
|
||||
|
||||
<p>
|
||||
<sect1>Commands
|
||||
<label id="command_section">
|
||||
<p>
|
||||
|
||||
|
||||
Many data acquisition devices have the capability to directly
|
||||
control acquisition using either an on-board timer or an external
|
||||
triggering input. Comedi commands are used to control this kind
|
||||
of acquisition. The <ref id="comedi_cmd" name="comedi_cmd"> structure is
|
||||
used to control acquisition and query the capabilities of a device
|
||||
(see also <ref id="comedi_command" name="comedi_command()">,
|
||||
<ref id="comedi_command_test" name="comedi_command_test()">, and
|
||||
<ref id="comedi_get_cmd_src_mask" name="comedi_get_cmd_src_mask()">).
|
||||
|
||||
Commands specify a particular data acquisition sequence, which
|
||||
is comprised of a number of scans. Each scan is comprised of
|
||||
a number of conversions, which usually corresponds to a single
|
||||
A/D or D/A conversion. The start and end of the sequence, and
|
||||
the start and end of each scan, and each conversion is called an
|
||||
event.
|
||||
|
||||
Each of these 5 types of events are caused by a triggering
|
||||
source, specified through the <tt/*_src/ members of the
|
||||
<ref id="comedi_cmd" name="comedi_cmd"> structure. The source types are:
|
||||
|
||||
<itemize>
|
||||
<item>TRIG_NONE: don't ever cause an event
|
||||
<item>TRIG_NOW: cause event to occur immediately
|
||||
<item>TRIG_FOLLOW: see notes below
|
||||
<item>TRIG_TIME: cause event to occur at a particular time
|
||||
<item>TRIG_TIMER: cause event to occur repeatedly at a specific rate
|
||||
<item>TRIG_COUNT: cause event when count reaches specific value
|
||||
<item>TRIG_EXT: external signal causes event
|
||||
<item>TRIG_INT: internal signal causes event
|
||||
<item>TRIG_OTHER: driver-specific meaning
|
||||
</itemize>
|
||||
|
||||
Not all triggers are applicable to all events. Supported triggers
|
||||
for specific events depend significantly on your particular
|
||||
device. The <ref id="comedi_get_cmd_src_mask" name="comedi_get_cmd_src_mask()">
|
||||
function is useful for determining what triggers a subdevice supports.
|
||||
|
||||
For every trigger, there is a corresponding
|
||||
argument (the <tt/*_arg/ members of the <ref id="comedi_cmd" name="comedi_cmd">
|
||||
structure) whose meaning depends on the type of trigger. The meanings
|
||||
of the arguments are as follows:
|
||||
|
||||
TRIG_NONE is typically used only as a <tt/stop_src/. The argument for TRIG_NONE
|
||||
is reserved and should be set to 0.
|
||||
|
||||
TRIG_NOW is most often used as a <tt/start_src/. The argument for TRIG_NOW is
|
||||
the number of nanoseconds between when the command is issued and when
|
||||
the event should occur. In the case of using TRIG now as a <tt/start_src/,
|
||||
it indicates a delay between issuing the command and the start of
|
||||
acquisition. Most drivers only support a delay of 0.
|
||||
|
||||
TRIG_FOLLOW is a special type of trigger for events that trigger on
|
||||
the completion of some other, logically connected event. The argument
|
||||
is reserved and should be set to 0. When used
|
||||
as a <tt/scan_begin_src/, it indicates that a trigger should occur as a
|
||||
logical continuation of convert events. This is done in order to
|
||||
properly describe boards that do not have separate timers for
|
||||
convert and scan_begin events. When used as a <tt/start_src/ for analog
|
||||
output subdevices, it indicates that conversion of output samples
|
||||
should begin when samples are written to the buffer.
|
||||
|
||||
TRIG_TIME is reserved for future use.
|
||||
|
||||
TRIG_TIMER is most often used as a <tt/convert_src/, a <tt/scan_begin_src/, or
|
||||
both. It indicates that triggers should occur at a specific rate.
|
||||
The argument specifies the interval between triggers in nanoseconds.
|
||||
|
||||
TRIG_COUNT is used for <tt/scan_end_src/ and <tt/stop_src/. It indicates that
|
||||
a trigger should occur when the specified number of corresponding
|
||||
lower-level triggers (convert and scan_begin, respectively) occur.
|
||||
The argument is the count of lower-level triggers.
|
||||
|
||||
TRIG_EXT can be useful as any of the trigger sources. It indicates
|
||||
that an external digital line should be used to trigger the event.
|
||||
The exact meaning of digital line is device-dependent. Some devices
|
||||
have one dedicated line, others may allow generic digital input
|
||||
lines to be used. The argument indicates the particular external
|
||||
line to use as the trigger.
|
||||
|
||||
TRIG_INT is typically used as a <tt/start_src/. This trigger occurs when
|
||||
the application performs an INSN_INTTRIG instruction. Using TRIG_INT
|
||||
is a method by which the application can accurately record the time of
|
||||
the start of acquisition, since the parsing and setup time of a
|
||||
particular command may be significant. The argument associated with
|
||||
TRIG_INT is reserved and should be set to 0.
|
||||
|
||||
TRIG_OTHER can be useful as any of the trigger sources. The exact
|
||||
meaning of TRIG_OTHER is driver-specific, and implements a feature
|
||||
that otherwise does not fit into the command interface. Configuration
|
||||
of TRIG_OTHER features are done by INSN_CONFIG insns. The argument
|
||||
is reserved and should be set to 0.
|
||||
|
||||
Ths <tt/subdev/ member of the <ref id="comedi_cmd" name="comedi_cmd">
|
||||
structure is the index of the subdevice the command is intended for. The
|
||||
<ref id="comedi_find_subdevice_by_type" name="comedi_find_subdevice_by_type()">
|
||||
function can be useful in discovering the index of your desired subdevice.
|
||||
|
||||
The <tt/chanlist/ member of the <ref id="comedi_cmd" name="comedi_cmd">
|
||||
structure should point to an array whose number of elements is specificed by <tt/chanlist_len/
|
||||
(this will generally be the same as the scan_end_arg).
|
||||
The chanlist specifies the sequence of channels and gains (and analog references)
|
||||
that should be stepped through for each scan. The elements of the chanlist array
|
||||
should be initialized by packing the channel, range and reference information
|
||||
together with the <ref id="CR_PACK" name="CR_PACK()"> macro.
|
||||
|
||||
The <tt/data/ and <tt/data_len/ members can be safely ignored when issueing commands
|
||||
from a user-space program. They only have meaning when a command is sent from a kernel
|
||||
module using the kcomedilib interface, in which case they specify the buffer where
|
||||
the driver should write/read its data to/from.
|
||||
|
||||
The final member of the <ref id="comedi_cmd" name="comedi_cmd"> structure is <tt/flags/.
|
||||
The following flags are valid, and can be bitwise-or'd together.
|
||||
|
||||
<itemize>
|
||||
<item>TRIG_BOGUS: do the motions??
|
||||
<item>TRIG_DITHER: enable dithering??
|
||||
<item>TRIG_DEGLITCH: enable deglitching??
|
||||
<item>TRIG_RT: ask driver to use a hard real-time interrupt handler. This will
|
||||
reduce latency in handling interrupts from your data aquisition hardware. It can
|
||||
be useful if you are sampling at high frequency, or if your hardware has a small onboard
|
||||
fifo. You must have a real-time kernel (RTAI or RTLinux) and must compile
|
||||
comedi with real-time support or this flag will do nothing.
|
||||
<item>TRIG_CONFIG: perform configuration, not triggering. This is a legacy of the
|
||||
deprecated comedi_trig_struct, and has no function at present.
|
||||
<item>TRIG_WAKE_EOS: some drivers will change their behaviour when this flag is set,
|
||||
trying to transfer data at the end of every scan (instead of, for example, passing
|
||||
data in chunks whenever the board's onboard fifo is half full). This flag
|
||||
may degrade a driver's performance at high frequencies.
|
||||
<item>TRIG_WRITE: write to bidirectional devices. Could be useful in principle, if someone
|
||||
wrote a driver that supported commands for a digital i/o device that could do either
|
||||
input or output.
|
||||
</itemize>
|
||||
There are also a few flags that indicate how timing arguments should be rounded
|
||||
if the hardware cannot achieve the exact timing requested.
|
||||
<itemize>
|
||||
<item>TRIG_ROUND_NEAREST: round to nearest supported timing period, the default.
|
||||
<item>TRIG_ROUND_DOWN: round period down.
|
||||
<item>TRIG_ROUND_UP: round period up.
|
||||
<item>TRIG_ROUND_UP_NEXT: this one doesn't do anything, and I don't know what it was intended
|
||||
to do??
|
||||
</itemize>
|
||||
|
||||
<p>
|
||||
|
||||
The typical sequence for executing a command is to first send
|
||||
the command through
|
||||
<ref id="comedi_command_test" name="comedi_command_test()">
|
||||
once or twice. The test will check that the command is valid for the particular
|
||||
device, and often makes some adjustments to the command arguments, which
|
||||
can then be read back by the user to see the actual values used. The
|
||||
command is executed with
|
||||
<ref id="comedi_command" name="comedi_command()">. For input/output commands, data
|
||||
is read from or written to the device file /dev/comedi[0..3] you are using.
|
||||
|
69
doc/docbook/comedilib.sgml
Normal file
69
doc/docbook/comedilib.sgml
Normal file
|
@ -0,0 +1,69 @@
|
|||
<!DOCTYPE article PUBLIC "-//OASIS//DTD DocBook V3.1//EN"
|
||||
"docbook/dtd/3.1/docbook.dtd" [
|
||||
<!ENTITY intro SYSTEM "intro.sgml">
|
||||
<!ENTITY install SYSTEM "install.sgml">
|
||||
<!ENTITY drivers SYSTEM "drivers.sgml">
|
||||
<!ENTITY reference SYSTEM "reference.sgml">
|
||||
<!ENTITY funcref SYSTEM "funcref.sgml">
|
||||
]>
|
||||
|
||||
<article>
|
||||
|
||||
<artheader>
|
||||
<title>
|
||||
Comedi Documentation
|
||||
</title>
|
||||
<author>
|
||||
<firstname>David</firstname>
|
||||
<surname>Schleef</surname>
|
||||
<affiliation>
|
||||
<address>
|
||||
ds@schleef.org
|
||||
</address>
|
||||
</affiliation>
|
||||
</author>
|
||||
<author>
|
||||
<firstname>Frank</firstname>
|
||||
<surname>Hess</surname>
|
||||
<affiliation>
|
||||
<address>
|
||||
fmhess@uiuc.edu
|
||||
</address>
|
||||
</affiliation>
|
||||
</author>
|
||||
</artheader>
|
||||
|
||||
&intro
|
||||
|
||||
&install
|
||||
|
||||
<section>
|
||||
<title>
|
||||
Writing programs that use Comedi
|
||||
</title>
|
||||
<para>
|
||||
Empty.
|
||||
</para>
|
||||
</section>
|
||||
|
||||
&drivers
|
||||
|
||||
<section>
|
||||
<title>
|
||||
Comedi Reference
|
||||
</title>
|
||||
<para>
|
||||
Reference for functions, macros, and constants.
|
||||
</para>
|
||||
|
||||
&reference
|
||||
|
||||
&funcref
|
||||
|
||||
</section>
|
||||
|
||||
|
||||
|
||||
|
||||
</article>
|
||||
|
1075
doc/docbook/drivers.txt
Normal file
1075
doc/docbook/drivers.txt
Normal file
File diff suppressed because it is too large
Load diff
355
doc/docbook/funcref
Normal file
355
doc/docbook/funcref
Normal file
|
@ -0,0 +1,355 @@
|
|||
Function: comedi_close -- close a Comedi device
|
||||
Retval: int
|
||||
Param: comedi * device
|
||||
Description:
|
||||
Close a device previously opened by comedi_open().
|
||||
|
||||
Function: comedi_open -- open a Comedi device
|
||||
Retval: comedi_t
|
||||
Param: const char * filename
|
||||
Description:
|
||||
Open a Comedi device represented by the file filename.
|
||||
|
||||
Function: comedi_loglevel -- change Comedilib logging properties
|
||||
Retval: int
|
||||
Param: int loglevel
|
||||
Description:
|
||||
|
||||
Function: comedi_perror -- print a Comedilib error message
|
||||
Retval: void
|
||||
Param: const char * s
|
||||
Description:
|
||||
|
||||
Function: comedi_strerror -- return string describing Comedilib error code
|
||||
Retval: char *
|
||||
Param: int errnum
|
||||
Description:
|
||||
|
||||
Function: comedi_errno -- number of last Comedilib error
|
||||
Retval: int
|
||||
Param: void
|
||||
Description:
|
||||
|
||||
Function: comedi_fileno -- integer descriptor of Comedilib device
|
||||
Retval: int
|
||||
Param: comedi_t * device
|
||||
Description:
|
||||
|
||||
Function: comedi_get_n_subdevices -- number of subdevices
|
||||
Retval: int
|
||||
Param: comedi_t * device
|
||||
Description:
|
||||
|
||||
Function: comedi_get_version_code -- Comedi version code
|
||||
Retval: int
|
||||
Param: comedi_t * device
|
||||
Description:
|
||||
|
||||
Function: comedi_get_driver_name -- Comedi driver name
|
||||
Retval: char *
|
||||
Param: comedi_t * device
|
||||
Description:
|
||||
|
||||
Function: comedi_get_board_name -- Comedi device name
|
||||
Retval: char *
|
||||
Param: comedi_t * device
|
||||
Description:
|
||||
|
||||
Function: comedi_get_subdevice_type -- type of subdevice
|
||||
Retval: int
|
||||
Param: comedi_t * device
|
||||
Param: unsigned int subdevice
|
||||
Description:
|
||||
|
||||
Function: comedi_find_subdevice_by_type -- search for subdevice type
|
||||
Retval: int
|
||||
Param: comedi_t * device
|
||||
Param: int type
|
||||
Param: unsigned int start_subdevice
|
||||
Description:
|
||||
|
||||
Function: comedi_get_subdevice_flags -- properties of subdevice
|
||||
Retval: int
|
||||
Param: comedi_t * device
|
||||
Param: unsigned int subdevice
|
||||
Description:
|
||||
|
||||
Function: comedi_get_n_channels -- number of subdevice channels
|
||||
Retval: int
|
||||
Param: comedi_t * device
|
||||
Param: unsigned int subdevice
|
||||
Description:
|
||||
|
||||
Function: comedi_range_is_chan_specific -- range information depends on channel
|
||||
Retval: int
|
||||
Param: comedi_t * device
|
||||
Param: unsigned int subdevice
|
||||
Description:
|
||||
|
||||
Function: comedi_maxdata_is_chan_specific -- maximum sample depends on channel
|
||||
Retval: int
|
||||
Param: comedi_t * device
|
||||
Param: unsigned int subdevice
|
||||
Description:
|
||||
|
||||
Function: comedi_get_maxdata -- maximum sample of channel
|
||||
Retval: lsampl_t
|
||||
Param: comedi_t * device
|
||||
Param: unsigned int subdevice
|
||||
Param: unsigned int channel
|
||||
Description:
|
||||
|
||||
Function: comedi_get_n_ranges -- number of ranges of channel
|
||||
Retval: int
|
||||
Param: comedi_t * device
|
||||
Param: unsigned int subdevice
|
||||
Param: unsigned int channel
|
||||
Description:
|
||||
|
||||
Function: comedi_get_range -- range information of channel
|
||||
Retval: comedi_range *
|
||||
Param: comedi_t * device
|
||||
Param: unsigned int subdevice
|
||||
Param: unsigned int channel
|
||||
Param: unsigned int range
|
||||
Description:
|
||||
|
||||
Function: comedi_find_range -- search for range
|
||||
Retval: int
|
||||
Param: comedi_t * device
|
||||
Param: unsigned int subdevice
|
||||
Param: unsigned int channel
|
||||
Param: unsigned int unit
|
||||
Param: double min
|
||||
Param: double max
|
||||
Description:
|
||||
|
||||
Function: comedi_get_buffer_size -- streaming buffer size of subdevice
|
||||
Retval: int
|
||||
Param: comedi_t * device
|
||||
Param: unsigned int subdevice
|
||||
Description:
|
||||
|
||||
Function: comedi_get_max_buffer_size -- maximum streaming buffer size
|
||||
Retval: int
|
||||
Param: comedi_t * device
|
||||
Param: unsigned int subdevice
|
||||
Description:
|
||||
|
||||
Function: comedi_set_buffer_size -- streaming buffer size of subdevice
|
||||
Retval: int
|
||||
Param: comedi_t * device
|
||||
Param: unsigned int subdevice
|
||||
Param: unsigned int size
|
||||
Description:
|
||||
|
||||
Function: comedi_trigger -- perform streaming input/output (deprecated)
|
||||
Retval: int
|
||||
Param: comedi_t * device
|
||||
Param: comedi_trig * trig
|
||||
Description:
|
||||
Status: deprecated
|
||||
|
||||
Function: comedi_do_insnlist -- perform multiple instructions
|
||||
Retval: int
|
||||
Param: comedi_t * device
|
||||
Param: comedi_insnlist * list
|
||||
Description:
|
||||
|
||||
Function: comedi_do_insn -- perform instruction
|
||||
Retval: int
|
||||
Param: comedi_t * device
|
||||
Param: comedi_insn * instruction
|
||||
Description:
|
||||
|
||||
Function: comedi_lock -- subdevice reservation
|
||||
Retval: int
|
||||
Param: comedi_t * device
|
||||
Param: unsigned int subdevice
|
||||
Description:
|
||||
|
||||
Function: comedi_unlock -- subdevice reservation
|
||||
Retval: int
|
||||
Param: comedi_t * device
|
||||
Param: unsigned int subdevice
|
||||
Description:
|
||||
|
||||
Function: comedi_to_phys -- convert sample to physical units
|
||||
Retval: double
|
||||
Param: lsampl_t data
|
||||
Param: comedi_range * range
|
||||
Param: lsampl_t maxdata
|
||||
Description:
|
||||
|
||||
Function: comedi_from_phys -- convert physical units to sample
|
||||
Retval: lsampl_t
|
||||
Param: double data
|
||||
Param: comedi_range * range
|
||||
Param: lsampl_t maxdata
|
||||
Description:
|
||||
|
||||
Function: comedi_data_read -- read single sample from channel
|
||||
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
|
||||
Description:
|
||||
|
||||
Function: comedi_data_write -- write single sample to channel
|
||||
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
|
||||
Description:
|
||||
|
||||
Function: comedi_dio_config -- change input/output properties of channel
|
||||
Retval: int
|
||||
Param: comedi_t * device
|
||||
Param: unsigned int subdevice
|
||||
Param: unsigned int channel
|
||||
Param: unsigned int direction
|
||||
Description:
|
||||
|
||||
Function: comedi_dio_read -- read single bit from digital channel
|
||||
Retval: int
|
||||
Param: comedi_t * device
|
||||
Param: unsigned int subdevice
|
||||
Param: unsigned int channel
|
||||
Param: unsigned int * bit
|
||||
Description:
|
||||
|
||||
Function: comedi_dio_write -- write single bit to digital channel
|
||||
Retval: int
|
||||
Param: comedi_t * device
|
||||
Param: unsigned int subdevice
|
||||
Param: unsigned int channel
|
||||
Param: unsigned int bit
|
||||
Description:
|
||||
|
||||
Function: comedi_dio_bitfield -- read/write multiple digital channels
|
||||
Retval: int
|
||||
Param: comedi_t * device
|
||||
Param: unsigned int subdevice
|
||||
Param: unsigned int write_mask
|
||||
Param: unsigned int * bits
|
||||
Description:
|
||||
|
||||
Function: comedi_sv_init -- slowly-varying inputs
|
||||
Retval: int
|
||||
Param: comedi_sv_t * sv
|
||||
Param: comedi_t * device
|
||||
Param: unsigned int subdevice
|
||||
Param: unsigned int channel
|
||||
Description:
|
||||
|
||||
Function: comedi_sv_update -- slowly-varying inputs
|
||||
Retval: int
|
||||
Param: comedi_sv_t * sv
|
||||
Description:
|
||||
|
||||
Function: comedi_sv_measure -- slowly-varying inputs
|
||||
Retval: int
|
||||
Param: comedi_sv_t * sv
|
||||
Param: double * data
|
||||
Description:
|
||||
|
||||
Function: comedi_get_cmd_src_mask -- streaming input/output capabilities
|
||||
Retval: int
|
||||
Param: comedi_t * device
|
||||
Param: unsigned int subdevice
|
||||
Param: comedi_cmd * command
|
||||
Description:
|
||||
|
||||
Function: comedi_get_cmd_generic_timed -- streaming input/output capabilities
|
||||
Retval: int
|
||||
Param: comedi_t * device
|
||||
Param: unsigned int subdevice
|
||||
Param: comedi_cmd * command
|
||||
Param: unsigned int period_ns
|
||||
Description:
|
||||
|
||||
Function: comedi_cancel -- stop streaming input/outpu in progress
|
||||
Retval: int
|
||||
Param: comedi_t * device
|
||||
Param: unsigned int subdevice
|
||||
Description:
|
||||
|
||||
Function: comedi_command -- start streaming input/output
|
||||
Retval: int
|
||||
Param: comedi_t * device
|
||||
Param: comedi_cmd * command
|
||||
Description:
|
||||
|
||||
Function: comedi_command_test -- test streaming input/output configuration
|
||||
Retval: int
|
||||
Param: comedi_t * device
|
||||
Param: comedi_cmd * command
|
||||
Description:
|
||||
|
||||
Function: comedi_poll -- force updating of streaming buffer
|
||||
Retval: int
|
||||
Param: comedi_t * device
|
||||
Param: unsigned int subdevice
|
||||
Description:
|
||||
|
||||
Function: comedi_set_max_buffer_size -- streaming buffer size of subdevice
|
||||
Retval: int
|
||||
Param: comedi_t * device
|
||||
Param: unsigned int subdevice
|
||||
Param: unsigned int max_size
|
||||
Description:
|
||||
|
||||
Function: comedi_get_buffer_contents -- streaming buffer status
|
||||
Retval: int
|
||||
Param: comedi_t * device
|
||||
Param: unsigned int subdevice
|
||||
Description:
|
||||
|
||||
Function: comedi_mark_buffer_read -- streaming buffer status
|
||||
Retval: int
|
||||
Param: comedi_t * device
|
||||
Param: unsigned int subdevice
|
||||
Param: unsigned int num_bytes
|
||||
Description:
|
||||
|
||||
Function: comedi_get_buffer_offset -- streaming buffer status
|
||||
Retval: int
|
||||
Param: comedi_t * device
|
||||
Param: unsigned int subdevice
|
||||
Description:
|
||||
|
||||
Function: comedi_get_timer -- timer information (deprecated)
|
||||
Retval: int
|
||||
Param: comedi_t * device
|
||||
Param: unsigned int subdevice
|
||||
Param: double frequency
|
||||
Param: unsigned int * trigvar
|
||||
Param: double * actual_frequency
|
||||
Description:
|
||||
Status: deprecated
|
||||
|
||||
Function: comedi_timed_1chan -- streaming input (deprecated)
|
||||
Retval: int
|
||||
Param: comedi_t * device
|
||||
Param: unsigned int subdevice
|
||||
Param: unsigned int channel
|
||||
Param: unsigned int range
|
||||
Param: unsigned int aref
|
||||
Param: double frequency
|
||||
Param: unsigned int num_samples
|
||||
Param: double * data
|
||||
Description:
|
||||
Status: deprecated
|
||||
|
||||
Function: comedi_set_global_oor_behavior -- out-of-range behavior
|
||||
Retval: int
|
||||
Param: enum comedi_oor_behavior behavior
|
||||
Description:
|
||||
Status: alpha
|
||||
|
234
doc/docbook/install.sgml
Normal file
234
doc/docbook/install.sgml
Normal file
|
@ -0,0 +1,234 @@
|
|||
<!DOCTYPE section PUBLIC "-//OASIS//DTD DocBook V3.1//EN">
|
||||
|
||||
|
||||
<section>
|
||||
<title>
|
||||
Installation and Configuration
|
||||
</title>
|
||||
|
||||
<para>
|
||||
I assume that your hardware device is in your computer, and that
|
||||
you know the relevant details about it, i.e., what kind of card
|
||||
it is, the I/O base, the IRQ, jumper settings related to input
|
||||
ranges, etc.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
To tell the comedi kernel module that you have a particular device, and
|
||||
some information about it, you will be running the comedi_config
|
||||
command. Perhaps you should read the man page now.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
In this tutorial, I will go through the process of configuring comedi
|
||||
for two devices, a National Instruments AT-MIO-16E-10
|
||||
and a Data Translation DT2821-F-8DI.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The NI board is plug-and-play, and the man page tells me that I need
|
||||
to configure the PnP part of the board with isapnptools. The isapnptools
|
||||
package is a little cryptic, but the concepts are simple. Once I
|
||||
learned how to use it, I settled on a /etc/isapnp.conf file that
|
||||
contained the lines:
|
||||
</para>
|
||||
|
||||
|
||||
<screen>
|
||||
# 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)
|
||||
))
|
||||
</screen>
|
||||
|
||||
|
||||
<para>
|
||||
It also contains a few lines about overall configuration and about my
|
||||
sound card. I found out after a bit of trial-and-error that the NI
|
||||
board does not always work with interrupts other than IRQ 3. YMMV.
|
||||
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.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
When I run comedi_config (as root, of course), I provide the same
|
||||
information. Since I want to have the board configured every time
|
||||
I boot, I put the line
|
||||
</para>
|
||||
|
||||
<screen>
|
||||
/usr/sbin/comedi_config /dev/comedi0 ni_atmio 0x260,3
|
||||
</screen>
|
||||
|
||||
<para>
|
||||
into <filename>/etc/rc.d/rc.local</filename>. You can, of course, run this command at
|
||||
a command prompt. The man page tells me that the option list
|
||||
is supposed to be "(I/O base),(IRQ)", so I used the same numbers
|
||||
as I put in /etc/isapnp.conf, i.e., 0x260,3.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
For the Data Translation board, I need to have a list of the
|
||||
jumper settings. Fortunately, I wrote them all down in the
|
||||
manual -- I hope they are still correct. However, I had to
|
||||
open the case to figure out which board in the series I had.
|
||||
It is a DT2821-f-8di. The man page of comedi_config tells
|
||||
me that I need to know the I/O base, IRQ, DMA 1, DMA 2. However,
|
||||
since I wrote the driver, I know that it also recognizes the
|
||||
differential/single-ended and unipolar/bipolar jumpers. As always,
|
||||
the source is the final authority, and looking in module/dt282x.c
|
||||
tells me that the options list is interpreted as:
|
||||
</para>
|
||||
|
||||
<!-- XXX
|
||||
<itemize>
|
||||
<item>I/O base
|
||||
<item>IRQ
|
||||
<item>1=differential, 0=single ended
|
||||
<item>ai 0=unipolar, 1=bipolar
|
||||
<item>ao0 0=unipolar, 1=bipolar
|
||||
<item>ao1 0=unipolar, 1=bipolar
|
||||
<item>dma1
|
||||
<item>dma2
|
||||
</itemize>
|
||||
-->
|
||||
|
||||
<para>
|
||||
(ai=analog input, ao=analog output.) From this, I decide that
|
||||
the appropriate options list is
|
||||
</para>
|
||||
|
||||
<screen>
|
||||
0x200,4,,1,1,1
|
||||
</screen>
|
||||
|
||||
<para>
|
||||
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.
|
||||
</para>
|
||||
|
||||
<screen>
|
||||
/usr/sbin/comedi_config /dev/comedi1 dt2821-f-8di 0x200,4,,1,1,1
|
||||
</screen>
|
||||
|
||||
<para>
|
||||
So now I think that I have my boards configured correctly.
|
||||
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):
|
||||
</para>
|
||||
|
||||
<screen>
|
||||
comedi0: ni_atmio: 0x0200 can't find board
|
||||
</screen>
|
||||
|
||||
<para>
|
||||
When it does work, I get:
|
||||
</para>
|
||||
|
||||
<screen>
|
||||
comedi0: ni_atmio: 0x0260 at-mio-16e-10 ( irq = 3 )
|
||||
</screen>
|
||||
|
||||
<para>
|
||||
Note that it also correctly identified my board.
|
||||
</para>
|
||||
|
||||
|
||||
<section>
|
||||
<title>
|
||||
Getting information from comedi
|
||||
</title>
|
||||
|
||||
<para>
|
||||
So now that we have comedi talking to the hardware, we want to
|
||||
talk to comedi. Here's some pretty low-level information --
|
||||
it's sometimes useful for debugging:
|
||||
</para>
|
||||
|
||||
<screen>
|
||||
cat /proc/comedi
|
||||
</screen>
|
||||
|
||||
<para>
|
||||
Right now, on my computer, this command gives:
|
||||
</para>
|
||||
|
||||
<screen>
|
||||
comedi version 0.6.4
|
||||
format string
|
||||
0: ni_atmio at-mio-16e-10 7
|
||||
1: dt282x dt2821-f-8di 4
|
||||
</screen>
|
||||
|
||||
<para>
|
||||
This is a feature that is not well-developed yet. Basically, it
|
||||
currently tells you driver name, device name, and number of
|
||||
subdevices.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
In the <filename>demo/</filename> directory, there is a command called
|
||||
<command>info</command>, which provides information about each
|
||||
subdevice on the
|
||||
board. The output of it is rather long, since I have 7
|
||||
subdevices (4 or fewer is common for other boards.)
|
||||
Here's part of the output of the NI board (which
|
||||
is on <filename>/dev/comedi0</filename>.) ('demo/info /dev/comedi0')
|
||||
</para>
|
||||
|
||||
<screen>
|
||||
overall info:
|
||||
version code: 0x000604
|
||||
driver name: ni_atmio
|
||||
board name: at-mio-16e-10
|
||||
number of subdevices: 7
|
||||
subdevice 0:
|
||||
type: 1 (unknown)
|
||||
number of channels: 16
|
||||
max data value: 4095
|
||||
...
|
||||
</screen>
|
||||
|
||||
<para>
|
||||
The overall info gives information about the device -- basically
|
||||
the same information as /proc/comedi.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
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.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Subdevice 0 is the analog input subdevice. You would have
|
||||
known this from the 'type: 1 (unknown)' line, if I've updated
|
||||
demo/info recently, because it would say 'type: 1 (analog input)'
|
||||
instead. The other lines should be self-explanitory. Comedi
|
||||
has more information about the device, but demo/info doesn't
|
||||
currently display this.
|
||||
</para>
|
||||
|
||||
|
||||
</section>
|
||||
</section>
|
315
doc/docbook/intro.sgml
Normal file
315
doc/docbook/intro.sgml
Normal file
|
@ -0,0 +1,315 @@
|
|||
<!DOCTYPE section PUBLIC "-//OASIS//DTD DocBook V3.1//EN">
|
||||
<!--ignore the error caused by previous line-->
|
||||
|
||||
<section>
|
||||
|
||||
<title>
|
||||
Introduction
|
||||
</title>
|
||||
|
||||
<para>
|
||||
This section gives high-level explanation about which functionality
|
||||
you can expect from the software. Details are given in the following
|
||||
sections of this document.
|
||||
</para>
|
||||
|
||||
<section>
|
||||
<title>
|
||||
What is a "device driver"?
|
||||
</title>
|
||||
<para>
|
||||
A device driver is a piece of software that interfaces a particular
|
||||
piece of hardware: a printer, a sound card, a motor drive, etc. It
|
||||
translates the primitive, device-dependent commands with which the
|
||||
hardware manufacturer want you to configure, read and write the
|
||||
electronics of the hardware interface into more abstract and generic
|
||||
function calls and data structures for the application programmer.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
David Schleef started the Comedi project to interface
|
||||
lots of different cards for measurement and control purposes. This
|
||||
type of cards are often called Data AcQuisition cards, or DAQ cards.
|
||||
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.
|
||||
</para>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>
|
||||
A general DAQ device driver package.
|
||||
</title>
|
||||
<para>
|
||||
From the point of view of system developers, it is worthwhile to
|
||||
standardize the structure and API (Application Programming Interface)
|
||||
for device drivers as much as possible:
|
||||
</para>
|
||||
|
||||
<para>
|
||||
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.
|
||||
|
||||
<para>
|
||||
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.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
In the case of Linux as the host operating system, device driver
|
||||
writers must keep the following Linux-specific issues in mind:
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Kernel space vs. User space.
|
||||
The Linux operating system has two levels: 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
|
||||
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.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Device files or device file system.
|
||||
The users that want to write an application for a particular device,
|
||||
must link their application to the device's device driver. This device
|
||||
driver, however, runs in kernel space, and the user application in
|
||||
user space. So, the operating system provides an interface between
|
||||
both. In Linux or Unix, these interfaces are in the form of "files"
|
||||
in the /dev directory (2.2.x kernels or earlier) or /devfs directory
|
||||
(2.4.x kernels and later). Each device has a representative, and can
|
||||
be accessed by the classical Unix file I/O calls: open, close, read,
|
||||
write, and ioctl.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
/proc interface.
|
||||
Linux offers a file-like interface to attached devices (and other
|
||||
OS-related information) via the /proc directories. This interface
|
||||
allows to inspect the current status of each device.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Direct Memory Access (DMA) vs. Programmed Input/Output (PIO).
|
||||
Almost all devices can be interfaced in PIO mode: the processor is
|
||||
responsible for accessing bus addresses allocated to the device, and
|
||||
to read or write data. Some devices also allow DMA: the device and the
|
||||
memory "talk" to each other directly, without needing the processor.
|
||||
DMA is a feature of the bus, not of the operating system (which has
|
||||
to support its processes to use the feature, of course).
|
||||
</para>
|
||||
|
||||
<para>
|
||||
If the device is to be used in a Real-Time Linux or RTAI application,
|
||||
there are a few extra requirements, because not all system calls are
|
||||
available in the RTOS kernel of Real-Time Linux or RTAI.
|
||||
</para>
|
||||
</section>
|
||||
|
||||
|
||||
<section>
|
||||
<title>
|
||||
Policy vs. mechanism.
|
||||
</title>
|
||||
<para>
|
||||
Device drivers are often written by application programmers, that have
|
||||
a particular application in mind. For example, one writes a driver for
|
||||
the parallel port, because one wants to use it to generate pulses that
|
||||
drive a stepper motor. This approach often leads to device drivers
|
||||
that depend too much on the application, and are not general enough to
|
||||
be re-used for other applications. One golden rule for the device
|
||||
driver writer is to separate mechanism and policy:
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Mechanism.
|
||||
The mechanism part of the device interface is a faithful
|
||||
representation of the bare functionality of the device, independent of
|
||||
what part of the functionality an application will use.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Policy.
|
||||
Once a device driver offers a software interface to the mechanism of
|
||||
the device, an application writer can use this mechanism interface to
|
||||
use the device in one particular fashion. That is, some of the data
|
||||
stuctures offered by the mechanism are interpreted in specific
|
||||
physical units, or some of them are taken together because this
|
||||
composition is relevant for the application. For example, a analog
|
||||
output card can be used to generate voltages that are the inputs for
|
||||
the electronic drivers of the motors of a robot; these voltages can be
|
||||
interpreted as setpoints for the desired velocity of these motors, and
|
||||
six of them are taken together to steer one particular robot with
|
||||
six-degrees of freedom. Some of the other outputs of the same physical
|
||||
device can be used by another application program, for example to
|
||||
generate a sine wave that drives a vibration shaker.
|
||||
</para>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>
|
||||
Overview of Comedi.
|
||||
</title>
|
||||
<para>
|
||||
The supported cards in Comedi have one or more of the following
|
||||
features: analog input channels, analog output channels, digital input
|
||||
channels, and digital output channels. The digital channels are
|
||||
conceptually quite simple, and don't need much configuration: the
|
||||
number of channels, their addresses on the bus, and their direction
|
||||
(input/output).
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The analog channels are a bit more complicated. Typically, an analog
|
||||
channel can be programmed to generate or read a voltage between a
|
||||
lower and an upper threshold (e.g., -10V and +10V); the card's
|
||||
electronics can be programmed to automatically sample a set of
|
||||
channels, in a prescribed order; top buffer sequences of data on the
|
||||
board; or to use DMA to dump the data in an available part of memory,
|
||||
without intervention from the processor.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Many interface cards have extra functionality, besides the analog and
|
||||
digital channels. For example, an EEPROM for configuration and board
|
||||
parameters, calibration inputs, counters and timers, encoders (=
|
||||
quadrature counter on two channels), etc. Therefore, Comedi offers
|
||||
more than just analog and digital data acquisition.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The kernel space structures that Comedi uses have the following
|
||||
hierarchy:
|
||||
</para>
|
||||
<para>
|
||||
- channel: the lowest-level component, that represents the properties
|
||||
of one single data channel (analog in or out; digital in or out).
|
||||
Each channel has parameters for: the voltage range, the reference
|
||||
voltage, the channel polarity (unipolar, bipolar), a conversion
|
||||
factor between voltages and physical units.
|
||||
</para>
|
||||
<para>
|
||||
- sub-device: a set of functionally identical channels that are
|
||||
physically implemented on the same (chip on an) interface card. For
|
||||
example, a set of 16 identical analog outputs.
|
||||
Each sub-device has parameters for: the number of channels, and the type
|
||||
of the channels.
|
||||
</para>
|
||||
<para>
|
||||
- device: a set of sub-devices that are physically implemented on the
|
||||
same interface card; in other words, the interface card itself.
|
||||
For example, the NI 6024E device has a sub-device with 16 analog input
|
||||
channels, another sub-device with two analog output channels, and a
|
||||
third sub-device with eight digital inputs/outputs.
|
||||
Each device has parameters for: the device identification tag from
|
||||
the manufacturer, the identification tag given by the operating system
|
||||
(in order to discriminate between multiple interface cards of the same
|
||||
type), the number of sub-devices, etc.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The basic functionalities offered by Comedi are:
|
||||
</para>
|
||||
<para>
|
||||
- instruction: to synchronously perform one single data acquisition on a
|
||||
specified channel, or to perform a configuration on the channel.
|
||||
"Synchronous" means that the calling process blocks until the data
|
||||
acquisition has finished.
|
||||
</para>
|
||||
<para>
|
||||
- scan: repeated instructions on a number of different channels, with a
|
||||
programmed sequence and timing.
|
||||
</para>
|
||||
<para>
|
||||
- command: start or stop an autonomous (and hence aynchronous) data
|
||||
acquisition (i.e., a number of scans) on a specified set of
|
||||
channels. "Autonomous" means: without interaction from the software,
|
||||
i.e., by means of on-board timers or possibly external triggers.
|
||||
</para>
|
||||
<para>
|
||||
This command functionality is not offered by all DAQ cards. When
|
||||
using RTAI or Real-Time Linux, the command functionality is emulated
|
||||
through the "comedi_rt_timer" virtual driver.
|
||||
The command functionality is very configurable, with respect to the
|
||||
choice of events with which to signal the progress of the programmed
|
||||
scans: external triggers, end of instruction, etc.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Comedi not only offers the API to access the functionality of the
|
||||
cards, but also to query the capabilities of the installed Comedi
|
||||
devices. That is, a user process can find out on-line what channels
|
||||
are available, and what their physical parameters are (range,
|
||||
direction of input/output, etc.).
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Buffers are an important aspect of device drivers: the data has
|
||||
to be stored in such buffers, if the application program cannot
|
||||
guarantee to read or write the data as soon as the interface board
|
||||
wants to do so. Therefore, Comedi offers functionality to configure
|
||||
and manage data buffers.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Comedi contains more than just procedural function calls: it also
|
||||
offers event-driven functionality. The data acquisition can signal
|
||||
its completion by means of an interrupt or a callback function call.
|
||||
Callbacks are also used to signal errors during the data
|
||||
acquisition or when writing to buffers, or at the end of a scan or
|
||||
acquisition that has been launched previously to take place
|
||||
asynchronously (i.e., the card fills up som shared memory buffer
|
||||
autonomously, and only warns the user program after it has finished).
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The mechanisms for synchronization and interrupt handling are a bit
|
||||
different when used in a real-time context (i.e., with either RTAI or
|
||||
Real-Time Linux), but both are encapsulated behind the same Comedi calls.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Because multiple devices can all be active at the same time, Comedi
|
||||
provides (non-SMP!) locking primitives to ensure atomic operations on
|
||||
critical sections of the code or data structures.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Finally, Comedi offers the above-mentioned "high-level" interaction,
|
||||
i.e., at the level of user space device drivers, through file
|
||||
operations on entries in the /dev directory (for access to the
|
||||
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.
|
||||
</para>
|
||||
</section>
|
||||
|
||||
|
||||
</section>
|
||||
|
149
doc/docbook/mkdr
Executable file
149
doc/docbook/mkdr
Executable file
|
@ -0,0 +1,149 @@
|
|||
#!/usr/bin/perl
|
||||
# vim: set ts=4:
|
||||
|
||||
$manvolnum="3";
|
||||
$header="#include <comedilib.h>";
|
||||
|
||||
$end = "";
|
||||
|
||||
print
|
||||
"<!--This file is autogenerated. Do not edit-->
|
||||
<section>
|
||||
<title>
|
||||
Low-level drivers
|
||||
</title>
|
||||
";
|
||||
|
||||
while(<>){
|
||||
push @lines,$_;
|
||||
}
|
||||
|
||||
$secend="";
|
||||
|
||||
while($s = shift @lines){
|
||||
@x = $s;
|
||||
if($s =~ m/^\w+\:/){
|
||||
$blank=0;
|
||||
chomp $s;
|
||||
$x = $s;
|
||||
LOOP: while($s = shift @lines){
|
||||
if($s =~ m/^\w+:/ or $s eq "\n"){
|
||||
unshift @lines, $s;
|
||||
last LOOP;
|
||||
}
|
||||
chomp $s;
|
||||
$x = "$x $s";
|
||||
}
|
||||
if($x =~ m/^Driver: (.*)/){
|
||||
$driver = $1;
|
||||
}elsif($x =~ m/^Description: (.*)/){
|
||||
$description = $1;
|
||||
}elsif($x =~ m/^Devices: (.*)/){
|
||||
$devices = $1;
|
||||
}elsif($x =~ m/^Author: (.*)/){
|
||||
$author = $1
|
||||
}
|
||||
}else{
|
||||
if($s eq "\n"){
|
||||
$blank ++;
|
||||
}else{
|
||||
$blank = 0;
|
||||
}
|
||||
$comment = $comment . $s;
|
||||
}
|
||||
if($blank==3){
|
||||
$comment =~ s/@/@/g;
|
||||
$comment =~ s/</</g;
|
||||
$comment =~ s/>/>/g;
|
||||
$author =~ s/@/@/g;
|
||||
$author =~ s/</</g;
|
||||
$author =~ s/>/>/g;
|
||||
print
|
||||
"
|
||||
<section>
|
||||
<title>
|
||||
$driver -- $description
|
||||
</title>
|
||||
<para>
|
||||
Author: $author
|
||||
</para>
|
||||
";
|
||||
if($devices ne ""){
|
||||
print
|
||||
" <informaltable>
|
||||
<tgroup cols='3' align='left'>
|
||||
<thead>
|
||||
<row>
|
||||
<entry>Manufacturer</entry>
|
||||
<entry>Device</entry>
|
||||
<entry>Name</entry>
|
||||
</row>
|
||||
</thead>
|
||||
<tbody>
|
||||
";
|
||||
while($devices){
|
||||
$_=$devices;
|
||||
if(m/^ *\[([^\]]+)\](.*)/){
|
||||
$mfr = $1;
|
||||
$devices = $2;
|
||||
}elsif(m/^ *\(([^\)]+)\)(.*)/){
|
||||
$name = $1;
|
||||
$devices = $2;
|
||||
}elsif(m/^ *([^\(,]+)(.*)/){
|
||||
$dev = $1;
|
||||
$devices = $2;
|
||||
$dev =~ s/ *$//;
|
||||
}elsif(m/^ *,(.*)/){
|
||||
$devices = $1;
|
||||
print
|
||||
" <row>
|
||||
<entry>
|
||||
$mfr
|
||||
</entry>
|
||||
<entry>
|
||||
$dev
|
||||
</entry>
|
||||
<entry>
|
||||
$name
|
||||
</entry>
|
||||
</row>
|
||||
";
|
||||
}else{
|
||||
die "parse error";
|
||||
}
|
||||
}
|
||||
print
|
||||
" <row>
|
||||
<entry>
|
||||
$mfr
|
||||
</entry>
|
||||
<entry>
|
||||
$dev
|
||||
</entry>
|
||||
<entry>
|
||||
$name
|
||||
</entry>
|
||||
</row>
|
||||
</tbody>
|
||||
</tgroup>
|
||||
</informaltable>
|
||||
";
|
||||
}
|
||||
print
|
||||
"
|
||||
<screen>
|
||||
$comment
|
||||
</screen>
|
||||
</section>";
|
||||
$blank=0;
|
||||
|
||||
$comment="";
|
||||
}
|
||||
}
|
||||
|
||||
print
|
||||
"</section>
|
||||
";
|
||||
|
||||
exit(0);
|
||||
|
106
doc/docbook/mkref
Executable file
106
doc/docbook/mkref
Executable file
|
@ -0,0 +1,106 @@
|
|||
#!/usr/bin/perl
|
||||
# vim: set ts=4:
|
||||
|
||||
$manvolnum="3";
|
||||
$header="#include <comedilib.h>";
|
||||
|
||||
$end = "";
|
||||
|
||||
print
|
||||
"<!--This file is autogenerated. Do not edit-->
|
||||
<section>
|
||||
<title>
|
||||
Comedi Function Reference
|
||||
</title>
|
||||
";
|
||||
|
||||
while($s = <>){
|
||||
chomp $s;
|
||||
if($s eq ""){
|
||||
print $end;
|
||||
print
|
||||
"</refentry>
|
||||
";
|
||||
$end = "";
|
||||
}elsif($s =~ m/^Function: (.*)/){
|
||||
$funcname = $1;
|
||||
$refpurpose = "";
|
||||
if($s =~ m/^Function: (.*) -- (.*)/){
|
||||
$funcname = $1;
|
||||
$refpurpose = $2;
|
||||
}
|
||||
$refname = $funcname;
|
||||
$refname =~ s/_/-/g;
|
||||
$refname = "func-ref-" . $refname;
|
||||
print $end;
|
||||
print
|
||||
"<refentry id=\"$refname\">
|
||||
|
||||
<refmeta>
|
||||
<refentrytitle>$funcname</refentrytitle>
|
||||
<manvolnum>$manvolnum</manvolnum>
|
||||
</refmeta>
|
||||
|
||||
<refnamediv>
|
||||
<refname>$funcname</refname>
|
||||
<refpurpose>$refpurpose</refpurpose>
|
||||
</refnamediv>
|
||||
";
|
||||
$end = "";
|
||||
}elsif($s =~ m/^Retval: (.*)/){
|
||||
print
|
||||
" <refsynopsisdiv>
|
||||
<funcsynopsis>
|
||||
<funcsynopsisinfo>$header</funcsynopsisinfo>
|
||||
<funcprototype>
|
||||
<funcdef>$1 <function>$funcname</function></funcdef>
|
||||
";
|
||||
$end =
|
||||
" </funcprototype>
|
||||
</funcsynopsis>
|
||||
</refsynopsisdiv>
|
||||
";
|
||||
}elsif($s =~ m/^Param: (.*)/){
|
||||
$p = $1;
|
||||
$p =~ m/(.*) (.*)/;
|
||||
print
|
||||
" <paramdef>$1 <parameter>$2</parameter></paramdef>
|
||||
"
|
||||
}elsif($s =~ m/^Description:/){
|
||||
print $end;
|
||||
print
|
||||
" <refsect1>
|
||||
<title>
|
||||
Description
|
||||
</title>
|
||||
<para>
|
||||
";
|
||||
$end =
|
||||
" </para>
|
||||
</refsect1>
|
||||
";
|
||||
}elsif($s =~ m/^Status: (.*)/){
|
||||
print $end;
|
||||
print
|
||||
" <refsect1>
|
||||
<title>
|
||||
Status
|
||||
</title>
|
||||
<para>
|
||||
$1
|
||||
";
|
||||
$end =
|
||||
" </para>
|
||||
</refsect1>
|
||||
";
|
||||
}elsif($s =~ m/^ (.*)/){
|
||||
print "$1\n";
|
||||
}
|
||||
}
|
||||
|
||||
print
|
||||
"</section>
|
||||
";
|
||||
|
||||
exit(0);
|
||||
|
672
doc/docbook/other.sgml
Normal file
672
doc/docbook/other.sgml
Normal file
|
@ -0,0 +1,672 @@
|
|||
<p>
|
||||
<sect1>Configuring comedi for your hardware
|
||||
<p>
|
||||
|
||||
|
||||
I assume that your hardware device is in your computer, and that
|
||||
you know the relevant details about it, i.e., what kind of card
|
||||
it is, the I/O base, the IRQ, jumper settings related to input
|
||||
ranges, etc.
|
||||
|
||||
To tell the comedi kernel module that you have a particular device, and
|
||||
some information about it, you will be running the <tt>comedi_config</tt>
|
||||
command. Perhaps you should read the man page now.
|
||||
|
||||
In this tutorial, I will go through the process of configuring comedi
|
||||
for two devices, a National Instruments AT-MIO-16E-10
|
||||
and a Data Translation DT2821-F-8DI.
|
||||
|
||||
The NI board is plug-and-play, and the man page tells me that I need
|
||||
to configure the PnP part of the board with isapnptools. The isapnptools
|
||||
package is a little cryptic, but the concepts are simple. Once I
|
||||
learned how to use it, I settled on a /etc/isapnp.conf file that
|
||||
contained the lines:
|
||||
|
||||
|
||||
<tscreen><verb>
|
||||
# ANSI string -->National Instruments, AT-MIO-16E-10<--
|
||||
(CONFIGURE NIC2400/10725401 (LD 0
|
||||
(IO 0 (BASE 0x0260))
|
||||
(INT 0 (IRQ 3 (MODE +E)))
|
||||
# (DMA 0 (CHANNEL 5))
|
||||
# (DMA 1 (CHANNEL 6))
|
||||
(ACT Y)
|
||||
))
|
||||
</verb></tscreen>
|
||||
|
||||
|
||||
It also contains a few lines about overall configuration and about my
|
||||
sound card. I found out after a bit of trial-and-error that the NI
|
||||
board does not always work with interrupts other than IRQ 3. YMMV.
|
||||
Currently, the driver doesn't use DMA, but it may in the future, so
|
||||
I commented out the DMA lines. It is a curious fact that the device
|
||||
ignores the IRQ and DMA information given here, however, I keep the
|
||||
information here to remind myself that the numbers aren't arbitrary.
|
||||
|
||||
When I run comedi_config (as root, of course), I provide the same
|
||||
information. Since I want to have the board configured every time
|
||||
I boot, I put the line
|
||||
|
||||
<tscreen><verb>
|
||||
/usr/sbin/comedi_config /dev/comedi0 atmio-E 0x260,3
|
||||
</verb></tscreen>
|
||||
|
||||
into <tt>/etc/rc.d/rc.local</tt>. You can, of course, run this command at
|
||||
a command prompt. The man page tells me that the option list
|
||||
is supposed to be "(I/O base),(IRQ)", so I used the same numbers
|
||||
as I put in /etc/isapnp.conf, i.e., 0x260,3.
|
||||
|
||||
For the Data Translation board, I need to have a list of the
|
||||
jumper settings. Fortunately, I wrote them all down in the
|
||||
manual -- I hope they are still correct. However, I had to
|
||||
open the case to figure out which board in the series I had.
|
||||
It is a DT2821-f-8di. The man page of comedi_config tells
|
||||
me that I need to know the I/O base, IRQ, DMA 1, DMA 2. However,
|
||||
since I wrote the driver, I know that it also recognizes the
|
||||
differential/single-ended and unipolar/bipolar jumpers. As always,
|
||||
the source is the final authority, and looking in module/dt282x.c
|
||||
tells me that the options list is interpreted as:
|
||||
|
||||
<itemize>
|
||||
<item>I/O base
|
||||
<item>IRQ
|
||||
<item>1=differential, 0=single ended
|
||||
<item>ai 0=unipolar, 1=bipolar
|
||||
<item>ao0 0=unipolar, 1=bipolar
|
||||
<item>ao1 0=unipolar, 1=bipolar
|
||||
<item>dma1
|
||||
<item>dma2
|
||||
</itemize>
|
||||
|
||||
(ai=analog input, ao=analog output.) From this, I decide that
|
||||
the appropriate options list is
|
||||
|
||||
<tscreen><verb>
|
||||
0x200,4,,1,1,1
|
||||
</verb></tscreen>
|
||||
|
||||
I left the differential/single-ended number blank, since the
|
||||
driver already knowns (from the board name), that it is
|
||||
differential. I also left the DMA numbers blank, since I
|
||||
don't want the driver to use DMA. (Don't want it to interfere
|
||||
with my sound card -- life is full of difficult choices.)
|
||||
Keep in mind that things commented in the source, but not in
|
||||
the documentation are about as likely to change as the weather,
|
||||
so I put good comments next to the following line when I put
|
||||
it in rc.local.
|
||||
|
||||
<tscreen><verb>
|
||||
/usr/sbin/comedi_config /dev/comedi1 dt2821-f-8di 0x200,4,,1,1,1
|
||||
</verb></tscreen>
|
||||
|
||||
So now I think that I have my boards configured correctly.
|
||||
Since data acquisition boards are not typically well-engineered,
|
||||
comedi sometimes can't figure out if the board is actually there.
|
||||
If it can't, it assumes you are right. Both of these boards
|
||||
are well-made, so comedi will give me an error message if it
|
||||
can't find them. The comedi kernel module, since it is a part
|
||||
of the kernel, prints messages to the kernel logs, which you
|
||||
can access through the command 'dmesg' or /var/log/messages.
|
||||
Here is a configuration failure (from dmesg):
|
||||
|
||||
<tscreen><verb>
|
||||
comedi0: ni_E: 0x0200 can't find board
|
||||
</verb></tscreen>
|
||||
|
||||
When it does work, I get:
|
||||
|
||||
<tscreen><verb>
|
||||
comedi0: ni_E: 0x0260 at-mio-16e-10 ( irq = 3 )
|
||||
</verb></tscreen>
|
||||
|
||||
Note that it also correctly identified my board.
|
||||
|
||||
|
||||
<p>
|
||||
<sect1>Getting information from comedi
|
||||
<p>
|
||||
|
||||
|
||||
So now that we have comedi talking to the hardware, we want to
|
||||
talk to comedi. Here's some pretty low-level information --
|
||||
it's sometimes useful for debugging:
|
||||
|
||||
<p>
|
||||
|
||||
<tscreen><verb>
|
||||
cat /proc/comedi
|
||||
</verb></tscreen>
|
||||
|
||||
Right now, on my computer, this command gives:
|
||||
|
||||
<tscreen><verb>
|
||||
comedi version 0.6.4
|
||||
format string
|
||||
0: atmio-E at-mio-16e-10 7
|
||||
1: dt282x dt2821-f-8di 4
|
||||
</verb></tscreen>
|
||||
|
||||
This is a feature that is not well-developed yet. Basically, it
|
||||
currently tells you driver name, device name, and number of
|
||||
subdevices.
|
||||
|
||||
In the <tt>demo/</tt> directory, there is a command called
|
||||
<tt>info</tt>, which provides information about each subdevice on the
|
||||
board. The output of it is rather long, since I have 7
|
||||
subdevices (4 or fewer is common for other boards.)
|
||||
Here's part of the output of the NI board (which
|
||||
is on <tt>/dev/comedi0</tt>.) ('demo/info /dev/comedi0')
|
||||
|
||||
<tscreen><verb>
|
||||
overall info:
|
||||
version code: 0x000604
|
||||
driver name: atmio-E
|
||||
board name: at-mio-16e-10
|
||||
number of subdevices: 7
|
||||
subdevice 0:
|
||||
type: 1 (unknown)
|
||||
number of channels: 16
|
||||
max data value: 4095
|
||||
</verb>
|
||||
...
|
||||
</tscreen>
|
||||
|
||||
The overall info gives information about the device -- basically
|
||||
the same information as /proc/comedi.
|
||||
|
||||
This board has 7 subdevices. Devices are separated into
|
||||
subdevices that each have a distinct purpose -- e.g., analog
|
||||
input, analog output, digital input/output. This board also
|
||||
has an EEPROM and calibration DACs that are also subdevices.
|
||||
|
||||
Subdevice 0 is the analog input subdevice. You would have
|
||||
known this from the 'type: 1 (unknown)' line, if I've updated
|
||||
demo/info recently, because it would say 'type: 1 (analog input)'
|
||||
instead. The other lines should be self-explanitory. Comedi
|
||||
has more information about the device, but demo/info doesn't
|
||||
currently display this.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<sect>Writing programs that use comedi and comedilib
|
||||
<p>
|
||||
|
||||
<sect1>Your first comedi program
|
||||
<p>
|
||||
|
||||
This example requires a card that has analog or
|
||||
digital input. Right to the source:
|
||||
|
||||
<tscreen><verb>
|
||||
#include <stdio.h> /* for printf() */
|
||||
#include <comedilib.h>
|
||||
|
||||
int subdev = 0; /* change this to your input subdevice */
|
||||
int chan = 0; /* change this to your channel */
|
||||
int range = 0; /* more on this later */
|
||||
int aref = AREF_GROUND; /* more on this later */
|
||||
|
||||
int main(int argc,char *argv[])
|
||||
{
|
||||
comedi_t *it;
|
||||
lsampl_t data;
|
||||
|
||||
it=comedi_open("/dev/comedi0");
|
||||
|
||||
comedi_data_read(it,subdev,chan,range,aref,&data);
|
||||
|
||||
printf("%d\n",data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
</verb></tscreen>
|
||||
|
||||
|
||||
Should be understandable: open the device, get the data,
|
||||
print it out. This is basically the guts of <tt>demo/inp.c</tt>,
|
||||
without error checking or fancy options.
|
||||
Compile it using
|
||||
|
||||
<tscreen><verb>
|
||||
cc tut1.c -lcomedi -o tut1
|
||||
</verb></tscreen>
|
||||
|
||||
A few notes: The range variable tells comedi which gain
|
||||
to use when measuring an analog voltage. Since we don't
|
||||
know (yet) which numbers are valid, or what each means,
|
||||
we'll use 0, because it won't cause errors. Likewise with
|
||||
aref, which determines the analog reference used.
|
||||
|
||||
|
||||
<p>
|
||||
<sect1>Converting samples to voltages
|
||||
<p>
|
||||
|
||||
If you selected an analog input subdevice, you probably noticed
|
||||
that the output of <tt>tut1</tt> is a number between
|
||||
0 and 4095, or 0 and 65535, depending on the number of bits
|
||||
in the A/D converter. Comedi samples are <bf>always</bf> unsigned,
|
||||
with 0 representing the lowest voltage of the ADC, and 4095
|
||||
the highest. Comedi compensates for
|
||||
anything else the manual for your device says. However,
|
||||
you probably prefer to have this number translated to
|
||||
a voltage. Naturally, as a good programmer, your first
|
||||
question is: "How do I do this in a device-independent
|
||||
manner?"
|
||||
|
||||
Most devices give you a choice of gain and unipolar/bipolar
|
||||
input, and Comedi allows you to select which of these to
|
||||
use. This parameter is called the "range parameter", since
|
||||
it specifies the "input range" for analog input (or "output range"
|
||||
for analog output.) The range parameter represents both the gain
|
||||
and the unipolar/bipolar aspects.
|
||||
|
||||
Comedi keeps the number of available ranges and the largest
|
||||
sample value for each subdevice/channel combination. (Some
|
||||
devices allow different input/output ranges for different
|
||||
channels in a subdevice.)
|
||||
|
||||
The largest sample value can be found using the function:
|
||||
|
||||
comedi_get_maxdata()
|
||||
|
||||
The number of available ranges can be found using the function:
|
||||
|
||||
comedi_get_n_ranges()
|
||||
|
||||
For each value of the range parameter for a particular
|
||||
subdevice/channel, you can get range information using the
|
||||
function:
|
||||
|
||||
ptr=comedi_get_range(comedi_file,subdevice,channel,
|
||||
range)
|
||||
|
||||
which returns a pointer to a comedi_range structure.
|
||||
The comedi_range structure looks like
|
||||
|
||||
<p>
|
||||
<tscreen><verb>
|
||||
typedef struct{
|
||||
double min;
|
||||
double max;
|
||||
unsigned int unit;
|
||||
}comedi_range;
|
||||
</verb></tscreen>
|
||||
|
||||
The structure element 'min' represents
|
||||
the voltage corresponding to comedi_data_read() returning 0,
|
||||
and 'max' represents comedi_data_read() returning 'maxdata',
|
||||
(i.e., 4095 for 12 bit A/C converters, 65535 for 16 bit,
|
||||
or, 1 for digital input -- more on this in a bit.) The
|
||||
'unit' entry tells you if min and
|
||||
max refer to voltage, current, etc.
|
||||
|
||||
"Could it get easier?", you say. Well, yes. Use
|
||||
the function comedi_to_phys(), which converts data
|
||||
values to physical units. Call it using something like
|
||||
|
||||
<tscreen><verb>
|
||||
volts=comedi_to_phys(it,data,range,maxdata);
|
||||
</verb></tscreen>
|
||||
|
||||
and the opposite
|
||||
|
||||
<tscreen><verb>
|
||||
data=comedi_from_phys(it,volts,range,maxdata);
|
||||
</verb></tscreen>
|
||||
|
||||
|
||||
<p>
|
||||
<sect1>Another section
|
||||
<p>
|
||||
|
||||
|
||||
In addition to providing low level routines for data
|
||||
access, the comedi library provides higher-level access,
|
||||
much like the standard C library provides fopen(), etc.
|
||||
as a high-level (and portable) alternative to the direct
|
||||
UNIX system calls open(), etc. Similarily to fopen(),
|
||||
we have comedi_open():
|
||||
|
||||
<p>
|
||||
<tscreen><verb>
|
||||
file=comedi_open("/dev/comedi0");
|
||||
</verb></tscreen>
|
||||
|
||||
where file is of type <tt>(comedi_t *)</tt>. This function
|
||||
calls <tt>open()</tt>, like we did explicitly in a previous
|
||||
section, but also fills the <tt>comedi_t</tt> structure with
|
||||
lots of goodies -- information that we will need to use
|
||||
soon.
|
||||
|
||||
Specifically, we needed to know maxdata for a specific
|
||||
subdevice/channel. How about:
|
||||
|
||||
<tscreen><verb>
|
||||
maxdata=comedi_get_maxdata(file,subdevice,channel);
|
||||
</verb></tscreen>
|
||||
|
||||
Wow. How easy. And the range type?
|
||||
|
||||
<tscreen><verb>
|
||||
range_type=comedi_get_rangetype(file,subdevice,channel);
|
||||
</verb></tscreen>
|
||||
|
||||
Cool. Other information you need to know about a channel
|
||||
can be gotten in a similar way.
|
||||
|
||||
|
||||
|
||||
<sect1>Your second comedi program
|
||||
<p>
|
||||
|
||||
|
||||
Actually, this is the first comedi program again, just
|
||||
that we've added what we've learned.
|
||||
|
||||
|
||||
<tscreen><verb>
|
||||
#include <stdio.h> /* for printf() */
|
||||
#include <comedi.h> /* also included by comedilib.h */
|
||||
#include <comedilib.h> /* 'cuz we're using comedilib */
|
||||
|
||||
int subdev = 0; /* change this to your input subdevice */
|
||||
int chan = 0; /* change this to your channel */
|
||||
int range = 0; /* more on this later */
|
||||
int aref = 0; /* more on this later */
|
||||
|
||||
int main(int argc,char *argv[])
|
||||
{
|
||||
comedi_t *cf;
|
||||
int chan=0;
|
||||
lsampl_t data;
|
||||
int maxdata,rangetype;
|
||||
double volts;
|
||||
|
||||
cf=comedi_open("/dev/comedi0");
|
||||
|
||||
maxdata=comedi_get_maxdata(cf,subdev,chan);
|
||||
|
||||
rangetype=comedi_get_rangetype(cf,subdev,chan);
|
||||
|
||||
comedi_data_read(cf->fd,subdev,chan,range,aref,&data);
|
||||
|
||||
volts=comedi_to_phys(data,rangetype,range,maxdata);
|
||||
|
||||
printf("%d %g\n",data,volts);
|
||||
|
||||
return 0;
|
||||
}
|
||||
</verb></tscreen>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<p>
|
||||
<sect>Application-specific functions
|
||||
<p>
|
||||
|
||||
<sect1>Digital Input/Output
|
||||
<p>
|
||||
|
||||
Many boards supported by comedi have digital input and output
|
||||
channels. Some boards allow the direction of a channel to be
|
||||
specified in software.
|
||||
|
||||
Comedi groups digital channels into subdevice, which is a group
|
||||
of digital channels that have the same characteristics. For
|
||||
example, digital output lines will be grouped into a digital
|
||||
output subdevice, bidirectional digital lines will be grouped
|
||||
into a digital I/O subdevice. Thus, there can be multiple
|
||||
digital subdevices on a particular board.
|
||||
|
||||
Individual digital lines can be read and written using the
|
||||
functions
|
||||
|
||||
<tt/comedi_dio_read(device,subdevice,channel,unsigned int *bit);/
|
||||
<tt/comedi_dio_write(device,subdevice,channel,unsigned int bit);/
|
||||
|
||||
The direction of bidirectional lines can be configured using
|
||||
the function
|
||||
|
||||
<tt/comedi_dio_config(device,subdevice,channel,unsigned int dir);/
|
||||
|
||||
The parameter <tt/dir/ should be either COMEDI_INPUT or COMEDI_OUTPUT.
|
||||
Many digital I/O subdevices group channels into blocks for
|
||||
configuring direction. Changing one channel in a block changes
|
||||
the entire block.
|
||||
|
||||
Multiple channels can be read and written simultaneously using the
|
||||
function
|
||||
|
||||
<tt/comedi_dio_bitfield(device,subdevice,unsigned int write_mask,unsigned int *bits);/
|
||||
|
||||
Each channel is assigned to a bit in the <tt/write_mask/ and <tt/bits/
|
||||
bitfield. If a bit in <tt/write_mask/ is set, the corresponding bit
|
||||
in <tt/*bits/ will be written to the corresponding digital output line.
|
||||
Each digital line is then read and placed into <tt/*bits/. The value
|
||||
of bits in <tt/*bits/ corresponding to digital output lines is
|
||||
undefined and device-specific. Channel 0 is the least significant
|
||||
bit in the bitfield; channel 31 is the most significant bit. Channels
|
||||
higher than 31 cannot be accessed using this method.
|
||||
|
||||
|
||||
<p>
|
||||
<sect1>Slowly-varying inputs
|
||||
<p>
|
||||
|
||||
|
||||
Sometimes, your input channels change slowly enough that
|
||||
you are able to average many sucessive input values to get a
|
||||
more accurate measurement of the actual value. In general,
|
||||
the more samples you average, the better your estimate
|
||||
gets, roughly by a factor of sqrt(number_of_samples).
|
||||
Obviously, there are limitations to this:
|
||||
|
||||
<p>
|
||||
<itemize>
|
||||
<item>
|
||||
you are ultimately limited by "spurious free dynamic range"
|
||||
|
||||
<item>
|
||||
you need to have _some_ noise on the input channel,
|
||||
otherwise you will be averaging the same number N times.
|
||||
|
||||
<item>
|
||||
the more noise you have, the greater your SFDR, but it
|
||||
takes many more samples to compensate for the increased
|
||||
noise
|
||||
|
||||
<item>
|
||||
if you feel the need to average samples for 2 seconds,
|
||||
your signal will need to be _very_ slowly-varying, i.e.,
|
||||
not varying more than your target uncertainty for the
|
||||
entire 2 seconds.
|
||||
|
||||
</itemize>
|
||||
|
||||
As you might have guessed, the comedi library has functions
|
||||
to help you in your quest to accurately measure slowly varying
|
||||
inputs. I use these functions to measure thermocouple voltages
|
||||
-- actually, the library functions came from a section of code
|
||||
that was previously part of the thermocouple reading program.
|
||||
|
||||
The comedi self-calibration utility also uses these functions.
|
||||
On some hardware, it is possible to tell it to measure an
|
||||
internal stable voltage reference, which is typically going
|
||||
to be very slowly varying -- on the kilosecond time scale
|
||||
or more. So it is reasonable to measure millions of samples,
|
||||
to get a very accurate measurement of the A/D converter output
|
||||
value that corresponds to the voltage reference. Sometimes,
|
||||
however, this is overkill, since there is no need to
|
||||
perform a part-per-million calibration to a standard that
|
||||
is only accurate to part-per-thousand.
|
||||
|
||||
|
||||
<p>
|
||||
<sect1>Commands
|
||||
<label id="command_section">
|
||||
<p>
|
||||
|
||||
|
||||
Many data acquisition devices have the capability to directly
|
||||
control acquisition using either an on-board timer or an external
|
||||
triggering input. Comedi commands are used to control this kind
|
||||
of acquisition. The <ref id="comedi_cmd" name="comedi_cmd"> structure is
|
||||
used to control acquisition and query the capabilities of a device
|
||||
(see also <ref id="comedi_command" name="comedi_command()">,
|
||||
<ref id="comedi_command_test" name="comedi_command_test()">, and
|
||||
<ref id="comedi_get_cmd_src_mask" name="comedi_get_cmd_src_mask()">).
|
||||
|
||||
Commands specify a particular data acquisition sequence, which
|
||||
is comprised of a number of scans. Each scan is comprised of
|
||||
a number of conversions, which usually corresponds to a single
|
||||
A/D or D/A conversion. The start and end of the sequence, and
|
||||
the start and end of each scan, and each conversion is called an
|
||||
event.
|
||||
|
||||
Each of these 5 types of events are caused by a triggering
|
||||
source, specified through the <tt/*_src/ members of the
|
||||
<ref id="comedi_cmd" name="comedi_cmd"> structure. The source types are:
|
||||
|
||||
<itemize>
|
||||
<item>TRIG_NONE: don't ever cause an event
|
||||
<item>TRIG_NOW: cause event to occur immediately
|
||||
<item>TRIG_FOLLOW: see notes below
|
||||
<item>TRIG_TIME: cause event to occur at a particular time
|
||||
<item>TRIG_TIMER: cause event to occur repeatedly at a specific rate
|
||||
<item>TRIG_COUNT: cause event when count reaches specific value
|
||||
<item>TRIG_EXT: external signal causes event
|
||||
<item>TRIG_INT: internal signal causes event
|
||||
<item>TRIG_OTHER: driver-specific meaning
|
||||
</itemize>
|
||||
|
||||
Not all triggers are applicable to all events. Supported triggers
|
||||
for specific events depend significantly on your particular
|
||||
device. The <ref id="comedi_get_cmd_src_mask" name="comedi_get_cmd_src_mask()">
|
||||
function is useful for determining what triggers a subdevice supports.
|
||||
|
||||
For every trigger, there is a corresponding
|
||||
argument (the <tt/*_arg/ members of the <ref id="comedi_cmd" name="comedi_cmd">
|
||||
structure) whose meaning depends on the type of trigger. The meanings
|
||||
of the arguments are as follows:
|
||||
|
||||
TRIG_NONE is typically used only as a <tt/stop_src/. The argument for TRIG_NONE
|
||||
is reserved and should be set to 0.
|
||||
|
||||
TRIG_NOW is most often used as a <tt/start_src/. The argument for TRIG_NOW is
|
||||
the number of nanoseconds between when the command is issued and when
|
||||
the event should occur. In the case of using TRIG now as a <tt/start_src/,
|
||||
it indicates a delay between issuing the command and the start of
|
||||
acquisition. Most drivers only support a delay of 0.
|
||||
|
||||
TRIG_FOLLOW is a special type of trigger for events that trigger on
|
||||
the completion of some other, logically connected event. The argument
|
||||
is reserved and should be set to 0. When used
|
||||
as a <tt/scan_begin_src/, it indicates that a trigger should occur as a
|
||||
logical continuation of convert events. This is done in order to
|
||||
properly describe boards that do not have separate timers for
|
||||
convert and scan_begin events. When used as a <tt/start_src/ for analog
|
||||
output subdevices, it indicates that conversion of output samples
|
||||
should begin when samples are written to the buffer.
|
||||
|
||||
TRIG_TIME is reserved for future use.
|
||||
|
||||
TRIG_TIMER is most often used as a <tt/convert_src/, a <tt/scan_begin_src/, or
|
||||
both. It indicates that triggers should occur at a specific rate.
|
||||
The argument specifies the interval between triggers in nanoseconds.
|
||||
|
||||
TRIG_COUNT is used for <tt/scan_end_src/ and <tt/stop_src/. It indicates that
|
||||
a trigger should occur when the specified number of corresponding
|
||||
lower-level triggers (convert and scan_begin, respectively) occur.
|
||||
The argument is the count of lower-level triggers.
|
||||
|
||||
TRIG_EXT can be useful as any of the trigger sources. It indicates
|
||||
that an external digital line should be used to trigger the event.
|
||||
The exact meaning of digital line is device-dependent. Some devices
|
||||
have one dedicated line, others may allow generic digital input
|
||||
lines to be used. The argument indicates the particular external
|
||||
line to use as the trigger.
|
||||
|
||||
TRIG_INT is typically used as a <tt/start_src/. This trigger occurs when
|
||||
the application performs an INSN_INTTRIG instruction. Using TRIG_INT
|
||||
is a method by which the application can accurately record the time of
|
||||
the start of acquisition, since the parsing and setup time of a
|
||||
particular command may be significant. The argument associated with
|
||||
TRIG_INT is reserved and should be set to 0.
|
||||
|
||||
TRIG_OTHER can be useful as any of the trigger sources. The exact
|
||||
meaning of TRIG_OTHER is driver-specific, and implements a feature
|
||||
that otherwise does not fit into the command interface. Configuration
|
||||
of TRIG_OTHER features are done by INSN_CONFIG insns. The argument
|
||||
is reserved and should be set to 0.
|
||||
|
||||
Ths <tt/subdev/ member of the <ref id="comedi_cmd" name="comedi_cmd">
|
||||
structure is the index of the subdevice the command is intended for. The
|
||||
<ref id="comedi_find_subdevice_by_type" name="comedi_find_subdevice_by_type()">
|
||||
function can be useful in discovering the index of your desired subdevice.
|
||||
|
||||
The <tt/chanlist/ member of the <ref id="comedi_cmd" name="comedi_cmd">
|
||||
structure should point to an array whose number of elements is specificed by <tt/chanlist_len/
|
||||
(this will generally be the same as the scan_end_arg).
|
||||
The chanlist specifies the sequence of channels and gains (and analog references)
|
||||
that should be stepped through for each scan. The elements of the chanlist array
|
||||
should be initialized by packing the channel, range and reference information
|
||||
together with the <ref id="CR_PACK" name="CR_PACK()"> macro.
|
||||
|
||||
The <tt/data/ and <tt/data_len/ members can be safely ignored when issueing commands
|
||||
from a user-space program. They only have meaning when a command is sent from a kernel
|
||||
module using the kcomedilib interface, in which case they specify the buffer where
|
||||
the driver should write/read its data to/from.
|
||||
|
||||
The final member of the <ref id="comedi_cmd" name="comedi_cmd"> structure is <tt/flags/.
|
||||
The following flags are valid, and can be bitwise-or'd together.
|
||||
|
||||
<itemize>
|
||||
<item>TRIG_BOGUS: do the motions??
|
||||
<item>TRIG_DITHER: enable dithering??
|
||||
<item>TRIG_DEGLITCH: enable deglitching??
|
||||
<item>TRIG_RT: ask driver to use a hard real-time interrupt handler. This will
|
||||
reduce latency in handling interrupts from your data aquisition hardware. It can
|
||||
be useful if you are sampling at high frequency, or if your hardware has a small onboard
|
||||
fifo. You must have a real-time kernel (RTAI or RTLinux) and must compile
|
||||
comedi with real-time support or this flag will do nothing.
|
||||
<item>TRIG_CONFIG: perform configuration, not triggering. This is a legacy of the
|
||||
deprecated comedi_trig_struct, and has no function at present.
|
||||
<item>TRIG_WAKE_EOS: some drivers will change their behaviour when this flag is set,
|
||||
trying to transfer data at the end of every scan (instead of, for example, passing
|
||||
data in chunks whenever the board's onboard fifo is half full). This flag
|
||||
may degrade a driver's performance at high frequencies.
|
||||
<item>TRIG_WRITE: write to bidirectional devices. Could be useful in principle, if someone
|
||||
wrote a driver that supported commands for a digital i/o device that could do either
|
||||
input or output.
|
||||
</itemize>
|
||||
There are also a few flags that indicate how timing arguments should be rounded
|
||||
if the hardware cannot achieve the exact timing requested.
|
||||
<itemize>
|
||||
<item>TRIG_ROUND_NEAREST: round to nearest supported timing period, the default.
|
||||
<item>TRIG_ROUND_DOWN: round period down.
|
||||
<item>TRIG_ROUND_UP: round period up.
|
||||
<item>TRIG_ROUND_UP_NEXT: this one doesn't do anything, and I don't know what it was intended
|
||||
to do??
|
||||
</itemize>
|
||||
|
||||
<p>
|
||||
|
||||
The typical sequence for executing a command is to first send
|
||||
the command through
|
||||
<ref id="comedi_command_test" name="comedi_command_test()">
|
||||
once or twice. The test will check that the command is valid for the particular
|
||||
device, and often makes some adjustments to the command arguments, which
|
||||
can then be read back by the user to see the actual values used. The
|
||||
command is executed with
|
||||
<ref id="comedi_command" name="comedi_command()">. For input/output commands, data
|
||||
is read from or written to the device file /dev/comedi[0..3] you are using.
|
||||
|
987
doc/docbook/reference.sgml
Normal file
987
doc/docbook/reference.sgml
Normal file
|
@ -0,0 +1,987 @@
|
|||
<!DOCTYPE section PUBLIC "-//OASIS//DTD DocBook V3.1//EN">
|
||||
|
||||
<section>
|
||||
<title>
|
||||
Constants and Macros
|
||||
</title>
|
||||
|
||||
<section id="ref-macro-CR-PACK">
|
||||
<title>
|
||||
CR_PACK
|
||||
</title>
|
||||
|
||||
<para>
|
||||
CR_PACK is used to initialize the elements of the chanlist array in the
|
||||
comedi_cmd structure, and the chanspec member
|
||||
of the comedi_insn structure.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The channel argument is the channel you wish to use, with the channel
|
||||
numbering starting at zero.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The range is an index, starting at zero, whose meaning
|
||||
is device dependent. The
|
||||
comedi_get_n_ranges() and
|
||||
comedi_get_range functions
|
||||
are useful in discovering information about the available ranges.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The aref argument indicates what reference you want the device to use. It
|
||||
can be any of the following:
|
||||
</para>
|
||||
|
||||
<variablelist>
|
||||
<varlistentry>
|
||||
<term>AREF_GROUND</term>
|
||||
<listitem>
|
||||
<para>
|
||||
is for inputs/outputs referenced to ground
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term>AREF_COMMON</term>
|
||||
<listitem>
|
||||
<para>
|
||||
is for a `common' reference (the low inputs of all the channels are tied
|
||||
together, but are isolated from ground)
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term>AREF_DIFF</term>
|
||||
<listitem>
|
||||
<para>
|
||||
is for differential inputs/outputs
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term>AREF_OTHER</term>
|
||||
<listitem>
|
||||
<para>
|
||||
is for any reference that does not fit into the above categories
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
|
||||
<para>
|
||||
Particular drivers may or may not use the AREF flags. If they are
|
||||
not supported, they are silently ignored.
|
||||
</para>
|
||||
</section>
|
||||
|
||||
<section id="ref-macro-RANGE-LENGTH">
|
||||
<title>
|
||||
RANGE_LENGTH (deprecated)
|
||||
</title>
|
||||
|
||||
<para>
|
||||
Rangetype values are library-internal tokens that represent an
|
||||
array of range information structures. These numbers are primarily
|
||||
used for communication between the kernel and library.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The RANGE_LENGTH() macro returns the length of the array that is
|
||||
specified by the rangetype token.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The RANGE_LENGTH() macro is deprecated, and should not be used in
|
||||
new applications. It is scheduled to be removed from the header
|
||||
file at version 1.0. Binary compatibility may be broken for version
|
||||
1.1.
|
||||
</para>
|
||||
</section>
|
||||
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>
|
||||
Data Types and Structures
|
||||
</title>
|
||||
|
||||
<section id="ref-type-comedi-t">
|
||||
<title>
|
||||
comedi_t
|
||||
</title>
|
||||
|
||||
<para>
|
||||
The data type comedi_t is used to represent an open Comedi
|
||||
device. A valid comedi_t pointer is returned by a successful
|
||||
call to comedi_open(), and should be used for subsequent
|
||||
access to the device.
|
||||
It is a transparent type, and pointers to type comedi_t
|
||||
should not be dereferenced by the application.
|
||||
</para>
|
||||
|
||||
</section>
|
||||
|
||||
|
||||
<section id="ref-type-sampl-t">
|
||||
<title>
|
||||
sampl_t
|
||||
</title>
|
||||
|
||||
|
||||
<para>
|
||||
The data type sampl_t is one of the generic types used to represent
|
||||
data values in Comedilib. It is used in a few places where a data type
|
||||
shorter than lsampl_t is useful. On most architectures, sampl_t
|
||||
is defined to be uint16.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Most drivers represent data trasferred by read() and write()
|
||||
using sampl_t. Applications should check the subdevice flag
|
||||
SDF_LSAMPL to determine if the subdevice uses sampl_t or
|
||||
lsampl_t.
|
||||
</para>
|
||||
|
||||
</section>
|
||||
|
||||
<section id="ref-type-lsampl-t">
|
||||
<title>
|
||||
lsampl_t
|
||||
</title>
|
||||
|
||||
<para>
|
||||
The data type lsampl_t is the data type typically used to represent
|
||||
data values in libcomedi. On most architectures, lsampl_t is
|
||||
defined to be uint32.
|
||||
</para>
|
||||
</section>
|
||||
|
||||
|
||||
<section id="ref-type-comedi-trig">
|
||||
<title>
|
||||
comedi_trig (deprecated)
|
||||
</title>
|
||||
|
||||
<programlisting>
|
||||
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];
|
||||
}
|
||||
</programlisting>
|
||||
|
||||
<para>
|
||||
The comedi_trig structure is a control structure used by the
|
||||
COMEDI_TRIG ioctl, an older method of communicating
|
||||
instructions to the driver and hardware. Use of comedi_trig is
|
||||
deprecated, and should not be used in new applications.
|
||||
</para>
|
||||
</section>
|
||||
|
||||
<section id="ref-type-comedi-sv-t">
|
||||
<title>
|
||||
comedi_sv_t
|
||||
</title>
|
||||
|
||||
<programlisting>
|
||||
struct comedi_sv_struct{
|
||||
comedi_t *dev;
|
||||
unsigned int subdevice;
|
||||
unsigned int chan;
|
||||
|
||||
/* range policy */
|
||||
int range;
|
||||
int aref;
|
||||
|
||||
/* number of measurements to average (for ai) */
|
||||
int n;
|
||||
|
||||
lsampl_t maxdata;
|
||||
}
|
||||
</programlisting>
|
||||
|
||||
<para>
|
||||
The comedi_sv_t structure is used by the comedi_sv_*()
|
||||
functions to provide a simple method of accurately measuring
|
||||
slowly varying inputs. See the relevant section for more
|
||||
details.
|
||||
</para>
|
||||
</section>
|
||||
|
||||
<section id="ref-type-comedi-cmd">
|
||||
<title>
|
||||
comedi_cmd
|
||||
</title>
|
||||
|
||||
<programlisting>
|
||||
typedef struct comedi_cmd_struct comedi_cmd;
|
||||
|
||||
struct comedi_cmd_struct{
|
||||
unsigned int subdev;
|
||||
unsigned int flags;
|
||||
|
||||
unsigned int start_src;
|
||||
unsigned int start_arg;
|
||||
|
||||
unsigned int scan_begin_src;
|
||||
unsigned int scan_begin_arg;
|
||||
|
||||
unsigned int convert_src;
|
||||
unsigned int convert_arg;
|
||||
|
||||
unsigned int scan_end_src;
|
||||
unsigned int scan_end_arg;
|
||||
|
||||
unsigned int stop_src;
|
||||
unsigned int stop_arg;
|
||||
|
||||
unsigned int *chanlist;
|
||||
unsigned int chanlist_len;
|
||||
|
||||
sampl_t *data;
|
||||
unsigned int data_len;
|
||||
};
|
||||
</programlisting>
|
||||
|
||||
<para>
|
||||
More information on using commands can be found in the
|
||||
command section.
|
||||
</para>
|
||||
</section>
|
||||
|
||||
<section id="ref-type-comedi-insn">
|
||||
<title>
|
||||
comedi_insn
|
||||
</title>
|
||||
|
||||
<programlisting>
|
||||
typedef struct comedi_insn_struct comedi_insn;
|
||||
|
||||
struct comedi_insn_struct{
|
||||
unsigned int insn;
|
||||
unsigned int n;
|
||||
lsampl_t *data;
|
||||
unsigned int subdev;
|
||||
unsigned int chanspec;
|
||||
unsigned int unused[3];
|
||||
};
|
||||
</programlisting>
|
||||
|
||||
<para>
|
||||
Comedi instructions are described by the comedi_insn structure.
|
||||
Applications send instructions to the driver in order to preform
|
||||
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
|
||||
describe acquisition that involves timers or external events.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The field insn determines the type of instruction that is sent
|
||||
to the driver. Valid instruction types are
|
||||
</para>
|
||||
|
||||
<variablelist>
|
||||
<varlistentry>
|
||||
<term>
|
||||
INSN_READ
|
||||
</term>
|
||||
<listitem>
|
||||
<para>
|
||||
read values from an input channel
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term>
|
||||
INSN_WRITE
|
||||
</term>
|
||||
<listitem>
|
||||
<para>
|
||||
write values to an output channel
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term>
|
||||
INSN_BITS
|
||||
</term>
|
||||
<listitem>
|
||||
<para>
|
||||
read/write values on multiple digital I/O channels
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term>
|
||||
INSN_CONFIG
|
||||
</term>
|
||||
<listitem>
|
||||
<para>
|
||||
configure a subdevice
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term>
|
||||
INSN_GTOD
|
||||
</term>
|
||||
<listitem>
|
||||
<para>
|
||||
read a timestamp, identical to gettimeofday()
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term>
|
||||
INSN_WAIT
|
||||
</term>
|
||||
<listitem>
|
||||
<para>
|
||||
wait a specified number of nanoseconds
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
|
||||
<para>
|
||||
The number of samples to read or write, or the size of the configuration
|
||||
structure is specified by the field n, and the buffer for those
|
||||
samples by data. The field subdev is the subdevice index
|
||||
that the instruction is sent to. The field chanspec specifies
|
||||
the channel, range, and analog reference (if applicable).
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Instructions can be sent to drivers using comedi_do_insn().
|
||||
Multiple instructions can be sent to drivers in the same system
|
||||
call using comedi_do_insnlist().
|
||||
</para>
|
||||
</section>
|
||||
|
||||
<section id="ref-type-comedi-range">
|
||||
<title>
|
||||
comedi_range
|
||||
</title>
|
||||
|
||||
<programlisting>
|
||||
typedef struct{
|
||||
double min;
|
||||
double max;
|
||||
unsigned int unit;
|
||||
}comedi_range;
|
||||
</programlisting>
|
||||
|
||||
<para>
|
||||
The comedi_range structure conveys part of the information
|
||||
necessary to translate sample values to physical units, in particular,
|
||||
the endpoints of the range and the physical unit type. The
|
||||
physical unit type is specified by the field unit, which may
|
||||
take the values UNIT_volt for volts, UNIT_mA for milliamps,
|
||||
or UNIT_none for unitless. The endpoints are specified by
|
||||
the fields min and max.
|
||||
</para>
|
||||
</section>
|
||||
|
||||
<section id="ref-type-comedi-krange">
|
||||
<title>
|
||||
comedi_krange
|
||||
</title>
|
||||
|
||||
<programlisting>
|
||||
struct comedi_krange_struct{
|
||||
int min;
|
||||
int max;
|
||||
unsigned int flags;
|
||||
};
|
||||
</programlisting>
|
||||
|
||||
<para>
|
||||
The comedi_krange structure is used to transfer range information
|
||||
between the driver and Comedilib, and should not normally be used
|
||||
by applications. The structure conveys the same information as the
|
||||
comedi_range structure, except the fields min and max
|
||||
are integers, multiplied by a factor of 1000000 compared to the
|
||||
counterparts in comedi_range.
|
||||
</para>
|
||||
</section>
|
||||
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>
|
||||
Interface reference
|
||||
</title>
|
||||
|
||||
<para>
|
||||
This chapter is meant to be a reference for some of the advanced
|
||||
features of Comedi.
|
||||
</para>
|
||||
|
||||
<section>
|
||||
<title>
|
||||
Digital input combining machines
|
||||
</title>
|
||||
|
||||
<para>
|
||||
When one or several digital inputs are used to modify an output
|
||||
value, either an accumulator or a single digital line or bit,
|
||||
a bitfield structure is typically used in the Comedi interface.
|
||||
The digital inputs have two properties, "sensitive" inputs and
|
||||
"modifier" inputs. Edge transitions on sensitive inputs cause
|
||||
changes in the output signal, whereas modifier inputs change the
|
||||
effect of edge transitions on sensitive inputs. Note that inputs
|
||||
can be both modifier inputs and sensitive inputs.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
For simplification purposes, it is assumed that multiple digital
|
||||
inputs do not change simultaneously.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The combined state of the modifier inputs determine a modifier
|
||||
state. For each combination of modifier state and sensitive
|
||||
input, there is a set of bits that determine the effect on the
|
||||
output value due to positive or negative transitions of the
|
||||
sensitive input. For each transition direction, there are two
|
||||
bits defined as follows:
|
||||
</para>
|
||||
|
||||
<variablelist>
|
||||
<varlistentry>
|
||||
<term>
|
||||
00
|
||||
</term>
|
||||
<listitem>
|
||||
<para>
|
||||
transition is ignored
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term>
|
||||
01
|
||||
</term>
|
||||
<listitem>
|
||||
<para>
|
||||
accumulator is incremented, or output is set
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term>
|
||||
10
|
||||
</term>
|
||||
<listitem>
|
||||
<para>
|
||||
accumulator is decremented, or output is cleared
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term>
|
||||
11
|
||||
</term>
|
||||
<listitem>
|
||||
<para>
|
||||
reserved
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
|
||||
<para>
|
||||
For example, a simple digital follower is specified by the bit
|
||||
pattern 01 10, because it sets the output on positive transitions
|
||||
of the input, and clears the output on negative transitions. A
|
||||
digital inverter is similarily 10 01. These systems have only
|
||||
one sensitive input.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
As another example, a simple up counter, which increments on
|
||||
positive transitions of one input, is specified by 01 00. This
|
||||
system has only one sensitive input.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
When multiple digital inputs are used, the inputs are divided
|
||||
into two types, inputs which cause changes in the accumulator, and
|
||||
those that only modify the meaning of transitions on other inputs.
|
||||
Modifier inputs do not require bitfields, but there needs to be
|
||||
a bitfield of length 4*(2^(N-1)) for each edge sensitive input,
|
||||
where N is the total number of inputs. Since N is usually 2 or
|
||||
3, with only one edge sensitive input, the scaling issues are
|
||||
not significant.
|
||||
</para>
|
||||
</section>
|
||||
|
||||
|
||||
<section>
|
||||
<title>
|
||||
INSN_CONFIG
|
||||
</title>
|
||||
|
||||
<para>
|
||||
Configuration instructions are used to access device and driver features
|
||||
that do not fit well into other parts of the Comedi interface. This
|
||||
includes changing the direction of configurable digital I/O lines,
|
||||
configuring complex triggering engines, and counter/timer configuration.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
If a specified ID is not supported, the driver must return -EINVAL.
|
||||
</para>
|
||||
|
||||
|
||||
<section>
|
||||
<title>
|
||||
Digital I/O configuration
|
||||
</title>
|
||||
|
||||
<para></para>
|
||||
<simplelist>
|
||||
<member>
|
||||
Status: Implemented
|
||||
</member>
|
||||
<member>
|
||||
ID: COMEDI_INPUT, COMEDI_OUTPUT, COMEDI_OPENDRAIN
|
||||
</member>
|
||||
<member>
|
||||
Length: 1
|
||||
</member>
|
||||
<member>
|
||||
Chanspec: used to specify channel
|
||||
</member>
|
||||
</simplelist>
|
||||
|
||||
<para>
|
||||
These IDs are used to configure direction of digital I/O lines.
|
||||
Direction is chosen by the ID. On typical devices, multiple
|
||||
channels are grouped together in blocks for determining direction.
|
||||
Configuring one channel in a block configures the entire block.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
There should also be a method to read the configuration.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Errors: Should return -EINVAL if the ID is not supported.
|
||||
</para>
|
||||
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>
|
||||
Analog conversion configuration
|
||||
</title>
|
||||
|
||||
<simplelist>
|
||||
<member>
|
||||
Status: design
|
||||
</member>
|
||||
<member>
|
||||
ID: not assigned
|
||||
</member>
|
||||
<member>
|
||||
Length:
|
||||
</member>
|
||||
<member>
|
||||
Chanspec: used to specify channel
|
||||
</member>
|
||||
</simplelist>
|
||||
|
||||
<para>
|
||||
Some devices have the capability to add white noise (dithering) to
|
||||
analog input measurement. This additional noise can then be averaged
|
||||
out, to get a more accurate measurement of the input signal. It
|
||||
should not be assumed that channels can be separately configured.
|
||||
A simple design can use 1 bit to turn this feature on/off.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Some devices have the capability of changing the glitch characteristics
|
||||
of analog output subsytems. The default (off) case should be where
|
||||
the average settling time is lowest. A simple design can use 1 bit
|
||||
to turn this feature on/off.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Some devices have a configurable analog filters as part of the analog
|
||||
input stage. A simple designe can use 1 bit to enable/disable the
|
||||
filter. Default is disabled, i.e., the filter being bypassed, or if
|
||||
the choice is between two filters, the filter with the largest
|
||||
bandwidth.
|
||||
</para>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>
|
||||
Analog Output Waveform Generation
|
||||
</title>
|
||||
|
||||
<simplelist>
|
||||
<member>
|
||||
Status: design
|
||||
</member>
|
||||
<member>
|
||||
ID: not assigned
|
||||
</member>
|
||||
<member>
|
||||
Length:
|
||||
</member>
|
||||
<member>
|
||||
Chanspec: ignored
|
||||
</member>
|
||||
</simplelist>
|
||||
|
||||
<para>
|
||||
Some devices have the ability to cyclicly loop through samples kept in
|
||||
an on-board analog output FIFO. This config should allow the user to
|
||||
enable/disable this mode.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
This config should allow the user to configure the number of samples
|
||||
to loop through. It may be necessary to configure the channels used.
|
||||
</para>
|
||||
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>
|
||||
Extended Triggering
|
||||
</title>
|
||||
|
||||
<simplelist>
|
||||
<member>
|
||||
Status: alpha
|
||||
</member>
|
||||
<member>
|
||||
ID: not assigned
|
||||
</member>
|
||||
<member>
|
||||
Chanspec: ignored
|
||||
</member>
|
||||
</simplelist>
|
||||
|
||||
<para>
|
||||
This section covers common information for all extended
|
||||
triggering configuration, and doesn't describe a particular
|
||||
type of extended trigger.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Extended triggering is used to configure triggering engines that
|
||||
do not fit into commands. In a typical programming sequence, the
|
||||
application will use configuration instructions to configure an
|
||||
extended trigger, and the issue a command, specifying TRIG_OTHER
|
||||
as one of the trigger sources.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Extended trigger configuration should be designed in such a way
|
||||
that the user can probe for valid parameters, similar to how
|
||||
command testing works. An extended trigger config instruction
|
||||
should not configure the hardware directly, rather, the configuration
|
||||
should be saved until the subsequent command is issued. This
|
||||
allows more flexibility for future interface changes.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
It has not been decided whether the config stage should return a
|
||||
token that is then used as the trigger argument in the command.
|
||||
Using tokens is one method to satisfy the problem that extended
|
||||
trigger configurations may have subtle compatiblity issues with
|
||||
other trigger sources/arguments that can only be determined at
|
||||
command test time. Passing all stages of a command test should
|
||||
only be allowed with a properly configured extended trigger.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Extended triggers must use data[1] as flags. The upper 16 bits
|
||||
are reserved and used only for flags that are common to
|
||||
all extended triggers. The lower 16 bits may be defined by the
|
||||
particular type of extended trigger.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Various types of extended triggers must use data[1] to know which
|
||||
event the extended trigger will be assigned to in the command
|
||||
structure. The possible values are an OR'd mask of the following:
|
||||
</para>
|
||||
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para>
|
||||
COMEDI_EV_START
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
COMEDI_EV_SCAN_BEGIN
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
COMEDI_EV_CONVERT
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
COMEDI_EV_SCAN_END
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
COMEDI_EV_STOP
|
||||
</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>
|
||||
Analog Triggering
|
||||
</title>
|
||||
|
||||
<simplelist>
|
||||
<member>
|
||||
Status: alpha
|
||||
</member>
|
||||
<member>
|
||||
ID: not assigned
|
||||
</member>
|
||||
<member>
|
||||
Implementation: ni_mio_common
|
||||
</member>
|
||||
<member>
|
||||
Chanspec: ignored
|
||||
</member>
|
||||
</simplelist>
|
||||
|
||||
<simplelist>
|
||||
<member>
|
||||
data 1 - trigger and combining machine configuration
|
||||
</member>
|
||||
<member>
|
||||
data 2 - analog triggering signal chanspec
|
||||
</member>
|
||||
<member>
|
||||
data 3 - primary analog level
|
||||
</member>
|
||||
<member>
|
||||
data 4 - secondary analog level
|
||||
</member>
|
||||
</simplelist>
|
||||
|
||||
<para>
|
||||
Analog triggering is described by a digital combining machine that
|
||||
has two sensitive digital inputs. The sensitive digital inputs are
|
||||
generated by configurable analog comparators. The analog comparators
|
||||
generate a digital 1 when the analog triggering signal is greater
|
||||
than the comparator level. The digital inputs are not modifier
|
||||
inputs. Note, however, there is an effective modifier due to the
|
||||
restriction that the primary analog comparator level must be less
|
||||
than the secondary analog comparator level.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
If only one analog comparator signal is used, the combining machine
|
||||
for the secondary input should be set to ignored, and the secondary
|
||||
analog level should be set to 0.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The interpretation of the chanspec and voltage levels is device
|
||||
dependent, but should correspond to similar values of the analog
|
||||
input subdevice, if possible.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Notes: Reading range information is not addressed. This makes it
|
||||
difficult to convert comparator voltages to data values.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Possible extensions: A parameter that specifies the necessary time
|
||||
that the set condition has to be true before the trigger is generated.
|
||||
A parameter that specifies the necessary time that the reset condition
|
||||
has to be true before the state machine is reset.
|
||||
</para>
|
||||
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>
|
||||
Bitfield Pattern Matching Extended Trigger
|
||||
</title>
|
||||
|
||||
<simplelist>
|
||||
<member>
|
||||
Status: design
|
||||
</member>
|
||||
<member>
|
||||
ID: not assigned
|
||||
</member>
|
||||
<member>
|
||||
Chanspec: ignored
|
||||
</member>
|
||||
</simplelist>
|
||||
|
||||
<simplelist>
|
||||
<member>
|
||||
data 1 - trigger flags
|
||||
</member>
|
||||
<member>
|
||||
data 2 - mask
|
||||
</member>
|
||||
<member>
|
||||
data 3 - pattern
|
||||
</member>
|
||||
</simplelist>
|
||||
|
||||
<para>
|
||||
The pattern matching trigger issues a trigger when all of a specifed
|
||||
set of input lines match a specified pattern. If the device allows,
|
||||
the input lines should correspond to the input lines of a digital input
|
||||
subdevice, however, this will necessarily be device dependent. Each
|
||||
possible digital line that can be matched is assigned a bit in the
|
||||
mask and pattern. A bit set in the mask indicates that the
|
||||
input line must match the corresponding bit in the pattern.
|
||||
A bit cleared in the mask indicates that the input line is ignored.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Notes: This only allows 32 bits in the pattern/mask, which may be
|
||||
too few. Devices may support selecting different sets of lines from
|
||||
which to match a pattern.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Discovery: The number of bits can be discovered by setting the mask
|
||||
to all 1's. The driver must modify this value and return -EAGAIN.
|
||||
</para>
|
||||
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>
|
||||
Counter configuration
|
||||
</title>
|
||||
|
||||
<simplelist>
|
||||
<member>
|
||||
Status: design
|
||||
</member>
|
||||
<member>
|
||||
ID: not assigned
|
||||
</member>
|
||||
<member>
|
||||
Chanspec: used to specify counter
|
||||
</member>
|
||||
</simplelist>
|
||||
|
||||
<simplelist>
|
||||
<member>
|
||||
data 1 - trigger configuration
|
||||
</member>
|
||||
<member>
|
||||
data 2 - primary input chanspec
|
||||
</member>
|
||||
<member>
|
||||
data 3 - primary combining machine configuration
|
||||
</member>
|
||||
<member>
|
||||
data 4 - secondary input chanspec
|
||||
</member>
|
||||
<member>
|
||||
data 5 - secondary combining machine configuration
|
||||
</member>
|
||||
<member>
|
||||
data 6 - latch configuration
|
||||
</member>
|
||||
</simplelist>
|
||||
|
||||
<para>
|
||||
Counters can be operated either in synchronous mode (using insn_read)
|
||||
or asynchronous mode (using commands), similar to analog input subdevices.
|
||||
The input signal for both modes is the accumulator.
|
||||
Commands on counter subdevices are almost always specified using
|
||||
scan_begin_src=TRIG_OTHER, with the counter configuration also serving
|
||||
as the extended configuration for the scan begin source.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Counters are made up of an accumulator and a combining machine that
|
||||
determines when the accumulator should be incremented or decremented
|
||||
based on the values of the input signals. The combining machine
|
||||
optionally determines when the accumulator should be latched and
|
||||
put into a buffer. This feature is used in asynchronous mode.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Notes: How to access multiple pieces of data acquired at each event?
|
||||
</para>
|
||||
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>
|
||||
One source plus auxiliary counter configuration
|
||||
</title>
|
||||
|
||||
<simplelist>
|
||||
<member>
|
||||
Status: design
|
||||
</member>
|
||||
<member>
|
||||
ID: not assigned
|
||||
</member>
|
||||
<member>
|
||||
Chanspec: ?
|
||||
</member>
|
||||
</simplelist>
|
||||
|
||||
<para>
|
||||
data[1] is flags, including the flags for the command triggering
|
||||
configuration. If a command is not subsequently issued on the
|
||||
subdevice, the command triggering portion of the flags are ignored.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
data[2] determines the mode of operation. The mode of operation
|
||||
is actually a bitfield that encodes what to do for various
|
||||
transitions of the source signals.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
data[3] and data[4] determine the primary source for the counter,
|
||||
similar to _src and _arg used in commands.
|
||||
</para>
|
||||
|
||||
|
||||
<para>
|
||||
Notes: How to specify which events cause a latch and push, and what
|
||||
should get latched.
|
||||
</para>
|
||||
</section>
|
||||
|
||||
</section>
|
||||
</section>
|
||||
|
||||
|
672
doc/docbook/tutorial.sgml
Normal file
672
doc/docbook/tutorial.sgml
Normal file
|
@ -0,0 +1,672 @@
|
|||
<p>
|
||||
<sect1>Configuring comedi for your hardware
|
||||
<p>
|
||||
|
||||
|
||||
I assume that your hardware device is in your computer, and that
|
||||
you know the relevant details about it, i.e., what kind of card
|
||||
it is, the I/O base, the IRQ, jumper settings related to input
|
||||
ranges, etc.
|
||||
|
||||
To tell the comedi kernel module that you have a particular device, and
|
||||
some information about it, you will be running the <tt>comedi_config</tt>
|
||||
command. Perhaps you should read the man page now.
|
||||
|
||||
In this tutorial, I will go through the process of configuring comedi
|
||||
for two devices, a National Instruments AT-MIO-16E-10
|
||||
and a Data Translation DT2821-F-8DI.
|
||||
|
||||
The NI board is plug-and-play, and the man page tells me that I need
|
||||
to configure the PnP part of the board with isapnptools. The isapnptools
|
||||
package is a little cryptic, but the concepts are simple. Once I
|
||||
learned how to use it, I settled on a /etc/isapnp.conf file that
|
||||
contained the lines:
|
||||
|
||||
|
||||
<tscreen><verb>
|
||||
# ANSI string -->National Instruments, AT-MIO-16E-10<--
|
||||
(CONFIGURE NIC2400/10725401 (LD 0
|
||||
(IO 0 (BASE 0x0260))
|
||||
(INT 0 (IRQ 3 (MODE +E)))
|
||||
# (DMA 0 (CHANNEL 5))
|
||||
# (DMA 1 (CHANNEL 6))
|
||||
(ACT Y)
|
||||
))
|
||||
</verb></tscreen>
|
||||
|
||||
|
||||
It also contains a few lines about overall configuration and about my
|
||||
sound card. I found out after a bit of trial-and-error that the NI
|
||||
board does not always work with interrupts other than IRQ 3. YMMV.
|
||||
Currently, the driver doesn't use DMA, but it may in the future, so
|
||||
I commented out the DMA lines. It is a curious fact that the device
|
||||
ignores the IRQ and DMA information given here, however, I keep the
|
||||
information here to remind myself that the numbers aren't arbitrary.
|
||||
|
||||
When I run comedi_config (as root, of course), I provide the same
|
||||
information. Since I want to have the board configured every time
|
||||
I boot, I put the line
|
||||
|
||||
<tscreen><verb>
|
||||
/usr/sbin/comedi_config /dev/comedi0 atmio-E 0x260,3
|
||||
</verb></tscreen>
|
||||
|
||||
into <tt>/etc/rc.d/rc.local</tt>. You can, of course, run this command at
|
||||
a command prompt. The man page tells me that the option list
|
||||
is supposed to be "(I/O base),(IRQ)", so I used the same numbers
|
||||
as I put in /etc/isapnp.conf, i.e., 0x260,3.
|
||||
|
||||
For the Data Translation board, I need to have a list of the
|
||||
jumper settings. Fortunately, I wrote them all down in the
|
||||
manual -- I hope they are still correct. However, I had to
|
||||
open the case to figure out which board in the series I had.
|
||||
It is a DT2821-f-8di. The man page of comedi_config tells
|
||||
me that I need to know the I/O base, IRQ, DMA 1, DMA 2. However,
|
||||
since I wrote the driver, I know that it also recognizes the
|
||||
differential/single-ended and unipolar/bipolar jumpers. As always,
|
||||
the source is the final authority, and looking in module/dt282x.c
|
||||
tells me that the options list is interpreted as:
|
||||
|
||||
<itemize>
|
||||
<item>I/O base
|
||||
<item>IRQ
|
||||
<item>1=differential, 0=single ended
|
||||
<item>ai 0=unipolar, 1=bipolar
|
||||
<item>ao0 0=unipolar, 1=bipolar
|
||||
<item>ao1 0=unipolar, 1=bipolar
|
||||
<item>dma1
|
||||
<item>dma2
|
||||
</itemize>
|
||||
|
||||
(ai=analog input, ao=analog output.) From this, I decide that
|
||||
the appropriate options list is
|
||||
|
||||
<tscreen><verb>
|
||||
0x200,4,,1,1,1
|
||||
</verb></tscreen>
|
||||
|
||||
I left the differential/single-ended number blank, since the
|
||||
driver already knowns (from the board name), that it is
|
||||
differential. I also left the DMA numbers blank, since I
|
||||
don't want the driver to use DMA. (Don't want it to interfere
|
||||
with my sound card -- life is full of difficult choices.)
|
||||
Keep in mind that things commented in the source, but not in
|
||||
the documentation are about as likely to change as the weather,
|
||||
so I put good comments next to the following line when I put
|
||||
it in rc.local.
|
||||
|
||||
<tscreen><verb>
|
||||
/usr/sbin/comedi_config /dev/comedi1 dt2821-f-8di 0x200,4,,1,1,1
|
||||
</verb></tscreen>
|
||||
|
||||
So now I think that I have my boards configured correctly.
|
||||
Since data acquisition boards are not typically well-engineered,
|
||||
comedi sometimes can't figure out if the board is actually there.
|
||||
If it can't, it assumes you are right. Both of these boards
|
||||
are well-made, so comedi will give me an error message if it
|
||||
can't find them. The comedi kernel module, since it is a part
|
||||
of the kernel, prints messages to the kernel logs, which you
|
||||
can access through the command 'dmesg' or /var/log/messages.
|
||||
Here is a configuration failure (from dmesg):
|
||||
|
||||
<tscreen><verb>
|
||||
comedi0: ni_E: 0x0200 can't find board
|
||||
</verb></tscreen>
|
||||
|
||||
When it does work, I get:
|
||||
|
||||
<tscreen><verb>
|
||||
comedi0: ni_E: 0x0260 at-mio-16e-10 ( irq = 3 )
|
||||
</verb></tscreen>
|
||||
|
||||
Note that it also correctly identified my board.
|
||||
|
||||
|
||||
<p>
|
||||
<sect1>Getting information from comedi
|
||||
<p>
|
||||
|
||||
|
||||
So now that we have comedi talking to the hardware, we want to
|
||||
talk to comedi. Here's some pretty low-level information --
|
||||
it's sometimes useful for debugging:
|
||||
|
||||
<p>
|
||||
|
||||
<tscreen><verb>
|
||||
cat /proc/comedi
|
||||
</verb></tscreen>
|
||||
|
||||
Right now, on my computer, this command gives:
|
||||
|
||||
<tscreen><verb>
|
||||
comedi version 0.6.4
|
||||
format string
|
||||
0: atmio-E at-mio-16e-10 7
|
||||
1: dt282x dt2821-f-8di 4
|
||||
</verb></tscreen>
|
||||
|
||||
This is a feature that is not well-developed yet. Basically, it
|
||||
currently tells you driver name, device name, and number of
|
||||
subdevices.
|
||||
|
||||
In the <tt>demo/</tt> directory, there is a command called
|
||||
<tt>info</tt>, which provides information about each subdevice on the
|
||||
board. The output of it is rather long, since I have 7
|
||||
subdevices (4 or fewer is common for other boards.)
|
||||
Here's part of the output of the NI board (which
|
||||
is on <tt>/dev/comedi0</tt>.) ('demo/info /dev/comedi0')
|
||||
|
||||
<tscreen><verb>
|
||||
overall info:
|
||||
version code: 0x000604
|
||||
driver name: atmio-E
|
||||
board name: at-mio-16e-10
|
||||
number of subdevices: 7
|
||||
subdevice 0:
|
||||
type: 1 (unknown)
|
||||
number of channels: 16
|
||||
max data value: 4095
|
||||
</verb>
|
||||
...
|
||||
</tscreen>
|
||||
|
||||
The overall info gives information about the device -- basically
|
||||
the same information as /proc/comedi.
|
||||
|
||||
This board has 7 subdevices. Devices are separated into
|
||||
subdevices that each have a distinct purpose -- e.g., analog
|
||||
input, analog output, digital input/output. This board also
|
||||
has an EEPROM and calibration DACs that are also subdevices.
|
||||
|
||||
Subdevice 0 is the analog input subdevice. You would have
|
||||
known this from the 'type: 1 (unknown)' line, if I've updated
|
||||
demo/info recently, because it would say 'type: 1 (analog input)'
|
||||
instead. The other lines should be self-explanitory. Comedi
|
||||
has more information about the device, but demo/info doesn't
|
||||
currently display this.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<sect>Writing programs that use comedi and comedilib
|
||||
<p>
|
||||
|
||||
<sect1>Your first comedi program
|
||||
<p>
|
||||
|
||||
This example requires a card that has analog or
|
||||
digital input. Right to the source:
|
||||
|
||||
<tscreen><verb>
|
||||
#include <stdio.h> /* for printf() */
|
||||
#include <comedilib.h>
|
||||
|
||||
int subdev = 0; /* change this to your input subdevice */
|
||||
int chan = 0; /* change this to your channel */
|
||||
int range = 0; /* more on this later */
|
||||
int aref = AREF_GROUND; /* more on this later */
|
||||
|
||||
int main(int argc,char *argv[])
|
||||
{
|
||||
comedi_t *it;
|
||||
lsampl_t data;
|
||||
|
||||
it=comedi_open("/dev/comedi0");
|
||||
|
||||
comedi_data_read(it,subdev,chan,range,aref,&data);
|
||||
|
||||
printf("%d\n",data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
</verb></tscreen>
|
||||
|
||||
|
||||
Should be understandable: open the device, get the data,
|
||||
print it out. This is basically the guts of <tt>demo/inp.c</tt>,
|
||||
without error checking or fancy options.
|
||||
Compile it using
|
||||
|
||||
<tscreen><verb>
|
||||
cc tut1.c -lcomedi -o tut1
|
||||
</verb></tscreen>
|
||||
|
||||
A few notes: The range variable tells comedi which gain
|
||||
to use when measuring an analog voltage. Since we don't
|
||||
know (yet) which numbers are valid, or what each means,
|
||||
we'll use 0, because it won't cause errors. Likewise with
|
||||
aref, which determines the analog reference used.
|
||||
|
||||
|
||||
<p>
|
||||
<sect1>Converting samples to voltages
|
||||
<p>
|
||||
|
||||
If you selected an analog input subdevice, you probably noticed
|
||||
that the output of <tt>tut1</tt> is a number between
|
||||
0 and 4095, or 0 and 65535, depending on the number of bits
|
||||
in the A/D converter. Comedi samples are <bf>always</bf> unsigned,
|
||||
with 0 representing the lowest voltage of the ADC, and 4095
|
||||
the highest. Comedi compensates for
|
||||
anything else the manual for your device says. However,
|
||||
you probably prefer to have this number translated to
|
||||
a voltage. Naturally, as a good programmer, your first
|
||||
question is: "How do I do this in a device-independent
|
||||
manner?"
|
||||
|
||||
Most devices give you a choice of gain and unipolar/bipolar
|
||||
input, and Comedi allows you to select which of these to
|
||||
use. This parameter is called the "range parameter", since
|
||||
it specifies the "input range" for analog input (or "output range"
|
||||
for analog output.) The range parameter represents both the gain
|
||||
and the unipolar/bipolar aspects.
|
||||
|
||||
Comedi keeps the number of available ranges and the largest
|
||||
sample value for each subdevice/channel combination. (Some
|
||||
devices allow different input/output ranges for different
|
||||
channels in a subdevice.)
|
||||
|
||||
The largest sample value can be found using the function:
|
||||
|
||||
comedi_get_maxdata()
|
||||
|
||||
The number of available ranges can be found using the function:
|
||||
|
||||
comedi_get_n_ranges()
|
||||
|
||||
For each value of the range parameter for a particular
|
||||
subdevice/channel, you can get range information using the
|
||||
function:
|
||||
|
||||
ptr=comedi_get_range(comedi_file,subdevice,channel,
|
||||
range)
|
||||
|
||||
which returns a pointer to a comedi_range structure.
|
||||
The comedi_range structure looks like
|
||||
|
||||
<p>
|
||||
<tscreen><verb>
|
||||
typedef struct{
|
||||
double min;
|
||||
double max;
|
||||
unsigned int unit;
|
||||
}comedi_range;
|
||||
</verb></tscreen>
|
||||
|
||||
The structure element 'min' represents
|
||||
the voltage corresponding to comedi_data_read() returning 0,
|
||||
and 'max' represents comedi_data_read() returning 'maxdata',
|
||||
(i.e., 4095 for 12 bit A/C converters, 65535 for 16 bit,
|
||||
or, 1 for digital input -- more on this in a bit.) The
|
||||
'unit' entry tells you if min and
|
||||
max refer to voltage, current, etc.
|
||||
|
||||
"Could it get easier?", you say. Well, yes. Use
|
||||
the function comedi_to_phys(), which converts data
|
||||
values to physical units. Call it using something like
|
||||
|
||||
<tscreen><verb>
|
||||
volts=comedi_to_phys(it,data,range,maxdata);
|
||||
</verb></tscreen>
|
||||
|
||||
and the opposite
|
||||
|
||||
<tscreen><verb>
|
||||
data=comedi_from_phys(it,volts,range,maxdata);
|
||||
</verb></tscreen>
|
||||
|
||||
|
||||
<p>
|
||||
<sect1>Another section
|
||||
<p>
|
||||
|
||||
|
||||
In addition to providing low level routines for data
|
||||
access, the comedi library provides higher-level access,
|
||||
much like the standard C library provides fopen(), etc.
|
||||
as a high-level (and portable) alternative to the direct
|
||||
UNIX system calls open(), etc. Similarily to fopen(),
|
||||
we have comedi_open():
|
||||
|
||||
<p>
|
||||
<tscreen><verb>
|
||||
file=comedi_open("/dev/comedi0");
|
||||
</verb></tscreen>
|
||||
|
||||
where file is of type <tt>(comedi_t *)</tt>. This function
|
||||
calls <tt>open()</tt>, like we did explicitly in a previous
|
||||
section, but also fills the <tt>comedi_t</tt> structure with
|
||||
lots of goodies -- information that we will need to use
|
||||
soon.
|
||||
|
||||
Specifically, we needed to know maxdata for a specific
|
||||
subdevice/channel. How about:
|
||||
|
||||
<tscreen><verb>
|
||||
maxdata=comedi_get_maxdata(file,subdevice,channel);
|
||||
</verb></tscreen>
|
||||
|
||||
Wow. How easy. And the range type?
|
||||
|
||||
<tscreen><verb>
|
||||
range_type=comedi_get_rangetype(file,subdevice,channel);
|
||||
</verb></tscreen>
|
||||
|
||||
Cool. Other information you need to know about a channel
|
||||
can be gotten in a similar way.
|
||||
|
||||
|
||||
|
||||
<sect1>Your second comedi program
|
||||
<p>
|
||||
|
||||
|
||||
Actually, this is the first comedi program again, just
|
||||
that we've added what we've learned.
|
||||
|
||||
|
||||
<tscreen><verb>
|
||||
#include <stdio.h> /* for printf() */
|
||||
#include <comedi.h> /* also included by comedilib.h */
|
||||
#include <comedilib.h> /* 'cuz we're using comedilib */
|
||||
|
||||
int subdev = 0; /* change this to your input subdevice */
|
||||
int chan = 0; /* change this to your channel */
|
||||
int range = 0; /* more on this later */
|
||||
int aref = 0; /* more on this later */
|
||||
|
||||
int main(int argc,char *argv[])
|
||||
{
|
||||
comedi_t *cf;
|
||||
int chan=0;
|
||||
lsampl_t data;
|
||||
int maxdata,rangetype;
|
||||
double volts;
|
||||
|
||||
cf=comedi_open("/dev/comedi0");
|
||||
|
||||
maxdata=comedi_get_maxdata(cf,subdev,chan);
|
||||
|
||||
rangetype=comedi_get_rangetype(cf,subdev,chan);
|
||||
|
||||
comedi_data_read(cf->fd,subdev,chan,range,aref,&data);
|
||||
|
||||
volts=comedi_to_phys(data,rangetype,range,maxdata);
|
||||
|
||||
printf("%d %g\n",data,volts);
|
||||
|
||||
return 0;
|
||||
}
|
||||
</verb></tscreen>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<p>
|
||||
<sect>Application-specific functions
|
||||
<p>
|
||||
|
||||
<sect1>Digital Input/Output
|
||||
<p>
|
||||
|
||||
Many boards supported by comedi have digital input and output
|
||||
channels. Some boards allow the direction of a channel to be
|
||||
specified in software.
|
||||
|
||||
Comedi groups digital channels into subdevice, which is a group
|
||||
of digital channels that have the same characteristics. For
|
||||
example, digital output lines will be grouped into a digital
|
||||
output subdevice, bidirectional digital lines will be grouped
|
||||
into a digital I/O subdevice. Thus, there can be multiple
|
||||
digital subdevices on a particular board.
|
||||
|
||||
Individual digital lines can be read and written using the
|
||||
functions
|
||||
|
||||
<tt/comedi_dio_read(device,subdevice,channel,unsigned int *bit);/
|
||||
<tt/comedi_dio_write(device,subdevice,channel,unsigned int bit);/
|
||||
|
||||
The direction of bidirectional lines can be configured using
|
||||
the function
|
||||
|
||||
<tt/comedi_dio_config(device,subdevice,channel,unsigned int dir);/
|
||||
|
||||
The parameter <tt/dir/ should be either COMEDI_INPUT or COMEDI_OUTPUT.
|
||||
Many digital I/O subdevices group channels into blocks for
|
||||
configuring direction. Changing one channel in a block changes
|
||||
the entire block.
|
||||
|
||||
Multiple channels can be read and written simultaneously using the
|
||||
function
|
||||
|
||||
<tt/comedi_dio_bitfield(device,subdevice,unsigned int write_mask,unsigned int *bits);/
|
||||
|
||||
Each channel is assigned to a bit in the <tt/write_mask/ and <tt/bits/
|
||||
bitfield. If a bit in <tt/write_mask/ is set, the corresponding bit
|
||||
in <tt/*bits/ will be written to the corresponding digital output line.
|
||||
Each digital line is then read and placed into <tt/*bits/. The value
|
||||
of bits in <tt/*bits/ corresponding to digital output lines is
|
||||
undefined and device-specific. Channel 0 is the least significant
|
||||
bit in the bitfield; channel 31 is the most significant bit. Channels
|
||||
higher than 31 cannot be accessed using this method.
|
||||
|
||||
|
||||
<p>
|
||||
<sect1>Slowly-varying inputs
|
||||
<p>
|
||||
|
||||
|
||||
Sometimes, your input channels change slowly enough that
|
||||
you are able to average many sucessive input values to get a
|
||||
more accurate measurement of the actual value. In general,
|
||||
the more samples you average, the better your estimate
|
||||
gets, roughly by a factor of sqrt(number_of_samples).
|
||||
Obviously, there are limitations to this:
|
||||
|
||||
<p>
|
||||
<itemize>
|
||||
<item>
|
||||
you are ultimately limited by "spurious free dynamic range"
|
||||
|
||||
<item>
|
||||
you need to have _some_ noise on the input channel,
|
||||
otherwise you will be averaging the same number N times.
|
||||
|
||||
<item>
|
||||
the more noise you have, the greater your SFDR, but it
|
||||
takes many more samples to compensate for the increased
|
||||
noise
|
||||
|
||||
<item>
|
||||
if you feel the need to average samples for 2 seconds,
|
||||
your signal will need to be _very_ slowly-varying, i.e.,
|
||||
not varying more than your target uncertainty for the
|
||||
entire 2 seconds.
|
||||
|
||||
</itemize>
|
||||
|
||||
As you might have guessed, the comedi library has functions
|
||||
to help you in your quest to accurately measure slowly varying
|
||||
inputs. I use these functions to measure thermocouple voltages
|
||||
-- actually, the library functions came from a section of code
|
||||
that was previously part of the thermocouple reading program.
|
||||
|
||||
The comedi self-calibration utility also uses these functions.
|
||||
On some hardware, it is possible to tell it to measure an
|
||||
internal stable voltage reference, which is typically going
|
||||
to be very slowly varying -- on the kilosecond time scale
|
||||
or more. So it is reasonable to measure millions of samples,
|
||||
to get a very accurate measurement of the A/D converter output
|
||||
value that corresponds to the voltage reference. Sometimes,
|
||||
however, this is overkill, since there is no need to
|
||||
perform a part-per-million calibration to a standard that
|
||||
is only accurate to part-per-thousand.
|
||||
|
||||
|
||||
<p>
|
||||
<sect1>Commands
|
||||
<label id="command_section">
|
||||
<p>
|
||||
|
||||
|
||||
Many data acquisition devices have the capability to directly
|
||||
control acquisition using either an on-board timer or an external
|
||||
triggering input. Comedi commands are used to control this kind
|
||||
of acquisition. The <ref id="comedi_cmd" name="comedi_cmd"> structure is
|
||||
used to control acquisition and query the capabilities of a device
|
||||
(see also <ref id="comedi_command" name="comedi_command()">,
|
||||
<ref id="comedi_command_test" name="comedi_command_test()">, and
|
||||
<ref id="comedi_get_cmd_src_mask" name="comedi_get_cmd_src_mask()">).
|
||||
|
||||
Commands specify a particular data acquisition sequence, which
|
||||
is comprised of a number of scans. Each scan is comprised of
|
||||
a number of conversions, which usually corresponds to a single
|
||||
A/D or D/A conversion. The start and end of the sequence, and
|
||||
the start and end of each scan, and each conversion is called an
|
||||
event.
|
||||
|
||||
Each of these 5 types of events are caused by a triggering
|
||||
source, specified through the <tt/*_src/ members of the
|
||||
<ref id="comedi_cmd" name="comedi_cmd"> structure. The source types are:
|
||||
|
||||
<itemize>
|
||||
<item>TRIG_NONE: don't ever cause an event
|
||||
<item>TRIG_NOW: cause event to occur immediately
|
||||
<item>TRIG_FOLLOW: see notes below
|
||||
<item>TRIG_TIME: cause event to occur at a particular time
|
||||
<item>TRIG_TIMER: cause event to occur repeatedly at a specific rate
|
||||
<item>TRIG_COUNT: cause event when count reaches specific value
|
||||
<item>TRIG_EXT: external signal causes event
|
||||
<item>TRIG_INT: internal signal causes event
|
||||
<item>TRIG_OTHER: driver-specific meaning
|
||||
</itemize>
|
||||
|
||||
Not all triggers are applicable to all events. Supported triggers
|
||||
for specific events depend significantly on your particular
|
||||
device. The <ref id="comedi_get_cmd_src_mask" name="comedi_get_cmd_src_mask()">
|
||||
function is useful for determining what triggers a subdevice supports.
|
||||
|
||||
For every trigger, there is a corresponding
|
||||
argument (the <tt/*_arg/ members of the <ref id="comedi_cmd" name="comedi_cmd">
|
||||
structure) whose meaning depends on the type of trigger. The meanings
|
||||
of the arguments are as follows:
|
||||
|
||||
TRIG_NONE is typically used only as a <tt/stop_src/. The argument for TRIG_NONE
|
||||
is reserved and should be set to 0.
|
||||
|
||||
TRIG_NOW is most often used as a <tt/start_src/. The argument for TRIG_NOW is
|
||||
the number of nanoseconds between when the command is issued and when
|
||||
the event should occur. In the case of using TRIG now as a <tt/start_src/,
|
||||
it indicates a delay between issuing the command and the start of
|
||||
acquisition. Most drivers only support a delay of 0.
|
||||
|
||||
TRIG_FOLLOW is a special type of trigger for events that trigger on
|
||||
the completion of some other, logically connected event. The argument
|
||||
is reserved and should be set to 0. When used
|
||||
as a <tt/scan_begin_src/, it indicates that a trigger should occur as a
|
||||
logical continuation of convert events. This is done in order to
|
||||
properly describe boards that do not have separate timers for
|
||||
convert and scan_begin events. When used as a <tt/start_src/ for analog
|
||||
output subdevices, it indicates that conversion of output samples
|
||||
should begin when samples are written to the buffer.
|
||||
|
||||
TRIG_TIME is reserved for future use.
|
||||
|
||||
TRIG_TIMER is most often used as a <tt/convert_src/, a <tt/scan_begin_src/, or
|
||||
both. It indicates that triggers should occur at a specific rate.
|
||||
The argument specifies the interval between triggers in nanoseconds.
|
||||
|
||||
TRIG_COUNT is used for <tt/scan_end_src/ and <tt/stop_src/. It indicates that
|
||||
a trigger should occur when the specified number of corresponding
|
||||
lower-level triggers (convert and scan_begin, respectively) occur.
|
||||
The argument is the count of lower-level triggers.
|
||||
|
||||
TRIG_EXT can be useful as any of the trigger sources. It indicates
|
||||
that an external digital line should be used to trigger the event.
|
||||
The exact meaning of digital line is device-dependent. Some devices
|
||||
have one dedicated line, others may allow generic digital input
|
||||
lines to be used. The argument indicates the particular external
|
||||
line to use as the trigger.
|
||||
|
||||
TRIG_INT is typically used as a <tt/start_src/. This trigger occurs when
|
||||
the application performs an INSN_INTTRIG instruction. Using TRIG_INT
|
||||
is a method by which the application can accurately record the time of
|
||||
the start of acquisition, since the parsing and setup time of a
|
||||
particular command may be significant. The argument associated with
|
||||
TRIG_INT is reserved and should be set to 0.
|
||||
|
||||
TRIG_OTHER can be useful as any of the trigger sources. The exact
|
||||
meaning of TRIG_OTHER is driver-specific, and implements a feature
|
||||
that otherwise does not fit into the command interface. Configuration
|
||||
of TRIG_OTHER features are done by INSN_CONFIG insns. The argument
|
||||
is reserved and should be set to 0.
|
||||
|
||||
Ths <tt/subdev/ member of the <ref id="comedi_cmd" name="comedi_cmd">
|
||||
structure is the index of the subdevice the command is intended for. The
|
||||
<ref id="comedi_find_subdevice_by_type" name="comedi_find_subdevice_by_type()">
|
||||
function can be useful in discovering the index of your desired subdevice.
|
||||
|
||||
The <tt/chanlist/ member of the <ref id="comedi_cmd" name="comedi_cmd">
|
||||
structure should point to an array whose number of elements is specificed by <tt/chanlist_len/
|
||||
(this will generally be the same as the scan_end_arg).
|
||||
The chanlist specifies the sequence of channels and gains (and analog references)
|
||||
that should be stepped through for each scan. The elements of the chanlist array
|
||||
should be initialized by packing the channel, range and reference information
|
||||
together with the <ref id="CR_PACK" name="CR_PACK()"> macro.
|
||||
|
||||
The <tt/data/ and <tt/data_len/ members can be safely ignored when issueing commands
|
||||
from a user-space program. They only have meaning when a command is sent from a kernel
|
||||
module using the kcomedilib interface, in which case they specify the buffer where
|
||||
the driver should write/read its data to/from.
|
||||
|
||||
The final member of the <ref id="comedi_cmd" name="comedi_cmd"> structure is <tt/flags/.
|
||||
The following flags are valid, and can be bitwise-or'd together.
|
||||
|
||||
<itemize>
|
||||
<item>TRIG_BOGUS: do the motions??
|
||||
<item>TRIG_DITHER: enable dithering??
|
||||
<item>TRIG_DEGLITCH: enable deglitching??
|
||||
<item>TRIG_RT: ask driver to use a hard real-time interrupt handler. This will
|
||||
reduce latency in handling interrupts from your data aquisition hardware. It can
|
||||
be useful if you are sampling at high frequency, or if your hardware has a small onboard
|
||||
fifo. You must have a real-time kernel (RTAI or RTLinux) and must compile
|
||||
comedi with real-time support or this flag will do nothing.
|
||||
<item>TRIG_CONFIG: perform configuration, not triggering. This is a legacy of the
|
||||
deprecated comedi_trig_struct, and has no function at present.
|
||||
<item>TRIG_WAKE_EOS: some drivers will change their behaviour when this flag is set,
|
||||
trying to transfer data at the end of every scan (instead of, for example, passing
|
||||
data in chunks whenever the board's onboard fifo is half full). This flag
|
||||
may degrade a driver's performance at high frequencies.
|
||||
<item>TRIG_WRITE: write to bidirectional devices. Could be useful in principle, if someone
|
||||
wrote a driver that supported commands for a digital i/o device that could do either
|
||||
input or output.
|
||||
</itemize>
|
||||
There are also a few flags that indicate how timing arguments should be rounded
|
||||
if the hardware cannot achieve the exact timing requested.
|
||||
<itemize>
|
||||
<item>TRIG_ROUND_NEAREST: round to nearest supported timing period, the default.
|
||||
<item>TRIG_ROUND_DOWN: round period down.
|
||||
<item>TRIG_ROUND_UP: round period up.
|
||||
<item>TRIG_ROUND_UP_NEXT: this one doesn't do anything, and I don't know what it was intended
|
||||
to do??
|
||||
</itemize>
|
||||
|
||||
<p>
|
||||
|
||||
The typical sequence for executing a command is to first send
|
||||
the command through
|
||||
<ref id="comedi_command_test" name="comedi_command_test()">
|
||||
once or twice. The test will check that the command is valid for the particular
|
||||
device, and often makes some adjustments to the command arguments, which
|
||||
can then be read back by the user to see the actual values used. The
|
||||
command is executed with
|
||||
<ref id="comedi_command" name="comedi_command()">. For input/output commands, data
|
||||
is read from or written to the device file /dev/comedi[0..3] you are using.
|
||||
|
Loading…
Add table
Reference in a new issue