NI Measurement Hardware Driver Development Kit (MHDDK) Readme
Copyright 2011 National Instruments.
All rights reserved. For patents covering National Instruments products,
refer to ni.com/patents.
CVI, LabVIEW, Measurement Studio, National Instruments, NI,
ni.com, and NI-DAQmx are trademarks of National Instruments Corporation.
Product and company names mentioned herein are trademarks or trade names of their respective companies.
Thank you for using the NI Measurement Hardware Driver Development Kit.
This readme contains the following sections:
General Information
MHDDK KBs
Measurement Hardware Driver Development Kit (MHDDK) Frequently Asked Questions
Measurement Hardware Driver Development Kit (MHDDK) Register Level Programming Architecture
Measurement Hardware Driver Development Kit (MHDDK) Technical Support
How to Make an iBus
How to Use a Chip Object
Register Level Programming (RLP) Examples for Legacy Devices
Register Level Programming for the NI 6013 and 6014 Devices
Reference Build System
All device examples provide a reference makefile to build the examples. The makefiles list the files that need to be compiled to create an
example, which can be used as a reference to build examples in a different build environment.
To build the examples, complete the following steps:
- Set up the build environment by running any shell or batch file to add the compiler to the system path.
Also, include 'make' in your environment.
- Download the appropriate OS interface.
- Set OSINTERFACE_DIR and OSINTERFACE_MAKEFILE in the makefile to point to the OS interface .mak file.
- If using DMA, set DMA_LIBRARY_DIR in the makefile to the root directory of the DMA library files.
- Edit the examples to use the appropriate PXI resource string. Some OS interfaces build a helper utility (lsdaq) to list all available devices and resource strings.
- Run make.
Free versions of make.exe for Windows
are available on the Web.
DMA Libraries
The MHDDK provides support for data transfer via DMA on devices that support this functionality. The DMA libraries
implement interfaces for controlling the DMA channels on the device and the memory buffers on the host. In hardware, DMA
bus mastering is performed by the bus interface chip on the device: either the MITE or the CHInCh.
MITE-based Devices
E and M Series devices feature the MITE bus interface chip for their DMA controller. The following
files are included in the MITE DMA library:
- tDMAChannel.h, tDMAChannel.cpp—Provides the API to the DMA library. Programs the MITE for DMA transfers and manages the DMA buffer (read/write locations).
- tDMABuffer.h, tDMABuffer.cpp—An abstract interface used by tDMAChannel to move data in and out of the DMA buffer.
- tLinearDMABuffer.h, tLinearDMABuffer.cpp—Implements the tDMABuffer that handles data movement to/from a contiguous buffer.
- tMITE.h, tMITE.ipp, tMITE.cpp—Implements the MITE ChipObject.
CHInCh-based Devices
X Series devices feature the CHInCh bus interface chip for their DMA controller.
The following files are included in the CHInCh DMA library:
- tDMAChannel.h, tDMAChannel.cpp—Provides the API to the DMA library. Programs the CHInCh for DMA transfers and manages the DMA buffer (read/write locations).
- tDMABuffer.h, tDMABuffer.cpp—An abstract interface used by tDMAChannel to move data in and out of the DMA buffer.
- tLinearDMABuffer.h, tLinearDMABuffer.cpp—Implements the tDMABuffer that handles data movement to/from a contiguous buffer.
- tCHInChScatterGatherDMABuffer.h, tCHInChScatterGatherDMABuffer.cpp, tCHInChSGL.h, tCHInChSGL.cpp, tCHInChSGLChunkyLink.h, tCHInChSGLChunkyLink.cpp—Implements the tDMABuffer that handles data movement to/from a scatter-gather buffer.
- tCHInChDMAChannelController.h, tCHInChDMAChannelController.cpp—Implements the chip-level interface for DMA programming on the CHInCh.
- tCHInCh.h, tCHInCh.ipp, tCHInCh.cpp, tCHInChValues.h, tDMAController.h, tDMAController.ipp, tDMAController.cpp, tDMAControllerValues.h—Implements the CHInCh and DMA Controller ChipObjects.
OS Interface
osiBus.h and osiTypes.h expose the OS interface used by the device examples. Each
specific OS interface implementation provides the definitions for the following functions:
- acquireBoard()—Creates an iBus.
This function must use the VISA PXI Resource String to locate the hardware and map the device addresses to the user process.
- releaseBoard()—Destroys an iBus.
- iBus::allocDMA()—Allocates a block of memory suitable for DMA.
- iBus::freeDMA()—Frees up DMA memory.
Additionally, the OS interface implementation provides a .mak file for inclusion in the example makefiles.
This file
provides the appropriate rules for building executables for the target OS.
All OS interface implementations use the VISA PXI resource string: PXI<bus>::<device>::INSTR to identify the hardware.
NI-VISA (Win32/Win64 and Linux32/Linux64)
NI-VISA provides a convenient API to communicate with PCI/PXI device at the register
level. Install NI-VISA (available at www.ni.com) before building the examples.
NI-VISA includes win-visa.mak and linux-visa.mak for building examples on Windows and Linux. To build the
device examples, set OSINTERFACE_MAKEFILE to win-visa.mak or linux-visa.mak. Change the
Makefile variables VISA_INCLUDE_DIR and VISA_LIB_DIR to
point to the correct header and library directories for your installation of NI-VISA. Refer to nimhddk_visa.inf for further installation instructions.
Windows WDM (Windows Driver Model) (Win32/Win64)
The Windows WDM OS Interface includes a native WDM driver (nirlpk.sys)
to provide access to the hardware.
The nirlpk.sys binaries are available for the
Win32 x86 and Win64 x64 platforms.
Use the NI-VISA PXI resource string format to specify a device in acquireBoard (main.cpp). Part of the build process for this OS interface builds the lsdaq.exe utility.
lsdaq.exe lists the devices detected by nirlpk.sys and their VISA PXI resource strings. Source code for lsdaq is included in ./WindowsWDM/lsdaq.cpp.
Installing nirlpk.sys
To install nirlpk.sys, complete the following steps:
- Copy .\WindowsWDM\nirlpk\nirlp.inf to C:\Windows\inf
- Copy .\WindowsWDM\nirplk\<build_dir>\nirlp.sys to C:\Windows\system32\drivers, where <build_dir> can be:
- fre_win7_amd64—Release build for Windows 7 x86_64
- chk_win7_amd64—Debug build for Windowx 7 x86_64
- fre_win7_ia64—Release build for Windows 7 IA64
- chk_win7_ia64—Debug build for Windows 7 IA64
- fre_win7_x86—Release build for Windows 7 x86
- chk_win7_x86—Debug build for Windows 7 x86
- fre_wxp_x86—Release build for Windows XP x86
- chk_wxp_x86—Debug build for Windows XP x86
Power Management
nirlpk.sys cannot detect if an application is using the hardware and stop the system from powering down (standby, hibernate or shutdown).
The application ensures that the system does not power-down while the hardware is in use. However,
nirlpk.sys can be built to detect open
references to the driver and stop the system from powering down. See power.h for more details.
Building nirlpk.sys
To build nirlpk.sys, complete the following steps:
- The source code for nirlpk.sys is located in the following directory: ./osinterface/windows/nirlpk.
- Install the Windows Driver Development Kit.
Binaries included in this package were built using the Windows Server 2003 Service Pack 1 (SP1) Driver Development Kit (DDK).
- Open the command prompt and change the working directory to ./osinterface/windows/nirlpk.
- Setup the DDK build environment for the desired platform. The setupEnvDDK.bat batch file shows how to configure for the Windows Server 2003 SP1 DDK.
- Run build. The build.exe is the DDK build utility.
To build the device examples, set OSINTERFACE_MAKEFILE to win-wdm.mak.
Linux Native (Linux32/Linux64)
The LinuxKernel OS Interface includes a native kernel driver (nirlpk.ko) that provides access to the hardware.
The nirplk.ko driver is distributed
as source (under the BSD license) and must be built for the running kernel.
Use the NI-VISA PXI resource string format to specify a device in acquireBoard (main.cpp). This OS
interface includes a lsdaq script that lists the devices, and their VISA PXI resource strings, as detected by nirlpk.ko. This script
reads a /proc file entry, which is created when nirlpk.ko is loaded.
Building and Installing nirlpk.ko
To build and install nirlpk.ko, complete the following steps:
- Setup your kernel sources for kernel module development. To do so, refer to your distribution's documentation.
- 'cd' to ./LinuxKernel/nirlpk.
- Run make. The makefile invokes configure to create Makefile.in.
- Run make install. This operation requires root access.
- To remove, run make uninstall.
To build the device examples, set OSINTERFACE_MAKEFILE
to LinuxKernel.mak.
The install/uninstall procedure runs the nirlp startup script.
You can set up this script as part
of your boot startup scripts. The nirlp startup script loads nirlpk and
creates the appropriate /dev nodes.
Linux /dev/mem
This OS interface uses the linux /dev/mem file to access the PCI device address spaces. PCI
devices are located by searching the /proc/bus/pci/devices directory.
The device memory is mapped to user-mode using mmap, which is located in the /dev/mem directory.
This operation usually requires root access, because unrestricted access to /dev/mem can compromise system security.
DMA is not supported. DMA memory allocation requires kernel level support.
To build the device examples, set OSINTERFACE_MAKEFILE
to LinuxDevMem.mak. No kernel support
is required.
QNX Neutrino
The QNX microkernel architecture allows writing (and debugging) device drivers in user-mode. The OS interface
uses the QNX PCI API to access PCI devices and the mmap call to allocate DMA memory. Refer to Talking to hardware under QNX Neutrino
and mmap for more information.
To build the device examples, set OSINTERFACE_MAKEFILE to QNX.mak.
The examples were tested using QNX Neutrino 6.2.
Ardence RTX
The RTX OS Interface provides a native implementation to locate and access hardware. The MHDDK examples
access the hardware directly without an intermediate kernel driver.
To build the device examples, set OSINTERFACE_MAKEFILE to RTX.mak.
The bus and device number for a PCI device are obtainable from the Windows Device Manager.
The Ardence SDK provides an example (ScanBus.c) about how to dynamically read the bus and device numbers of any PCI device.
The examples were tested using Ardence RTX 6.1 (Windows XP host).
Windows CE
The Windows CE OS Interface is included in the CF-6004 examples.
Embedded Visual C++ 4.0 is included in the examples for building the driver and examples.
Windows CE is the only OS Interface that works with PCMCIA devices. nirlpdriver
is a Windows CE driver designed to detect
the PCMCIA devices and provide access to the hardware. On some architectures, such as the x86, PCMCIA address spaces are not memory
mapped and register IO is possible only in the kernel. The nirlpdriver provides functions to proxy register IO requests from user-mode.
On platforms where all IO is memory mapped (ARM), the PCMCIA address space is mapped to user-mode. Memory-mapped IO is significantly
faster than register IO through the nirlpdriver.
Device Examples
NI X Series
- Device
- boardBringup.cpp—Self test an X Series device
- Analog Input
- aiex1.cpp—Single-point on-demand analog input
- aiex2.cpp—Retriggerable finite hardware-timed analog input
- aiex3.cpp—Continuous hardware-timed analog input with optimized DMA
- aiex4.cpp—Finite hardware-timed analog input with reference trigger
- aiex5.cpp—Continuous single-wire hardware-timed analog input with optimized DMA
- aiex6.cpp—Multi-device continuous hardware-timed analog input with optimized DMA
- Analog Output
- aoex1.cpp—Single-point on-demand analog output
- aoex2.cpp—Single-point on-demand analog output with simultaneous updates
- aoex3.cpp—Retriggerable finite hardware-timed analog output
- aoex4.cpp—Finite hardware-timed analog output with external reference clock
- aoex5.cpp—Continuous hardware-timed analog output with regeneration
- aoex6.cpp—Continuous hardware-timed analog output with optimized DMA
- Digital Input
- dioex1.cpp—Single-point on-demand digital input and output on ports 0, 1, and 2
- dioex2.cpp—Finite hardware-timed digital input
- dioex3.cpp—Finite hardware-timed digital input with change detection
- dioex4.cpp—Continuous hardware-timed digital input with optimized DMA
- Digital Output
- dioex5.cpp—Continuous hardware-timed digital output with regeneration
- dioex6.cpp—Continuous hardware-timed digital output with the Watchdog Timer
- Counter Input
- gpctex1.cpp—Single-point on-demand pulse train measurement
- gpctex2.cpp—Single-point edge counting with start trigger and hardware controlled direction
- gpctex3.cpp—Averaged continuous hardware-timed frequency input with optimized DMA
- gpctex4.cpp—Finite hardware-timed position measurement with digital filtering
- gpctex5.cpp—Continuous hardware-timed edge counting with optimized DMA
- Counter Output
- gpctex6.cpp—Pulse train output using the frequency generator
- gpctex7.cpp—Single-point implicitly-timed pulse train output
- gpctex8.cpp—Finite implicitly-timed pulse train output with start trigger
- gpctex9.cpp—Continuous hardware-timed pulse train output with regeneration
Known Issues
- Software timing too slow for Linux
- Some DDK examples may execute too slowly on Linux. For example, analog input aiex4 counts down
while it waits for a reference trigger. While it counts down, it prints to the console
how much longer it will wait. When run on Linux, the count down is about 10 times slower than
wall time. The reason for this behavior is an implementation difference between the platforms:
on Windows, clock() measures the absolute number of processor ticks since the program
began, but on Linux clock() measures the number of processor ticks consumed by the program.
- Workaround: use time(), which measures the number of seconds after a fixed
date on both platforms. This has the side-effect of losing sub-second precision for software-timing.
For Linux only, retain sub-second precision by using the GNU-extended time.h function
clock_gettime() with the monotonic clock.
- DMA allocation fails for small, finite scatter-gather buffers
- The implementation of the CHInCh DMA classes for scatter-gather buffers requires a minimum
buffer size of 16 bytes, and requesting a smaller buffer will fail.
- Workaround: increase the number of samples per channel (sampsPerChan); or use a
linear DMA buffer topology.
- aiex2 misses short fast scans
- Analog input example aiex2 may not detect the arrival of the start trigger. Because it
polls the status bit InTimer::Status_2_Register::SC_Q_St, aiex2 may not detect a
scan counter state change from idle to counting if the operation is short. For example, if the
acquisition is configured to measure 2 samples on 1 channel at the maximum rate, the first
trigger will correctly start the measurement but the second will cause an SC TC error and the
example will exit early with this error.
- gpctex2 cannot detect a missing source signal
- After the hardware-arm trigger arrives, counter example gpctex2 will incorrectly report
a trigger timeout if there is not a signal present on the counter's source terminal.
- Counter output examples do not check for minimum pulse length
- Counter examples gpctex7..9 do not enforce the DAQ-STC3 requirement that pulse specifications
must be at least 2 timebase ticks long. If the time specifications for a pulse and the timebase rate
for the counter cause the scaler to calculate a pulse length of 1 tick, the counter will count
0xFFFFFFFF + 1 ticks instead.
- gpctex9 does not generate a pulse train after a DMA error
- Counter example gpctex9 will not generate a pulse train if a DMA error happened previously.
In addition, gpctex9 will not detect or report an error. This behavior was observed on Linux
(with the LinuxKernel OS Interface component), but not on Windows (with the NI-VISA OS Interface component).
- Workaround: run gpctex8 to restore gpctex9.
NI M Series
- aiex1.cpp—On-demand acquisition (single/multichannel)
- aiex2.cpp—Hardware-timed acquisition (single/multichannel)
- aiex3.cpp—Hardware-timed acquisition with DMA
- aoex1.cpp—Single channel on-demand generation
- aoex2.cpp—Multichannel on-demand generation
- aoex3.cpp—Single channel hardware-timed generation
- aoex4.cpp—Multichannel hardware-timed generation
- dioex1.cpp—Digital read/write on port 0
- dioex2.cpp—Digital read/write on port 1 and 2 (PFI lines)
- gpctex1.cpp—Simple event counting
- gpctex2.cpp—Simple event counting, hardware controlled up/down
- gpctex3.cpp—Period measurement
NI E Series
- aiex1.cpp—On-demand acquisition (single channel)
- aiex2.cpp—Hardware-timed acquisition (multi-channel)
- aiex3.cpp—Hardware-timed acquisition with simulated interrupts (multi-channel)
- aiex5.cpp—Start-triggered acquisition
- aiex6.cpp—Start/stop-triggered acquisition
- aiex7.cpp—External convert clock acquisition
- aiex8.cpp—One channel acquisition using AMUX-64T
- aiex9.cpp—Eight channels acquisition using AMUX-64T
- aoex1.cpp—Single channel on-demand generation
- aoex2.cpp—Generates a ramp waveform using polled writes to the data FIFO.
- aoex3.cpp—Generates a ramp waveform using local buffer mode
- aoex4.cpp—Generates a ramp waveform using local buffer mode with external UPDATE and external trigger
- aoex5.cpp—Generates a ramp waveform using simulated interrupts to write data to the data FIFO
- digex1.cpp—Configures all the digital lines as outputs and writes out 0 through 0xFF consecutively
- digex2.cpp—Read/write, configures the even digital lines as outputs and the odd digital lines as inputs
- gpct1.cpp—Simple gated event counting
- gpct2.cpp—Buffered pulsewidth measurement
- gpct3.cpp—Continuous pulse train generation
NI 6143
- aiex1.cpp—Hardware-timed acquisition
- aiex2.cpp—On-demand acquisition
- dioex1.cpp—Digital output
- dioex2.cpp—Digital input
- gpctex1.cpp—Simple-event counting
- gpctex2.cpp—Continuous pulse train generation
NI 6110/6111
- aiex1.cpp—On-demand acquisition (single channel)
- aiex2.cpp—Hardware timed acquisition (multi-channel)
- aiex3.cpp—Hardware timed acquisition with simulated interrupts (multi-channel)
- aiex4.cpp—Start-triggered acquisition
- aiex5.cpp—Start/stop-triggered acquisition
- aiex6.cpp—External sample clock acquisition
- aoex1.cpp—Single channel on-demand generation
- aoex2.cpp—Generates a ramp waveform using polled writes to the data FIFO
- aoex3.cpp—Generates a ramp waveform using local buffer mode
- aoex4.cpp—Generates a ramp waveform using local buffer mode with external UPDATE and external trigger
- aoex5.cpp—Generates a ramp waveform using simulated interrupts to write data to the data FIFO
- aoex6.cpp—Writes single point to channel 0 and 1 using Later mode
- digex1.cpp—Configures all the digital lines as outputs and writes out 0 through 0xFF consecutively
- digex2.cpp—Read/write, configures the even digital lines as outputs and the odd digital lines as inputs
- gpct1.cpp—Simple gated event counting
- gpct2.cpp—Buffered pulsewidth measurement
- gpct3.cpp—Continuous pulse train generation
NI 671x/673x - Waveform AO
- aoex1.cpp—Single channel on-demand generation
- aoex2.cpp—Generates a ramp waveform using polled writes to the data FIFO
- aoex3.cpp—Generates a ramp waveform using local buffer mode
- aoex4.cpp—Generates a ramp waveform using local buffer mode with external UPDATE and external trigger
- aoex5.cpp—Generates a ramp waveform using simulated interrupts to write data to the data FIFO
- aoex6.cpp—Writes single point to channel 0 and 1 using Later mode
- digex1.cpp—Configures all the digital lines as outputs and writes out 0 through 0xFF consecutively
- digex2.cpp—Read/write, configures the even digital lines as outputs and the odd digital lines as input
- gpct1.cpp—Simple gated event counting
- gpct2.cpp—Buffered pulsewidth measurement
- gpct3.cpp—Continuous pulse train generation
NI 670x - Static AO
- aoex1.cpp—Voltage output
- aoex2.cpp—Current output
- digex1.cpp—Configures all the digital lines as outputs and writes out 0 through 0xFF consecutively
NI Static DIO
- digex1.cpp—Configures all the digital lines as outputs and writes out 0 through 0xFF consecutively
- digex2.cpp—Configures Port 0 as an output port and toggles Line0 as high and then low
- digex3.cpp—Configures Port 0 as an input port
- digex4.cpp—Configures Port 0 as an input port and reads the value of TTL voltage connected to Line0
- digex5.cpp—Configures Port 0, Port 1, and Port 3 as output ports and writes out 0 through 0xFF consecutively to all of these ports
NI PCI-DIO-96
- digex1.cpp—Configures Port A of PPIA as output port and writes out 0 through 0xFF consecutively to this port
- digex2.cpp—Configures Port A of PPIA as output port and toggles Line0 as high and then low
- digex3.cpp—Configures Port A of PPIA as input port
- digex4.cpp—Configures Port A of PPIA as input port and reads the value of TTL voltage connected to Line0
- digex5.cpp—Configures Port A of PPIA, Port A of PPIB and Port A of PPIC as output ports and writes out 0 through 0xFF consecutively to all of these ports
NI CF-6004
- aiex1.cpp—Hardware timed finite AI acquisition
- dioex1.cpp—Digital read/write
NI 660x
- gpct_ex1.cpp—Verifies communication with the 660x hardware
- gpct_ex2.cpp—Simple event counting
- gpct_ex3.cpp—Continuous pulses generation
- gpct_ex4.cpp—Single pulse generation
- gpct_ex5.cpp—Pulsewidth measurement
- gpct_ex6.cpp—Period measurement
- gpct_ex7.cpp—Quadrature encoding
- gpct_ex8.cpp—Buffered event counting
- gpct_ex9.cpp—Continuous pulses generation using both TIOs
NI 7831R
- rio_ex1.cpp—Simple communication (read and write of scalars)
- rio_ex2.cpp—Simple Communication (read and write of arrays)