4.7. Experimental functionality
Prev | 4. Acquisition and configuration functions | Next

### 4.7. Experimental functionality

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.

#### 4.7.1. Digital input combining machines

(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:

00
  transition is ignored.
01
  accumulator is incremented, or output is set.
10
  accumulator is decremented, or output is cleared.
11
  reserved.

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.

#### 4.7.2. Analog filtering configuration

(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.

#### 4.7.3. Analog Output Waveform Generation

(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.

#### 4.7.4. Extended Triggering

(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`

#### 4.7.5. Analog Triggering

(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:

data[1]
  trigger and combining machine configuration.
data[2]
  analog triggering signal chanspec.
data[3]
  primary analog level.
data[4]
  secondary analog level.

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.

#### 4.7.6. Bitfield Pattern Matching Extended Trigger

(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:

data[1]
  trigger flags.
data[2]
  mask.
data[3]
  pattern.

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`.

#### 4.7.7. Counter configuration

(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:

data[1]
  trigger configuration.
data[2]
  primary input chanspec.
data[3]
  primary combining machine configuration.
data[4]
  secondary input chanspec.
data[5]
  secondary combining machine configuration.
data[6]
  latch configuration.

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?

#### 4.7.8. One source plus auxiliary counter configuration

(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:

data[1]
  is flags, including the flags for the command triggering configuration. If a command is not subsequently issued on the subdevice, the command triggering portion of the flags are ignored.
data[2]
  determines the mode of operation. The mode of operation is actually a bitfield that encodes what to do for various transitions of the source signals.
data[3], data[4]
  determine the primary source for the counter, similar to the `…_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?

#### 4.7.9. National Instruments General Purpose Counters/Timers (GPCT)

Counters/timers and pulse generators are fairly different in terms of functionality, but they correspond to similar devices seen either as input or output. When generalising, these devices are both referred to as "counters". The NI boards provide a couple of such counters, under the name of GPCT. A counter is made of the following basic elements:

Input source
  the signal measured or the clock of the pulse generation.
Gate
  controls when the counting (or sampling) occurs.
Register
  holds the current count.
Out
  for the output counters (pulse generators), the output signal.

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`.

#### 4.7.10. National Instruments RTSI trigger bus

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;
    }

Prev | Up | Next
4.6. Slowly-varying inputs | Home | 5. Comedi reference