Transition from Traditional NI-DAQ (Legacy) to NI-DAQmx using ANSI C and LabWindows/CVI: Part Three

Panoramica

This is the third document in the four-part series, “Transitioning from Traditional NI-DAQ (Legacy) to NI-DAQmx Using ANSI C and NI LabWindows/CVI.” Refer to the “Links to Other Topics in This Series” section for the full series.

In previous installments of this series, you configured the data acquisition operation. This document explains how to differentiate the run-time behaviors of the drivers by looking at how to start and stop the operations and how to read and write code. The type of task initially configured determines how the I/O is performed.

Contents

Accessing Help in NI-DAQmx

This series references two manuals that are shipped with NI-DAQmx.

  • NI-DAQmx Core Help – references the language-agnostic manual for NI-DAQmx. The NI-DAQmx Core Help explains NI-DAQmx concepts and provides background about the various features and capabilities of the driver. Refer to Start » Programs » National Instruments » NI-DAQ » NI-DAQmx Help to access this manual.
  • NI-DAQmx C Reference Help – references the manual for the DAQmx ANSI C API. This reference is specific to the C API and provides documentation for NI-DAQmx function calls. There are also concept topics specific to using the DAQmx ANSI C API. Part of the NI-DAQmx driver software, this manual is located at Start » All Programs » National Instruments » NI-DAQ » NI-DAQmx C Reference Help.

Run

After configuring the task, you need to start the task to actually begin the operation. You start the configured task by using the DAQ_Start() function.

  DAQ_Start(iDevice, iChan, iGain, piBuffer, ulCount, iSampTB, uSampInt);

With NI-DAQmx, you do not need to start the task explicitly. Calling the Read and Write methods automatically starts the task. You can also call start the task explicitly:

 DAQmxStartTask(taskHandle);

For information about the various types of timing supported by NI-DAQmx, refer to the “Task State Model” section in the NI-DAQmx Core Help.

Refer to the NI-DAQmx C Help “Task State Model” topic for more information about the task state model in the DAQmx ANSI C API.

Read

Traditional NI-DAQ (Legacy)

Traditional NI-DAQ (Legacy) has many functions to perform reads:

A simple low-level read:

 AI_VRead_Scan (1, piBuffer);

Or,

 DAQ_Check(iDevice, &iDAQstopped, &ulRetrieved); 

Or, an all inclusive function that sets up the acquisition and performs the read in one call:

 DAQ_Op(iDevice, iChan, iGain, piBuffer, ulCount, dSampRate);

NI-DAQmx

NI-DAQmx uses one read function, regardless of the measurement type:

 // Read 1000 points into array ‘data’ of length 1000

DAQmxReadAnalogF64 (taskHandle, 1000, 10.0, DAQmx_Val_GroupByChannel, data, 1000, &numRead, NULL));

   

Write

Traditional NI- DAQ (Legacy)

DAQmx ANSI C API calls, such as the AO_VWrite() and WFM_OP() (for analog output), DIG_Out_Line and DIG_Out_Prt (for digital output) generate output signals using the Traditional NI-DAQ (Legacy) driver. You use the Write function calls to write the data to the internal driver buffer before the data can be generated at the output channels.

 // Scale data from voltage to binary data array
WFM_Scale(iDevice, iChan, ulCount, 1.0, pdBuffer, piBuffer);

// Generate binary data

WFM_Op(iDevice, iNumChans, piChanVect, piBuffer, ulCount, ulIterations, dUpdateRate);

You can use the same calls for generating data on multiple channels by passing a multidimensional array and specifying more channels.

NI-DAQmx

To write out data using the NI-DAQmx ANSI C API, use the DAQmx calls.

  • DAQmxWriteAnalogF64
  • DAQmxWriteAnalogScalarF64
  • DAQmxWriteBinaryI16
  • DAQmxWriteBinaryU16
  • DAQmxWriteCtrFreq
  • DAQmxWriteCtrFreqScalar
  • DAQmxWriteCtrTicks
  • DAQmxWriteCtrTicksScalar
  • DAQmxWriteCtrTime
  • DAQmxWriteCtrTimeScalar
  • DAQmxWriteDigitalLines
  • DAQmxWriteDigitalScalarU32
  • DAQmxWriteDigitalU16
  • DAQmxWriteDigitalU32
  • DAQmxWriteDigitalU8
  • DAQmxWriteRaw


The following example shows how to write an array to memory and start generation.  

  // Create and configure the task, channel, and sample rate

DAQmxCreateTask("",&gTaskHandle); 

