826
Sensoray's model 826 is a versatile analog and digital I/O system on a PCI Express board. It 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. The board's high performance, compact size, and abundant resources make it ideally suited for a wide range of measurement and control applications.
Contents |
ADC
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 826 product page.
Apparent nonlinearity
Many of our customers have observed ADC nonlinearity during development and discovered that that it was caused by excessive common-mode voltage (CMV). This often happens when the ADC is used to measure an isolated voltage source such as a battery, thermocouple, or isolated power supply. Since the source is isolated, the CMV may float up or down until it exceeds the maximum allowed CMV of the ADC's input circuitry.
If you are using an isolated source, be sure to connect one side of the source to the ADC power supply ground as shown in the diagram to the right. This will prevent high CMV that might othewise result in apparent non-linearity or calibration errors.ADC accuracy specification
Resolution and no missing codes are both 16 bits minimum.
PARAMETER | VALUE | UNITS | ||
---|---|---|---|---|
MIN | TYP | MAX | ||
Integral Nonlinearity Error | ±0.75 | ±1.5 | LSB | |
Differential Nonlinearity Error | ±0.5 | ±1.25 | LSB | |
Gain Error | ±2 | ±40 | LSB | |
Gain Error Temperature Drift | ±0.3 | ppm/°C | ||
Zero Error | ±0.8 | mV | ||
Zero Temperature Drift | ±0.3 | ppm/°C |
Maximum input voltage
The analog inputs accept common mode voltages up to ±12V with no resulting input current or damage. CMV up to ±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.
DAC
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.
DAC accuracy specification
Resolution and monotonicity are both 16 bits minimum.
PARAMETER | CONDITIONS | VALUE | UNITS | ||
---|---|---|---|---|---|
MIN | TYP | MAX | |||
Integral Nonlinearity | ±2 | LSB | |||
Differential Nonlinearity | ±1 | LSB | |||
Gain Error | ±4 | ±20 | LSB | ||
Gain Temperature Coefficient | ±2 | ppm/°C | |||
Unipolar Zero-Scale Error | 5V unipolar range, 25°C 10V unipolar range, 25°C 5V unipolar range 10V unipolar range |
±80 ±100 ±140 ±150 |
±200 ±300 ±400 ±600 |
µV µV µV µV | |
V_offset Temperature Coefficient | All unipolar ranges | ±2 | µV/°C | ||
Bipolar Zero Error | All bipolar ranges | ±2 | ±12 | LSB |
Counters
Snapshot counts upon match
When a snapshot is caused by counts equal to a compare register, the snapshot counts will always be equal to the compare register value.
How to use interrupts
- I want to use interrupts to perform actions after a time delay. I see that counters have an "Interrupt System (IRQ)" signal but have no idea how to access it.
Every hardware IRQ is associated with a blocking API function. In the case of counter IRQs the function is S826_CounterSnapshotRead()
. A counter IRQ is automatically generated when a counter snapshot is captured. Fortunately, the API has been designed to handle interrupts for you so that you need not be concerned with the complexities of interrupt handlers -- simply call the API function and IRQs will automatically be generated and handled.
A simple way to implement a delayed interrupt is to configure the counter to count down, preload the counter with the desired time delay, and have it capture a snapshot (and thus generate an IRQ) when it reaches zero counts. To wait for the interrupt, call S826_CounterSnapshotRead()
with a non-zero tmax
value (maximum wait time, which must be longer than the delay time). The function will return upon interrupt and you can then perform the desired actions. For example:
#include "826api.h" // Wait 0.5 seconds while other threads are allowed to run S826_CounterModeWrite(0, 0, 0x01400020); // Configure counter0 as 1 MHz down counter; auto preload @startup. S826_CounterPreloadWrite(0, 0, 0, 500000); // Delay time in microseconds (0.5 seconds). S826_CounterSnapshotConfigWrite(0, 0, // Configure snapshots: S826_SSRMASK_ZERO // capture snapshot when counts==0 | (S826_SSRMASK_ZERO << 16), // disable subsequent snapshots when counts==0 S826_BITSET); // don't alter any other snapshot enables S826_CounterStateWrite(0, 0, 1); // Start the delay timer running. S826_CounterSnapshotRead(0, 0, // Block while waiting for timer: NULL, NULL, NULL, // ignore snapshot counts, timestamp and reason S826_WAIT_INFINITE); // disable function timeout printf("Delay time has elapsed!"); // TODO: INSERT YOUR DESIRED ACTIONS HERE
Note that your program cannot do anything else while it waits for S826_CounterSnapshotRead()
to return. To get around this you can call the function in a separate thread so that while the thread is waiting for the interrupt, other threads can do productive work.
Periodic timer
- How can I use a counter to call a function periodically?
Configure the counter so that it repeatedly counts down to zero and then preloads. The preload value determines the time period. A snapshot is captured every time zero counts is reached, which causes S826_CounterSnapshotRead()
to return, whereupon you can call your periodic function.
#include "826api.h" // Call PeriodicFunction() 10 times per second S826_CounterModeWrite(0, 0, 0x01C02020); // Counter0: 1 MHz down counter; preload @startup and counts==0. S826_CounterPreloadWrite(0, 0, 0, 100000); // Period in microseconds (0.1 seconds). S826_CounterSnapshotConfigWrite(0, 0, // Configure snapshots: S826_SSRMASK_ZERO, // capture snapshot when counts==0 S826_BITWRITE); // disable all other snapshot triggers S826_CounterStateWrite(0, 0, 1); // Start the periodic timer running. while (1) { // Repeat forever: S826_CounterSnapshotRead(0, 0, // Block while waiting for elapsed period: NULL, NULL, NULL, // ignore snapshot values S826_WAIT_INFINITE); // disable function timeout PeriodicFunction(); // Execute the periodic function. }
Programming incremental encoders
- Which functions should I use for incremental encoders?
The flexible counter architecture allows for many options, but basic operation works as follows:
First configure and enable the counter channel:
#include "826api.h" S826_CounterModeWrite(0, 0, 0x00000070); // Configure counter 0 as incremental encoder interface. S826_CounterStateWrite(0, 0, 1); // Start tracking encoder counts.
To read the instantaneous encoder counts without invoking a snapshot:
uint counts; S826_CounterRead(0, 0, &counts); // Read current encoder counts. printf("Encoder counts = %d\n", counts); // Display encoder counts.
The encoder counts can be changed to an arbitrary value at any time. This is typically done when the encoder is at a known reference position (e.g., at startup or whenever mechanical registration is required), but not at other times as it would disrupt position tracking. To change the counts, write the new counts value to the Preload0 register and then call S826_CounterPreload()
to force a preload:
S826_CounterPreloadWrite(0, 0, 0, 12345); // Write desired counts value (12345) to Preload0 register. S826_CounterPreload(0, 0, 0, 0); // Jam Preload0 value into counter.
Using interrupts
This code snippet employs hardware interrupts to block the calling thread until the encoder counts equals a particular value. Other threads are allowed to run while the calling thread waits for the counter to reach the target value. A snapshot is captured when the target count is reached. The snapshot generates an interrupt request, which in turn causes S826_CounterSnapshotRead()
to return. The example ignores the snapshot counts (which will always equal the target value), the timestamp, and the reason code (which will always indicate a Match0 event).
S826_CounterCompareWrite(0, 0, 0, // Set Compare0 register to target value: 5000); // 5000 counts (for this example) S826_CounterSnapshotConfigWrite(0, 0, // Enable snapshots: S826_SSRMASK_MATCH0, // when counts==Compare0 S826_BITWRITE); // disable all other snapshot triggers S826_CounterSnapshotRead(0, 0, // Wait for counter to reach target counts: NULL, NULL, NULL, // ignore snapshot counts, timestamp and reason S826_WAIT_INFINITE); // disable function timeout printf("Counter reached target counts");
In some cases you may want to wait for more than one counter event at the same time. This example shows how to wait for the encoder counts to reach an upper or lower limit (whichever occurs first). Furthermore, it will only wait for a limited amount of time. To set this up, the count limits are programmed into Compare registers and then snapshots are enabled for matches to both Compare registers. To set a limit on how long to wait, a time limit value is specified by tmax
when calling S826_CounterSnapshotRead
. Since a snapshot can be caused by different events, we must know what triggered a snapshot in order to decide how to handle it; this is indicated by the snapshot's reason flags.
uint counts; // encoder counts when the snapshot was captured uint timestamp; // time the snapshot was captured uint reason; // event(s) that caused the snapshot uint errcode; // API error code S826_CounterCompareWrite(0, 0, 0, 3000); // Set Compare0 register to low limit (3000 counts) S826_CounterCompareWrite(0, 0, 1, 4000); // Set Compare1 register to high limit (4000 counts) S826_CounterSnapshotConfigWrite(0, 0, // Enable snapshots: S826_SSRMASK_MATCH0 // when counts==low limit | S826_SSRMASK_MATCH1, // or when counts==high limit S826_BITWRITE); // disable all other snapshot triggers errcode = S826_CounterSnapshotRead(0, 0, // Wait for a snapshot: &counts, ×tamp, &reason, // receive the snapshot info here 10000000); // timeout if wait exceeds 10 seconds (10000000 us) switch (errcode) { // Decode and handle the snapshot: case S826_ERR_NOTREADY: printf("Timeout -- counter didn't hit limits within 10 seconds; current counts = %d", counts); break; case S826_ERR_OK: if (reason & S826_SSRMASK_MATCH0) printf("Counter reached upper limit at timestamp %d", timestamp); if (reason & S826_SSRMASK_MATCH1) printf("Counter reached lower limit at timestamp %d", timestamp); }
Unexpected snapshots
- Why do I occasionally get two snapshots (upon counts match) from my incremental encoder when only one is expected?
Assuming your encoder has not changed direction, the unexpected snapshots are probably being triggered by noise or slow edges on the encoder clock signals. This is possible even when the snapshot timestamps are identical, because encoder clocks are sampled every 20 nanoseconds whereas timestamp counts are incremented only once per microsecond.
If this is what is happening, unexpected snapshots can be prevented by calling S826_CounterFilterWrite()
to establish a clock filter. A small filter value is usually sufficient -- just enough to clean up clock edges, but not so long that valid encoder counts will be missed. For example, this will set the clock filter to 100 ns (index input will not be filtered):
#define FILT_NS 100 // Filter time in ns -- change as desired to multiple of 20. #define FILT_RES 20 // Filter resolution in nanoseconds. #define FILT_CLK (1 << 30) // Bit flag to enable clock filtering. S826_CounterFilterWrite(0, 0, // Activate clock filter on counter 0. FILT_CLK + FILT_NS / FILT_RES);
Another way to handle this is to configure the snapshot trigger to become automatically disabled when it fires:
S826_CounterSnapshotConfigWrite(0, 5, // Configure snapshots on counter 5: S826_SSRMASK_MATCH0 // enable snapshot upon Match0 (counts==Compare0 register) | (S826_SSRMASK_MATCH0 << 16), // disable subsequent Match0 snapshots upon Match0 S826_BITWRITE); // disable all other snapshot triggers
ExtOut timing
- When ExtOut is configured to act upon counts=zero (or =match), what is the delay from input clock edge to edge of ExtOut?
The input clock is sampled every 20 ns, so a clock edge is recognized 0 to 20 ns after the edge. The counts change 20 ns after the edge is recognized. ExtOut changes 20 ns after the counter transitions to zero (or match value). So, the delay from input clock edge to ExtOut edge is 40 to 60 ns.
How to configure a counter for PWM operation
This example shows how to configure counter channel 0 to operate as a PWM generator with the output appearing on DIO channel 0. Note that you must call S826_SafeWrenWrite()
before calling S826_DioOutputSourceWrite()
; if you neglect to do this then the PWM signal will not appear on DIO 0.
#include "826api.h" uint data[2]= {1, 0}; // DIO 0 S826_SafeWrenWrite(0, 2); // Enable writes to DIO signal router. S826_DioOutputSourceWrite(0, data); // Route counter0 output to DIO 0. S826_CounterModeWrite(0, 0, 0x01682020); // Configure counter0 for PWM, with auto-preload when starting. S826_CounterPreloadWrite(0, 0, 0, 900); // On time in us (0.9 ms). S826_CounterPreloadWrite(0, 0, 1, 500); // Off time in us (0.5 ms). S826_CounterStateWrite(0, 0, 1); // Start the PWM generator.
Mode register decoder utility
- Is there an easy way to convert a mode register value to a human readable description of counter settings?
Yes: download Sensoray's counter mode decoder utility program (Windows compatible). Run the program and enter the mode register value to see an English language description of the counter mode settings.
DIOs
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 many ways to do this but the methods you can use depend on a number of factors. Here are two general strategies:
1. If high precision is not needed you can call S826_DioOutputWrite()
multiple times, with 10ms software delays between the calls. The precision of this method depends on your operating system and system load. The code for this method is simple and straightforward.
2. If you need precise timing then you could use two counter channels. For example, using counter channels 0 and 1:
- Counter 0: Configure as PWM generator with 10ms on/off times, with external enable. Connect its enable input to counter 1's output. The goal here is for counter 0 to enable counter 1 to output pulses; counter 0 will enable the PWM generator until 5 pulses have been generated.
- Counter 1: Configure as event down-counter, with preload (value=5) upon enable, with external clock, with output active when counts not zero. Connect its clock input (must be external connection) to counter 0's output. The goal here is for counter 1 to count PWM pulses until it counts from 5 down to 0; it will then set its output low, thus disabling the PWM generator.
Software
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).
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. To get required libraries on Ubuntu, type:
"sudo apt-get install mono-complete"
For a C# development environment, type:
"sudo apt-get install monodevelop"
Resources for custom driver development
- I want to develop my own 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 and middleware, comprising a complete 826 API for Linux. The middleware core is operating system independent, which makes this SDK a great starting point for porting. The SDK can be found on the Downloads tab of the 826 product page.
- Model 826 Technical Manual - This comprehensive manual covers the API and 826 hardware operation 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.
See also
- GPIO interfacing - design tips for DIO circuits