Overview
See how to read data from a C Series I/O Module that can been deployed to NI CompactDAQ or NI CompactRIO - with a single LabVIEW application.
Table of Contents
Introduction
Engineers and scientists often work on several projects, each with different software and hardware requirements. For the hardware, one test setup might need to read hundreds of analog signal values, where another might require digital I/O to monitor switches and control relays. For the software, one project could require power spectrum analysis and a user interface (UI) with several graphs. Another could require control logic to determine alarm states and a UI with pushbutton switches and LED displays.
The deployment of the test or control setup is also important. One might integrate with applications on a desktop computer, where another could require the ruggedness and stand-alone capability of an embedded system. These varying needs necessitate flexible, modular hardware that can be reused from project to project and software that can control the hardware and provide analysis, file I/O and UI functionality.
NI C series modules are self-contained measurement modules that provide connectivity to a wide variety of signal ypes, including analog input and output and digital I/O. You can mix and match these modules and use them across USB, Ethernet, and wireless systems.
Using LabVIEW, you can write code to access C Series modules in a USB Single Module Carrier, CompactDAQ or in an embedded CompactRIO system. Once the data has been acquired, you can take advantage of the rich library of analysis, file I/O and UI functionality included with LabVIEW. This document demonstrates LabVIEW code for acquiring and analyzing data from a C series module deployed in any platform.
Programming Overview
Driver
When C series modules are in a USB configuration, you use DAQmx, the Windows driver software, to interact with them. When in an embedded CompactRIO system, you use NI-RIO, the FPGA driver.
By developing modular LabVIEW code, you can separate the C series I/O from other parts of your application, such as data analysis and file I/O. This modularity allows you to change deployment platforms with little to no code modification.
Hardware Setup
This example uses the NI 9219 Universal Input Module deployed in three configurations – the USB-based CompactDAQ (USB-9162) and single module carrier (USB-9219), and the Ethernet-based CompactRIO (cRIO-9074). We have a J type thermocouple wired into channel 0 of the NI 9219, monitoring the temperature of the room.
Software Design Best Practices
1. Modularity and I/O Abstraction
2. State Machine Design Pattern
3. Producer/Consumer Loops
1.) This example program uses a modular architecture for acquiring thermocouple readings from the NI 9219 Universal Input Module. The program’s analysis, file I/O and UI all work independently of the code to read the temperature. The code to initialize, read, and clear the DAQmx or FPGA references are placed into subVIs, and user selection and case structures determine which of these subVIs execute. This abstraction lets the “Read DAQmx” or “Read FPGA” subVI return an array of scaled data from the thermocouple which can then be used in the file I/O and analysis sections of the VI without worrying about how it was obtained.
2.) This program makes use of state machines, which are a powerful design pattern for developing flexible, scalable applications in LabVIEW. In this example program, we use a state machine to separate execution into three states: Initialize, Read, and Stop. With this architecture, we can easily repeat sections of code and make arbitrary jumps from state to state. In each of these three states, we have conditional logic for branching into either the NI-DAQmx driver or NI-RIO.
3.) This program also uses a Producer/Consumer architecture to separate the acquisition loop from the file I/O and analysis loop. By using two parallel while loops and a queue to pass data between them, we can ensure that our acquisition loop reads data from the module frequently enough and isn’t slowed down by the slower tasks of writing values to disk and analyzing the data.
Code Walkthrough
Download the example VI and project used in this tutorial here:
Example VI for C Series I/O Modules in Any Deployment.
Host VI - Front Panel
Here, the user can select whether to target DAQmx or FPGA on the Front Panel in an enum control. DAQmx would be used when the NI 9219 is deployed in a USB Single Module carrier or in CompactDAQ. FPGA would be used when the NI 9219 is inside a CompactRIO chassis.
Next, there is a Boolean control to select whether or not to write the acquired data to a text file, as well as what to name the file. The data from the thermocouple is displayed on a Waveform chart and a thermometer. When the user wishes to stop the VI there is a prominently positioned Boolean named Stop.
Host VI - Block Diagram
As shown below, the Acquisition Loop acquires data from the NI 9219 module and is a state machine. A state machine is a while loop with a case structure and a shift register to keep track of the state. During each iteration, the code in one case structure will execute.
In this application, the three states are Initialize, Read and Stop. Based on whether the user has selected DAQmx or FPGA on the Front Panel enum, different code will execute in each of these three states. For example, in the screenshot above, you can see the Read state with FPGA selected.
Let’s first examine the situation where the user has selected DAQmx.
In the Initialize state, there is a subVI called Initialize DAQ.vi, shown below.

