The following subsections document functionality that has not yet matured. Most of this functionality has even not been implemented yet in any single device driver. This information is included here, in order to stimulate discussion about their API, and to encourage pioneering implementations.
(Status: experimental (i.e., no driver implements this yet))
When one or several digital inputs are used to modify an output value, either an accumulator or a single digital line or bit, a bitfield structure is typically used in the Comedi interface. The digital inputs have two properties, “sensitive” inputs and “modifier” inputs. Edge transitions on sensitive inputs cause changes in the output signal, whereas modifier inputs change the effect of edge transitions on sensitive inputs. Note that inputs can be both modifier inputs and sensitive inputs.
For simplification purposes, it is assumed that multiple digital inputs do not change simultaneously.
The combined state of the modifier inputs determine a modifier state. For each combination of modifier state and sensitive input, there is a set of bits that determine the effect on the output value due to positive or negative transitions of the sensitive input. For each transition direction, there are two bits defined as follows:
For example, a simple digital follower is specified by the bit pattern 01 10, because it sets the output on positive transitions of the input, and clears the output on negative transitions. A digital inverter is similarily 10 01. These systems have only one sensitive input.
As another example, a simple up counter, which increments on positive transitions of one input, is specified by 01 00. This system has only one sensitive input.
When multiple digital inputs are used, the inputs are divided into two types, inputs which cause changes in the accumulator, and those that only modify the meaning of transitions on other inputs. Modifier inputs do not require bitfields, but there needs to be a bitfield of length 4*(2^(N-1)) for each edge sensitive input, where N is the total number of inputs. Since N is usually 2 or 3, with only one edge sensitive input, the scaling issues are not significant.
(Status: design (i.e., no driver implements this yet).)
The insn
field of the
instruction data structure
has not been assigned yet.
The chanspec
field
of the instruction data
structure is ignored.
Some devices have the capability to add white noise (dithering) to analog input measurement. This additional noise can then be averaged out, to get a more accurate measurement of the input signal. It should not be assumed that channels can be separately configured. A simple design can use 1 bit to turn this feature on/off.
Some devices have the capability of changing the glitch characteristics of analog output subsytems. The default (off) case should be where the average settling time is lowest. A simple design can use 1 bit to turn this feature on/off.
Some devices have a configurable analog filters as part of the analog input stage. A simple design can use 1 bit to enable/disable the filter. Default is disabled, i.e., the filter being bypassed, or if the choice is between two filters, the filter with the largest bandwidth.
(Status: design (i.e., no driver implements this yet).)
The insn
field of the
instruction data structure
has not been assigned yet.
The chanspec
field
of the instruction data
structure is ignored.
Some devices have the ability to cyclicly loop through samples kept in an on-board analog output FIFO. This config should allow the user to enable/disable this mode.
This config should allow the user to configure the number of samples to loop through. It may be necessary to configure the channels used.
(Status: alpha.)
The insn
field of the
instruction data structure
has not been assigned yet.
The chanspec
field
of the instruction data
structure is ignored.
This section covers common information for all extended triggering configuration, and doesn't describe a particular type of extended trigger.
Extended triggering is used to configure triggering engines that
do not fit into commands. In a typical programming sequence, the
application will use
configuration instructions
to configure an extended trigger, and a
command,
specifying
TRIG_OTHER
as one of the trigger sources.
Extended trigger configuration should be designed in such a way that the user can probe for valid parameters, similar to how command testing works. An extended trigger configuration instruction should not configure the hardware directly, rather, the configuration should be saved until the subsequent command is issued. This allows more flexibility for future interface changes.
It has not been decided whether the configuration stage should return a token that is then used as the trigger argument in the command. Using tokens is one method to satisfy the problem that extended trigger configurations may have subtle compatiblity issues with other trigger sources/arguments that can only be determined at command test time. Passing all stages of a command test should only be allowed with a properly configured extended trigger.
Extended triggers must use
data
[1] as flags. The
upper 16 bits are reserved and used only for flags that are common to
all extended triggers. The lower 16 bits may be defined by the
particular type of extended trigger.
Various types of extended triggers must use
data
[1] to know which
event the extended trigger will be assigned to in the command
structure. The possible values are an OR'd mask of the following:
COMEDI_EV_START
COMEDI_EV_SCAN_BEGIN
COMEDI_EV_CONVERT
COMEDI_EV_SCAN_END
COMEDI_EV_STOP
(Status: alpha. The ni_mio_common.c
driver
implements this feature.)
The insn
field of the
instruction data structure
has not been assigned yet.
The chanspec
field
of the instruction data
structure is ignored.
The data
field
of the instruction data
structure is used as follows:
Analog triggering is described by a digital combining machine that has two sensitive digital inputs. The sensitive digital inputs are generated by configurable analog comparators. The analog comparators generate a digital 1 when the analog triggering signal is greater than the comparator level. The digital inputs are not modifier inputs. Note, however, there is an effective modifier due to the restriction that the primary analog comparator level must be less than the secondary analog comparator level.
If only one analog comparator signal is used, the combining machine
for the secondary input should be set to ignored, and the secondary
analog level should be set to 0
.
The interpretation of the chanspec and voltage levels is device dependent, but should correspond to similar values of the analog input subdevice, if possible.
Notes: Reading range information is not addressed. This makes it difficult to convert comparator voltages to data values.
Possible extensions: A parameter that specifies the necessary time that the set condition has to be true before the trigger is generated. A parameter that specifies the necessary time that the reset condition has to be true before the state machine is reset.
(Status: design. No driver implements this feature yet.)
The insn
field of the
instruction data structure
has not been assigned yet.
The chanspec
field
of the instruction data
structure is ignored.
The data
field
of the instruction data
structure is used as follows:
The pattern matching trigger issues a trigger when all of a specifed set of input lines match a specified pattern. If the device allows, the input lines should correspond to the input lines of a digital input subdevice, however, this will necessarily be device dependent. Each possible digital line that can be matched is assigned a bit in the mask and pattern. A bit set in the mask indicates that the input line must match the corresponding bit in the pattern. A bit cleared in the mask indicates that the input line is ignored.
Notes: This only allows 32 bits in the pattern/mask, which may be too few. Devices may support selecting different sets of lines from which to match a pattern.
Discovery: The number of bits can be discovered by setting the mask
to all 1's. The driver must modify this value and return
-EAGAIN
.
(Status: design. No driver implements this feature yet.)
The insn
field of the
instruction data structure
has not been assigned yet.
The chanspec
field
of the instruction data
structure is used to specify which counter to use. (I.e., the
counter is a Comedi channel.)
The data
field
of the instruction data
structure is used as follows:
Note that this configuration is only useful if the counting has to be done in software. Many cards offer configurable counters in hardware; e.g., general purpose timer cards can be configured to act as pulse generators, frequency counters, timers, encoders, etc.
Counters can be operated either in synchronous mode (using
INSN_READ
)
or asynchronous mode (using
commands), similar to analog
input subdevices.
The input signal for both modes is the accumulator.
Commands on counter subdevices are almost always specified using
scan_begin_src
= TRIG_OTHER
,
with the counter configuration also serving as the extended configuration for
the “scan begin” source.
Counters are made up of an accumulator and a combining machine that determines when the accumulator should be incremented or decremented based on the values of the input signals. The combining machine optionally determines when the accumulator should be latched and put into a buffer. This feature is used in asynchronous mode.
Note: How to access multiple pieces of data acquired at each event?
(Status: design. No driver implements this feature yet.)
The insn
field of the
instruction data structure
has not been assigned yet.
The chanspec
field
of the instruction data
structure is used to …
The data
field
of the instruction data
structure is used as follows:
…_src
and the
…_arg
fields
used in the
command data structure.
Notes: How to specify which events cause a latch and push, and what should get latched?
There are many different ways to count, time or generate pulses. All the modes rely on the counter and some particular configuration. For example, in a typical buffered counting mode, the source is the (digital) signal that is measured, the counter is increased at every rising edge of the signal, the gate is the (digital) signal indicating when to save the counter to memory. It is preferable you get first familiarized with these various modes by reading your NI board documentation before reading the following description on the mapping to the comedi interface.
Each counter of the board is represented in comedi as a subdevice of type
COMEDI_SUBD_COUNTER
. Each subdevice has a device file
associated (eg, /dev/comedi0_subd11
) in order to read or
write buffered data from or to the counter.
Note that the comedi subdevice has three "channels". In most case, only channel
0 is to be used. Reading or writing on channel 0 corresponds to reading/writing
the counter value. The GPCT also has two registers named A and B, they can be
accessed respectively via channels 1 and 2.
To configure the behaviour of the counter with comedi, the
function comedi_set_counter_mode
is used.
The possible mode values are to be found in the ni_gpct_mode_bits
enum constants. For instance, by default the counter is cumulative, even in
buffered counting. To reinitialise it after each sampling (ie, after an edge on
the gate signal), one can add the NI_GPCT_LOADING_ON_GATE_BIT
to the mode. In that case, the counter will be reset to the value of the A
register after each sampling.
To configure the signal to be used as the "source", one uses the
comedi_set_clock_source
with one constant from the
ni_gpct_clock_source_bits enum. When the period of the signal is
fixed and known, it should be specified as the last parameter of the method, otherwise
0
should be passed. Note that in comedi this is called "clock"
because in timer and pulse generator, this signal is used as the clock.
To configure the signal to be used as the "gate", one uses the
comedi_set_gate_source
with one constant from the
ni_gpct_gate_select enum. When the gate signal is not be used,
NI_GPCT_DISABLED_GATE_SELECT
should be specified. Some
NI boards have two gates, but the behaviour associated with the second gate is
usually unknown so it is recommended to disable it. Note that this is called
"gate" because in some modes, this signal is used to block/unblock the counter.
The function comedi_reset
will stop and reset a counter.
After being configured, to start a counter, it should be "armed", which can be
done either with the comedi_arm
function (for simple counting
mode), or with the start_src
member of the command
(for buffered counting).
One side thing to mention is the signal routing of the NI card, which is done
via the PFIs (Programmable Function Inputs).
NI's naming is confusing because they use the same name for the
terminal (ie, physical input/output pins) and for the signal
(ie, the logical information that controls/indicates a specific event).
The routing allows to configure which signal goes to a PFI terminal.
This is done via comedi_set_routing
, with subdevice being
the special DIO comedi subdevice (eg, 7 on M-series), the PFI terminal
number as channel, the signal that should be routed to it encoded as source with
one of the constants from the ni_pfi_routing enum.
The direction of the pin must also be correctly configured (ie, whether it is
used as input or output). This is done via comedi_dio_config
with the same subdevice and channel, and either COMEDI_INPUT
or COMEDI_OUTPUT
.
A number of NI boards support the RTSI (Real Time System Integration) bus.
It's primary use is to synchronize multiple DAQ cards.
On PXI boards, the RTSI lines correspond to the PXI trigger lines 0 to 7. PCI
boards use cables to connect to their RTSI ports.
The RTSI bus consists of 8 digital signal lines numbered 0 to 7 that are bi-directional.
Each of these signal lines
can be configured as an input or output, and the signal appearing on the output
of each line can be configured to one of several internal board timing signals
(although on older boards RTSI line 7 can only be used for the clock signal).
The ni_pcimio
, ni_atmio
, and
ni_mio_cs
drivers expose the RTSI bus
as a digital I/O subdevice (subdevice number 10).
The functions comedi_dio_config
and
comedi_dio_get_config
can be used on
the RTSI subdevice to
set/query the direction (input or output) of each of the RTSI lines individually.
The subdevice also supports the
INSN_CONFIG_SET_CLOCK_SRC
and
INSN_CONFIG_GET_CLOCK_SRC
configuration
instructions, which can be
used to configure/query what source the board uses to synchronize its
master clock to. The various possibilities are defined in the comedi.h
header file:
Clock Source | Description |
---|---|
NI_MIO_INTERNAL_CLOCK | Use the board's internal oscillator. |
NI_MIO_RTSI_CLOCK |
Use the RTSI line 7 as the master clock. This source is
only supported on pre-m-series boards. The newer m-series boards
use NI_MIO_PLL_RTSI_CLOCK instead.
|
NI_MIO_PLL_PXI_STAR_TRIGGER_CLOCK | Only available for newer m-series PXI boards. Synchronizes the board's phased-locked loop (which runs at 80MHz) to the PXI star trigger line. |
NI_MIO_PLL_PXI10_CLOCK | Only available for newer m-series PXI boards. Synchronizes the board's phased-locked loop (which runs at 80MHz) to the 10 MHz PXI backplane clock. |
NI_MIO_PLL_RTSI_CLOCK (n )
| Only available for newer m-series boards. The function returns a clock source which will cause the board's phased-locked loop (which runs at 80MHz) to syncronize to the RTSI line specified in the function argument. |
For all clock sources except NI_MIO_INTERNAL_CLOCK
and NI_MIO_PLL_PXI10_CLOCK
,
you should pass the period of the clock your are feeding to the board when
using INSN_CONFIG_SET_CLOCK_SRC
.
Finally, the configuration instructions
INSN_CONFIG_SET_ROUTING
and
INSN_CONFIG_GET_ROUTING
can be used to select/query which internal signal
will appear on a given RTSI output line. The header file comedi.h
defines
the following signal sources which can be routed to an RTSI line:
Signal Source | Description |
---|---|
NI_RTSI_OUTPUT_ADR_START1 | ADR_START1, an analog input start signal. See the NI's DAQ-STC Technical Reference Manual for more information. |
NI_RTSI_OUTPUT_ADR_START2 | ADR_START2, an analog input stop signal. See the NI's DAQ-STC Technical Reference Manual for more information. |
NI_RTSI_OUTPUT_SCLKG | SCLKG, a sample clock signal. See the NI's DAQ-STC Technical Reference Manual for more information. |
NI_RTSI_OUTPUT_DACUPDN | DACUPDN, a dac update signal. See the NI's DAQ-STC Technical Reference Manual for more information. |
NI_RTSI_OUTPUT_DA_START1 | DA_START1, an analog output start signal. See the NI's DAQ-STC Technical Reference Manual for more information. |
NI_RTSI_OUTPUT_G_SRC0 | G_SRC0, the source signal to general purpose counter 0. See the NI's DAQ-STC Technical Reference Manual for more information. |
NI_RTSI_OUTPUT_G_GATE0 | G_GATE0, the gate signal to general purpose counter 0. See the NI's DAQ-STC Technical Reference Manual for more information. |
NI_RTSI_OUTPUT_RGOUT0 | RGOUT0, the output signal of general purpose counter 0. See the NI's DAQ-STC Technical Reference Manual for more information. |
NI_RTSI_OUTPUT_RTSI_BRD (n )
| RTSI_BRD0 though RTSI_BRD3 are four internal signals which can have various other signals routed to them in turn. Currently, comedi provides no way to configure the signals routed to the RTSI_BRD lines. See the NI's DAQ-STC Technical Reference Manual for more information. |
NI_RTSI_OUTPUT_RTSI_OSC | The RTSI clock signal. On pre-m-series boards, this signal is always routed to RTSI line 7, and cannot be routed to lines 0 through 6. On m-series boards, any RTSI line can be configured to output the clock signal. |
The RTSI bus pins may be used as trigger inputs for many of the
Comedi trigger functions. To use the RTSI bus pins, set the source to be
TRIG_EXT
and the source argument using the return values
from the NI_EXT_RTSI
(n
) function (or similarly the
NI_EXT_PFI
(n
) function if you want
to trigger from a PFI line). The CR_EDGE
and
CR_INVERT
flags may
also be set on the trigger source argument to specify edge and
falling edge/low level triggering.
An example to set up a device as a master is given below.
void comediEnableMaster(comedi_t *dev){ comedi_insn configCmd; lsampl_t configData[2]; int ret; unsigned int d = 0; static const unsigned rtsi_subdev = 10; static const unsigned rtsi_clock_line = 7; /* Route RTSI clock to line 7 (not needed on pre-m-series boards since their clock is always on line 7). */ memset(&configCmd, 0, sizeof(configCmd)); memset(&configData, 0, sizeof(configData)); configCmd.insn = INSN_CONFIG; configCmd.subdev = rtsi_subdev; configCmd.chanspec = rtsi_clock_line; configCmd.n = 2; configCmd.data = configData; configCmd.data[0] = INSN_CONFIG_SET_ROUTING; configCmd.data[1] = NI_RTSI_OUTPUT_RTSI_OSC; ret = comedi_do_insn(dev, &configCmd); if(ret < 0){ comedi_perror("comedi_do_insn: INSN_CONFIG"); exit(1); } // Set clock RTSI line as output ret = comedi_dio_config(dev, rtsi_subdev, rtsi_clock_line, INSN_CONFIG_DIO_OUTPUT); if(ret < 0){ comedi_perror("comedi_dio_config"); exit(1); } /* Set routing of the 3 main AI RTSI signals and their direction to output. We're reusing the already initialized configCmd instruction here since it's mostly the same. */ configCmd.chanspec = 0; configCmd.data[1] = NI_RTSI_OUTPUT_ADR_START1; ret = comedi_do_insn(dev, &configCmd); if(ret < 0){ comedi_perror("comedi_do_insn: INSN_CONFIG"); exit(1); } ret = comedi_dio_config(dev, rtsi_subdev, 0, INSN_CONFIG_DIO_OUTPUT); if(ret < 0){ comedi_perror("comedi_dio_config"); exit(1); } configCmd.chanspec = 1; configCmd.data[1] = NI_RTSI_OUTPUT_ADR_START2; ret = comedi_do_insn(dev, &configCmd); if(ret < 0){ comedi_perror("comedi_do_insn: INSN_CONFIG"); exit(1); } ret = comedi_dio_config(dev, rtsi_subdev, 1, INSN_CONFIG_DIO_OUTPUT); if(ret < 0){ comedi_perror("comedi_dio_config"); exit(1); } configCmd.chanspec = 2; configCmd.data[1] = NI_RTSI_OUTPUT_SCLKG; ret = comedi_do_insn(dev, &configCmd); if(ret < 0){ comedi_perror("comedi_do_insn: INSN_CONFIG"); exit(1); } ret = comedi_dio_config(dev, rtsi_subdev, 2, INSN_CONFIG_DIO_OUTPUT); if(ret < 0){ comedi_perror("comedi_dio_config"); exit(1); } }
An example to slave a m-series device from this master follows. A pre-m-series
device would need to use NI_MIO_RTSI_CLOCK
for
the clock source instead. In
your code, you may also wish to configure the master device to use the
external clock source instead of using its internal clock directly (for
best syncronization).
void comediEnableSlave(comedi_t *dev){ comedi_insn configCmd; lsampl_t configData[3]; int ret; unsigned int d = 0;; static const unsigned rtsi_subdev = 10; static const unsigned rtsi_clock_line = 7; memset(&configCmd, 0, sizeof(configCmd)); memset(&configData, 0, sizeof(configData)); configCmd.insn = INSN_CONFIG; configCmd.subdev = rtsi_subdev; configCmd.chanspec = 0; configCmd.n = 3; configCmd.data = configData; configCmd.data[0] = INSN_CONFIG_SET_CLOCK_SRC; configCmd.data[1] = NI_MIO_PLL_RTSI_CLOCK(rtsi_clock_line); configCmd.data[2] = 100; /* need to give it correct external clock period */ ret = comedi_do_insn(dev, &configCmd); if(ret < 0){ comedi_perror("comedi_do_insn: INSN_CONFIG"); exit(1); } /* configure RTSI clock line as input */ ret = comedi_dio_config(dev, rtsi_subdev, rtsi_clock_line, INSN_CONFIG_DIO_INPUT); if(ret < 0){ comedi_perror("comedi_dio_config"); exit(1); } /* Configure RTSI lines we are using for AI signals as inputs. */ ret = comedi_dio_config(dev, rtsi_subdev, 0, INSN_CONFIG_DIO_INPUT); if(ret < 0){ comedi_perror("comedi_dio_config"); exit(1); } ret = comedi_dio_config(dev, rtsi_subdev, 1, INSN_CONFIG_DIO_INPUT); if(ret < 0){ comedi_perror("comedi_dio_config"); exit(1); } ret = comedi_dio_config(dev, rtsi_subdev, 2, INSN_CONFIG_DIO_INPUT); if(ret < 0){ comedi_perror("comedi_dio_config"); exit(1); } } int comediSlaveStart(comedi_t *dev){ comedi_cmd cmd; unsigned int nChannels = 8; double sampleRate = 50000; unsigned int chanList[8]; int i; // Setup chan list for(i = 0; i < nChannels; i++){ chanList[i] = CR_PACK(i, 0, AREF_GROUND); } // Set up command memset(&cmd, 0, sizeof(cmd)); ret = comedi_get_cmd_generic_timed(dev, subdevice, &cmd, (int)(1e9/(nChannels * sampleRate))); if(ret<0){ printf("comedi_get_cmd_generic_timed failed\n"); return ret; } cmd.chanlist = chanList; cmd.chanlist_len = nChannels; cmd.scan_end_arg = nChannels; cmd.start_src = TRIG_EXT; cmd.start_arg = CR_EDGE | NI_EXT_RTSI(0); cmd.convert_src = TRIG_EXT; cmd.convert_arg = CR_INVERT | CR_EDGE | NI_EXT_RTSI(2); cmd.stop_src = TRIG_NONE; ret = comedi_command(dev0, &cmd0); if(ret<0){ printf("comedi_command failed\n"); return ret; } return 0; }