DAQmxCreateAOVoltageChan(gTaskHandle,chan,"",min,max,DAQmx_Val_Volts,NULL); (DAQmxCfgSampClkTiming(gTaskHandle,"",rate,DAQmx_Val_Rising,DAQmx_Val_ContSamps,1000);

// Write ‘data’ to buffer

DAQmxWriteAnalogF64(gTaskHandle,sampsPerCycle,0,10.0,DAQmx_Val_GroupByChannel,data,&written,NULL);

// Start generation

DAQmxStartTask(gTaskHandle);

// Wait 10 seconds until generation is complete

DAQmxWaitUntilTaskDone (gTaskHandle, 10.0);

// Stop and Clear Task

DAQmxStopTask(gTaskHandle);

DAQmxClearTask(gTaskHandle);

 

Events

Traditional NI-DAQ (Legacy)

Traditional NI-DAQ (Legacy) event message functions are an efficient way to monitor your background data acquisition processes without dedicating your foreground process for status checking. The Event Message dispatcher notifies your application when a user-specified data acquisition event occurs. Using event messaging eliminates continuous polling of data acquisition processes.

NI-DAQmx events serve a different purpose. The next section explains the events NI-DAQmx supports.

NI-DAQmx

NI-DAQmx software events provide an asynchronous notification mechanism for a set of data acquisition events. Unlike hardware events, software events do not require you to use a thread to wait until data is available. Using event-based programming, you write an application that continues to perform work while waiting for data without resorting to developing a multithreaded application.

NI-DAQmx includes the following software events:

  • Every N Samples Acquired Into Buffer Event – Occurs when the user-defined number of samples is written from the device to the PC buffer. This event works only with devices that support buffered tasks.
  • Every N Samples Transferred From Buffer Event – Occurs when the user-defined number of samples is written from the PC buffer to the device. This event works only with devices that support buffered tasks.
  • Done Event – Occurs when the task completes execution or when an error causes the task to finish. Recoverable errors that do not cause the task to finish do not cause this event to fire. Calling the Stop Task function/VI to complete execution similarly does not cause this event to fire.
  • Signal Event – Occurs when the specified hardware signal occurs. Supported signals include the counter output event, change detection event, sample complete event, and the sample clock.

For more information about events NI-DAQmx supports, refer to the NI-DAQmx Core Help topic by selecting Contents >> Key NI-DAQmx Concepts >> Timing and Triggering.  

Resource Cleanup

Traditional NI-DAQ (Legacy)

You use the DAQ_Clear() function to release any resources that were reserved during the configuration. In most cases, you do not need to call DAQ_Clear() explicitly.

NI-DAQmx

You use the Stop and Clear Task functions to deterministically release any NI-DAQmx resources that are reserved when the task is configured.

 // Stop and Clear Task

DAQmxStopTask(gTaskHandle);

DAQmxClearTask(gTaskHandle);

 

Error Handling

Traditional NI-DAQ (Legacy)

Each Traditional NI-DAQ (Legacy) function returns a status code that indicates whether the function performed successfully. When a Traditional NI-DAQ (Legacy) function returns a code that is a negative number, it means that the function did not execute. When a positive status code is returned, it means that the function did execute but with a potentially serious side effect.

For example:

 // Acquire binary data

iStatus = DAQ_Op(iDevice, iChan, iGain, piBuffer, ulCount, dSampRate);

// Check for error

iRetVal = NIDAQErrorHandler(iStatus, "DAQ_Op", iIgnoreWarning);

// Scale to voltage

iStatus = DAQ_VScale(iDevice, iChan, iGain, dGainAdjust, dOffset, ulCount, piBuffer, pdVoltBuffer);< /SPAN >

// Check for error

iRetVal = NIDAQErrorHandler(iStatus, "DAQ_VScale", iIgnoreWarning);

NI-DAQmx

Each NI-DAQmx C function returns a status code. A common method of dealing with NI-DAQmx errors is to define a function to check the status of each call. This is an example of a finite voltage acquisition with error handling:

 // Define error checking function

#define DAQmxErrChk(functionCall) if( DAQmxFailed(error= (functionCall)) ) goto Error; else

  TaskHandle  taskHandle= 0;

  DAQmxErrChk (DAQmxCreateTask("",&taskHandle));  // Create Task

  // Create voltage input channel

  DAQmxErrChk(DAQmxCreateAIVoltageChan(taskHandle,"Dev1/ai0","",DAQmx_Val_Cfg_Default,-  10,10,DAQmx_Val_Volts,NULL));                  

  // Configure sample clock timing

  DAQmxErrChk(DAQmxCfgSampClkTiming  (taskHandle,"",1000,DAQmx_Val_Rising,DAQmx_Val_FiniteSamps,1000));

  DAQmxErrChk (DAQmxStartTask(taskHandle));     // Start Task

  // Read data

  DAQmxErrChk (DAQmxReadAnalogF64  (taskHandle,1000,10.0,DAQmx_Val_GroupByChannel,data,1000,&numRead,NULL));

  Error:

  if( DAQmxFailed(error) )

             DAQmxGetExtendedErrorInfo(errBuff,2048);

  if( taskHandle!= 0 ) {         

             // DAQmx Stop Code

             DAQmxStopTask(taskHandle);

             DAQmxClearTask(taskHandle);

  }

  if( data )

             free(data);

  if( DAQmxFailed(error) )

             MessagePopup("DAQmx Error",errBuff);