C# & Electronic Test Instruments

With C# and I/O libraries compliant with the Virtual Instrument Software Architecture specification, you can create test and measurement applications-complete with GUIs.


April 01, 2004
URL:http://www.drdobbs.com/c-electronic-test-instruments/184401787

April 04:

Although normally associated with enterprise application development, C# can also be used in the lab, on the factory floor, and in similar environments that involve electronic test and measurement instrument control. In fact, with C# and I/O libraries compliant with the Virtual Instrument Software Architecture (VISA) specification (http://www.vxipnp.org/), you can create test and measurement applications, complete with GUIs. VISA is a specification developed by the VXIplug&play Systems Alliance, which identifies a common I/O API for communicating over a variety of bus interfaces, including GPIB, VXI, USB, and Ethernet. A number of vendors have tools that support this specification, including Agilent (the company I work for). In this article, I'll use C# and VISA-compliant software to develop test programs. Then I'll improve the test program by using software compliant with the Interchangeable Virtual Instrument (IVI) specification. The IVI spec was developed by the IVI Foundation (http://www.ivifoundation.org/) to promote specifications for programming test instruments.

While test program applications are traditionally written in C, C++, and Visual Basic, C# provides several benefits when compared to these languages [1]. With C#, for instance, C/C++ programmers can easily create applications with a GUI and not have to learn MFC or Visual Basic. You can develop programs immune to memory leaks. You can also use arrays and automatically detect any out-of-bound array accesses. And with C#, you can write programs free of assignment mistakes (if (a=3) {...} instead of if (a==3) {...}). And since C# applications are compiled (not interpreted), they offer faster execution times than Visual Basic programs.

While learning any new programming language can be daunting, learning C# does not have to be — especially for experienced C/C++ programmers. It supports the familiar bool, int, float, and double types and multidimensional arrays of types. The syntax for declarations (int a;) and expressions (a=3;) is very much like C and C++. C# has the familiar operators, such as +, -, !, ~, *, /, ++, , <=, ==, and the like. C# has if-else, switch, continue, break, goto, and the for, while, and do-while looping constructs to control program execution. Enumerated types and structs can be defined. You can create C# classes with private, protected, and public variables and methods. Classes can inherit from base classes, and base classes can have virtual members. All in all, this is very much like C++.

But as you might expect, there are some new types in C#. The string type, for instance, contains an immutable set of Unicode characters. Here is an example of how straightforward it is to declare and manipulate strings in C#:

string s = "What";
s = s + " a beautiful morning.";
s = s.ToLower();
s = s.Insert(0,"Oh,");

After all of this, s = "Oh, what a beautiful morning."

There are many other programming tasks that C# makes easy. Creating GUIs in C# is as easy as in Visual Basic. All you need to do is drag the Label, Button, GroupBox, CheckBox, and like items from the Toolbox onto a Windows Form. Tooltip help, tabbing, and pull-down menus are also easy to set up.

Another simple task is creating a distributed application that consists of a server application running on one computer and a client program running on a different one. When both computers are connected on the network, the client can call methods on the server (a process referred to as "remoting").

Moreover, it is just as easy to create an application that remembers state information; for example, a UI setting. This is done using the DynamicProperties of an object. Once a DynamicProperty for an object has been set up, the IDE creates an XML app.config file that stores the state information. The next time the application starts, the state information reads from app.config.

C# provides get and set accessors to read and write object properties (member variables). These are naturally grouped together, as shown in Listing 1(a) and Listing 1(b).

Of course, there are things that are commonly done in C/C++ that are more difficult in C# — the use of pointers, for instance. Pointers for memory manipulation must be done inside unsafe code blocks and the fixed keyword must pin the object the pointer is pointing to so that it won't be moved by the garbage collector.

fixed ( byte *pSource = &myDataSinkBuf[idx1] )
fixed ( byte *pDest = &myDataSourceBuf[idx2] )
{
    *pDest = *pSource;
}

Pointer arithmetic is disallowed:

fixed ( byte *pSource = &myDataSinkBuf[idx1] )
fixed ( byte *pDest = &myDataSourceBuf[idx2] )
{
   *pDest++ = *pSource++;   // won't compile
}

Multiple inheritance is something else you can do in C/C++ that you can't do in C#. There is no multiple inheritance in C#, although a C# class can implement multiple interface classes. This may cause concern for some programmers, but over the years I've only once used multiple inheritance in C++.

Building a Test/Measurement Application

