Difference between revisions of "826"

From Sensoray Technical Wiki
Jump to: navigation, search
(Usage)
 
(43 intermediate revisions by one user not shown)
Line 1: Line 1:
 
[[File:826 photo.jpg|thumb|Model 826 board]]
 
[[File:826 photo.jpg|thumb|Model 826 board]]
  
This is the technical wiki page for Sensoray's [http://www.sensoray.com/products/826.htm model 826], a versatile analog and digital I/O system on a PCI Express board. The board has 48 digital I/Os with edge detection, sixteen 16-bit analog inputs, eight 16-bit analog outputs, six 32-bit counter channels, a watchdog timer with fail-safe controller, and a flexible signal router.
+
This is the top-level wiki page for Sensoray's [http://www.sensoray.com/products/826.htm model 826], a versatile analog and digital I/O system on a PCI Express board. The board has 48 digital I/Os with edge detection, sixteen 16-bit analog inputs, eight 16-bit analog outputs, six 32-bit counter channels with quadrature clock decoders, three-stage watchdog timer with fail-safe controller, and a flexible signal router.
 +
 
 +
;Related pages
 +
Each board subsystem has a dedicated wiki page:
 +
* [[826 ADC|ADC]] - analog input system
 +
* [[826 DAC|DACs]] - analog output system
 +
* [[826 DIOs|DIOs]] - general-purpose digital I/Os
 +
* [[826 counters|Counters]] - counter/timers, including appnotes for interfacing incremental encoders
 +
* [[826 watchdog|Watchdog]] - watchdog timer and fail-safe controller
  
 
;Please note:
 