Initialize DAQ.vi contains another subVI called Find 9219.vi. This subVI uses the DAQmx API to scan through the recognized DAQ devices in the system. If multiple 9219 modules are found, Find 9219.vi displays a dialog where the user can select the desired module. If only one is found, a reference to that device is returned. If there are no 9219 modules found in the system, Find 9219.vi will return a custom error, -8000, “9219 Not Found in System” and will cause the main VI to stop.
We then use DAQmx Create Channel.vi to create a task on channel 0 of this module, with type J thermocouple scaling. We then set the sampling rate to 50 Hz, continuous samples, with DAQmx Timing.vi, and use a DAQmx Channel Property node to set the timing mode to be high speed. We do this because the 9219 has various modes of operation. Finally, we call DAQmx Start Task.vi to begin acquiring data.
In the Read state, we have a subVI to perform a DAQmx Read of 10, which is then passed into an queue for use in our Analysis and File I/O loop. If the user has selected to stop or if we receive an error from Read DAQ.vi (such as if the user disconnects the module during the read), then we proceed to the Stop state. Otherwise, we continue in the read state.

This application is intended to either run on a Windows PC or in the Real-Time controller of the CompactRIO. Because the CompactRIO does not use the DAQmx driver, we need to make sure in the code that we do not download the DAQmx VIs to the controller. To accomplish this, in the Initialize and Read states, we have a Conditional Disable structure. This structure lets us disable sections of code dependent on deployment conditions. In this instance, we have used the TARGET_TYPE parameter. If this is Windows, we can execute our Initialize DAQ.vi as usual. However, if this parameter is equal to RT, we can notify the user that DAQmx is not supported on CompactRIO and stop the VI. These two screenshots are from the Initialize state:


In the Stop state, we call a DAQmx Clear Task.vi in order to stop the acquisition and clear up resources.
Next, let’s examine the situation where the user has selected FPGA on the front panel.
In the Initialize State, we use an Open FPGA VI Reference to download the Read_Thermocouple.vi FPGA VI to the CompactRIO and run it. You will need to change the input to this node so that it has the correct VISA Resource and IP address for your CompactRIO. If there was an error, we go to the stop state; else, we proceed to the Read state.

In the Read State, we call Read FPGA.vi, shown below. This VI uses the NI-RIO driver to interface with the FPGA. Specifically, we call an invoke method to Read 20 elements from FIFO, which is a DMA FIFO data structure used to pass elements from the FPGA VI back to the host. As we’ll see in the FPGA VI, the samples and CJC values are entered into this data structure, and since it is a FIFO (First-in, First-out) data structure, we know that the order we read from it is the same as the order in which elements were written to it. We then use the subVI, NI 9219 Convert Binary to Thermocouple.VI, to apply the CJC (Cold Junction Compensation) value to the channel values and convert them to a double data type.

Back in the main VI, this converted array is then fed into a queue for use in the Analysis and File I/O loop. If the user has pressed the Stop button on the front panel or if we receive an error from Read FPGA.vi, we proceed to the Stop state. Otherwise, we continue in the Read state.
In the Stop State, we close the reference and reset the FPGA with Close FPGA VI Reference.
The bottom loop is the Analysis and File I/O loop, which is a consumer of the data produced and enqueued in the Acquisition Loop. It displays the data on a Waveform Chart and displays averaged data on a Thermometer indicator. If the user has selected on the front panel to write the data to file, this loop uses the Write data to file.vi subVI to write the samples and time stamps to a spreadsheet file. This spreadsheet file is stored in the same directory as the VI or executable. This loop will stop when the Acquisition loop calls Release Queue in the Stop state, which causes the Dequeue Element to return an error.
FPGA VI - Block Diagram
Our simple FPGA VI, Read_Thermocouple.vi, is responsible for taking samples of channel 0 of the 9219 and the Cold Junction Compensation value, then passing these into a DMA FIFO. These values are read out in the Host VI in the Read FPGA.vi subVI. We use a flat sequence structure and a loop timer to ensure that we are sampling at 50 Hz.

Future Extensions
This program provides a basic architecture for acquiring temperature data from a NI 9219 module, analyzing it, and writing that data to disk. Since the data acquisition code has been modularized, you could easily expand the functionality to include:
1. Additional C series modules, such as digital I/O or analog output
2. Additional analysis functions for the data, such as a power spectrum
3. Different formats for the file I/O, such as binary, for faster access and smaller size
4. Shared variables for communication back to a host PC or Touch Panel Computer
5. Web Server for displaying the data on the front panel to remote users in a web browser
Conclusion
C series modules can easily be reused in different applications, either on the same platform or on a different platform. By using LabVIEW and good programming practices such as modularization, the state-machine design pattern, and producer/consumer loops, we have shown an architecture that can work with C series modules in all possible deployment configurations with minimal code changes.
Download the example VI and project used in this tutorial here:
Example VI for C Series I/O Modules in Any Deployment.
Related Links
Deploying from USB to Embedded with C Series Hardware
Build Your Own NI CompactDAQ System
Build Your Own CompactRIO System
Reader Comments | Submit a comment »
Legal
This tutorial (this "tutorial") was developed by National Instruments ("NI"). Although technical support of this tutorial may be made available by National Instruments, the content in this tutorial may not be completely tested and verified, and NI does not guarantee its quality in any way or that NI will continue to support this content with each new revision of related products and drivers. THIS TUTORIAL IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND AND SUBJECT TO CERTAIN RESTRICTIONS AS MORE SPECIFICALLY SET FORTH IN NI.COM'S TERMS OF USE (http://ni.com/legal/termsofuse/unitedstates/us/).
