Introduction
As oscilloscopes acquire new features, it becomes more difficult to understand all of their buttons, dials, and menus. But if you can code PC applications that interface an oscilloscope, you can execute precise, complex tests without knowing anything about its front panel. You can also control how and when the scope's measurements are transferred to the computer.
This article explains how to code applications that access many, but not all, of the capabilities of a Rigol oscilloscope. To be specific, my development environment consists of Visual Studio 2012 running on a Windows PC connected to a Rigol DS1104Z. But the content of this article should apply to other operating systems and test instruments.
1. Overview of Interfacing Rigol Oscilloscopes
On their website, Rigol Technologies provides a driver package for each of their oscilloscopes. If you install it on a Windows 7 computer, the C:\Program Files (x86) directory will hold a directory called IVI Foundation. This contains the libraries and header files needed to build the applications described in this article.
A particularly important file is visa32.lib, which is located in IVI Foundation\VISA\WinNT\lib\msc. The functions in this library make it possible to interface compliant test instruments. These functions are declared in visa.h, which is located in IVI Foundation\VISA\WinNT\include. The next section explains how to use these functions in an application, but first, I want to briefly explain what IVI is and how it relates to VISA.
IVI stands for Interchangeable Virtual Instruments, and the IVI Foundation manages specifications that define how test instruments should be accessed in software. Current members of the consortium include Tektronix, Keithley Instruments, National Instruments, and Rohde & Schwartz.
One of the most important IVI specifications is the Virtual Instrument Software Architecture standard, shortened to VISA. If a test instrument is VISA-compliant, you can access it with a PC application using the software routines and data structures defined in the VISA standard.
2. VISA Functions
The current version VISA specification is the VPP-4.3 Standard, Revision 5.4. The PDF can be downloaded here. This standard employs a specific set of terms to describe test instruments and their PC interfaces. Three important terms are given as follows:
- Resource - Represents a test instrument such as a Rigol oscilloscope
- Resource manager – Data object that makes it possible to find, open, and close resources
- Session - Communication path between a PC and a resource manager or between a PC and a reource
Using this terminology, the process of interfacing a VISA-compliant oscilloscope in software consists of five steps:
- Open a session (
ViSession
) with the resource manager with viOpenDefaultRM
. - Access a resource (
ViRsrc
) with viFindRsrc
. - Open a session (
ViSession
) with the resource using viOpen
. - Send commands to the resource with
viWrite
and read data back to the PC with viRead
. - Close the sessions with
viClose
.
To clarify how these functions can be called, Table 1 lists the signatures of ten VISA functions and provides a description of each. The VISA standard defines more functions than those listed, but these are the core functions needed to interface an oscilloscope.
Table 1: VISA Functions for Interfacing Test Instruments Function Name | Description |
viOpenDefaultRM(ViSession) | Open a session to communicate with the resource manager |
viFindRsrc(ViSession, String,
ViFindList, ViUInt32, ViRsrc) | Access the first resource that meets the given criteria |
viOpen(ViSession, ViRsrc,
ViAccessMode, uint32, ViSession) | Opens a session to communicate with the resource |
viWrite(ViSession, ViBuf, uint32,
uint32) | Synchronously writes data from the buffer to the resource |
viWriteAsync(ViSession, viBuf,
uint32, viJobId) | Asynchronously writes data from the buffer to the resource |
viWriteFromFile(ViSession,
string, uint32, uint32) | Synchronously writes data from a file to the resource |
viRead(ViSession, ViBuf, uint32,
uint32) | Synchronously reads data from the resource to a buffer |
viReadAsync(ViSession, ViBuf,
uint32, ViJobId) | Asynchronously reads data from the resource to a buffer |
viReadToFile(ViSession,
string, uint32, uint32) | Synchronously reads data from the resource to a file |
viClose(ViSession) | Closes the session with the resource manager |
The second parameter of ViFindRsrc
sets the criteria used by the resource manager to select the resource. The format of this string is complicated, but if the oscilloscope connects to the PC through a USB connection, the string USB?*INSTR
will be sufficient to access the scope.
As an example, the following code accesses the first connected resource that meets the USB?*INSTR criteria. It calls viWrite
to deliver a command (:IDN?
) and calls viRead
to read the device's identification string.
#include <stdio.h>
#include <string.h>
#include <visa.h>
int main() {
ViSession rmSession, scopeSession;
ViFindList resourceList;
ViUInt32 numResources;
char readStr[512];
char usbResource[VI_FIND_BUFLEN];
viOpenDefaultRM(&rmSession);
viFindRsrc(rmSession, "USB?*INSTR", &resourceList, &numResources, usbResource);
viOpen(rmSession, usbResource, VI_NULL, VI_NULL, &scopeSession);
viWrite(scopeSession, (ViBuf)"*IDN?\n", 512, VI_NULL);
viRead(scopeSession, (ViBuf)readStr, 512, VI_NULL);
printf("Resource ID: %s\n", readStr);
viClose(scopeSession);
viClose(rmSession);
return 0;
}
When a Rigol oscilloscope receives the *IDN?
command, it responds to ViRead
with its full identification string. This includes the device's name, model, and serial number.
3. Overview of Rigol Commands
VISA functions make it possible to deliver commands to a test instrument and read back responses. The hard part of coding test applications is understanding the many commands available. At a broad level, they can be divided into two groups: IEEE-488.2 commands and Rigol-specific commands. Rigol-specific commands can be further divided according to the nature of the command string. Figure 1 gives an idea of what these categories look like.
Figure 1: Commands Divided into Categories by Syntax
The IEEE-488 standard was created in the late 1960s to define commands for accessing test instruments connected by the General-Purpose Interface Bus (GPIB). Each command starts with an asterisk and the preceding example used *IDN?
to query the identification string. Similarly, *RST
resets the instrument and *TST
performs a self-test. *OPC?
checks to see if the current operation is continuing and *WAI
waits until the operation has finished. These commands are good to know, but I rarely use them in my test applications.
Rigol supplements the IEEE-488 commands with commands specifically suited to interfacing Rigol oscilloscopes. There are three points to understand about them:
- Each command consists of a colon followed by capital letters. In this article, if a command is given as
:CLEar
or :TIMebase
, only the capital letters are important. Additional letters are printed for clarity. - A command that ends with a question mark is a query. It reads (gets) information from the instrument.
- The initial command name may be followed by specifiers, each preceded by a colon. Examples include
:CHANnel2:SCALe
and :TIMebase:DELay:ENABle
. Commands with specifiers are called complex commands and commands without specifiers are called simple statements. There are no simple queries.
The following sections go into greater depth concerning simple and complex commands.
4. Simple Statements
Rigol's simple statements tell the oscilloscope to perform an action. Table 2 lists all six of them and provides a description of each.
Table 2: Simple Statements for Rigol Oscilloscopes Statement | Description |
:RUN | Tells the oscilloscope to wait for a trigger and start measurements |
:STOP | Halts the oscilloscope's operation |
:CLEar | Clears the waveforms on the screen |
:SINGle | Sets single-trigger mode (see below) |
:TFORce | Forces a trigger condition |
:AUToscale | Tells the oscilloscope to configure the vertical scale, horizontal scale
(timebase), and trigger according to the input signal |
These statements are easy to understand. It's important to see that :RUN
doesn't necessarily tell the oscilloscope to measure and record data. It tells the oscilloscope to wait for a trigger condition, at which point it will start measurement. When its measurements are complete, the scope will wait for another trigger unless it's placed in single-trigger mode with :SINGle
.
Trigger conditions can be configured in a variety of ways, and this will be explained in a later section. If you want the scope to start measuring without waiting, :TFORce
forces a trigger condition.
In my test applications, the commands sent to the oscilloscope tend to follow a five-step sequence:
- Configure the scope's operation (vertical/horizontal scale, data acquisition, trigger, waveform)
:RUN
tells the oscilloscope to wait for the trigger - (wait enough time for the measurement to complete)
:STOP
tells the oscilloscope to halt its operation :WAVeform:DATA?
reads the measured data to the PC
Step 1 can be accomplished simply with the :AUToscale
command, which performs the same function as the AUTO button. But Rigol provides many, many commands that customize the scope's operation. The following sections explain how to configure the instrument's channels, horizontal scale (timebase), data acquisition, trigger condition, and waveform. I won't present all the available commands, but only those that I've found useful. For the full list of commands, I recommend Rigol's DS1000Z Programming Guide.
5. Channel Configuration
In Rigol parlance, a channel is an input capable of receiving a signal. Most Rigol scopes have four channels, and each can be configured individually with :
CHANnel<n>
, where <n> identifies the channel number (1-4). For example, a command that starts with :CHAN3
applies to the third channel.
All channel configuration commands are complex, which means :
CHANnel<n>
is followed by specifiers that determine the command's operation. A channel command takes one of two general forms:
Table 3 lists the specifiers that can be used with :
CHANnel<n>
commands. For each specifier, the table lists possible arguments and a description.
Table 3: Channel Configuration Commands Specifier | Arguments | Description | |
:UNITs | VOLTage , WATT ,
AMPere , UNKnown | Gets/sets the units measured by the channel | |
:DISPLAY | 0/OFF or 1/ON | Gets/sets whether the channel is displayed on the scope | |
:BWLimit | 20M or OFF | Gets/sets whether the 20 MHz bandwidth limit is set | |
:COUPling | AC , DC , or GND | Gets/sets whether AC and DC components are blocked (GND),
DC components are blocked (AC),
or no components are blocked (DC)
| |
:INVert | 0/OFF or 1/ON | Gets/sets whether the display mode is inverted | |
:TCAL | -100 to 100 | Gets/sets the time allowed to calibrate the zero offset | |
:PROBe | 0.01 , 0.02 , 0.05 ,
0.1 , 0.2 , 0.5 , 1 ,
2 , 5 , 10 , 20 , 50 ,
100 , 200 , 500 , 1000 | Gets/sets the probe ratio | |
:VERNier | 0/OFF or 1/ON | Gets/sets whether fine adjustment of the vertical scale is enabled | |
:SCALe | See below | Gets/sets the vertical scale | |
:OFFSet | See below | Gets/sets the vertical offset | |
:RANGe | See below | Gets/sets the vertical range | |
Most of these entries are straightforward, but those involving the probe and the vertical scale can be difficult to understand. First, it's important to distinguish between the attenuation of a physical probe and the oscilloscope's probe ratio.
The physical probes that ship with Rigol oscilloscopes have a button that sets the attenuation to 1x or 10x. The default setting on the physical probe is 10x, which means the probe reduces the incoming signal by a factor of ten. The oscilloscope doesn't know the physical probe's attenuation, so it multiplies the incoming signal by a probe ratio to make up for the probe's reduction. Because the default physical probe attenuation is 10x, the scope's default probe ratio is 10, which amplifies the incoming signal ten times.
Each channel's probe ratio can be configured individually. If a channel's probe ratio is 5 and the physical probe's attenuation is 10x, the displayed signal strength will be half that of the real signal. If the probe ratio is 20, the displayed signal strength will be twice that of the real signal. The following command sets Channel 2's probe ratio to 20:
:CHAN2:PROB 20
The vertical scale sets the number of volts per division in the display. When a channel's probe ratio changes, the vertical scale changes inversely. That is, if the probe ratio is multiplied by x, the scale is divided by x.
The vertical scale is set with :SCALe
, and the range of acceptable scale values depends on the probe ratio.
- If the probe ratio is
1
, the scale can be set between 1mV and 10 V - If the probe ratio is
10
, the scale can be set between 10mV and 100 V
By default, if the probe ratio is 1, the scale can be set in 1-2-5 increments, such as 1mV, 2mV, 5mV and 10mV, 20mV, 50mV. But if :VERNier
is set to 1
or ON
, the vertical scale can be adjusted in smaller increments.
The :OFFSet
command gets or sets an offset to the incoming signal. The range of the offset depends on the channel's probe ratio and the vertical scale.
-
If the probe ratio is 1
and the vertical scale is greater than 500 mV/division, the offset can be set between -100V and 100V.
-
If the probe ratio is 1
and the vertical scale is less than 500 mV/division, the offset can be set between -2V and 2V.
-
Rigol's documentation doesn't give the offset range if the probe ratio is 10, but it's safe to assume the offset can be set between -100V and 100 V.
It's important to understand the difference between :SCALe
and :RANGe
. The :SCALe
command gets or sets the number of volts per division. The :RANGe
command gets or sets the number of volts that can be presented in the entire display. If an oscilloscope can only display eight divisions, its vertical range equals eight times the vertical scale.
As mentioned earlier, if the probe ratio is set to 1
, a channel's vertical scale can be set between 1mV and 10V. Because the vertical range is eight times the scale, the vertical range can be set between 8mV and 80V when the probe ratio is 1.
6. Timebase Configuration
Just as the channel commands configure the vertical display, the timebase commands configure the horizontal display. There's one important difference: a channel command affects one channel only and a timebase command affects all channels.
Timebase commands start with :TIMebase
. Table 4 lists its specifiers and possible arguments.
Table 4: Timebase Configuration Commands Specifier | Arguments | Description | |
:MODE | MAIN , XY , or ROLL | Get/sets how incoming signals are displayed | |
[:MAIN]:SCALe | Main: 5ns to 50s
Roll: 200ms to 50s | Gets/sets the horizontal timescale (sweep speed) | |
[:MAIN]:OFFSet | -Screen/2 to 1s or
-Screen/2 to 5000s | Gets/sets the horizontal offset | |
:DELay:ENABle | o/OFF or 1/ON | Gets/sets delayed sweep
| |
:DELay:OFFSet | -(LeftTime - Range/2)
(RightTime - Range/2) | Gets/sets the timebase offset of the delay | |
:DELay:SCALe | See below | Gets/sets the delayed timebase scale (secs/div)
| |
Regarding the :MODE
command, the main mode (MAIN
) is referred to as the YT Mode because the y-axis represents voltage and the x-axis represents time. In XY
Mode, two channels are displayed at once: the first channel is displayed vertically and the second is displayed horizontally. In ROLL
mode, the waveform proceeds from right to left, displaying results in advance of the full sweep.
The motion of the trace across the screen is called the sweep. The sweep speed is determined by the horizontal scale, which sets the number of seconds per division. The :SCALe
command sets this value, and the range of values depends on the mode. For both scales, the values are limited to 1-2-5 triples, such as 1s, 2s, 5s, or 10s, 20s, or 50s.
In the MAIN
mode, delayed sweep makes it possible to observe a signal after a time interval has elapsed. The :DELay:ENABle
command enables this capability and :DELay:OFFSet
sets the time interval. The :DELay:SCALe
command makes it possible to zoom into the delayed signal. The maximum scale for the delayed signal is that of the main horizontal scale.
7. Data Acquisition Configuration
The :ACQuire
commands are related to the oscilloscope's sample rate and the number of samples it stores with each run. Table 5 lists the different commands available.
Table 5: Acquisition Configuration Commands Specifier | Arguments | Description | |
:SRATe | -- | Gets the number of samples the oscilloscope
can read per second | |
:MDEPth | See below | Gets/sets the number of waveform points to
be stored during measurement | |
:TYPE | NORMal , AVERages ,
PEAK , HRESolution | Gets/sets how the scope samples the incoming
signal | |
:AVERages | 2^n (n between 1 and 10) | Identifies how many samples are averaged
together
| |
An oscilloscope's sampling rate tells you how many signal values it can read per second. This rate can't be changed with Rigol commands, but it can be read with the :SRATe?
command. The sampling rate decreases as more channels are used.
Memory depth refers to the number of samples that can be stored in a single trigger sample. By default, memory depth is obtained with the following equation:
Memory depth = Sample rate x Waveform length
For custom settings, the possible number of samples depends on the number of active channels:
-
1 channel: 12000, 120000, 1200000, 12000000, 24000000
-
2 channels: 6000, 60000, 600000, 6000000, 12000000
-
4 channels: 3000, 30000, 300000, 3000000, 6000000
The scope can sample the incoming signal in four possible ways:
-
NORMal
- samples at equal intervals
-
AVERages
- reduces noise by recording averages of incoming signals
-
PEAK
- acquires the minimum and maximum values within the interval
-
High resolution (HRESolution
) - Uses ultra-sampling to average neighboring points to reduce random noise
The :TYPE
command identifies which of the four sampling methods should be employed. If the AVERages
mode is selected, :AVERages
tells the scope how many samples should be averaged together.
8. Trigger Configuration
The scope starts reading signal data when the trigger condition is met. To set the trigger condition in an application, two steps are generally needed:
- Call the
:TRIGger:MODE
command with the appropriate trigger mode. - Call the
:TRIGger
commands specific to the selected mode.
Rigol oscilloscopes support fifteen trigger modes and there are a vast array of mode-specific commands. Therefore, this discussion is limited to presenting the fifteen modes, which are listed in Table 6.
Table 6: Trigger Modes Trigger Mode | Description | |
EDGE | Triggers on the specified edge of an input signal | |
PULSe | Triggers on a positive/negative pulse of a given width | |
WINDow | Triggers when the input passes through the high input trigger level or the low trigger level |
NEDG | Triggers on the nth edge after the specified idle time |
RUNT | Triggers when a signal passes through one trigger level but fails to pass through the other trigger level |
SLOPe | Triggers on a positive/negative slope for a given time |
VIDeo | Triggers on the video signal field |
PATTern | Triggers on the AND combination of two channels |
DELay | Triggers when the time difference between two edges meets the preset time limit |
TIMeout | Triggers when the time interval from a signal's rising/falling edge to the neighboring falling/rising edge passes through the trigger level |
DURation | Triggers on a specified pattern for the given duration |
SHOLd | Triggers when the internal state of the setup/hold time is changed by logic data input |
RS232 | Trigger according to the start frame, error frame, check error, or data |
IIC | Trigger on the start condition, restart, stop, missing ack, or the read/write frame for a specific address and data value |
SPI | Trigger on the SPI data pattern |
As an example, let's look at how to set the scope to trigger on a channel's rising edge. First, the trigger mode must be set to EDGE with the following command:
:TRIG:MODE EDGE
The :TRIGger:EDGe:SOURce
command identifies which channel's edge should be monitored, and can be used in the following way:
:TRIG:EDG:SOUR CHAN2
The :TRIGger:EDGe:SLOPe
identifies the type of edge, which can be set to POSitive
, NEGative
, or RFALI
(rising/falling).
:TRIG:EDG:SLOP NEG
Rigol provides similar mode-specific commands for each of the modes listed in Table 6.
8. Waveform Configuration
One important advantage of a PC-oscilloscope interface is the ability to download the scope's measurements. This is made possible by :WAVeform
commands, which configure properties of the measurements such as the format and source. Table 7 lists the specifiers associated with :WAVeform
.
Table 7: Trigger Modes Command | Arguments | Description | |
:DATA? | -- | Retrieves the waveform's data | |
:SOURce | CHANnel1 , CHANnel2 ,
CHANnel3 , CHANnel4 , MATH | Gets/sets the source of the waveform display | |
:MODE | NORMal , MAXimum ,
RAW | Gets/sets how the waveform data is read | |
:FORMat | BYTE , WORD , ASCii | Gets/sets the format of the waveform data |
:XINCrement? | -- | Gets the time difference between adjacent samples |
:XORIgin? | -- | Gets the time from the trigger point to the reference time of the channel source |
:XREFerence? | -- | Gets the reference time of the specified channel source |
:YINCrement? | -- | Gets the time difference between adjacent samples (equals vertical scale/25) |
:YORIgin? | -- | Gets the time from the trigger point to the reference time of the channel source |
:YREFerence? | -- | Gets the reference position of the specified channel source |
:STARt | See below | Get/set the start position of reading the internal memory waveform |
:STOP | See below | Get/set the stop position of reading the internal memory waveform |
When the scope's measurements are completed, the :WAVeform:DATA?
command transfers the sample data to the computer. The :WAVeform:SOURce
command identifies the channel and :WAVeform:MODE
specifies what data should be read. This accepts three options:
NORMal
- reads data displayed on the screen MAXimum
- reads data on the screen when the scope is in run state, internal memory data in the stop state RAW
- reads data from internal memory
Every Rigol oscilloscope I've worked with stores eight bits per sample, The :WAVeform:FORMat
command specifies how these eight bits are formatted. This command can accept one of three values:
BYTE
- each sample is provided in an 8-bit value between 0 and 255 WORD
- each sample is provided in a 16-bit value and only the low 8-bits are valid ASCii
- each sample is provided in text using floating-point notation (e.g. -1.96e-03
)
The :WAVeform:START
and :WAVeform:STOP
commands identify the starting and ending positions of the data read from the scope. Both commands accept an integer argument that depends on the mode setting:
NORMal
mode - 1-1200 MAXimum
- 1 to the effective number of points on the display RAW
- 1 to the memory depth (can be obtained with :ACQuire:MDEPth
)
9. Full Test Example
At this point, you should have a basic familiarity with VISA functions and Rigol commands. Now let's look at a test application that makes use of both. The incoming signal has the following properties:
- Input channel: Channel 2
- Voltage range: -1.5V to 1.5V
- Periodic with frequency of 1 kHz
- Desired sampling frequency: as high as possible
With this information, we can code an application that reads the signal and transfers the measured data to the PC. Instead of using the :AUT
command, this application configures each aspect of the measurement:
- To ensure that the -1.5V to 1.5 range can be measured, the vertical range is set to 4V with the command
:CHAN2:RANG 4
. - If the signal is periodic with a frequency of 1 kHz, each period equals 1/1000 or 1 ms. To see five periods, the horizontal range must be at least 5ms. If there are twelve divisions, the horizontal scale must be at least 5ms/12 = .4166 ms/div. To be safe, the horizontal scale will be set to 1 ms/div with the command
:TIM:MAIN:SCAL 0.001
. - To reduce noise, the 20 MHz bandwidth limit is set with
:CHAN2:BWL 20M
. - The
:WAV:MODE RAW
command specifies that measurement data sent to the PC should be read from internal memory. - The commands
:WAV:STAR 1
and :WAV:STAR 800000
specify that only the first 800,000 samples are read to the PC. - The trigger is set to single trigger with
:SING
.
The following code uses VISA functions to open a session to the oscilloscope. Then it delivers the set of configuration commands followed by :RUN
and :TFOR
. Then the data is read to the computer with the :WAV:DATA?
command.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <visa.h>
#include "Windows.h"
#define NUM_CMDS 10
#define CMD_SIZE 64
static ViSession rmSession, scopeSession;
static ViFindList resourceList;
static ViUInt32 numResources;
static ViStatus status;
static char usbResource[VI_FIND_BUFLEN];
void run_commands(char commands[NUM_CMDS][CMD_SIZE]) {
int i;
for(i=0; i<NUM_CMDS; i++) {
status = viWrite(scopeSession, (ViBuf)commands[i], 512, VI_NULL);
if(status != VI_SUCCESS) {
printf("Error executing %s\n", commands[i]);
exit(-1);
}
}
}
int main() {
static char cmds[NUM_CMDS][CMD_SIZE] =
{":CHAN2:RANG 4\n", ":CHAN2:BWL 20M\n", ":TIM:MAIN:SCAL 0.001\n",
":WAV:MODE RAW\n", "WAV:SOUR CHAN2\n", ":WAV:STAR 1\n", ":WAV:STOP 800000\n",
":SING\n", ":RUN\n", ":TFOR\n"};
viOpenDefaultRM(&rmSession);
viFindRsrc(rmSession, "USB?*INSTR", &resourceList, &numResources, usbResource);
viOpen(rmSession, usbResource, VI_NULL, VI_NULL, &scopeSession);
run_commands(cmds);
Sleep(1000);
viWrite(scopeSession, (ViBuf)":STOP\n", 512, VI_NULL);
viWrite(scopeSession, (ViBuf)":WAV:DATA?\n", 512, VI_NULL);
viReadToFile(scopeSession, "wave.dat", 800000, VI_NULL);
viClose(scopeSession);
viClose(rmSession);
return 0;
}
In my applications, I've noticed that I can't transfer all 2,400,000 samples to the PC at once. For this reason, my applications perform three separate read operations of 800,000 samples each.
Using the code
To compile the example applications in this article, the C compiler needs to know where to find visa.h and a suitable VISA library, such as visa32.lib. The second example application relies on Windows.h for the Sleep
function, but this can be easily changed to work with other operating systems.
Points of Interest
As an electrical engineer, learning how to interface an oscilloscope in code has dramatically increased my productivity. In addition, I learned a great deal about the different capabilities and features of modern oscilloscopes.
History
- 2/6/2015 - Fixed the image hyperlink
- 2/6/2015 - Uploaded the archive containing the two source files - id_check.c and full_test.c