;Please note:
* Examples are intended to function as described, but this is not guaranteed. If you discover an error, please [http://www.sensoray.com/support/message.htm?s=Web%20feedback inform the webmaster].
+
* Code and circuit examples are intended to function as described, but this is not guaranteed. If you discover an error, please [http://www.sensoray.com/support/message.htm?s=Web%20feedback inform the webmaster].
 
* In code examples, error checking has been simplified or omitted for clarity. It is recommended to always perform error checking in your production software.
 
* In code examples, error checking has been simplified or omitted for clarity. It is recommended to always perform error checking in your production software.
 
* C language examples depend on header file <code>826api.h</code>, which should be included at the top of your source code like this:
 
* C language examples depend on header file <code>826api.h</code>, which should be included at the top of your source code like this:
 
  #include "826api.h"
 
  #include "826api.h"
  
==Counters==
+
==Timestamp generator==
  
See [[826 counters]] for information about counter channels and incremental encoder interfacing.
+
The timestamp generator is a free-running 32-bit counter that serves as a time reference. The counter increments once per microsecond and overflows (to zero, without notification) every 2<sup>32</sup> &micro;s (approximately 71.6 minutes). It is a binary counter and consequently does not keep track of the date or time-of-day. At any moment, the current count may be sampled; such a sample is called a ''timestamp''.
  
==ADC==
+
A timestamp is automatically appended to every counter snapshot and to every ADC sample so that application programs can know (to within 1 &micro;s) when each sample was acquired. Also, application programs can read the timestamp generator at any time to get a timestamp.
 
+
===Connections===
+
 
+
{{ImportantNote|The board's analog input (AIN) pins are not isolated.}}
+
 
+
The AIN common mode voltage (CMV) is referenced to the GND pins on connector J1, which are internally connected to the PCIe power supply return. The voltage applied to any AIN pin must not exceed the absolute maximum CMV (see AIN specifications for CMV range). Use an isolation amplifier, level translator, or voltage divider when connecting signals that exceed the allowed CMV range.
+
 
+
====Measuring isolated sources====
+
 
+
[[File:AdgGroundReference.gif|thumb|300px|To prevent measurement errors, connect isolated sources to ADC ground directly or through a resistor]]
+
 
+
Measurement accuracy can be significantly degraded if an analog input is connected to an unreferenced voltage source such as a battery, thermocouple, or isolated power supply. Since the source is isolated, the analog input has no current return to ground and, as a result, the input signal may be pulled (by protection circuitry) beyond the maximum allowed CMV. The resulting measurement errors can manifest in various ways (e.g., apparent non-linearity or calibration error) and can be difficult to characterize.
+
 
+
When measuring an isolated source, be sure to provide an input return to ground by connecting one side of the source to the ADC power supply ground, either directly (as shown in the diagram to the right) or through a resistor. This connection may be made to either the positive or negative terminal of the differential input pair.{{clear}}
+
 
+
====Anti-aliasing====
+
 
+
The 826 analog input system employs a multiplexed successive-approximation ADC to facilitate high speed acquisition of switched inputs. Anti-alias filters are intentionally excluded to allow the highest possible performance in a wide range of applications. Consequently, to avoid aliasing, you must sample each analog input (differential pair) frequently enough to satisfy Nyquist. If, due to high signal frequencies, this isn't possible for a particular input, it is recommended to use an external anti-alias filter to attenuate high-frequency components and thus satisfy Nyquist. Depending on the application, this may be as simple as adding an RC filter across the differential input pair.
+
 
+
====Application examples====
+
 
+
=====Floating source=====
+
 
+
When measuring an unreferenced voltage source such as a battery or isolated power supply, or a sensor powered by such sources, connect the signal ground to GND to avoid high CMV. GND may be connected to either +AIN or -AIN.
+
[[File:AinBattery.gif|left|]]
+
{{Clear}}
+
 
+
=====IC temperature sensor=====
+
This circuit uses an LM35 integrated circuit to measure temperature. 5 V is routed from the J2 or J3 connector to power the IC. GND is connected to -AIN because it is the reference for +AIN. The LM35 output voltage is 10 mV/&deg;C, which gives a measurement resolution of better than 0.002 &deg;C on the &plusmn;1V measurement range.
+
[[File:AinLM35.gif|left|]]
+
{{Clear}}
+
 
+
=====Potentiometer position detector=====
+
In this circuit, the potentiometer wiper position can be determined by measuring the wiper voltage. Use a linear taper pot to get consistent measurement resolution over the full operating range. For improved accuracy, use a second AIN channel to measure the 5V excitation voltage (as in the thermistor example below).
+
[[File:AinPot.gif|left|]]
+
{{Clear}}
+
 
+
=====Precision thermistor measurement=====
+
Two AIN channels are used in this circuit, one to measure thermistor voltage (AIN1) and another to measure the 5 V excitation (AIN0). This allows the software to compensate for excitation variations, which is done by computing the ratio of AIN1/AIN0 and then converting the ratio to temperature units. The cable shield should only be terminated at one end, to avoid ground loops. Choose R to obtain the desired measurement resolution.
+
[[File:AinPrecisionThermistor.gif|left|]]
+
{{Clear}}
+
 
+
=====Isolated sensors=====
+
Sensors which are powered by batteries and isolated power supplies can be directly connected to AIN. Connect 826 GND to sensor GND to avoid high CMV. For best performance and accuracy, sense GND at the sensor (by running a dedicated wire from sensor GND to -AIN), and use a twisted pair to reduce common mode noise.
+
[[File:AinSensorGalvanic.gif|left|]]
+
{{Clear}}
+
 
+
=====Mitigating high CMV=====
+
In cases where the CMV would exceed the AIN absolute maximum rating, an isolation amplifier can be used to eliminate the excessive CMV.
+
[[File:AinSensorIsolated.gif|left|]]
+
{{Clear}}
+
 
+
===Programming basics===
+
 
+
;<u>1. Assign slots</u>
+
 
+
Each analog measurement requires one timeslot (a "slot"). There are 16 slots (numbered 0 to 15), so you can perform up to 16 measurements in a conversion burst.
+
 
+
Start by assigning a slot number to each measurement. It's not required to use all slots, and it's permissible to leave gaps between used slots. For example, you could use only slots 0, 5 and 11 if desired, although it's usually simpler to just use consecutive slots.
+
 
+
;<u>2. Configure slots</u>
+
 
+
For every slot you assigned, call <code>S826_AdcSlotConfigWrite</code> to configure the slot for the desired analog input channel (AIN), input range and settling time.
+
 
+
{{ImportantNote|You must call <code>S826_AdcSlotConfigWrite</code> once for each slot you are using. Unconfigured slots will measure AIN0 by default.}}
+
 
+
Each slot can be used to measure any input channel. For example, the following will configure slot 0 to measure AIN13 using the &plusmn;10 V range, with 30 &micro;s delay between input switching and start of conversion:
+
 
+
S826_AdcSlotConfigWrite(board, 0, 13, 30, S826_ADC_GAIN_1); // slot 0: measure AIN13 on &plusmn;10V range; tsettle = 30&micro;s
+
 
+
;<u>3. Enable slots</u>
+
 
+
Call <code>S826_AdcSlotlistWrite</code> to enable all of the slots you are using. The second argument is a set of flags in which each bit is associated with a particular slot (bit 0 = slot 0, bit 1 = slot 1, etc.): '1' enables the slot and '0' disables it. For example, if you are only using slot 0 (e.g., as shown above, to measure AIN13):
+
 
+
S826_AdcSlotlistWrite(board, 0x0001, S826_BITWRITE);    // enable only slot 0
+
 
+
If instead you were using slots 0, 1 and 15, you would do this:
+
 
+
S826_AdcSlotlistWrite(board, 0x8003, S826_BITWRITE);    // enable slots 0, 1 and 15
+
 
+
Or if using all slots, do this:
+
+
S826_AdcSlotlistWrite(board, 0xFFFF, S826_BITWRITE);    // enable all slots (0 to 15)
+
 
+
Note: You can call <code>S826_AdcSlotlistWrite</code> while the ADC is running to dynamically enable/disable particular slots.
+
 
+
;<u>4. Select trigger mode.</u>
+
 
+
Use Continuous Mode if you want conversions to run automatically (disables hardware triggering):
+
 
+
S826_AdcTrigModeWrite(board, 0);      // trigger mode = continuous
+
 
+
;<u>5. Start ADC conversions</u>
+
 
+
S826_AdcEnableWrite(board, 1);        // start adc conversions
+
 
+
;<u>6. Read ADC data</u>
+
 
+
Call <code>S826_AdcRead</code> to receive ADC samples. Note that within each sample, the binary ADC data is the least-significant 16 bits. For example, to read a sample from only slot 0:
+
 
+
int samples[16];        // adc samples -- Note: buffer must always be sized for 16 slots
+
uint slotlist = 0x0001;  // read slot 0
+
S826_AdcRead(0, samples, NULL, &slotlist, S826_WAIT_INFINITE); // get adc data
+
printf("Binary adc data = %d", samples[0] & 0xFFFF);
+
 
+
Or you could do this to read all slots:
+
 
+
int samples[16];        // adc samples
+
uint slotlist = 0xFFFF;  // read all slots
+
S826_AdcRead(0, samples, NULL, &slotlist, S826_WAIT_INFINITE); // get adc data
+
for (int i = 0; i < 16; i++)
+
  printf("AIN%d binary adc data = %d"\n, i, samples[i] & 0xFFFF);
+
 
+
===Converting ADC data to volts===
+
 
+
This function converts an ADC sample to volts:
+
 
+
double GetAdcVolts(int sample, uint range)  // note: assumes valid range
+
{
+
  short adcdata = (short)(sample & 0xFFFF); // extract binary adc data from sample
+
  double frac = adcdata * (1.0 / 0x7FFF);
+
  switch (range) {
+
    case S826_ADC_GAIN_10: return frac;        break;  // -1V to +1V
+
    case S826_ADC_GAIN_5:  return frac * 2.0;  break;  // -2V to +2V
+
    case S826_ADC_GAIN_2:  return frac * 5.0;  break;  // -5V to +5V
+
    case S826_ADC_GAIN_1:  return frac * 10.0; break;  // -10V to +10V
+
  }
+
}
+
 
+
Example usage:
+
 
+
int samples[16];        // adc sample buffer
+
uint slotlist = 0x0001;  // only slot 0 is used in this example
+
S826_AdcRead(0, samples, NULL, &slotlist, S826_WAIT_INFINITE); // acquire sample
+
printf("ADC measured %f volts", GetAdcVolts(samples[0], S826_ADC_GAIN_1));
+
 
+
===Handling ADC interrupts===
+
 
+
An interrupt request (IRQ) is generated when the ADC completes a conversion burst. These IRQs are managed by the blocking function <code>S826_AdcRead</code>, which configures ADC interrupts and handles the resulting IRQs as required. To wait for the next IRQ, simply call <code>S826_AdcRead</code>; the function will return when ADC data is available, and will block and allow other threads to run while ADC data is unavailable.
+
 
+
The following example shows how this works. In this example, only slot 0 is being used, and timestamps are not used. Note that the ADC (and its trigger source) must have been previously configured and enabled (see next example).
+
 
+
void AdcHandler(void)
+
{
+
  int errcode;
+
  int adcdata[16];  // buffer must be sized for 16 slots
+
  while (1) {
+
    uint slotlist = 1;  // only slot 0 is of interest in this example
+
    errcode = S826_AdcRead(0, adcdata, NULL, &slotlist, S826_WAIT_INFINITE); // wait for IRQ
+
    if (errcode != S826_ERR_OK)
+
      break;
+
    printf("Raw adc data = %d", adcdata[0] & 0xFFFF);
+
  }
+
}
+
 
+
===Periodic ADC conversions (self-paced)===
+
 
+
:''Can the ADC periodically acquire samples without using a counter to pace it?''
+
 
+
Yes: To do this, configure the ADC for continuous triggering mode and use the slot settling times to control the sampling rate. Note that the sampling period will have a small amount of jitter (&le; 1 &micro;s) because ADC conversion time is 2-3 &micro;s. This example shows how to acquire approximately 20 samples per second from analog input 0:
+
 
+
#define SAMPLING_PERIOD 50000        // Sampling period in microseconds (50000 = 20 samples/s).
+
#define TSETTLE SAMPLING_PERIOD - 3;  // Compensate for nominal ADC conversion time.
+
+
// Configure the ADC subsystem and start it running
+
S826_AdcSlotConfigWrite(board, 0, 0, TSETTLE, S826_ADC_GAIN_1); // measuring ain 0 on slot 0
+
S826_AdcSlotlistWrite(board, 1, S826_BITWRITE);                // enable slot 0
+
S826_AdcTrigModeWrite(board, 0);                                // trigger mode = continuous
+
S826_AdcEnableWrite(board, 1);                                  // start adc conversions
+
+
while (1) {
+
  AdcHandler();  // Handle periodic ADC interrupts (using code from earlier example)
+
}
+
 
+
===Periodic ADC conversions (using counter)===
+
 
+
A counter can be used to periodically trigger ADC bursts. To do this, configure the counter as a periodic timer that outputs a pulse at the end of each period. The output pulse need not be routed to a DIO pin because the board can internally route it directly to the ADC's trigger input. Also, it's not necessary to generate counter snapshots because the ADC will notify software when a conversion burst has completed.
+
 
+
The following code shows how to digitize all 16 analog inputs in a burst, at a rate of <code>period</code> &micro;s/burst. The bursts are triggered by counter0.
+
 
+
// Use counter0 to periodically trigger ADC conversions -------------
+
+
void StartAdc16(uint period)  // Configure/start ADC and trigger generator
+
{
+
  int i;
+
  for (i = 0; i < 16; i++)  // Configure all timeslots: 1 slot per AIN; 20&micro;s/slot settling time.
+
    S826_AdcSlotConfigWrite(0, i, i, 20, S826_ADC_GAIN_1);
+
  S826_AdcSlotlistWrite(0, 0xFFFF, S826_BITWRITE); // Enable all 16 timeslots.
+
  S826_AdcTrigModeWrite(0, 0xB0);                  // Hardware triggered, source = counter0 ExtOut.
+
  S826_AdcEnableWrite(0, 1);                      // Enable ADC conversions.
+
  [[826 counters#Hardware timer|CreateHwTimer]](0, 0, period);                    // Create and start the trigger generator.
+
}
+
+
int ReadAdc16(int *adcbuf)
+
{
+
  uint slotlist = 0xFFFF;    // Wait for ADC burst completion.
+
  return S826_AdcRead(0, adcbuf, NULL, &slotlist, S826_WAIT_INFINITE);
+
}
+
 
+
In the following example, all 16 AINs are digitized and processed ten times per second.
+
 
+
int adcbuf[16];      // sample buffer -- always set size=16 (even if fewer samples needed)
+
StartAdc16(100000);  // Configure adc; start adc and trigger generator (trig every 100K us).
+
while (ReadAdc16(adcbuf) == S826_ERR_OK) {    // Repeat forever:
+
  // TODO: PROCESS ADC SAMPLES
+
}
+
 
+
===Oversampling===
+
 
+
Oversampling is a useful technique for reducing noise and increasing resolution. Model 826 facilitates oversampling by allowing an analog input (AIN) to be measured multiple times in a single ADC burst. After the burst, the samples can be averaged to obtain an enhanced sample value.
+
 
+
;Example
+
The following code shows how to oversample two AINs. In each burst, AIN0 is digitized eight times and then AIN1 is digitized eight times. To set this up, each AIN is assigned to eight contiguous slots. For each AIN, the first slot includes a settling time because the ADC input has just switched. The seven subsequent slots do not require settling time (and thus settling times are set to 0 &micro;s) because the input has not switched.
+
 
+
The ADC's internal timing is used to pace conversions, by selecting the continuous triggering mode and assigning appropriate settling times. Slot 0 is assigned a long settling time, which effectively determines the sampling period and also provides a necessary settling delay. Slot 8 has a shorter settling time because it only needs to delay long enough for signal settling.
+
 
+
Note that error checking has been omitted here for clarity, but should always be included in robust application code.
+
 
+
// Settling times in microseconds
+
#define TSETTLE0 10000  // Delay after switching to AIN0 (tweak this to adjust sampling rate).
+
#define TSETTLE1 20    // Delay after switching ADC to AIN1.
+
 
+
// Average 8 samples and convert to volts (assumes +/-10V measurement range)
+
#define VOLTS(SUM)  ((SUM) * 10.0 / (8.0 * 32768.0))
+
+
int slot;
+
int adcbuf[16];  // Sample buffer -- always set size=16 (even if fewer samples needed)
+
+
// Adc timeslot attributes.
+
struct SLOTATTR {
+
  uint chan;      // analog input channel
+
  uint tsettle;  // settling time in microseconds
+
} attr[16] = {                                            // During each burst:
+
  {0, TSETTLE0},                                          //  switch to AIN0, delay, then digitize
+
  {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},  //  digitize AIN0 7 more times without delay
+
  {1, TSETTLE1},                                          //  switch to AIN1, delay, then digitize
+
  {1, 0}, {1, 0}, {1, 0}, {1, 0}, {1, 0}, {1, 0}, {1, 0}}; //  digitize AIN1 7 more times without delay
+
+
// Configure all 16 timeslots.
+
for (slot = 0; slot < 16; slot++)
+
  S826_AdcSlotConfigWrite(board, slot, attr[slot].chan, attr[slot].tsettle, S826_ADC_GAIN_1);
+
+
// Configure adc system and start it running.
+
S826_AdcSlotlistWrite(board, 0xFFFF, S826_BITWRITE);  // Enable all 16 timeslots.
+
S826_AdcTrigModeWrite(board, 0);                      // Select continuous triggering mode.
+
S826_AdcEnableWrite(board, 1);                        // Start conversions.
+
+
while (1)    // Repeatedly fetch and display oversampled data:
+
{
+
  int sum[] = {0, 0};                                // Reset sample accumulators (one per AIN).
+
  uint slotlist = 0xFFFF;                            // Wait for ADC burst
+
  S826_AdcRead(board, adcbuf, NULL, &slotlist, 1000); //  and read samples from 16 slots.
+
+
  for (slot = 0; slot < 16; slot++)                  // Sum the 8 samples from each AIN
+
    sum[slot >> 3] += (short)(adcbuf[slot] & 0xFFFF); //  while masking off sample meta-data.
+
+
  // Compute and report measured voltages.
+
  printf("AIN0=%3.3fV, AIN1=%3.3fV\n", VOLTS(sum[0]), VOLTS(sum[1]));
+
}
+
 
+
===Polled operation===
+
 
+
:''How can I read analog inputs as fast as possible?''
+
 
+
In event-driven applications, the <code>S826_AdcRead</code> function is allowed to block until ADC samples are available. However, blocking necessarily involves context switching, which introduces overhead. To avoid this overhead, blocking must be disabled. This is done by setting argument <code>tmax=0</code>, which allows the ADC subsystem to be polled without blocking.
+
 
+
;Example
+
 
+
The following code employs polling to acquire data from all 16 AINs. Each call to <code>S826_AdcRead</code> will return immediately, with <code>slotlist</code> bits indicating the AINs that have new samples waiting in <code>adcbuf</code>. If desired, individual samples may be processed when they arrive in <code>adcbuf</code> (each time <code>S826_AdcRead</code> executes), or the program can wait for all 16 AINs and then process them en masse.
+
 
+
In this example a 7 &micro;s settling delay is allowed before each conversion begins. The ADC conversion time is &le;3&nbsp;&micro;s, so the total time per AIN is &le;10&nbsp;&micro;s and the burst time for all AINs is &le;160&nbsp;&micro;s. Note that individual settling times may need adjustment depending on factors such as source impedance and required accuracy.
+
 
+
#define TSETTLE    7        // Settling delay after switching AIN (adjust as necessary).
+
#define SLOTFLAGS  0xFFFF  // Timeslot flags: use all 16 timeslots.
+
+
int i;
+
int adcbuf[16];  // Sample buffer -- always set size=16 (even if fewer samples needed)
+
+
// Configure all timeslots: 1 slot per AIN; constant settling time for all slots.
+
for (i = 0; i < 16; i++)
+
  S826_AdcSlotConfigWrite(board, i, i, TSETTLE, S826_ADC_GAIN_1);
+
+
// Configure adc system and start it running.
+
S826_AdcSlotlistWrite(board, SLOTFLAGS, S826_BITWRITE); // Enable all 16 timeslots.
+
S826_AdcTrigModeWrite(board, 0);                        // Select free-running mode.
+
S826_AdcEnableWrite(board, 1);                          // Start conversions.
+
+
while (1)    // Repeat forever:
+
{
+
  uint remaining = SLOTFLAGS;      // timeslots that have not yet been read
+
  do {
+
    uint slotlist = remaining;    // Read all available remaining timeslots.
+
    int errcode = S826_AdcRead(board, adcbuf, NULL, &slotlist, 0); // note: tmax=0
+
    remaining &= ~slotlist;
+
+
    // OPTIONAL: NEWLY ARRIVED SAMPLES MAY BE PROCESSED WHILE WAITING FOR REMAINING SAMPLES
+
+
  } while (errcode == S826_ERR_NOTREADY);
+
+
  if (errcode != S826_ERR_OK)
+
    break; // error
+
+
  // TODO: PROCESS ALL UNPROCESSED SAMPLES IN ADCBUF
+
}
+
 
+
===Software triggering===
+
 
+
Some applications require that ADC conversion bursts be triggered by software. To set this up, configure the ADC for hardware-triggering and select one of the board's virtual digital outputs as the trigger source. The software can then write to the virtual digital output to start an ADC conversion burst. The following code shows how to use virtual digital output 0 as a software-controlled ADC trigger:
+
 
+
#define TSETTLE    7        // Settling delay after switching AIN (in microseconds; adjust as necessary).
+
#define SLOTFLAGS  0xFFFF  // Timeslot flags: use all 16 timeslots.
+
#define VIRTOUT    0        // Use virtual digital output 0 as ADC burst trigger.
+
+
void InitAdc(uint board)
+
{
+
  // Configure all timeslots: 1 slot per AIN; constant settling time for all slots.
+
  int i;
+
  for (i = 0; i < 16; i++)
+
    S826_AdcSlotConfigWrite(board, i, i, TSETTLE, S826_ADC_GAIN_1);
+
+
  // Configure adc system and start it running.
+
  S826_AdcSlotlistWrite(board, SLOTFLAGS, S826_BITWRITE); // Enable all 16 timeslots.
+
  S826_AdcTrigModeWrite(board, VIRTOUT + 182);            // Use virtual digital output as trigger.
+
  S826_AdcEnableWrite(board, 1);                          // Enable conversions.
+
}
+
 
+
This triggers a conversion burst:
+
 
+
void TriggerBurst(uint board)
+
{
+
  S826_VirtualWrite(board, 1 << VIRTOUT, S826_BITSET);  // Rising edge of trigger starts conversion.
+
  S826_VirtualWrite(board, 1 << VIRTOUT, S826_BITCLR);  // Falling edge of trigger pulse.
+
}
+
 
+
This returns True if a conversion burst has completed:
+
 
+
int IsBurstDone(uint board)
+
{
+
  uint status;
+
  S826_StatusRead(board, &status);
+
  return (status == SLOTFLAGS);
+
}
+
 
+
This example demonstrates how to use the above code:
+
 
+
InitAdc(0);
+
while (1)    // Repeat forever:
+
{
+
  uint slotlist;
+
  int slot;
+
  int adcbuf[16];  // Sample buffer -- always set size=16 (even if fewer samples needed)
+
+
  TriggerBurst(0);                      // Trigger a conversion burst.
+
  do {} while (!IsBurstDone(0));        // Poll until burst completes.
+
  slotlist = SLOTFLAGS;                // Copy ADC data into adcbuf.
+
  int errcode = S826_AdcRead(0, adcbuf, NULL, &slotlist, 0); // note: tmax=0
+
+
  for (slot = 0; slot < 16; slot++)    // Display all samples.
+
    printf("Slot %d sample value = %d\n", slot, adcbuf[slot]);
+
}
+
 
+
===Calibration errors caused by missing shunt===
+
 
+
On 826 SDKs earlier than version 3.2.0, analog calibration values will not be applied without J6 (labeled "Calibration Enable") installed. A missing shunt is intended to protect against accidental overwriting of calibration values, but in these SDKs it also prevents the reading of those values. This is resolved in SDK version 3.2.0 and above; in these versions the shunt functions as intended and must be installed only when calibrating the board (though leaving it installed all the time is okay).
+
 
+
If board calibration is incorrect, make sure J6 is installed or upgrade to SDK version 3.2.0 or higher. The 826 SDK can be downloaded from the [http://www.sensoray.com/products/826.htm 826 product page].
+
 
+
===ADC transfer functions===
+
 
+
Each ADC input voltage range extends slightly beyond the negative end of its indicated range:
+
 
+
{| class="wikitable"
+
|- align="center"
+
!| Range: !! &plusmn;10V !! &plusmn;5V !! &plusmn;2V !! &plusmn;1V
+
|- align="center"
+
! | ADC data
+
! colspan="4" | Input voltage (V)
+
|- align="center"
+
| | 0x7FFF || +10.0 || +5.0 || +2.0 || +1.0
+
|- align="center"
+
| | 0x0000 || 0.0 || 0.0 || 0.0 || 0.0
+
|- align="center"
+
| | 0x8001 || -10.0 || -5.0 || -2.0 || -1.0
+
|- align="center"
+
| | 0x8000 || -10.0003 || -5.00015 || -2.00006 || -1.00003
+
|}
+
 
+
===ADC accuracy specification===
+
 
+
Resolution and no missing codes are both 16 bits minimum.
+
 
+
{| class="wikitable"
+
!rowspan="2"| Parameter
+
!colspan="3"| Value
+
!rowspan="2"| Units
+
!rowspan="2"| Description
+
|-
+
! Min
+
! Typ
+
! Max
+
|-
+
|Integral Nonlinearity Error
+
|
+
| &plusmn;0.75
+
| &plusmn;1.5
+
| LSB
+
| Deviation of ADC transfer function from best-fit line
+
|-
+
| Differential Nonlinearity Error
+
|
+
| &plusmn;0.5
+
| &plusmn;1.25
+
| LSB
+
| Deviation of ADC code width from ideal code width
+
|-
+
| Gain Error
+
|
+
| &plusmn;2
+
| &plusmn;40
+
| LSB
+
| Deviation of difference between actual level of last data transition and actual level of first transition from difference between ideal levels
+
|-
+
| Gain Error Temperature Drift
+
|
+
| &plusmn;0.3
+
|
+
| ppm/&deg;C
+
|
+
|-
+
| Zero Error
+
|
+
|
+
| &plusmn;0.8
+
| mV
+
| Difference between ideal midscale voltage and actual voltage producing the midscale output code
+
|-
+
| Zero Temperature Drift
+
|
+
| &plusmn;0.3
+
|
+
| ppm/&deg;C
+
|
+
|}
+
 
+
===Maximum input voltage===
+
 
+
The analog inputs accept common mode voltages (CMV) up to &plusmn;12V with no resulting input current or damage. CMV up to &plusmn;25V is tolerated continuously, though this will cause currents to flow in the analog inputs. CMV greater than 25V may be tolerated for brief intervals, but this can cause significant currents to flow in the analog inputs and is not specified nor guaranteed to be safe.
+
 
+
{{ImportantNote|Analog inputs are not isolated}}
+
 
+
The input CMV is referenced to the GND signal on connector J1, which is internally connected to the PCIe power supply return.
+
 
+
==DAC==
+
 
+
===Bipolar transfer functions===
+
 
+
In the DAC bipolar output modes (&plusmn;5 V, &plusmn;10 V), the actual voltage range extends slightly beyond the negative end of the indicated output range:
+
 
+
{| class="wikitable"
+
|+ &plusmn;5 V range
+
|-
+
! DAC data
+
! Output (V)
+
|-
+
| 0x0000
+
| -5.0002
+
|-
+
| 0x0001
+
| -5.0
+
|-
+
| 0x8000
+
| 0.0
+
|-
+
| 0xFFFF
+
| +5.0
+
|}
+
 
+
{| class="wikitable"
+
|+ &plusmn;10 V range
+
|-
+
! DAC data
+
! Output (V)
+
|-
+
| 0x0000
+
| -10.0003
+
|-
+
| 0x0001
+
| -10.0
+
|-
+
| 0x8000
+
| 0.0
+
|-
+
| 0xFFFF
+
| +10.0
+
|}
+
 
+
===Specifying setpoint in Volts===
+
 
+
:''Is there a way to program the analog outputs using Volts units?''
+
 
+
The following function will set a DAC output to the specified voltage (note: the function does not check for illegal voltage values that are outside the specified range):
+
 
+
int SetDacOutput(uint board, uint chan, uint range, double volts)
+
{
+
  uint setpoint;
+
  switch (range) {  // conversion is based on dac output range:
+
    case S826_DAC_SPAN_0_5:  setpoint = (uint)(volts * 0xFFFF /  5);          break; // 0 to +5V
+
    case S826_DAC_SPAN_0_10:  setpoint = (uint)(volts * 0xFFFF / 10);          break; // 0 to +10V
+
    case S826_DAC_SPAN_5_5:  setpoint = (uint)(volts * 0xFFFF / 10) + 0x8000; break; // -5V to +5V
+
    case S826_DAC_SPAN_10_10: setpoint = (uint)(volts * 0xFFFF / 20) + 0x8000; break; // -10V to +10V
+
    default:                  return S826_ERR_VALUE;                                  // invalid range
+
  }
+
  return S826_DacDataWrite(board, chan, setpoint, 0);  // program DAC output and return error code
+
}
+
 
+
Many applications use a particular, fixed DAC output range, and never change it. In such cases, the range can be "hard coded" when setting the DAC output. For example, if the application uses the &plusmn;10 V range:
+
 
+
// Program board0, dac0 output to -7.35 V
+
int errcode = SetDacOutput(0, 0, S826_DAC_SPAN_10_10, -7.35); // hard-coded &plusmn;10 V range
+
 
+
Other applications may switch ranges dynamically. In such cases, if the current DAC output range is unknown (e.g., upon warm restart), you can call <code>S826_DacRead</code> first to determine the range:
+
 
+
// Program board0, dac0 output to +0.573 V
+
uint range, setpoint;
+
S826_DacRead(0, 0, &range, &setpoint, 0);  // Get the active range (and DAC data, which we ignore).
+
SetDacOutput(0, 0, range, 0.573);          // Now we can set the DAC output without changing the range.
+
 
+
====Setpoint readback====
+
 
+
The following function will return a DAC's programmed output voltage. Note that the returned <code>volts</code> may not exactly match the value specified in the last <code>SetDacOutput</code> call because the output voltage can only be programmed to discrete values.
+
 
+
int GetDacOutput(uint board, uint chan, double *volts)
+
{
+
  uint range, setpoint;
+
  int errcode = S826_DacRead(board, chan, &range, &setpoint, 0);    // Get DAC output range & setpoint.
+
  switch (range) {                                                  // Convert binary setpoint to volts:
+
    case S826_DAC_SPAN_0_5:  *volts = setpoint            * ( 5.0 / 0xFFFF); break;  // 0 to +5V
+
    case S826_DAC_SPAN_0_10:  *volts = setpoint            * (10.0 / 0xFFFF); break;  // 0 to +10V
+
    case S826_DAC_SPAN_5_5:  *volts = (setpoint - 0x8000) * ( 5.0 / 0x7FFF); break;  // -5V to +5V
+
    case S826_DAC_SPAN_10_10: *volts = (setpoint - 0x8000) * (10.0 / 0x7FFF); break;  // -10V to +10V
+
  }
+
  return errcode;
+
}
+
 
+
// Example usage: Read board0, dac0 programmed output voltage.
+
double volts;
+
if (GetDacOutput(0, 0, &volts) == S826_ERR_OK)
+
  printf("dac0 output is set to %f volts", volts);
+
else
+
  printf("error reading dac0");
+
 
+
===Control DAC voltage with an incremental encoder===
+
 
+
[[826 counters#Encoder-controlled voltage|This example]] shows how to use an incremental shaft encoder to control a DAC's output voltage.
+
 
+
===Connecting to a VFD===
+
 
+
In motor-control applications it's common to use a DAC to generate the control voltage for a variable-frequency drive (VFD). When doing this, it's essential to properly wire the ground signals so as to avoid noise and disruptive ground loops. In particular:
+
 
+
* Connect the VFD analog ground to an 826 GND pin, which is the analog reference for the DAC output signal.
+
* <u>Do not</u> connect the VFD analog ground net to anything else (e.g., chassis ground).
+
 
+
The recommended wiring shown below uses the DAC's signal ground (826 GND) as analog reference and avoids ground loops and thus ensures accurate, problem-free motor control:
+
 
+
<center>''Recommended wiring''</center>
+
[[File:Vfd wiring yes.gif|center|400px|alt=Recommended connections]]
+
 
+
 
+
<center>''Don't do this! It will create a ground loop.''</center>
+
[[File:Vfd wiring no1.gif|center|400px|alt=Don't do this!]]
+
 
+
 
+
<center>''Don't do this either! It has a noisy, inaccurate analog reference.''</center>
+
[[File:Vfd wiring no2.gif|center|400px|alt=Don't do this either!]]
+
 
+
===Linux Demo Sine Wave Generator counter error (-15)===
+
 
+
The Linux sine wave generator demo may experience a timeout and exit with an error code -15 (S826_ERR_FIFOOVERFLOW).  This occurs because the priority of the demo thread may be too low for the sample time.  Linux is not a RTOS and the process (or interrupt) may be delayed and not complete the DAC output in the specified time.
+
 
+
Older version of the demo will exit when S826_ERR_FIFOOVERFLOW occurs.  Later versions of the demo, however, will print an error code and continue outputting the sine wave.
+
 
+
In any case, if the DAC output sampling time requirements are very small and need to be precise, it is recommended to run the process at a higher priority.  You may also consider using a low-latency or rt kernel.
+
 
+
To run the demo at a higher priority:
+
 
+
"nice -n 19 ./s826demo"
+
 
+
For Ubuntu low-latency kernel:
+
 
+
"sudo apt-get install linux-lowlatency linux-headers-lowlatency"
+
 
+
For Ubuntu rt kernel:
+
 
+
"sudo apt-get install linux-rt linux-headers-rt"
+
 
+
In extreme high performance cases, you may consider using the raw DAC write command (S826_DacRawWrite) instead of S826_DacDataWrite. You must make sure to understand the DAC ranges before doing so.  This should normally not be necessary as S826_DacDataWrite is only marginally slower.
+
 
+
===Short-circuit protection===
+
 
+
The board's DAC devices do not have output short-circuit protection. However, each DAC automatically limits its output current to 38 mA. Consequently, a DAC can tolerate intermittent output shorts and will not be damaged as long as the DAC device does not overheat.
+
 
+
===DAC accuracy specification===
+
 
+
Resolution and monotonicity are both 16 bits minimum.
+
{| class="wikitable"
+
!rowspan="2" style="text-align:left;"| PARAMETER
+
!rowspan="2" style="text-align:left;"| CONDITIONS
+
!colspan="3"| VALUE
+
!rowspan="2" style="text-align:left;"| UNITS
+
|-
+
! MIN
+
! TYP
+
! MAX
+
|-
+
|Integral Nonlinearity
+
|
+
|
+
|
+
|&plusmn;2
+
|LSB
+
|-
+
|Differential Nonlinearity
+
|
+
|
+
|
+
|&plusmn;1
+
|LSB
+
|-
+
|Gain Error
+
|
+
|
+
|&plusmn;4
+
|&plusmn;20
+
|LSB
+
|-
+
|Gain Temperature Coefficient
+
|
+
|
+
|&plusmn;2
+
|
+
| ppm/&deg;C
+
|-
+
|Unipolar Zero-Scale Error
+
|5V unipolar range, 25&deg;C<br>10V unipolar range, 25&deg;C<br>5V unipolar range<br>10V unipolar range
+
|
+
|&plusmn;80<br>&plusmn;100<br>&plusmn;140<br>&plusmn;150
+
|&plusmn;200<br>&plusmn;300<br>&plusmn;400<br>&plusmn;600
+
|&micro;V<br>&micro;V<br>&micro;V<br>&micro;V
+
|-
+
|V_offset Temperature Coefficient
+
|All unipolar ranges
+
|
+
|&plusmn;2
+
|
+
|&micro;V/&deg;C
+
|-
+
|Bipolar Zero Error
+
|All bipolar ranges
+
|
+
|&plusmn;2
+
|&plusmn;12
+
|LSB
+
|}
+
 
+
==DIOs==
+
 
+
===DIO connections===
+
 
+
{{ImportantNote|DIOs are not isolated. An external isolator is required when connecting to a signal which is referenced to a different ground.}}
+
 
+
DIOs are referenced to the GND pins on connectors J2 and J3, which are internally connected to the PCIe power supply return. When using a DIO to monitor an external signal, make sure the signal is referenced to GND and does not exceed the absolute maximum DIO input voltage range:
+
 
+
* <u>Incompatible signal ground</u> - An optical isolator should be used when connecting to a signal that is referenced to a different ground, or in cases where a ground loop would be created by connecting signal ground to GND.
+
 
+
* <u>Unreferenced signal ground</u> - If the signal ground is unreferenced (e.g., the signal generator is powered by an isolated power supply), connect the signal ground to GND. This will reference the external signal to GND.
+
 
+
* <u>Incompatible signal voltage</u> - If the signal is referenced to GND but the signal voltage exceeds the DIO input range, use a level translator or voltage divider to bring it into range. In the latter case, keep in mind that the DIO has an internal pull-up resistor to +5 V.
+
 
+
====3.3 V compatibility====
+
 
+
DIOs are compatible with 3.3 V logic input signals. Note however, that every DIO has an internal 10 K&Omega; pull-up to +5 V, which can affect the signal voltage from high-impedance 3.3 V sources.
+
 
+
When operating as an output, a DIO produces a high logic level via its internal +5 V pull-up resistor. External 3.3 V circuitry must be 5 V tolerant or must limit the voltage (e.g., with Schottky diode to +3.3 V supply).
+
 
+
====DIO application examples====
+
 
+
=====Mechanical switch=====
+
To monitor the state of a mechanical switch, simply connect the switch between the DIO and GND. This takes advantage of the DIO's built-in pull-up resistor.
+
[[File:DioSwitch.gif|left|]]
+
{{Clear}}
+
 
+
=====Input isolator=====
+
An opto-isolator should be used when the input signal is differential or referenced to a different ground, or in cases where a ground loop would be created by connecting signal ground to GND.
+
[[File:DioOptoIsolator.gif|left|]]
+
{{Clear}}
+
 
+
=====Unreferenced 5V input=====
+
This circuit can be used to monitor an unreferenced (powered by an isolated supply) 5V logic signal. The signal ground is tied to 826 GND to keep it from floating.
+
[[File:DioIsolated5V.gif|left|]]
+
{{Clear}}
+
 
+
=====Unreferenced 12V logic input=====
+
Monitoring an unreferenced 12V logic signal (powered by an isolated supply). The Schottky diode limits the logic-high voltage applied to the DIO.
+
[[File:DioIsolated12V.gif|left|]]
+
{{Clear}}
+
 
+
=====Driving LEDs=====
+
A DIO can directly drive a low-power LED. This is commonly done to indicate status or to drive an optocoupler. High-current LEDs require an external buffer such as a SSR (see next example). A series resistor is required to limit the LED current.
+
[[File:DioLed.gif|left|]]
+
{{Clear}}
+
 
+
=====Controlling SSRs=====
+
A DIO can directly drive a typical solid-state relay. This allows a DIO to control high-power loads such as motors, solenoids, heaters and lighting.
+
[[File:DioSsr.gif|left|]]
+
{{Clear}}
+
 
+
===DIO programming basics===
+
 
+
To unconditionally program all DIO outputs, call <code>S826_DioOutputWrite</code> with the <code>mode</code> argument set to <code>S826_BITWRITE</code> (zero). For example, this code will turn on all DIOs:
+
 
+
uint dios[] = { // Specify DIOs that are to be turned on (driven to 0 V):
+
  0x00FFFFFF,  //  DIO 0-23
+
  0x00FFFFFF    //  DIO 24-47
+
}
+
S826_DioOutputWrite(0, dios, S826_BITWRITE); // Turn on all DIOs.
+
 
+
This example shows how to turn on DIOs 7, 13 and 38, and turn off all other DIOs:
+
 
+
uint dios[] = {        // Specify DIOs that are to be turned on:
+
  (1 << 7) + (1 << 13), //  DIOs 7 & 13 are in first 24-bit mask (DIOs 0-23),
+
  (1 << (38 - 24))      //  DIO 38 is in second 24-bit mask (DIOs 24-47).
+
}
+
S826_DioOutputWrite(0,  // Program all DIO outputs on board 0:
+
  dios,                //  desired output states
+
  S826_BITWRITE);      //  unconditionally change all DIOs
+
 
+
To read the pin levels of all DIOs, call <code>S826_DioInputRead</code>. This example shows how to read and display all DIO pins on board 0:
+
 
+
int i;
+
uint pins[2];                // Buffer for pin states.
+
S826_DioInputRead(0, pins);  // Read all DIO pin states into buffer.
+
for (i = 0; i < 24; i++)    // Display states of DIOs 0-23.
+
  printf("dio%d = %d\n", i, (pins[0] >> i) & 1);
+
for (i = 24; i < 48; i++)    // Display states of DIOs 24-47.
+
  printf("dio%d = %d\n", i, (pins[1] >> (i - 24)) & 1);
+
 
+
===Setting and clearing DIOs===
+
 
+
Sometimes it's necessary to set or clear particular DIOs without disturbing others. Typically, this is accomplished with a ''read-modify-write'' (RMW) sequence consisting of reading all DIOs into a buffer, modifying the buffer, and then writing the buffer back to the DIOs. This task is further complicated when multiple threads control the DIOs &mdash; a common practice in high-performance event-driven applications &mdash; because concurrent RMWs on different threads will conflict with each other. To avoid such conflicts, the RMW must be treated as a critical section (e.g., protected by a mutex or semaphore).
+
 
+
Fortunately, the 826 provides a fast, efficient way to set and clear DIOs without the complications of RMW. Simply call <code>S826_DioOutputWrite</code> with the <code>mode</code> argument set to <code>S826_BITSET</code> or <code>S826_BITCLR</code>, as shown in this example:
+
 
+
// Set and clear some DIOs without affecting other DIOs
+
uint maskA[] = {7, 0};  // bitmask for DIOs 0-2
+
uint maskB[] = {0, 1};  // bitmask for DIO 24
+
S826_DioOutputWrite(0, maskA, S826_BITSET);  // Set DIOs 0, 1 and 2.
+
S826_DioOutputWrite(0, maskB, S826_BITCLR);  // Clear DIO 24.
+
 
+
===Debouncing inputs===
+
 
+
[[File:SwitchBounce.png|thumb|Oscilloscope screenshot showing the "bouncing" voltage when a switch turns on]]
+
DIOs are commonly used to monitor real-world signals that have glitches or other noise. For example, the electrical contacts of mechanical switches may "bounce" when first connected, thus producing pulses that may be incorrectly interpreted by the application program as state changes. This problem can be solved by implementing a software debounce algorithm, but an easier way is to use the input filters provided by the DIO system.
+
 
+
To use the DIO input filters, simply specify a filter interval and designate the DIOs to be filtered. Choose a filter interval that is long enough to suppress the longest expected noise pulse, but not so long that valid state changes will be missed. The following example shows how to suppress pulses up to 200 &micro;s wide on dio0 and dio2:
+
 
+
// Debounce dio0 and dio2 ------------------
+
+
#define FILT_USEC 200                  // Desired filter interval (max = 1.31 ms).
+
uint tfilt = FILT_USEC * 50;          // Interval specified as multiple of 20ns.
+
uint enabs[] = {5, 0};                // Enable filters on dio0 and dio2.
+
S826_DioFilterWrite(0, tfilt, enabs);  // Activate the DIO filters.
+
 
+
===Edge detection===
+
 
+
Event-driven software often must execute code in response to DIO input changes (e.g., when a pushbutton is pressed, a photoelectric sensor is activated, etc.). Polling is not a good way to detect these events because it degrades system responsiveness and tends to make the source code convoluted and difficult to maintain. However, the alternative &mdash; using interrupts &mdash; can complicate and prolong program development.
+
 
+
Fortunately, model 826 provides the best of both worlds: it implements DIO edge detection circuitry with interrupts for efficient event monitoring, and the API makes the interrupts easy to use. For example, the following function will return when a falling edge is detected on a particular DIO. It is a blocking function, meaning that other threads can run while it waits for the DIO edge.
+
 
+
int WaitForDioFallingEdge(uint board, uint dio)
+
{
+
  uint rise[] = {0, 0};      // not interested in rising edge
+
  uint fall[] = {(dio < 24) << (dio % 24), (dio > 23) << (dio % 24)}; // interested in falling edge
+
  S826_DioCapEnablesWrite(board, rise, fall, S826_BITSET);  // Enable falling edge detection.
+
  return S826_DioCapRead(board, fall, 0, INFINITE_WAIT);    // Block until falling edge.
+
}
+
+
WaitForDioFallingEdge(0, 29);  // Example usage: wait for falling edge on dio29 of board number 0.
+
 
+
===Controlling output rise-time with an external pull-up===
+
 
+
Each DIO has an output driver that actively drives the signal to 0 V in the ''on'' state and is high-impedance in the ''off'' state. In the ''off'' state, the channel's internal 10 K&Omega; resistor passively pulls up the signal to +5 V. When the output switches to the ''off'' state, this 10 K&Omega; source resistance (combined with circuit capacitance) stretches the DIO rise time, which delays its transition to logic '1'. If the rise time is too long, it can be shortened by adding an external pull-up resistor (and reducing external capacitance) on the signal net.
+
 
+
The following table shows nominal rise times for unloaded DIO pins. Lower resistance values may be needed to compensate external circuit capacitance, but do not use an external pull-up resistor having less than 220 &Omega; (to prevent excessive DIO output current).
+
 
+
{| class="wikitable"
+
! style="text-align:left;"| External pull-up
+
! style="text-align:left;"| DIO rise time
+
|-
+
|None
+
|200 ns
+
|-
+
|1.2 K&Omega;
+
|20 ns
+
|-
+
|680 &Omega;
+
|10 ns
+
|}
+
 
+
===Generating a burst of pulses===
+
 
+
:''How can I make a DIO go active for 10ms and then inactive for another 10ms, and repeat this five times?''
+
 
+
There are several ways to do this but the methods you can use depend on a number of factors. Here are two approaches:
+
 
+
* If you need precise timing then you can use two counters as shown in [[826 counters#High resolution pulse-burst generator|High resolution pulse-burst generator]].
+
 
+
* If high precision is not needed then you could bit-bang the DIO, using a hardware timer (or system <code>Sleep</code> function) to pace the DIO writes like this:
+
 
+
int i;
+
uint mask[] = {1, 0};      // bitmask for dio0
+
[[826 counters#Software timer|CreateTimer]](0, 0, 10000);  // Create 10 ms timer and start it running.
+
for (i = 0; i < 5; i++) {
+
  [[826 counters#Software timer|WaitForTimer]](0, 0);                        // Wait for timer tick.
+
  S826_DioOutputWrite(0, mask, S826_BITSET);  // Set dio0 active.
+
  [[826 counters#Software timer|WaitForTimer]](0, 0);                        // Wait for timer tick.
+
  S826_DioOutputWrite(0, mask, S826_BITCLR);  // Set dio0 inactive.
+
}
+
 
+
===Example application: I<sup>2</sup>C Emulator===
+
 
+
:''Can I use DIOs to communicate with I<sup>2</sup>C devices?''
+
 
+
Have a look at [http://www.sensoray.com/downloads/appnote_826_i2c_emulator.pdf Sensoray's I<sup>2</sup>C emulator], which uses two DIOs to bit-bang an I<sup>2</sup>C bus. This open source software implements a full-featured I<sup>2</sup>C master emulator with bus arbitration and bus-hang resolver. All 826-specific code resides in a hardware abstraction layer (HAL) &mdash; an architectural feature that belongs in all production-quality software.
+
 
+
If you need to monitor an I<sup>2</sup>C bus or emulate a slave device, consider [[826 counters#Serial data capture (synchronous)|using a counter to capture synchronous serial data]].
+
 
+
===Interfacing RS-422 signals===
+
 
+
:''I need to monitor the state of an RS-422 signal pair. Can I do this with a DIO channel?''
+
 
+
You may be able to use one of the signals if it has no common-mode voltage and is appropriately terminated but, in general, DIO channels are TTL/CMOS compatible and therefore not compatible with incoming RS-422 signals. However, it is possible to use a counter channel to monitor the state of an RS-422 signal pair, using the technique described [[826 counters#Encoder FAULT output|here]].
+
 
+
===Relay rack compatibility===
+
 
+
{{ImportantNote|The DIO circuitry on model 826 is not compatible with 15 or 24 VDC modules. When choosing modules for your relay rack, be sure to select modules that are compatible with 5 VDC logic levels.}}
+
 
+
Each 50-pin DIO header carries 24 DIO signals that have 5 V logic levels. The header pinout is compatible with the following module racks and other racks that have the same pinouts:
+
 
+
Grayhill (various module types):
+
* 8 channels: 70RCK8-HL, 70MRCK8-HL, 70GRCK8-HL, 70LRCK8-HL, 70RCK8-DIN
+
* 16 channels: 70RCK16-HL, 70MRCK16-HL, 70GRCK16-HL, 70LRCK16-HL, 70RCK16-DIN
+
* 24 channels: 70MRCK24-HL, 70LRCK24-HL, 70LRCK24-DIN
+
 
+
Opto-22 (type G4 modules):
+
* 8 channels: G4PB8
+
* 16 channels: G4PB16
+
* 24 channels: G4PB24
+
 
+
===Maximum output current===
+
 
+
DIOs are subject to two different maximum current limits: individual and group. Each DIO is capable of continuously sinking up to 24 mA; this is the maximum individual current. However, it's not permitted for all DIOs to do this simultaneously because this would exceed the maximum group current.
+
 
+
DIO channels are organized into groups as shown below. In any particular group, the group current is the total of all individual currents in the group. For example, in Group4 the group current is the sum of the currents flowing in DIO24 through DIO29.
+
 
+
{| class="wikitable"
+
! style="text-align:left;"| Group
+
! style="text-align:left;"| DIOs
+
|-
+
|0
+
|0-5
+
|-
+
|1
+
|6-11
+
|-
+
|2
+
|12-17
+
|-
+
|3
+
|18-23
+
|-
+
|4
+
|24-29
+
|-
+
|5
+
|30-35
+
|-
+
|6
+
|36-41
+
|-
+
|7
+
|42-47
+
|}
+
 
+
Each group is capable of continuously sinking up to 72 mA. As shown in the following table, all DIOs in a group may be active at the same time if their individual currents are less than or equal to 12 mA (because 6 * 12 &le; 72). However, when the individual currents are higher than 12 mA, it is not allowed for all DIOs in a group to be active at the same time.
+
 
+
{| class="wikitable"
+
! style="text-align:left;"| DIO current (mA)
+
! style="text-align:left;"| Maximum number of active DIOs
+
|-
+
|i &le; 12
+
|6
+
|-
+
|12 &lt; i &le; 14.4
+
|5
+
|-
+
|14.4 &lt; i &le; 18
+
|4
+
|-
+
|18 &lt; i &le; 24
+
|3
+
|}
+
 
+
==Watchdog timer and fail-safe system==
+
 
+
===Activating safemode with an E-stop contact===
+
 
+
An external emergency stop (E-stop) switch can be used to force analog and digital outputs to fail-safe states. To implement this, you must convert the E-stop signal to active-low TTL/CMOS and apply it to DIO47, so that DIO47 will be driven low when the E-stop button is pressed. The following schematic shows a robust, reliable way to do this for a 24V E-stop contact. This circuit ensures that safemode will be activated if the E-stop pushbutton is pressed, or 24VDC power is lost, or the relay coil opens.
+
 
+
 
+
<center><i>A robust interface circuit for a 24V E-stop contact</i></center>
+
[[File:826_estop.gif|500px|center|alt=A robust, reliable way to condition a 24V E-stop contact]]
+
 
+
 
+
Typically, the application program will configure the fail-safe system during initialization, before I/O operations begin. This is done by programming the analog and digital safemode states, and then "arming" the system by enabling DIO47 to trigger safemode operation. The following example illustrates this process:
+
 
+
// Configure and arm the fail-safe system ---------------
+
+
int i;
+
+
// The desired fail-safe output conditions (change as required):
+
uint safeDioEnables[2] = {0x00FFFFFF, 0x00FFFFFF};  // Switch all DIOs to fail-safe states.
+
uint safeDioData[2]    = {0, 0};                    // Safemode DIO states.
+
uint safeAoutRange[4]  = {0, 0, 0, 0};              // Safemode analog output ranges.
+
uint safeAoutLevel[4]  = {0, 0, 0, 0};              // Safemode analog output levels.
+
+
// Allow modifications to fail-safe settings.
+
S826_SafeWrenWrite(0, S826_SAFEN_SWE);
+
+
// Program analog output fail-safe conditions.
+
for (i = 0; i < S826_NUM_DAC; i++) {
+
  S826_DacRangeWrite(0, i, safeAoutRange[i], 1);
+
  S826_DacDataWrite(0, i, safeAoutLevel[i], 1);
+
}
+
+
// Program digital output fail-safe conditions.
+
S826_SafeEnablesWrite(0, safeDioEnables);
+
S826_DioSafeWrite(0, safeDioData, S826_BITWRITE);
+
+
// Allow the E-stop switch to activate fail-safe operation.
+
S826_SafeControlWrite(0, S826_CONFIG_XSF, S826_BITSET);
+
+
// Prevent errant software from modifying fail-safe settings.
+
S826_SafeWrenWrite(0, S826_SAFEN_SWD);
+
 
+
In many cases the application program must be alerted when the E-stop pushbutton is pressed, so that it can execute relevant tasks (e.g., record the event to an error log). The following example shows how to detect and handle an E-stop event.
+
 
+
// Detect and handle an E-stop pushbutton press ------------
+
+
[[826 DIOs#Edge detection|WaitForDioFallingEdge]](0, 47); // Wait for DIO47 falling edge.
+
printf("E-stop pushbutton was pressed!");
+
 
+
===Activating safemode with the watchdog===
+
 
+
In many control applications, the analog and digital outputs must automatically switch to safe states if software fails to execute normally. This can be implemented by using watchdog Timer0 to activate safemode. To set this up, configure the watchdog and safemode systems during initialization (before I/O operations begin):
+
 
+
#define WD_MILLISECONDS 100  // Watchdog Timer0 will timeout if unkicked for this long
+
wdtiming[] = {WD_MILLISECONDS * 50000, 1, 1, 0, 0};
+
+
S826_SafeWrenWrite(0, S826_SAFEN_SWE);      // Write-enable watchdog/safemode settings.
+
S826_WatchdogConfigWrite(0, 0x10, wdtiming); // Set t0 interval; t0 triggers safemode.
+
+
// TODO: [[826#Default safemode states|Program safemode states]]
+
+
S826_WatchdogEnableWrite(0, 1);              // Start the watchdog running.
+
S826_SafeWrenWrite(0, S826_SAFEN_SWD);      // Write-protect watchdog/safemode settings.
+
 
+
The above code starts the watchdog timer, so the application program must now regularly "kick" the watchdog to prevent a timeout. This is typically done by periodically executing a kick algorithm. The kick algorithm may be simple or complex, depending on the number of running threads and other factors. The simplest algorithm will simply kick the watchdog, unconditionally:
+
 
+
[[826 counters#Periodic timer|CreateTimer]](0, 0, 100000);  // Execute this loop every 100 milliseconds:
+
while (1) {
+
  S826_WatchdogKick(0, 0x5A55AA5A);  // Unconditionally kick the watchdog.
+
  [[826 counters#Periodic timer|WaitForTimer]](0, 0);
+
}
+
 
+
Here's a slightly more complex version that monitors the states of two other threads (threadA and threadB). When each monitored thread completes its task, it stores a special value in a reserved memory location. The special values, when OR'ed together, form the value required for a watchdog kick.
+
 
+
int a_kick;  // ThreadA stores 0x5A550000 here when it completes.
+
int b_kick;  // ThreadB stores 0x0000AA5A here when it completes.
+
+
[[826 counters#Periodic timer|CreateTimer]](0, 0, 100000);  // Execute this loop every 100 milliseconds:
+
while (1) {
+
  S826_WatchdogKick(0, a_kick | b_kick);  // Kick watchdog if both threads completed.
+
  a_kick = b_kick = 0;                    // Reset completion status.
+
  [[826 counters#Periodic timer|WaitForTimer]](0, 0);
+
}
+
 
+
When watchdog timer0 times out, it may be necessary to notify "system health" monitoring software so it can take appropriate corrective action. This is easily done, as shown in the following code:
+
 
+
if (S826_WatchdogEventWait(0, INFINITE_WAIT) == S826_ERR_OK) {
+
  // Watchdog timer0 timed out, so take appropriate corrective action
+
}
+
 
+
===Output watchdog on a DIO===
+
 
+
The outputs from watchdog timer1 and timer2 can be routed to select DIOs. As explained in the API manual (see <code>S826_DioOutputSourceWrite</code>), timer1 can be routed to DIOs 7, 15, 23, 31, 39 and 47, and timer2 can be routed to DIOs 6, 14, 22, 30, 38 and 46.
+
 
+
The following code will route the output of timer1 to dio7 so that dio7 will be turned on (driven low, to 0 V) when timer1 times out. As always, timer0 is used to time the kicks. Timer1 is assigned a minimum delay (DELAY1=1) so that it will timeout (and thereby activate dio7) one clock (20 ns) after timer0 times out.
+
 
+
#define WD_MILLISECONDS 100  // Watchdog Timer0 will timeout if unkicked for this long
+
+
wdtiming[] = {WD_MILLISECONDS * 50000, 1, 1, 0, 0};  // timing parameters
+
uint routing[] = {1 << 7, 0};                        // mask for dio7
+
+
S826_SafeWrenWrite(0, S826_SAFEN_SWE);  // Write-enable protected settings.
+
S826_WatchdogConfigWrite(0,              // Configure watchdog:
+
  S826_WD_NIE,                          //  connect timer1 to NMI net
+
  wdtiming);                            //  set timer0 & timer1 intervals
+
S826_DioOutputSourceWrite(0, routing);  // Route NMI net to dio7.
+
S826_WatchdogEnableWrite(0, 1);          // Start the watchdog (AND START KICKING!)
+
S826_SafeWrenWrite(0, S826_SAFEN_SWD);  // Write-disable protected settings.
+
 
+
===Turning off the relay===
+
 
+
After the watchdog has activated the Reset Out relay, the application program can deactivate the relay by calling <code>S826_WatchdogEnableWrite()</code> with <code>enable=0</code>, as shown below:
+
 
+
S826_WatchdogEnableWrite(0, 0);  // Turn off relay on board0.
+
 
+
===Default safemode states===
+
 
+
:''In safemode, will all outputs default to power-on/reset conditions if safemode data registers have not been programmed?''
+
 
+
Yes, because upon power-up or reset, all safemode data registers are initialized to match their runmode counterparts: DIO outputs are initialized to '0' (i.e., outputs will be pulled up to +5V) and analog outputs are initialized to 0V using the 0 to +5V output range.
+
 
+
===Programming safemode states===
+
 
+
It's recommended to program the safemode data registers even if you will be using default values. This will serve to document fail-safe operation in your code and enable you to easily change safemode states if you need to. The following example shows how to do this for board number 0:
+
 
+
// Program fail-safe states for all analog and digital outputs --------------
+
+
int aout;                  // analog output channel number
+
uint SafeDio[] = {0, 0};    // fail-safe DIO states
+
+
S826_SafeWrenWrite(0, S826_SAFEN_SWE);                // Write-enable safemode data registers.
+
S826_DioSafeWrite(0, SafeDio, S826_BITWRITE);        // Program safemode DIO states.
+
for (aout = 0; aout < S826_NUM_DAC; aout++) {        // Program safemode analog output condition:
+
  S826_DacRangeWrite(0, aout, S826_DAC_SPAN_0_5, 1);  //  output range
+
  S826_DacDataWrite(0, aout, 0, 1);                  //  output voltage
+
}
+
S826_SafeWrenWrite(0, S826_SAFEN_SWD);                // Protect safemode data registers.
+
 
+
===P2 mating connector===
+
 
+
These connectors (or equivalents) will mate to the board's three-pin Watchdog Reset Out header (P2):
+
* Molex 22-01-2037 (ramp only)
+
* Molex 22-01-3037 (ramp + alignment ribs)
+
 
+
==Timestamp generator==
+
  
The timestamp generator is a high-resolution "clock" based on a free-running 32-bit counter. The counter increments every microsecond and overflows (to zero, without notification) every 2<sup>32</sup> &micro;s (approximately 71.6 minutes). It is a binary counter and consequently does not keep track of the date or time-of-day.
+
===Usage===
  
The generator's current time is automatically appended to every counter snapshot and every ADC sample so that application programs can know (to within 1 &micro;s) when each sample was acquired. It is particularly useful for precisely measuring the elapsed time between hardware events. Calculation of elapsed time is easy (a single subtraction) as long as the time doesn't exceed 71.6 minutes. It can be used in a variety of ways, including [[826 counters#Measuring speed|measuring speed]] and [[826 counters#Serial data capture|capturing serial data]].
+
Timestamps are particularly useful for precisely measuring the elapsed time between hardware events. Calculation of elapsed time is easy (a single subtraction) as long as the time interval doesn't exceed 71.6 minutes. It can be used in a variety of ways, including [[826 counters#Measuring speed|measuring speed]] and [[826 counters#Serial data capture|capturing serial data]].
  
If desired, an application program can also directly read the current time as shown below:
+
If desired, an application program can directly read the current time as shown below:
  
  // Read the timestamp generator's current time.  
+
  // Read the timestamp generator's current count.  
 
  uint CurrentTimestamp(uint board)
 
  uint CurrentTimestamp(uint board)
 
  {  
 
  {  
Line 1,089: Line 40:
  
 
  // Example: Use board0 to measure system Sleep() time.
 
  // Example: Use board0 to measure system Sleep() time.
  uint t1, t0 = CurrentTimestamp(0); // Get start time.
+
  uint t1, t0 = CurrentTimestamp(0);   // Get start time.
  Sleep(100);                         // Sleep approximately 100 ms.
+
  Sleep(25);                           // Sleep approximately 25 ms.
  t1 = CurrentTimestamp(0);           // Get end time.
+
  t1 = CurrentTimestamp(0);           // Get end time.
  printf("Slept %d &micro;s", t1 - t0);     // Display actual sleep time.
+
  printf("Slept %d &micro;s", t1 - t0);     // Display actual sleep time.
  
 
==Board ID==
 
==Board ID==
Line 1,468: Line 419:
 
:''Do you recommend specific Linux distributions for use with the 826?''
 
:''Do you recommend specific Linux distributions for use with the 826?''
  
We no longer support the obsolete kernel 2.4, but otherwise have no specific recommendation as it depends on the application (e.g., it might be desirable to use a low-latency kernel). The 826 driver is compatible to kernel versions 2.6 and higher.
+
We no longer support the obsolete kernel 2.4, but otherwise have no specific recommendation as it depends on the application (e.g., it might be desirable to use a low-latency kernel). The 826 driver is compatible with kernel versions 2.6 and higher.
 +
 
 +
====Troubleshooting====
 +
 
 +
:''The board worked yesterday but it doesn't work today. I didn't change anything. What could be the problem?''
 +
 
 +
It's likely that the operating system upgraded the Linux kernel during an automatic update. See [[Linux Troubleshooting|this appnote]] for details.
  
 
====Build errors====
 
====Build errors====
Line 1,478: Line 435:
  
 
In such cases, it's likely that the 826 driver successfully installed and you are simply seeing a warning. You can confirm this by trying "<code>sudo modprobe s826</code>" and the 826 demo application.
 
In such cases, it's likely that the 826 driver successfully installed and you are simply seeing a warning. You can confirm this by trying "<code>sudo modprobe s826</code>" and the 826 demo application.
 +
 +
=====Different gcc version=====
 +
 +
''Why do I get the following error when building the demo?"
 +
 +
relocation R_X86_64_32S against `.bss' can not be used when making a PIE object; recompile with -fPIE
 +
 +
Answer: You have a newer version of gcc, so you must rebuild the middleware. To do this, switch to the SDK downloads directory and then:
 +
* call "make lib"
 +
* cd to middleware directory: "cd middleware"
 +
* "cp *.a ../demo/"
 +
* "cd .."
 +
 +
Now rebuild the demo with the new .a files:
 +
* "make -C demo s826demo"
  
 
==Remote access==
 
==Remote access==
Line 1,483: Line 455:
 
:''Is there any way to use an 826 with a laptop?''
 
:''Is there any way to use an 826 with a laptop?''
  
Not directly, because laptops don't provide PCI Express slots. However, it is possible to locate the 826 in a host computer and, with appropriate software, remotely access its interfaces from a laptop (e.g., via Ethernet or USB); in fact, several 826 users have reported that they do exactly that.
+
Not directly, because laptops don't provide exposed PCI Express slots. However, it is possible to locate the 826 in a host computer that does have PCIe slots and, with appropriate software, remotely access its interfaces from a laptop (e.g., via Ethernet or USB).
 +
 
 +
When designing such a system, it's important to consider that neither Ethernet nor USB are capable of real-time communication with register-based measurement and control hardware. Consequently, depending on the application, this may require the host to offload time-critical I/O functions from the laptop, such as interrupt handling, counter FIFO processing, and low-level register I/O sequences, in order to achieve real-time performance.
  
When designing such a system, it's important to consider that Ethernet and USB are not capable of real-time communication with register-based measurement and control hardware. Consequently, depending on the application, this may require the host to offload time-critical I/O functions from the laptop, such as interrupt handling, FIFO processing, and low-level register I/O sequences, in order to achieve real-time performance.
+
Functions that are not time-critical can be implemented in various ways. For example, the host computer could run a SNMP agent process that serves as a bridge between Ethernet and the 826. To do this, you will need to create a MIB and implement the associated functions in the agent.
  
 
==Environmental specifications==
 
==Environmental specifications==
Line 1,501: Line 475:
  
 
==Migrating from model 626==
 
==Migrating from model 626==
 +
 +
For users who are upgrading PCI systems to PCIe, we recommend model 826 as a replacement for model 626.
 +
 +
===Porting guide===
 +
 +
A migration aid is available to help C developers port applications to model 826. The aid consists of C code which provides, when feasible, equivalent 826 API calls for 626 API functions. In cases where equivalent functions are not available, compiler errors and runtime warnings are issued, and tips are given for resolving porting issues.
 +
 +
* [http://www.sensoray.com/downloads/port626.zip 626-to-826 migration aid]
  
 
===Differences between models 626 and 826===
 
===Differences between models 626 and 826===
  
When upgrading your PCI system to PCIe, we recommend model 826 as a replacement for model 626. The following table compares the interfaces on the two boards:
+
The following table compares the interfaces on the two boards:
  
 
{| class="wikitable"
 
{| class="wikitable"
Line 1,516: Line 498:
 
|-
 
|-
 
| Counters
 
| Counters
| 6 channels<br>24-bit resolution<br>24-bit sample latch
+
| 6 channels (3 A/B pairs)<br>24-bit resolution<br>24-bit sampling latch
| 6 channels<br>32-bit resolution<br>sample FIFO (16-deep) with 32-bit timestamps
+
| 6 channels (identical)<br>32-bit resolution<br>16-deep FIFO with timestamps
 
|-
 
|-
 
| GPIOs
 
| GPIOs
Line 1,536: Line 518:
 
|-
 
|-
 
| Fail-safe controller
 
| Fail-safe controller
| None
+
| n/a
 
| Integrated
 
| Integrated
 +
|-
 +
| Timestamp generator
 +
| n/a
 +
| 32 bits, 1 &micro;s resolution
 
|}
 
|}
  
Line 1,544: Line 530:
 
:''Do models 826 and 626 have the same connectors and pinouts?''
 
:''Do models 826 and 626 have the same connectors and pinouts?''
  
Both boards use identical connectors. The pinouts of the digital and counter connectors are identical, but analog connector pinouts differ slightly because the 826 has four additional analog outputs. The analog pinouts are identical except for pins 41, 43, 45 and 47, which convey DAC channel 4-7 outputs on the 826 (vs. remote sense inputs on the 626).
+
Both boards have identical connector types, and all connector pinouts are identical except for analog connector J1. Model 826 has four additional analog outputs (channels 4-7) on pins 41, 43, 45 and 47. Model 626 uses these pins as remote sense inputs for analog output channels 0-3.
 +
[[File:826 vs 626 pinouts.gif|left|350px]]
 +
{{Clear}}
  
===Using 626 cables with the 826===
+
===Software differences===
  
:''I have a 7505TDIN breakout board and 7501C1 (50-pin cable) for the 626. Can I use these with the 826?''
+
;Driver and API
 +
Models 826 and 626 use different device drivers and APIs. The driver and API for each model is exclusive to that model and is not compatible with the other model. The drivers and APIs are necessarily different due to the expanded capabilities of model 826 and to significant hardware architecture differences between the two models. In addition, the 826 API provides blocking functions to simplify development of both event-driven and polling applications.
  
The 7505TDIN and 7501C are both compatible with the 826. However, we recommend using an 826C2 cable instead of the 7501C because it has a low profile header at one end that results in a denser cable stackup. That said, the 7501C cable can be used if it doesn't cause mechanical interference in your system.
+
;Application code
 +
Since the APIs are different, it is necessary to revise application code when upgrading to model 826. Numerous examples can be found in this wiki to help in that endeavor. Also, consider using our [http://www.sensoray.com/downloads/port626.zip 626-to-826 migration aid] for C developers who are moving 626 applications to model 826.
  
==See also==
+
===Using 626 cables with the 826===
  
* [[GPIO interfacing]] - design tips for DIO circuits
+
:''I have a 7505TDIN breakout board and 7501C1 (50-pin cable) for the 626. Can I use these with the 826?''
  
 +
The 7505TDIN and 7501C are both compatible with the 826. However, we recommend using an 826C2 cable instead of the 7501C because it has a low profile header at one end that results in a denser cable stackup. That said, the 7501C cable can be used if it doesn't cause mechanical interference in your system.
  
  
  
 
[[Category:826| ]]
 
[[Category:826| ]]

Latest revision as of 08:18, 15 February 2022

Model 826 board

This is the top-level wiki page for Sensoray's model 826, a versatile analog and digital I/O system on a PCI Express board. The board has 48 digital I/Os with edge detection, sixteen 16-bit analog inputs, eight 16-bit analog outputs, six 32-bit counter channels with quadrature clock decoders, three-stage watchdog timer with fail-safe controller, and a flexible signal router.

Related pages

Each board subsystem has a dedicated wiki page:

  • ADC - analog input system
  • DACs - analog output system
  • DIOs - general-purpose digital I/Os
  • Counters - counter/timers, including appnotes for interfacing incremental encoders
  • Watchdog - watchdog timer and fail-safe controller
Please note
  • Code and circuit examples are intended to function as described, but this is not guaranteed. If you discover an error, please inform the webmaster.
  • In code examples, error checking has been simplified or omitted for clarity. It is recommended to always perform error checking in your production software.
  • C language examples depend on header file 826api.h, which should be included at the top of your source code like this:
#include "826api.h"

Contents

[edit] Timestamp generator

The timestamp generator is a free-running 32-bit counter that serves as a time reference. The counter increments once per microsecond and overflows (to zero, without notification) every 232 µs (approximately 71.6 minutes). It is a binary counter and consequently does not keep track of the date or time-of-day. At any moment, the current count may be sampled; such a sample is called a timestamp.

A timestamp is automatically appended to every counter snapshot and to every ADC sample so that application programs can know (to within 1 µs) when each sample was acquired. Also, application programs can read the timestamp generator at any time to get a timestamp.

[edit] Usage

Timestamps are particularly useful for precisely measuring the elapsed time between hardware events. Calculation of elapsed time is easy (a single subtraction) as long as the time interval doesn't exceed 71.6 minutes. It can be used in a variety of ways, including measuring speed and capturing serial data.

If desired, an application program can directly read the current time as shown below:

// Read the timestamp generator's current count. 
uint CurrentTimestamp(uint board)
{ 
  uint t;
  S826_TimestampRead(board, &t);
  return t;
}

The following example shows a simple application of direct timestamp reads:

// Example: Use board0 to measure system Sleep() time.
uint t1, t0 = CurrentTimestamp(0);   // Get start time.
Sleep(25);                           // Sleep approximately 25 ms.
t1 = CurrentTimestamp(0);            // Get end time.
printf("Slept %d µs", t1 - t0);      // Display actual sleep time.

[edit] Board ID

The "BOARD NUM" switches (at top edge of board near mounting bracket) assign the board ID used by software. The ID is binary coded on the four switches and can be programmed to any value from 0 (default) to 15. A board's ID determines the corresponding bit that will be set to '1' in the value returned by S826_SystemOpen. If you have a single 826 board, the return value will be (2^ID). If you have multiple boards, the return value is the sum of (2^ID) for each board. You can enter the return value here to quickly determine its meaning.

Examples
  • You have one board with ID set to 0, so the value returned by S826_SystemOpen will be (2^0) = 1.
  • You have two boards with IDs set to 1 and 4, so the value returned by S826_SystemOpen will be (2^1)+(2^4) = 2+16 = 18.

This code snippet will tell you the meaning of the value returned by S826_SystemOpen:

int id, flags = S826_SystemOpen();
if (flags < 0)
  printf("S826_SystemOpen returned error code %d", flags);
else if (flags == 0)
  printf("No boards were detected");
else {
  printf("Boards were detected with these IDs:");
  for (id = 0; id < 16; id++) {
    if (flags & (1 << id))
      printf(" %d", id);
  }
}

[edit] Hardware version

[edit] Reading the PWB revision

The circuit board revision (PWB rev) is visible on the solder-side of the 826 board (opposite the mounting bracket, on the bottom corner). S826_VersionRead returns the PWB rev as a numeric value with decimal range [0:31], which corresponds to a text string in the standard ASME version letter sequence. The following code shows how to convert this 32-bit value to the alphabetic revision code seen on the board:

// Read and display version info from board 0

// Extract major_version, minor_version and build_number from a 32-bit version number:
#define VER_FIELDS(N) ((N) >> 24) & 0xFF, ((N) >> 16) & 0xFF, (N) & 0xFFFF

const char *revchar[] = {  // ASME revision sequence
  "A",  "B",  "C",  "D",  "E",  "F",  "G",  "H",
  "J",  "K",  "L",  "M",  "N",  "P",  "R",  "T",
  "U",  "V",  "W",  "Y",  "AA", "AB", "AC", "AD",
  "AE", "AF", "AG", "AH", "AJ", "AK", "AL", "AM"
};

uint api, drv, bd, fpga;
int errcode = S826_VersionRead(0, &api, &drv, &bd, &fpga);  // Read version info.
if (errcode == S826_ERR_OK) {                               // If no errors then display info:
  printf("API version    = %d.%d.%d\n", VER_FIELDS(api));   //   API major.minor.build
  printf("Driver version = %d.%d.%d\n", VER_FIELDS(drv));   //   DRVR major.minor.build
  printf("FPGA version   = %d.%d.%d\n", VER_FIELDS(fpga));  //   FPGA major.minor.build
  printf("PWB revision   = Rev %s\n",   revchar[bd & 31]);  //   PWB rev as seen on circuit board
}
else
  printf(" S826_VersionRead returned error code %d", errcode);

[edit] Rev C changes

Sensoray has developed Revision C of the 826 circuit board. This change was necessary due to the impending EOL (end-of-life) of a critical component. Specifically, the critical component (PCI Express interface chip) and FPGA were removed and replaced by a new FPGA, which absorbed the functions of the two removed components.

Applications and developers are not affected by this change

The Rev C board is fully compatible with Rev B boards and applications:

  • Mechanical attributes are unchanged, including board dimensions and placements of connectors, switches, indicator LEDs, and hold-down bracket.
  • Connector pinouts, electrical and timing specifications are unchanged.
  • Rev C is 100% software compatible with Rev B on all software layers: application, API and driver (including user-developed drivers and APIs for RTOS, etc.).

Rev B and Rev C boards can be used interchangeably in new and existing applications. From an application's perspective, the only detectable differences between Rev B and Rev C boards are the version numbers returned by the API function S826_VersionRead():

  • S826_VersionRead() will report the PWB version as Rev B or Rev C as appropriate for the board's hardware version.
  • S826_VersionRead() will report FPGA version 0.0.70 or higher for Rev C boards, or version 0.0.69 or lower for Rev B boards.

[edit] Connector pinouts

The following drawings show the pinouts of the board's header connectors as viewed from the top (component) side of the circuit board:

[edit] Software

[edit] C examples

A variety of C programming examples have been collected together in a common source file to illustrate how to program resources on the 826.

[edit] VB.NET demo

To help you jump-start your project, we offer the VB.NET demo for model 826. This demo program provides a pre-built Windows executable with a GUI for nearly every hardware resource on the board. All source files are provided, along with a VisualStudio project.

[edit] Programming in C#

Each 826 SDK includes a C# demo application. These demos show how to call API functions from C#, and can serve as a useful starting point for a custom application.

[edit] Linux demo

In the Linux SDK, a C# GUI demo is available which uses Linux mono. To get the required libraries on Ubuntu, type:

"sudo apt-get install mono-complete"

For a C# development environment, type:

"sudo apt-get install monodevelop"

[edit] Pointer arguments

Many of the API functions have pointer arguments. This is no problem for C#, which allows you to pass function arguments by reference. To see how this is done, consider the S826_AdcEnableRead function:

The C prototype is:

int S826_AdcEnableRead(unsigned int board, unsigned int *enable);

So in C# you should declare the function this way:

[DllImport("s826.dll", CallingConvention = CallingConvention.StdCall)]
static extern Int32 S826_AdcEnableRead(UInt32 board, ref UInt32 enable); 

Now you can call the function this way:

Uint32 isEnabled;
Int32 errcode = S826_AdcEnableRead(0, ref isEnabled);

[edit] Labview

Before running an 826 virtual instrument (VI) under Labview, make sure you install the latest versions of the 826 DLL (s826.dll) and device driver (both are contained in the 826 SDK, which you can obtain from the Downloads tab of the 826 product page).

Each VI is a basically a wrapper for a DLL function and consequently the VIs are dependent on the DLL, which in turn depends on the driver. Board hardware and firmware version numbers will be automatically read from the 826 board by software when all dependencies are satisfied -- it is not necessary to manually enter any board selection information except the board number, which is specified by the board's switch settings (factory default is board number 0).

The VIs are not independently documented, but since each VI wraps a DLL function, the DLL documentation effectively explains the function of each associated VI. The DLL documentation can be found in the 826 product manual (download from the 826 product page Documentation tab).

The VIs may be installed under Labview's instrument library (e.g., "instr.lib\Sensoray 826") or elsewhere if desired. Refer to Labview documentation for information about paths and other relevant topics.

[edit] Matlab

To use an 826 with Matlab you must first install the latest 64-bit versions of the 826 API (s826.dll) and device driver; these are both part of the 826 SDK, which you can obtain from the Downloads tab of the 826 product page. You may then use Matlab's loadlibrary() function to enable access to the API, and calllib() to call API functions. The API functions are described in the 826 product manual, which can be found on the 826 product page Documentation tab. The following example illustrates how this works.

Note: Matlab cannot execute loadlibrary() unless a compatible C compiler is installed on your computer. If Matlab complains about an "Error using loadlibrary" because "No supported compiler was found" then you must download and install one of the Matlab-compatible compilers (e.g., MinGW-w64) to resolve this issue. Please consult Mathworks for a list of compatible compilers.

% Simple Matlab example: turn on general-purpose digital I/O 2 ***************************

% Change these values as required:
hdrPath = 'C:\Sensoray\826api.h';           % Path to API header
dllPath = 'C:\Windows\System32\s826.dll';   % Path to API executable
board = 0;                                  % Use 826 board #0 (i.e., board w/ID switches set to 0)

loadlibrary(dllPath, hdrPath, 'alias', 's826');     % Load the API.
boardflags = calllib('s826', 'S826_SystemOpen');    % Open API and detect all boards.
if (boardflags < 0)                                 % If API failed to open
    disp("S826_SystemOpen error");                  %   Report error.
else                                                % Else
    if (boardflags ~= bitshift(1, board))           %   If board #0 was not detected
        disp("Failed to detect 826 board");         %     Report error (check board's switch settings).
    else                                            %   Else ...
        buf = libpointer('uint32Ptr', [6 0]);       %     Allocate buffer for DIO state data.
        errcode = calllib('s826', ...               %     Turn on DIO1 and DIO2.
            'S826_DioOutputWrite', board, buf, 0);
        clear buf;                                  %     Free buffer.
        if (errcode ~= S826_ERR_OK)
            disp('DIO write problem')               %     Report error if DIO write failed.
        end
    end
    errcode = calllib('s826', 'S826_SystemClose');  %   Close API.
end
unloadlibrary s826;                                 % Unload the API.

[edit] Matlab SDK

Sensoray offers an open-source software development kit for Matlab programmers, which you can obtain from the Downloads tab of the 826 product page. The Matlab SDK includes two files:

  • s826.m is a class that defines useful constants and provides wrappers for all 826 API functions. Include this file in any project that interacts with model 826 boards.
  • s826_demo.m is a short program that demonstrates how to use the s826 class.

[edit] ROS (Robot Operating System)

Sensoray SDKs do not include a ROS package, but the Linux SDK has everything needed to create one. The simplest way to use ROS with model 826 is to install the Linux 826 device driver and API (shared library), and then call the API functions as shown in the following example. Note that this example is coded in C++, but you can easily call the API functions from Python or any other language.

///////////////////////////////////////////////////////////////////////////////////////////////////////////
// Simple ROS (Robot Operating System) example for Sensoray 826 PCI Express analog/digital IO board.
// Function: Publishes 16 analog inputs 10 times per second
///////////////////////////////////////////////////////////////////////////////////////////////////////////

#define X826(func)    if ((errcode = (func)) != S826_ERR_OK) { printf("\nERROR: %d\n", errcode); return; }    // Call function and process error

int PublishAINChannels()
{
    int adcbuf[16];     // adc data buffer
    uint slotlist;      // timeslot flags
    uint slot;          // timeslot index (and ain channel number)
    uint board = 0;     // board ID
    int errcode;

    // Initialize data array for publisher
    std_msgs::Int16MultiArray data_array;
    data_array.layout.dim.push_back(std_msgs::MultiArrayDimension());
    data_array.layout.dim[0].label = "board-" + to_string(board) + " AIN";
    data_array.layout.dim[0].size = 16;
    data_array.layout.dim[0].stride = 1;
    data_array.layout.data_offset = 0;
   
    // Configure adc and start it running.
    for (slot = 0; slot < 16, slot++ )                          // Configure slots: chan = slot number, settle time = 20 us, range = +/-10 V
        X826(S826_AdcSlotConfigWrite(board, slot, slot, 20, S826_ADC_GAIN_1));
    X826(S826_AdcSlotlistWrite(board, 0xFFFF, S826_BITWRITE));  // Enable all slots.
    X826(S826_AdcTrigModeWrite(board, 0));                      // Select continuous trigger mode.
    X826(S826_AdcEnableWrite(board, 1));                        // Start conversions.

    ROS_INFO("\tPublishing AIN Channels");

    ros::Rate loop_rate(10);

    while (ros::ok())
    {
        // Wait for next publish time. This is at top of loop so first ADC burst will complete before we try to read the data.
        // Otherwise we might get bogus data the first time through the loop.
        loop_rate.sleep();

        // Fetch ADC data
        data_array.data.clear();
        slotlist = 0xFFFF; // from all 16 slots
        errcode = S826_AdcRead(board, adcbuf, NULL, &slotlist, 0);

        // Handle errors
        if ((errcode != S826_ERR_OK) && (errcode != S826_ERR_MISSEDTRIG)) {  // this app doesn't care about adc missed triggers
            printf("\nERROR: %d\n", errcode);
            break;
        }

        // Publish ADC data
        for (slot = 0; slot < 16; slot++) {
            // Publishing raw data (16-bit signed int). Convert to +/-10V by multiplying by 10/32768.
            // example: cout << (double)(data_array.data[i] * 10) / 32768 << endl;
            data_array.data.push_back((short)(adcbuf[slot] & 0xFFFF));
        }
        analog_input_pub.publish(data_array);
    }

    S826_AdcEnableWrite(board, 0);          // Halt conversions.
    return errcode;
}

[edit] Resources for custom driver development

826 software stack. The generic API is portable — to port, simply adapt the Linux driver and HAL for your OS/RTOS.
I want to develop my own device driver for the 826. Does Sensoray offer any resources for custom driver development?

Yes, we provide these resources free of charge:

  • Linux Software Development Kit (SDK) - Includes source code for the 826 driver, hardware abstraction layer (HAL) and API middleware, comprising a complete 826 software stack for Linux. The generic API is operating system independent and thread-safe, which makes this SDK a great starting point for porting to any operating system. The stack has been carefully designed for reliable operation in and for easy porting to real-time operating systems. The SDK can be found on the Downloads tab of the 826 product page.
  • Model 826 Technical Manual - This comprehensive manual explains the API and 826 hardware in detail (download from the Documentation tab of the 826 product page).
  • Register Map - A map of the board's hardware registers is available here. The registers are accessed through PCI BAR 2. Registers appear in both banked and flat address spaces. The banked space is only required for rev 0 boards; you should use the flat space exclusively if you have a later rev, as this will yield superior performance.

[edit] Software updates

1. Windows 3.3.4

  • C# demo application added to SDK. Error checking for invalid modes to S826_CounterModeWrite.

2. Linux 3.3.5

  • C# GUI demo available, using Linux mono.

[edit] Windows

[edit] Custom installation and re-distribution

Sensoray's installer uses the Nullsoft Scriptable Install System (NSIS). It is created from a .NSI script. The core API is installed as follows in NSI script code:

Section "Core API"
SectionIn RO
${If} ${RunningX64}
 SetOutPath "$WINDIR\system32";
 !insertmacro  DisableX64FSRedirection
 File "..\mid-826\code\Release64\s826.dll";
 !insertmacro  EnableX64FSRedirection
 SetOutPath "$WINDIR\SysWOW64";
 File "..\mid-826\code\Release\s826.dll";
${Else}
 SetOutPath "$WINDIR\system32";
 File "..\mid-826\code\Release\s826.dll";
${EndIf}
SectionEnd

The drivers are installed via dpinst.exe in the NSI script as follows:

Section "Drivers"
SectionIn RO
CreateDirectory "$INSTDIR\driver\x64";
SetOutPath "$INSTDIR\driver\x64";
File "..\cd\driver\x64\dpinst.exe";
File "..\cd\driver\x64\s826.cat";
File "..\cd\driver\x64\s826.inf";
File "..\cd\driver\x64\s826.sys";
File "..\cd\driver\x64\s826filter.cat";
File "..\cd\driver\x64\s826filter.inf";
File "..\cd\driver\x64\s826filter.sys";
File "..\cd\driver\x64\WdfCoInstaller01009.dll";
CreateDirectory "$INSTDIR\driver\x32";
SetOutPath "$INSTDIR\driver\x32";
File "..\cd\driver\x32\dpinst.exe";
File "..\cd\driver\x32\s826.cat";
File "..\cd\driver\x32\s826.inf";
File "..\cd\driver\x32\s826.sys";
File "..\cd\driver\x32\s826filter.cat";
File "..\cd\driver\x32\s826filter.inf";
File "..\cd\driver\x32\s826filter.sys";
File "..\cd\driver\x32\WdfCoInstaller01009.dll";
MessageBox MB_OK "Driver installation dialog will pop-up. Follow the prompts and click Finish when done"
${If} ${RunningX64}
 ExecWait '"$INSTDIR\driver\x64\dpinst.exe" /f'
${Else}
 ExecWait '"$INSTDIR\driver\x32\dpinst.exe" /f'
${EndIf}
NoInstallDriver:
SectionEnd
What other libraries does the installer install as part of the Core API?

The 826 is compiled with Microsoft Visual Studio C++ 2008. The re-distributables for C++ must be installed. The installer installs this library silently running the command:

"vcredist_x86.exe /q"

and the following additional command on 64-bit systems:

"vcredist_x64.exe /q"

These re-distributables are available from Microsoft for x64 and x86.

Are any other libraries required? I installed the libraries above, but the demo doesn't work with my custom installer?

The demo is written using .NET libraries (version 3.5). These are also available from Microsoft here. The executable can be silently installed using this command:

"dotnetfx35setup.exe /qb"
Could I obtain the full 826 NSI script as a template for creating my own installer?

Yes, the full script is available here.

[edit] Silent install

I want to run the installer silently. Do you have any way to do this?

There are many options. For re-distribution, you may create your own installation package. Also, starting with version 3.3.9, there are additional command line options to quiet the setup.exe installer from command line or batch file. These options are described below.

What are the options for silent install?

The basic silent install is invoked by running the following command from command line or batch script:

"setup.exe /S"

Please note that the /S is case sensitive and must be upper case.

I've pre-installed the drivers and don't want to re-install them during the installation? Is there a command for that?
"setup.exe /S /no_driver=1"
Is there an additional command to not install the demo programs?

Yes, in version 3.3.9, the following command will install the required DLLs and system libraries, but no drivers or demo programs.

"setup.exe /S /no_driver=1 /no_demos=1"
I want to install the drivers silently, but there is always a pop-up to verify.

Unfortunately, there is no way around this. Windows requires confirmation from the user for driver install, even if the driver is signed.

I'm running the setup silently, but it pops up a dialog to confirm if I want to make changes to the PC (User Account Control). How do I prevent this?

Windows controls this through User Access Control. If running the setup from a standard windows console, the Windows User Account Control (UAC) will pop-up. This cannot be by-passed by Sensoray because the installer installs files to system directories.

One work-around is to launch the setup in an Windows Command Prompt Window started in administrator mode (right-click and select "Run As Administrator"). Another approach is to launch the setup as a user with administrator privileges. User access control may also be disabled, but we do not recommend this for security reasons.

[edit] Link error

I'm using VisualStudio (VS) on a 64-bit machine to build a 32-bit application for the 826. VS reports that linking failed because it can't open s826.lib. What could be the problem?

You should use the 32-bit DLL (and its associated LIB) because you are building a 32-bit application (x86). Use the 64-bit version only when building 64-bit apps (x64). To avoid confusion, you can copy the 32-bit DLL and its associated LIB file from the install directory to your project directory, then point the linker to it there. Make sure to also point the debugger to the 32-bit DLL by setting its working directory.

Note: You will always use the 64-bit driver on a 64-bit OS regardless of 32/64-bit application type, but you don't need to select this as it is automatically installed by the SDK installer.

[edit] Windows 10 IoT

Sensoray has created a Universal driver (UD) for the 826 under OneCoreUAP-based editions of Windows. It is similar to the standard driver, but compiled as Universal. This driver is in our SDK zip file under the "driver/sensoray_826_universal_driver" directory. Installation may be dependent on the specific Windows version. The inf is Windows Universal compatible. Sensoray does not currently have a demonstration Universal Windows App for the 826, but the .NET demo app may be portable.

[edit] Linux

[edit] Linux versions

Do you recommend specific Linux distributions for use with the 826?

We no longer support the obsolete kernel 2.4, but otherwise have no specific recommendation as it depends on the application (e.g., it might be desirable to use a low-latency kernel). The 826 driver is compatible with kernel versions 2.6 and higher.

[edit] Troubleshooting

The board worked yesterday but it doesn't work today. I didn't change anything. What could be the problem?

It's likely that the operating system upgraded the Linux kernel during an automatic update. See this appnote for details.

[edit] Build errors

On some Linux distributions, "sudo make install" may issue messages like these:

  • modprobe: ERROR: could not insert 's826': Required key not available
  • SSL error:02001002:system library:fopen:No such file or directory: ../crypto/bio/bss_file.c

In such cases, it's likely that the 826 driver successfully installed and you are simply seeing a warning. You can confirm this by trying "sudo modprobe s826" and the 826 demo application.

[edit] Different gcc version

Why do I get the following error when building the demo?"

relocation R_X86_64_32S against `.bss' can not be used when making a PIE object; recompile with -fPIE

Answer: You have a newer version of gcc, so you must rebuild the middleware. To do this, switch to the SDK downloads directory and then:

  • call "make lib"
  • cd to middleware directory: "cd middleware"
  • "cp *.a ../demo/"
  • "cd .."

Now rebuild the demo with the new .a files:

  • "make -C demo s826demo"

[edit] Remote access

Is there any way to use an 826 with a laptop?

Not directly, because laptops don't provide exposed PCI Express slots. However, it is possible to locate the 826 in a host computer that does have PCIe slots and, with appropriate software, remotely access its interfaces from a laptop (e.g., via Ethernet or USB).

When designing such a system, it's important to consider that neither Ethernet nor USB are capable of real-time communication with register-based measurement and control hardware. Consequently, depending on the application, this may require the host to offload time-critical I/O functions from the laptop, such as interrupt handling, counter FIFO processing, and low-level register I/O sequences, in order to achieve real-time performance.

Functions that are not time-critical can be implemented in various ways. For example, the host computer could run a SNMP agent process that serves as a bridge between Ethernet and the 826. To do this, you will need to create a MIB and implement the associated functions in the agent.

[edit] Environmental specifications

Parameter Value
Pressure, operating 650 to 1010 hPa
Humidity, operating 20% to 80% RH, non-condensing

[edit] Migrating from model 626

For users who are upgrading PCI systems to PCIe, we recommend model 826 as a replacement for model 626.

[edit] Porting guide

A migration aid is available to help C developers port applications to model 826. The aid consists of C code which provides, when feasible, equivalent 826 API calls for 626 API functions. In cases where equivalent functions are not available, compiler errors and runtime warnings are issued, and tips are given for resolving porting issues.

[edit] Differences between models 626 and 826

The following table compares the interfaces on the two boards:

Interface 626 826
System bus PCI PCI Express
Counters 6 channels (3 A/B pairs)
24-bit resolution
24-bit sampling latch
6 channels (identical)
32-bit resolution
16-deep FIFO with timestamps
GPIOs 48 channels
40 w/edge detection (1 Msps)
no debounce
not fail-safe
48 channels
48 w/edge detection (50 Msps)
debounce filter
fail-safe outputs
Analog out 4 channels
14-bit resolution
20 Ksps
not fail-safe
8 channels
16-bit resolution
900 Ksps
fail-safe outputs
Analog in 16 channels
16-bit resolution
15 Ksps
16 channels
16-bit resolution
300 Ksps
Watchdog timer Single stage
4 shunt-selectable intervals
3 timer stages
software programmable intervals
Fail-safe controller n/a Integrated
Timestamp generator n/a 32 bits, 1 µs resolution

[edit] Connector pinout differences

Do models 826 and 626 have the same connectors and pinouts?

Both boards have identical connector types, and all connector pinouts are identical except for analog connector J1. Model 826 has four additional analog outputs (channels 4-7) on pins 41, 43, 45 and 47. Model 626 uses these pins as remote sense inputs for analog output channels 0-3.

826 vs 626 pinouts.gif

[edit] Software differences

Driver and API

Models 826 and 626 use different device drivers and APIs. The driver and API for each model is exclusive to that model and is not compatible with the other model. The drivers and APIs are necessarily different due to the expanded capabilities of model 826 and to significant hardware architecture differences between the two models. In addition, the 826 API provides blocking functions to simplify development of both event-driven and polling applications.

Application code

Since the APIs are different, it is necessary to revise application code when upgrading to model 826. Numerous examples can be found in this wiki to help in that endeavor. Also, consider using our 626-to-826 migration aid for C developers who are moving 626 applications to model 826.

[edit] Using 626 cables with the 826

I have a 7505TDIN breakout board and 7501C1 (50-pin cable) for the 626. Can I use these with the 826?

The 7505TDIN and 7501C are both compatible with the 826. However, we recommend using an 826C2 cable instead of the 7501C because it has a low profile header at one end that results in a denser cable stackup. That said, the 7501C cable can be used if it doesn't cause mechanical interference in your system.

Personal tools
Namespaces

Variants
Actions
Toolbox