From: Bernd Porr Date: Fri, 27 Apr 2012 00:37:33 +0000 (+0100) Subject: I've updated the manual in the section configuration and X-Git-Url: http://git.tremily.us/?a=commitdiff_plain;h=df6a97ec6c1175086cf8c7e912b0b4ffaad3fad9;p=comedilib.git 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. --- diff --git a/demo/Makefile.am b/demo/Makefile.am index 6765420..523d3eb 100644 --- a/demo/Makefile.am +++ b/demo/Makefile.am @@ -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) diff --git a/demo/tut2.c b/demo/tut2.c index 4e9f8ea..23a03a6 100644 --- a/demo/tut2.c +++ b/demo/tut2.c @@ -13,6 +13,8 @@ #include /* for printf() */ #include #include +#include +#include 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; } diff --git a/demo/tut3.c b/demo/tut3.c new file mode 100644 index 0000000..c67ef2e --- /dev/null +++ b/demo/tut3.c @@ -0,0 +1,220 @@ +/* + * Example of using commands - asynchronous input + * Part of Comedilib + * + * Copyright (c) 1999,2000,2001 David A. Schleef + * 2008 Bernd Porr + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +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); +} diff --git a/demo/tut3_part.c b/demo/tut3_part.c new file mode 100644 index 0000000..1daa708 --- /dev/null +++ b/demo/tut3_part.c @@ -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; +} diff --git a/doc/install.xml b/doc/install.xml index 7d846b0..681a4e6 100644 --- a/doc/install.xml +++ b/doc/install.xml @@ -23,90 +23,108 @@ Configuration - Before being able to get information from a DAQ card, you first have - to tell the &comedi; core kernel module which device you have, which - driver you want to attach to the card, and which run-time options - you want to give to the driver. This configuration is done by running - the comedi_config command (as root). - Here is an example of how to use the command (perhaps you should read - its man 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. -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 - This command says that the file - /dev/comedi0 can be used to access the &comedi; - device that uses the labpc-1200 board, and that - you give it two run-time parameters (0x260 and - 3). 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. - This tutorial goes through the process of configuring &comedi; - for two devices, a - National Instruments AT-MIO-16E-10, and a - Data Translation DT2821-F-8DI. + 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 comedi_config command (as root). + Here is an example of how to use the command (perhaps you should read + its man page now): + + comedi_config /dev/comedi0 labpc-1200 0x260,3 + + This command says that the file + /dev/comedi0 can be used to access the &comedi; + device that uses the labpc-1200 board, and that + you give it two run-time parameters (0x260 and + 3). More parameters are possible, and their + meaning is driver dependant. - 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 - -comedi_config /dev/comedi0 ni_atmio - - 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 modprobe command can - be used to manually load/unload kernel modules, and lsmod - will list all the currently loaded modules. - - - For the Data Translation 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 - kernel drivers section about the dt282x - driver. - The card discussed here is a DT2821-f-8di. 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: - - dt282x configuration options: - [0] - I/O port base address - [1] - IRQ - [2] - DMA 1 - [3] - DMA 2 - [4] - AI jumpered for 0=single ended, 1=differential - [5] - AI jumpered for 0=straight binary, 1=2's complement - [6] - AO 0 jumpered for 0=straight binary, 1=2's complement - [7] - AO 1 jumpered for 0=straight binary, 1=2's complement - [8] - AI jumpered for 0=[-10,10]V, 1=[0,10], 2=[-5,5], 3=[0,5] - [9] - AO 0 jumpered for 0=[-10,10]V, 1=[0,10], 2=[-5,5], 3=[0,5], - 4=[-2.5,2.5] - [10]- A0 1 jumpered for 0=[-10,10]V, 1=[0,10], 2=[-5,5], 3=[0,5], - 4=[-2.5,2.5] - - - - - So, the appropriate options list might be: - -0x200,4,0,0,1,1,1,1,0,2,2 - - and the full configuration command is: - -comedi_config /dev/comedi1 dt2821-f-8di 0x200,4,0,0,1,1,1,1,0,2,2 - - Setting the DMA channels to 0 disables the use of DMA. + This tutorial goes 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. 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 + + comedi_config /dev/comedi0 ni_atmio + + 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 modprobe command can + be used to manually load/unload kernel modules, and lsmod + will list all the currently loaded modules. + + + For the Data Translation 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 + kernel drivers section about the dt282x + driver. + The card discussed here is a DT2821-f-8di. 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: + + dt282x configuration options: + [0] - I/O port base address + [1] - IRQ + [2] - DMA 1 + [3] - DMA 2 + [4] - AI jumpered for 0=single ended, 1=differential + [5] - AI jumpered for 0=straight binary, 1=2's complement + [6] - AO 0 jumpered for 0=straight binary, 1=2's complement + [7] - AO 1 jumpered for 0=straight binary, 1=2's complement + [8] - AI jumpered for 0=[-10,10]V, 1=[0,10], 2=[-5,5], 3=[0,5] + [9] - AO 0 jumpered for 0=[-10,10]V, 1=[0,10], 2=[-5,5], 3=[0,5], + 4=[-2.5,2.5] + [10]- A0 1 jumpered for 0=[-10,10]V, 1=[0,10], 2=[-5,5], 3=[0,5], + 4=[-2.5,2.5] + + + + + So, the appropriate options list might be: + + 0x200,4,0,0,1,1,1,1,0,2,2 + + and the full configuration command is: + + comedi_config /dev/comedi1 dt2821-f-8di 0x200,4,0,0,1,1,1,1,0,2,2 + + Setting the DMA channels to 0 disables the use of DMA. + + 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 ) - Note that it also correctly identified the board. + Note that it also correctly identified the board. - +
@@ -145,10 +163,10 @@ comedi0: ni_atmio: 0x0260 at-mio-16e-10 ( irq = 3 ) - 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: @@ -156,17 +174,17 @@ cat /proc/comedi - For example, on a computer with an NI pxi-6281 configured on - /dev/comedi0 and - a pxi-6602 configured on /dev/comedi1 you might - see something like: + For example, on a computer with an NI pxi-6281 configured on + /dev/comedi0 and + a pxi-6602 configured on /dev/comedi1 you might + see something like: - + 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: diff --git a/doc/tutorial.xml b/doc/tutorial.xml index ad1ee8d..a7fb4dc 100644 --- a/doc/tutorial.xml +++ b/doc/tutorial.xml @@ -6,138 +6,175 @@ ]>
- -Writing &comedi; programs - - -This section describes how &comedi; -can be used in an application, to communicate data with a set -of &comedi; devices. - gives more details about -the various acquisition functions with which the application -programmer can perform data acquisition in &comedi;. - - -Also don't forget to take a good look at the -demo -directory of the Comedilib source code. It contains lots of examples -for the basic functionalities of &comedi;. - - -
- -Your first &comedi; program - - - -This example requires a card that has analog or digital input. This -progam opens the device, gets the data, and prints it out: - - - - - -The source code file for the above program can be found in Comedilib, -at demo/tut1.c. You can compile the program using - - - -cc tut1.c -lcomedi -o tut1 - - -The - - comedi_open - call can only be successful if the -comedi0 device file is configured with a -valid &comedi; driver. explains -how this driver is linked to the device file. - - -The range variable tells -&comedi; which gain to use when measuring an analog voltage. Since we -don't know (yet) which numbers are valid, or what each means, we'll -use 0, because it won't cause errors. Likewise -with aref, which determines the -analog reference used. - -
- - -
- -Converting between integer data and physical units - - - -If you selected an analog input subdevice, you probably noticed -that the output of tut1 is an unsigned number, for -example between 0 and 65535 -for a 16 bit analog input. &comedi; samples are -unsigned, -with 0 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: How do I do this in a device-independent -manner? - - - -The functions -comedi_to_physical and -comedi_from_physical -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 comedi_polynomial_t -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. - - - -A comedi_polynomial_t may be obtained -from one of two functions: -comedi_get_hardcal_converter or -comedi_get_softcal_converter. -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 -comedi_get_subdevice_flags) -will be set for boards that use software calibration. - - + + Writing &comedi; programs + + + This section describes how &comedi; + can be used in an application, to communicate data with a set + of &comedi; devices. + gives more details about + the various acquisition functions with which the application + programmer can perform data acquisition in &comedi;. + + + Also don't forget to take a good look at the + demo + directory of the Comedilib source code. It contains lots of examples + for the basic functionalities of &comedi;. + + +
+ + Your first &comedi; program + + + + This example requires a card that has analog or digital input. This + progam opens the device, gets the data, and prints it out: + + + + + + The source code file for the above program can be found in Comedilib, + at demo/tut1.c. You can compile the program using + + + + cc tut1.c -lcomedi -o tut1 + + + The + + comedi_open + call can only be successful if the + comedi0 device file is configured with a + valid &comedi; driver. explains + how this driver is linked to the device file. + + + The range variable tells + &comedi; which gain to use when measuring an analog voltage. Since we + don't know (yet) which numbers are valid, or what each means, we'll + use 0, because it won't cause errors. Likewise + with aref, which determines the + analog reference used. + +
+ + +
+ + Converting between integer data and physical units + + + + If you selected an analog input subdevice, you probably noticed + that the output of tut1 is an unsigned number, for + example between 0 and 65535 + for a 16 bit analog input. &comedi; samples are + unsigned, + with 0 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: How do I do this in a device-independent + manner? + + + + The functions + comedi_to_physical, comedi_to_phys, + comedi_from_physical and comedi_from_phys + are used to convert between &comedi;'s integer data and floating point numbers corresponding + to physical values (voltages, etc.). + + +
+ +
+ + Your second &comedi; program + + + + + Actually, this is the first &comedi; program again, except + we've added code to convert the integer data value to physical units. + + + + + + + The source code file for the above program can be found in Comedilib, at demo/tut2.c. + +
+ +
+ + Asynchronous acquisition + + + 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 ktimetrace or comedirecord. + + + The program tut3.c 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. + + + The program below is a stripped down version + of the program cmd.c in + the demo directory. To compile it run: + + + gcc tut3.c -lcomedi -lm -o tut3 + + + 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 tut3.c showing the + relevant commands: + + + + +
+ +
+ Further examples + + See the demo subdirectory of Comedilib for more example programs. + The directory contains + a README file with descriptions of the various demo programs. + +
+
- -
- -Your second &comedi; program - - - - -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. - - - - - - -The source code file for the above program can be found in Comedilib, at demo/tut2.c. - -
- -
- Further examples - - See the demo subdirectory of Comedilib for more example programs. The directory contains - a README file with descriptions of the various demo programs. - -
-
-