The sample application I present here is a test and measurement system that communicates with a function generator. The application includes a GUI that lets users control the sine wave frequency.

To start, you need the correct software and equipment installed on a development PC:

The concepts and programming details I provide here are applicable even if other test equipment is used, and much of the detail presented here pertains to any C# application.

Step 1: Creating the Skeleton Application. Start the Visual Studio .NET IDE and select New Project. In the dialog box that pops up, select Visual C#, Windows Application, and an appropriate directory and project name. I used "functionGenerator_1" as the project name. The IDE then automatically creates several files; see Table 1.

Step 2: Creating the User Interface. To let users control the frequency, add a GUI TrackBar item onto Form1. If Form1.cs [design]* is not currently visible, make it visible by finding the Solution Explorer window, right-clicking on Form1.cs, and selecting View Designer. Move the mouse over the Toolbox area on the left, then drag a TrackBar onto Form1. Figure 1 shows the TrackBar about to be added to Form1. The Toolbox contains many familiar GUI controls, ready to add to the GUI just by dragging onto Form1.

After adding the TrackBar GUI item, add three Label items from the Toolbox and set the Text properties equal to: Frequency, 1 Hz, and 30 KHz. After selecting Build|Build Solution and Debug|Start, the skeleton app should run and display Figure 2. Naturally, your results will vary, depending on your placement of the Label boxes and the TrackBar properties; I set TrackBar minimum to 1, TrackBar maximum to 30,000.

At this point, I haven't manually typed in any C# code, yet I already have the beginnings of a GUI for the application.

Step 3: Adding VISA Initialization Calls. Here's where you add C# code to initialize the VISA software. The VISA software is a DLL, and for each DLL function a C# application calls, a DllImport (or DllImportAttribute) statement must be added so that the C# compiler knows how to insert code to call the function. Agilent (the company I work for) ships a visa32.cs file that contains all of the necessary DllImport functions for the VISA API so you don't have to actually type in a single DllImport statement — all you have to do is add the visa32.cs file to the project. For example, a DllImport statement for viOpenDefaultRM() is:

[DllImport("VISA32.DLL")] public 
    static extern int 
    viOpenDefaultRM(out int sesn);

To begin using the VISA software (but before sending any program messages to a device), two VISA API calls must be made:

Listing 2, for example, is inserted into the Form1 constructor in the Form1.cs file. The italic text represents code that has been manually added. For brevity, I've omitted error checking. Notice that the VISA viOpenDefaultRM() and viOpen() are done just after InitializeComponent(), after the TODO: comment that was automatically generated. viOpen() uses the rsrcName string functionGenerator, which is an alias set up by users when the function generator is first attached via USB.

Next, add the Form1 class variables in Listing 3. The programMessage byte array is used as a buffer when calling viWrite().

Step 4: Adding VISA Calls to Change the Frequency. The IDE automatically adds additional skeleton code that executes when users change the TrackBar slider position. To see this, make Form1.cs [Design]* visible in the IDE, then double left-click on the TrackBar. The IDE generates a skeleton event handler, switches the view to the Form1.cs code view, and prompts you to enter in code. Listing 4 is event-handler code that changes the function generator frequency.

Before compiling, add:


using System.Text;

near the top of Form1.cs, just after the existing using statements. This uses the types in the System.Text namespace, allowing Encoding.ASCII.GetBytes() to compile successfully. Encoding.ASCII.GetBytes() serves to convert a stream of Unicode characters into ASCII bytes and pack them into the programMessage byte array.

So after writing just 10 or so lines of C# code, you have a simple program — complete with a GUI — which can be used to control the frequency of a function generator.

C# and IVI Software

Admittedly, the program I've just presented has a limitation. The program message string syntax to change the frequency is hard-coded, and may need to change if a different function generator is used. This is because the syntax to change frequency may not be universally understood. IVI software, developed within the IVI Foundation (http://www.ivifoundation.org/), works around this problem by defining common APIs for function generators, digital multimeters (voltmeters), oscilloscopes, and other such instruments. If an application uses IVI software and only calls the specification-required functions, there should be no need to change the application code if a different instrument is used.

