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