Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C

Interfacing Rigol Oscilloscopes with C

4.73/5 (9 votes)
5 Feb 2015CPOL20 min read 31K   511  
This article explains how to develop C applications capable of accessing and controlling Rigol oscilloscopes.

This article appears in the Third Party Products and Tools section. Articles in this section are for the members only and must not be used to promote or advertise products in any way, shape or form. Please report any spam or advertising.

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:

  1. Open a session (ViSession) with the resource manager with viOpenDefaultRM.
  2. Access a resource (ViRsrc) with viFindRsrc.
  3. Open a session (ViSession) with the resource using viOpen.
  4. Send commands to the resource with viWrite and read data back to the PC with viRead.
  5. 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.

C++
#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];

  // Open session with the resource manager
  viOpenDefaultRM(&rmSession);

  // Find USB resources
  viFindRsrc(rmSession, "USB?*INSTR", &resourceList, &numResources, usbResource);

  // Open session to the resource
  viOpen(rmSession, usbResource, VI_NULL, VI_NULL, &scopeSession);

  // Send a command to the resource
  viWrite(scopeSession, (ViBuf)"*IDN?\n", 512, VI_NULL);

  // Read the response
  viRead(scopeSession, (ViBuf)readStr, 512, VI_NULL);

  // Print the ID string
  printf("Resource ID: %s\n", readStr);

  // Close the session to the resource
  viClose(scopeSession);

  // Close the session to the resource manager
  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.

Command categories

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:

  1. 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.
  2. A command that ends with a question mark is a query. It reads (gets) information from the instrument.
  3. 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:
  1. Configure the scope's operation (vertical/horizontal scale, data acquisition, trigger, waveform)
  2. :RUN tells the oscilloscope to wait for the trigger
  3. (wait enough time for the measurement to complete)
  4. :STOP tells the oscilloscope to halt its operation
  5. :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:

  • :CHANnel<n>:<spec>? - returns the configuration value

  • :CHANnel<n>:<spec> <arg> - sets the configuration to the value determined by <arg>

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:

  1. Call the :TRIGger:MODE command with the appropriate trigger mode.
  2. 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, WORDASCii 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:

  1. NORMal - reads data displayed on the screen
  2. MAXimum - reads data on the screen when the scope is in run state, internal memory data in the stop state
  3. 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:

  1. BYTE - each sample is provided in an 8-bit value between 0 and 255
  2. WORD - each sample is provided in a 16-bit value and only the low 8-bits are valid
  3. 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:

  1. 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.
  2. 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.
  3. To reduce noise, the 20 MHz bandwidth limit is set with :CHAN2:BWL 20M.
  4. The :WAV:MODE RAW command specifies that measurement data sent to the PC should be read from internal memory.
  5. The commands :WAV:STAR 1 and :WAV:STAR 800000 specify that only the first 800,000 samples are read to the PC.
  6. 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];

// Execute each command in a list
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() {

  // The list of commands
  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"};

  // Open session with the resource manager
  viOpenDefaultRM(&rmSession);
 
  // Find USB resources
  viFindRsrc(rmSession, "USB?*INSTR", &resourceList, &numResources, usbResource);
 
  // Open session to the resource
  viOpen(rmSession, usbResource, VI_NULL, VI_NULL, &scopeSession);

  // Configure the device and start measurement
  run_commands(cmds);

  // Wait for the scope's measurement
  Sleep(1000);

  // Read the response
  viWrite(scopeSession, (ViBuf)":STOP\n", 512, VI_NULL);
  viWrite(scopeSession, (ViBuf)":WAV:DATA?\n", 512, VI_NULL);
  viReadToFile(scopeSession, "wave.dat", 800000, VI_NULL);

  // Close the session to the resource
  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

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)