Since Agilent has IVI software available for the 33220 function generator, I'll use it to develop a second application that also sets the frequency. I use IVI-COM software because C# has built-in support for COM. This requires the installation of Agilent's IVI components and 33220 IVI-COM Driver (both available at http://adn.tm.agilent.com/).

After installing the software, go ahead and create a new project, but this time call it functionGenerator_IVI_1. Again, the IDE creates a skeleton application with a Form1.cs file. To use COM objects in C#, you must add a reference to the COM object. In the Solution Explorer window, add a reference by right-clicking on References, then select Add Reference, then select the COM tab. As in Figure 3, scroll down to the IVI Agilent 33220 (Agilent Technologies) 1.0 Type Library entry, then add it in using Select|OK. This is the only COM object reference that has to be manually added; the other necessary references are added automatically. The Solution Explorer window in Figure 4 should then appear. Next, with the IDE view of Form1.cs [design]*, drag the TrackBar and Label GUI items onto Form1, as in the first example. Switch to view the code in Form1.cs and add a Form1 class variable for the function generator IVI-COM object, fgen; see Listing 5.

In the Form1() constructor (after InitializeComponent()), the fgen object is instantiated (Listing 6). Again as in the first example, double left-click on the TrackBar in the Form1.cs [design]* window, and the IDE automatically adds a skeleton trackBar1_Scroll() function, and prompts you to enter code. The code I added was:

private void trackBar1_Scroll(object sender, System.EventArgs e)
{
   fgen.Output.Frequency = trackBar1.Value;
}

The application is now complete and fully functional — and after manually adding only four lines of code. These examples show how easy it is to use C# to communicate to electronic test instruments. They also illustrate how easy it is to use C# to call functions in existing DLLs and call COM object methods.

References

[1] Hill, Keith. "C#: A More Productive Alternative to C/C++ for T&M Software Development," Agilent Developer Network whitepaper (http://www.agilent.com/find/adn/), January 2003.


Andy Purcell is an R&D engineer for instrumentation at Agilent Technologies (http://www.agilent.com/). Andy can be contacted at [email protected].


April 04:

Figure 1: Using the Visual Studio .NET IDE to add a GUI TrackBar.

April 04:

Figure 2: GUI for controlling frequency.

April 04:

Figure 3: Adding a reference to the Agilent 3320 IVI-COM driver.

April 04:

Figure 4: Solution Explorer with added references.

April 04:

Listing 1: C# code that groups member variables.

(a)
public class employee
{
    private string lastName;
    private string firstName;
    private double hourlyWage;
    public employee( string lastName, string firstName)
    {
        this.lastName = lastName;
        this.firstName = firstName;
        this.hourlyWage = 10.00;
    }
    public double wage
    {
        get { return this.hourlyWage; }
        set { // value is implicit
            if ( value > 5.00 && value < 200.00 ) {
                this.hourlyWage = value;
            }
        }
    }
}

(b)
employee john = new employee("Doe","John");
john.wage = john.wage*1.20;




April 04: 

Listing 2: Inserting into Form1.cs.

public Form1()
{
    //
    // Required for Windows Form Designer support
    //
    InitializeComponent();

    //
    // TODO: Add any constructor code after InitializeComponent call
    //
    visa32.viOpenDefaultRM(out defaultRsrcMgrSess);
    visa32.viOpen(defaultRsrcMgrSess,"functionGenerator",0,0,out funcGenSess);
 ...
}




April 04: 

Listing 3: Adding Form1 class variables.

public class Form1 : System.Windows.Forms.Form
{
    private System.Windows.Forms.TrackBar trackBar1;
    private System.Windows.Forms.Label label1;
    private System.Windows.Forms.Label label2;
    private System.Windows.Forms.Label label3;
    /// <summary>
    /// Required designer variable.
    /// </summary>
    private int defaultRsrcMgrSess; // Default Resource Manager session
    private int funcGenSess;        // functionGenerator session
    private byte [] programMessage = new byte[256];
 ...
}




April 04: 

Listing 4: Event-handler code that changes the function generator IVI-COM object.

private void trackBar1_Scroll(object sender, System.EventArgs e)
{
    int returnCount;
    // Populate the programMessage byte array from a String
    programMessage = Encoding.ASCII.GetBytes("FREQ " 
                     + trackBar1.Value.ToString() 
                     + " Hz\n");
    visa32.viWrite(VisaHandle,
       programMessage,
       programMessage.Length,
       out returnCount); 
}




April 04: 

Listing 5: Adding a Form1 class variable for the function generator IVI-COM object.

public class Form1 : System.Windows.Forms.Form
{
    /// <summary>
    /// Required designer variable.
    /// </summary>
    /// 
 ... 
    Agilent.Agilent33220.Interop.Agilent33220 fgen;
 .. 
}


        

Terms of Service | Privacy Statement | Copyright © 2024 UBM Tech, All rights reserved.