4 0. Compiling and Installing
5 0. Insmod'ding the kernel module
6 0. Configuring comedi to use your hardware
7 0. Getting information from comedi
8 0. Your first comedi program
9 0. Converting samples to voltages
13 0. Compiling and Installing
20 0. Insmod'ding the kernel module
26 0. Configuring comedi to use your hardware
29 I assume that your hardware device is in your computer, and that
30 you know the relevant details about it, i.e., what kind of card
31 it is, the I/O base, the IRQ, jumper settings related to input
34 To tell the comedi kernel module that you have a particular device, and
35 some information about it, you will be running the 'comedi_config'
36 command. Perhaps you should read the man page now.
38 For this tutorial, I have two devices, a National Instruments AT-MIO-16E-10
39 and a Data Translation DT2821-F-8DI.
41 The NI board is plug-and-play, and the man page tells me that I need
42 to configure the PnP part of the board with isapnptools. The isapnptools
43 package is a little cryptic, but the concepts are simple. Once I
44 learned how to use it, I settled on a /etc/isapnp.conf file that
48 # ANSI string -->National Instruments, AT-MIO-16E-10<--
49 (CONFIGURE NIC2400/10725401 (LD 0
51 (INT 0 (IRQ 3 (MODE +E)))
58 It also contains a few lines about overall configuration and about my
59 sound card. I found out after a bit of trial-and-error that the NI
60 board does not always work with interrupts other than IRQ 3. YMMV.
61 Currently, the driver doesn't use DMA, but it may in the future, so
62 I commented out the DMA lines. It is a curious fact that the device
63 ignores the IRQ and DMA information given here, however, I keep the
64 information here to remind myself that the numbers aren't arbitrary.
66 When I run comedi_config (as root, of course), I provide the same
67 information. Since I want to have the board configured every time
68 I boot, I put the line
70 /usr/sbin/comedi_config /dev/comedi0 atmio-E 0x260,3
72 into /etc/rc.d/rc.local. You can, of course, run this command at
73 a command prompt. The man page tells me that the option list
74 is supposed to be "<I/O base>,<IRQ>", so I used the same numbers
75 as I put in /etc/isapnp.conf, i.e., 0x260,3.
77 For the Data Translation board, I need to have a list of the
78 jumper settings. Fortunately, I wrote them all down in the
79 manual -- I hope they are still correct. However, I had to
80 open the case to figure out which board in the series I had.
81 It is a DT2821-f-8di. The man page of comedi_config tells
82 me that I need to know the I/O base, IRQ, DMA 1, DMA 2. However,
83 since I wrote the driver, I know that it also recognizes the
84 differential/single-ended and unipolar/bipolar jumpers. As always,
85 the source is the final authority, and looking in module/dt282x.c
86 tells me that the options list is interpreted as:
90 1=differential, 0=single ended
91 ai 0=unipolar, 1=bipolar
92 ao0 0=unipolar, 1=bipolar
93 ao1 0=unipolar, 1=bipolar
97 (ai=analog input, ao=analog output.) From this, I decide that
98 the appropriate options list is
102 I left the differential/single-ended number blank, since the
103 driver already knowns (from the board name), that it is
104 differential. I also left the DMA numbers blank, since I
105 don't want the driver to use DMA. (Don't want it to interfere
106 with my sound card -- life is full of difficult choices.)
107 Keep in mind that things commented in the source, but not in
108 the documentation are about as likely to change as the weather,
109 so I put good comments next to the following line when I put
112 /usr/sbin/comedi_config /dev/comedi1 dt2821-f-8di 0x200,4,,1,1,1
114 So now I think that I have my boards configured correctly.
115 Since data acquisition boards are not typically well-engineered,
116 comedi sometimes can't figure out if the board is actually there.
117 If it can't, it assumes you are right. Both of these boards
118 are well-made, so comedi will give me an error message if it
119 can't find them. The comedi kernel module, since it is a part
120 of the kernel, prints messages to the kernel logs, which you
121 can access through the command 'dmesg' or /var/log/messages.
122 Here is a configuration failure (from dmesg):
124 comedi0: ni_E: 0x0200 can't find board
126 When it does work, I get:
128 comedi0: ni_E: 0x0260 at-mio-16e-10 ( irq = 3 )
130 Note that it also correctly identified my board.
135 0. Getting information from comedi
138 So now that we have comedi talking to the hardware, we want to
139 talk to comedi. Here's some pretty low-level information --
140 it's sometimes useful for debugging:
144 Right now, on my computer, this command gives:
148 0: atmio-E at-mio-16e-10 7
149 1: dt282x dt2821-f-8di 4
151 This is a feature that is not well-developed yet. Basically, it
152 currently tells you driver name, device name, and number of
155 In the demo/ directory, there is a command called
156 'info', which provides information about each subdevice on the
157 board. The output of it is rather long, since I have 7
158 subdevices (4 or fewer is more common.)
159 Here's part of the output of the NI board (which
160 is on /dev/comedi0.) ('demo/info /dev/comedi0')
163 version code: 0x000604
165 board name: at-mio-16e-10
166 number of subdevices: 7
169 number of channels: 16
173 The overall info gives information about the device -- basically
174 the same information as /proc/comedi.
176 This board has 7 subdevices. Devices are separated into
177 subdevices that each have a distinct purpose -- e.g., analog
178 input, analog output, digital input/output. This board also
179 has an EEPROM and calibration DACs that are also subdevices.
181 Subdevice 0 is the analog input subdevice. You would have
182 known this from the 'type: 1 (unknown)' line, if I've updated
183 demo/info recently, because it would say 'type: 1 (analog input)'
184 instead. The other lines should be self-explanitory. Comedi
185 has more information about the device, but demo/info doesn't
186 currently display this.
190 0. Your first comedi program
192 This example requires a card that has analog or
193 digital input. Right to the source:
195 #include <stdio.h> /* for printf() */
196 #include <comedilib.h>
198 int subdev = 0; /* change this to your input subdevice */
199 int chan = 0; /* change this to your channel */
200 int range = 0; /* more on this later */
201 int aref = AREF_GROUND; /* more on this later */
203 int main(int argc,char *argv[])
209 it=comedi_open("/dev/comedi0");
211 comedi_data_read(it,subdev,chan,range,aref,&data);
219 Should be understandable. Open the device, get the data,
220 print it out. This is basically the guts of demo/inp.c,
221 without error checking or fancy options. Including all
222 the appropriate headers is sometimes a little tricky.
223 Compile it using 'cc tut1.c -lcomedi -o tut1'. Hopefully
226 A few notes: The range variable tells comedi which gain
227 to use when measuring an analog voltage. Since we don't
228 know (yet) which numbers are valid, or what each means,
229 we'll use 0, because it won't cause errors. Likewise with
230 aref, which determines the analog reference used.
233 0. Converting samples to voltages
236 If you selected an analog input subdevice, you should notice
237 that the output of tut1 is a number between
238 0 and 4095, or 0 and 65535, depending on the number of bits
239 in the A/D converter. Comedi samples are *always* unsigned,
240 with 0 representing the lowest voltage of the ADC, and 4095
241 the highest. The hardware driver compensates for
242 anything else the manual for your device says. However,
243 you probably prefer to have this number translated to
244 a voltage. Naturally, as a good programmer, your first
245 question is: "How do I do this in a device-independent
248 For each subdevice, the comedi kernel module keeps a
249 'range_type' variable. This variable contains the number
250 of available ranges (i.e., gains) that you can select,
251 along with an offset in a list of range information
252 structures. If you know the range_type variable, you
253 can use these macros:
255 RANGE_OFFSET(range_type)
256 RANGE_LENGTH(range_type)
258 to extract such information. However, you want the
259 actual voltage information, not some integer offset
260 in a table. Rather than messing with the library
261 internals, use the function
263 ptr=comedi_get_range(comedi_file,subdevice,channel,
266 which returns a pointer to a comedi_range structure.
267 The comedi_range structure looks like
275 The element 'min' represents the voltage corresponding to
276 comedi_data_read() returning 0, and 'max' represents
277 comedi_data_read() returning 'maxdata', (i.e., 4095 for 12
278 bit A/C converters, 65535 for 16 bit, or, 1 for digital input
279 -- more on this in a bit.) The 'unit' entry tells you if min and
280 max refer to voltage, current, etc.
282 "Could it get easier?", you say. Well, yes. Use
283 the function comedi_to_phys(), which converts data
284 values to physical units. Call it using something like
286 volts=comedi_to_phys(it,data,range,maxdata);
290 data=comedi_from_phys(it,volts,range,maxdata);
292 You probably noticed (and were worried) that we haven't
293 discussed how to determine maxdata and range_type. Well,
294 you could ask the kernel this information each time you need
295 it, but since there are other variables, special cases,
296 and several subdevices to worry about, it would be nice
297 if the library could take care of this... (read on...)
304 In addition to providing low level routines for data
305 access, the comedi library provides higher-level access,
306 much like the standard C library provides fopen(), etc.
307 as a high-level (and portable) alternative to the direct
308 UNIX system calls open(), etc. Similarily to fopen(),
309 we have comedi_open():
311 file=comedi_open("/dev/comedi0");
313 where file is of type (comedi_t *). This function
314 calls open(), like we did explicitly in a previous
315 section, but also fills the comedi_t structure with
316 lots of goodies -- information that we will need to use
319 Specifically, we needed to know maxdata for a specific
320 subdevice/channel. How about:
322 maxdata=comedi_get_maxdata(file,subdevice,channel);
324 Wow. How easy. And the range type?
326 range_type=comedi_get_rangetype(file,subdevice,channel);
328 Cool. Other information you need to know about a channel
329 can be gotten in a similar way.
333 0. Your second comedi program
336 Actually, this is the first comedi program again, just
337 that we've added what we've learned.
340 #include <stdio.h> /* for printf() */
341 #include <comedi.h> /* also included by comedilib.h */
342 #include <comedilib.h> /* for comedi_get() */
344 int subdev = 0; /* change this to your input subdevice */
345 int chan = 0; /* change this to your channel */
346 int range = 0; /* more on this later */
347 int aref = 0; /* more on this later */
349 int main(int argc,char *argv[])
354 int maxdata,rangetype;
357 cf=comedi_open("/dev/comedi0");
359 maxdata=comedi_get_maxdata(cf,subdev,chan);
361 rangetype=comedi_get_rangetype(cf,subdev,chan);
363 data=comedi_get(cf->fd,subdev,chan,range,aref);
365 volts=comedi_to_phys(data,rangetype,range,maxdata);
367 printf("%d %g\n",data,volts);
373 By now, the comedi_read_data() line looks a little archaic, using
374 the UNIX file descriptor cf->fd instead of just cf. (By the
375 way, somewhere in the heart of comedi_open() is the line
376 cf->fd=open(filename,O_RDWR).) Well, there isn't one good
377 replacement, since it highly depends on your application
378 what additional features you might want in a comedi_get()
379 replacement. But this is the topic of a different section.
386 0. Slowly-varying inputs
389 Sometimes, your input channels change slowly enough that
390 you are able to average many sucessive input values to get a
391 more accurate measurement of the actual value. In general,
392 the more samples you average, the better your estimate
393 gets, roughly by a factor of sqrt(number_of_samples).
394 Obviously, there are limitations to this:
396 - you are ultimately limited by "spurious free dynamic range"
398 - you need to have _some_ noise on the input channel,
399 otherwise you will be averaging the same number N times.
401 - the more noise you have, the greater your SFDR, but it
402 takes many more samples to compensate for the increased
405 - if you feel the need to average samples for 2 seconds,
406 your signal will need to be _very_ slowly-varying, i.e.,
407 not varying more than your target uncertainty for the
410 As you might have guessed, the comedi library has functions
411 to help you in your quest to accurately measure slowly varying
412 inputs. I use these functions to measure thermocouple voltages
413 -- actually, the library functions came from a section of code
414 that was previously part of the thermocouple reading program.
416 The comedi self-calibration utility also uses these functions.
417 On some hardware, it is possible to tell it to measure an
418 internal stable voltage reference, which is typically going
419 to be very slowly varying -- on the kilosecond time scale
420 or more. So it is reasonable to measure millions of samples,
421 to get a very accurate measurement of the A/D converter output
422 value that corresponds to the voltage reference. Sometimes,
423 however, this is overkill, since there is no need to
424 perform a part-per-million calibration to a standard that
425 is only accurate to part-per-thousand.