4. Application-specific functions

4.1. Digital Input/Output

Many boards supported by comedi have digital input and output channels. Some boards allow the direction of a channel to be specified in software.

Comedi groups digital channels into subdevice, which is a group of digital channels that have the same characteristics. For example, digital output lines will be grouped into a digital output subdevice, bidirectional digital lines will be grouped into a digital I/O subdevice. Thus, there can be multiple digital subdevices on a particular board.

Individual digital lines can be read and written using the functions

  comedi_dio_read(device,subdevice,channel,unsigned int *bit);
  comedi_dio_write(device,subdevice,channel,unsigned int bit);

The direction of bidirectional lines can be configured using the function

  comedi_dio_config(device,subdevice,channel,unsigned int dir);

The parameter dir should be either COMEDI_INPUT or COMEDI_OUTPUT. Many digital I/O subdevices group channels into blocks for configuring direction. Changing one channel in a block changes the entire block.

Multiple channels can be read and written simultaneously using the function

  comedi_dio_bitfield(device,subdevice,unsigned int write_mask,unsigned int *bits);

Each channel is assigned to a bit in the write_mask and bits bitfield. If a bit in write_mask is set, the corresponding bit in *bits will be written to the corresponding digital output line. Each digital line is then read and placed into *bits. The value of bits in *bits corresponding to digital output lines is undefined and device-specific. Channel 0 is the least significant bit in the bitfield; channel 31 is the most significant bit. Channels higher than 31 cannot be accessed using this method.

4.2. Slowly-varying inputs

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

you are ultimately limited by "spurious free dynamic range"
you need to have _some_ noise on the input channel, otherwise you will be averaging the same number N times.
the more noise you have, the greater your SFDR, but it takes many more samples to compensate for the increased noise
if you feel the need to average samples for 2 seconds, your signal will need to be _very_ slowly-varying, i.e., not varying more than your target uncertainty for the entire 2 seconds.

As you might have guessed, the comedi library has functions to help you in your quest to accurately measure slowly varying inputs. I use these functions to measure thermocouple voltages -- actually, the library functions came from a section of code that was previously part of the thermocouple reading program.

The comedi self-calibration utility also uses these functions. On some hardware, it is possible to tell it to measure an internal stable voltage reference, which is typically going to be very slowly varying -- on the kilosecond time scale or more. So it is reasonable to measure millions of samples, to get a very accurate measurement of the A/D converter output value that corresponds to the voltage reference. Sometimes, however, this is overkill, since there is no need to perform a part-per-million calibration to a standard that is only accurate to part-per-thousand.

4.3. Commands and streaming input/output

Many data acquisition devices have the capability to directly control acquisition using either an on-board timer or an external triggering input. Comedi commands are used to control this kind of acquisition. The comedi_cmd structure is used to control acquisition and query the capabilities of a device (see also comedi_command(), comedi_command_test(), and comedi_get_cmd_src_mask()).

Commands specify a particular data acquisition sequence, which is comprised of a number of scans. Each scan is comprised of a number of conversions, which usually corresponds to a single A/D or D/A conversion. The start and end of the sequence, and the start and end of each scan, and each conversion is called an event.

Each of these 5 types of events are caused by a triggering source, specified through the *_src members of the comedi_cmd structure. The source types are:

TRIG_NONE don't ever cause an event
TRIG_NOW cause event to occur immediately
TRIG_FOLLOW see notes below
TRIG_TIME cause event to occur at a particular time
TRIG_TIMER cause event to occur repeatedly at a specific rate
TRIG_COUNT cause event when count reaches specific value
TRIG_EXT external signal causes event
TRIG_INT internal signal causes event
TRIG_OTHER driver-specific meaning

Not all triggers are applicable to all events. Supported triggers for specific events depend significantly on your particular device. The comedi_get_cmd_src_mask() function is useful for determining what triggers a subdevice supports.

For every trigger, there is a corresponding argument (the *_arg members of the comedi_cmd structure) whose meaning depends on the type of trigger. The meanings of the arguments are as follows:

TRIG_NONE is typically used only as a stop_src. The argument for TRIG_NONE is reserved and should be set to 0.

TRIG_NOW is most often used as a start_src. The argument for TRIG_NOW is the number of nanoseconds between when the command is issued and when the event should occur. In the case of using TRIG now as a start_src, it indicates a delay between issuing the command and the start of acquisition. Most drivers only support a delay of 0.

TRIG_FOLLOW is a special type of trigger for events that trigger on the completion of some other, logically connected event. The argument is reserved and should be set to 0. When used as a scan_begin_src, it indicates that a trigger should occur as a logical continuation of convert events. This is done in order to properly describe boards that do not have separate timers for convert and scan_begin events. When used as a start_src for analog output subdevices, it indicates that conversion of output samples should begin when samples are written to the buffer.

TRIG_TIME is reserved for future use.

TRIG_TIMER is most often used as a convert_src, a scan_begin_src, or both. It indicates that triggers should occur at a specific rate. The argument specifies the interval between triggers in nanoseconds.

TRIG_COUNT is used for scan_end_src and stop_src. It indicates that a trigger should occur when the specified number of corresponding lower-level triggers (convert and scan_begin, respectively) occur. The argument is the count of lower-level triggers.

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 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 subdev member of the comedi_cmd structure is the index of the subdevice the command is intended for. The comedi_find_subdevice_by_type() function can be useful in discovering the index of your desired subdevice.

The chanlist member of the comedi_cmd structure should point to an array whose number of elements is specificed by chanlist_len (this will generally be the same as the scan_end_arg). The chanlist specifies the sequence of channels and gains (and analog references) that should be stepped through for each scan. The elements of the chanlist array should be initialized by packing the channel, range and reference information together with the CR_PACK() macro.

The data and data_len members can be safely ignored when issueing commands from a user-space program. They only have meaning when a command is sent from a kernel module using the kcomedilib interface, in which case they specify the buffer where the driver should write/read its data to/from.

The final member of the comedi_cmd structure is flags. The following flags are valid, and can be bitwise-or'd together.

TRIG_BOGUS

do the motions??

TRIG_DITHER

enable dithering??

TRIG_DEGLITCH

enable deglitching??

TRIG_RT

ask driver to use a hard real-time interrupt handler. This will reduce latency in handling interrupts from your data aquisition hardware. It can be useful if you are sampling at high frequency, or if your hardware has a small onboard fifo. You must have a real-time kernel (RTAI or RTLinux) and must compile comedi with real-time support or this flag will do nothing.

TRIG_CONFIG

perform configuration, not triggering. This is a legacy of the deprecated comedi_trig_struct, and has no function at present.

TRIG_WAKE_EOS

some drivers will change their behaviour when this flag is set, trying to transfer data at the end of every scan (instead of, for example, passing data in chunks whenever the board's onboard fifo is half full). This flag may degrade a driver's performance at high frequencies.

TRIG_WRITE

write to bidirectional devices. Could be useful in principle, if someone wrote a driver that supported commands for a digital i/o device that could do either input or output.

TRIG_ROUND_NEAREST

round to nearest supported timing period, the default.

TRIG_ROUND_DOWN

round period down.

TRIG_ROUND_UP

round period up.

TRIG_ROUND_UP_NEXT

this one doesn't do anything, and I don't know what it was intended to do?? XXX

The last four flags indicate how timing arguments should be rounded if the hardware cannot achieve the exact timing requested.

The typical sequence for executing a command is to first send the command through comedi_command_test() once or twice. The test will check that the command is valid for the particular device, and often makes some adjustments to the command arguments, which can then be read back by the user to see the actual values used. The command is executed with comedi_command(). For input/output commands, data is read from or written to the device file /dev/comedi[0..3] you are using.