Archived: Overview of Traditional NI-DAQ (Legacy) Events and Occurrences

NI does not actively maintain this document.

This content provides support for older products and technology, so you may notice outdated links or obsolete information about operating systems or other relevant products.

Panoramica



Note: This page is about NI-DAQ also known as Traditional NI-DAQ (Legacy). NI-DAQmx replaced Traditional NI-DAQ (Legacy) in 2003. NI strongly recommends using NI-DAQmx for new designs and migrating existing Traditional NI-DAQ (Legacy) applications to NI-DAQmx. Please review the Getting Started with NI-DAQmx guide for more information on migration.

DAQ Events (known as occurrences in LabVIEW) provide a method for DAQ hardware to signal the controlling software when certain conditions have been met. Examples of such conditions are: number of points acquired, signal within voltage range, digital pattern matched, etc. Not all types of hardware support all DAQ Events. For more information about your specific hardware, check "Config_DAQ_Event_Message" in the related link, Traditional NI-DAQ (Legacy) Function Reference Manual.

There are different methods of implementing DAQ events depending on your programming platform. In LabVIEW, DAQ Events are implemented by means of an occurrence. For VisualBasic (and Microsoft Visual C++) an ActiveX control that fires an event is provided. Finally, in C, two methods are implemented: the callback function and the Windows message.

Contents

DAQ Occurrences in LabVIEW

The DAQ occurrence is an specific type of the general LabVIEW occurrence. There are a few specific differences, however. Rather than using Generate Occurrence to generate an occurrence refnum, use Configure DAQ Occurrence. Also, there is not a "Set Occurrence" function, as the device setting the occurrence is now the hardware, rather than a separate software routine.

Occurrence Function Descriptions
You can use the occurrence functions to control separate, synchronous activities. In particular, you use these functions when you want one VI or part of a block diagram to wait until another VI or part of a block diagram finishes a task without forcing LabVIEW to poll.

You can perform the same task using global variables, with one loop polling the value of the global until its value changes. However, global variables add overhead, because the loop that pulls the global variable uses execution time. With occurrences, the polling loop is replaced with a Wait on Occurrence function and does not use processor time. When some diagram sets the occurrence, LabVIEW activates all Wait on Occurrence functions in any block diagrams that are waiting for the specified occurrence.

The following illustration displays the options available on the Occurrences subpalette.



Generate Occurrence Creates an occurrence that you can pass to the Wait on Occurrence and Set Occurrence functions.


Ordinarily, only one Generate Occurrence node is connected to any set of Wait on Occurrence and Set Occurrence functions. You can connect a Generate Occurrence function to any number of Wait on Occurrence and Set Occurrence functions. You do not have to have the same number of Wait on Occurrence and Set Occurrence functions.

Unlike other synchronization VIs, each Generate Occurrence function on a block diagram represents a single, unique occurrence. In this way, you can think of the Generate Occurrence function as a constant. When a VI is running, every time a Generate Occurrence function executes, the node produces the same value. For example, if you place a Generate Occurrence function inside of a loop, the value produced by Generate Occurrence is the same for every iteration of the loop. If you place a Generate Occurrence function on the block diagram of a reentrant VI, Generate Occurrence produces a different value for each caller.

Set Occurrence Triggers the specified occurrence . All block diagrams that are waiting for this occurrence stop waiting.



Wait On Occurrence Waits for the Set Occurrence function to set or trigger the given occurrence .

General DAQ Event in VisualBasic

You use the General DAQ Event control to configure and enable a single data acquisition event. See the Event Message Functions section earlier in this chapter for a complete description of Traditional NI-DAQ (Legacy) events. Table 3-1 lists the properties for the General DAQ Event control.

Note: An n represents a generic number and is not the same value in every occurrence.

Table 3-1. General DAQ Event Control Properties
Property
Allowed Property Values
Name GeneralDAQEventn (default)
Board 1–n (default)
ChanStr See Config_DAQ_Event_Message in the NI-DAQ Function Reference Manual for PC Compatibles
DAQEvent 0—Acquired or generated n scans
1—Every n scans
2—Completed operation or stopped by error
3—Voltage out of bounds
4—Voltage within bounds
5—Analog positive slope triggering
6—Analog negative slope triggering
7—Digital pattern not matched
8—Digital pattern matched
9—Counter pulse event
DAQTrigVal0 Long
DAQTrigVal1 Long
TrigSkipCount Long
PreTrigScans Long
PostTrigScans Long
Index N/A
Tag N/A
Enabled 0—False (default)
1—True


Some General Traditional NI-DAQ (Legacy) Events can be implemented only by a select group of National Instruments DAQ devices. Also, some General Traditional NI-DAQ (Legacy) Events require that you set the asynchronous data acquisition or generation operation to use interrupts. See the related link for more information on the different types of General Traditional NI-DAQ (Legacy) Events, and refer to the description for the Config_DAQ_Event_Message function in Chapter 2, Function Reference, of the NI-DAQ Function Reference Manual for PC Compatibles.

Set each of these properties as follows:
GeneralDAQEvent n. property name = property value

For example, to set the ChanStr property to Analog Input channel 0 for GeneralDAQEvent 1:
GeneralDAQEvent1.ChanStr = "AI0"

Set up your program flow like this:

  1. Set the properties of the General DAQ Event control. Then, configure the acquisition or generation operations using the appropriate Traditional NI-DAQ (Legacy) functions.
  2. Set the Enabled property of the General DAQ Event control to 1 (True).
  3. Invoke the GeneralDAQEvent n.Refresh method to set the DAQ Event in the Traditional NI-DAQ (Legacy) driver. Each subsequent use of GeneralDAQEvent n.Refresh deletes the old DAQ Event and sets a new one with the current set of properties.
  4. Start an asynchronous data acquisition or generation operation.
  5. When the selected event occurs, the GeneralDAQEvent n_Fire procedure is called. You can perform the necessary event processing within this procedure, such as updating a global count variable, or toggling digital I/O lines.


The GeneralDAQEvent n_Fire procedure is prototyped as follows:
Sub GeneralDAQEvent n_Fire (DoneFlag As Integer, Scans As Long)

The parameter DoneFlag equals 1 if the acquisition was over when the DAQ Event fired. Otherwise, it is 0. Scan equals the number of the scan that caused the DAQ Event to fire.

Using a Callback function with DAQ Events in C


A callback function is a function that can be called by a section of code external to where the callback actually resides. Traditional NI-DAQ (Legacy) can be configured to call a callback function in response to a DAQ Event. To configure Traditional NI-DAQ (Legacy) to respond to an event by calling a callback, call Config_DAQ_Event_Message with a valid callbackAddr parameter. In order to do this, a function of the following prototype must be declared within the code:

void myCallback(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam)


The address of this function should be passed as parameter 12 of Config_DAQ_Event_Message. It is often necessary to explicitly cast this address as a u32 (unsigned long on most systems) to avoid a data type conflict compilation error:

Config_DAQ_Event_Message(1, 1, ... 0, 0, (unsigned long)(&myCallback))


The hwnd and message parameters received by the callback function are identical to the values passed to Config_DAQ_Event_Message. The least significant byte of wparam is the device number, the second least significant byte is a flag indicating whether the operation is complete. lparam contains the scan number at which the DAQ Event occurred. This information can be used inside of the callback function to make decisions on how to handle the event.

Using Windows Messaging with DAQ Events in C


Windows Messages
At the heart of the Windows operating system is a messaging system whereby messages contain various types of information passed to various applications. At the head of nearly all Windows programs is a message pump: a while loop that receives the messages and processes each message. Traditional NI-DAQ (Legacy) can be instructed via the Config_DAQ_Event_Message command to post a user-defined message to a specific window when the event conditions are met. Normally, this window would be part of the program in charge of the data acquisition. Your application can be written to receive this message and handle it appropriately. Note that when we are not using the callback function, NULL should be passed to the callbackAddr parameter of Config_DAQ_Event_Message.

Each specific Windows Message is identified by a unsigned integer. Because most integers are already defined as specific messages (#defines in winuser.h) it is important to not use these integers. Rather, one should define their own message within the range allotted for user-defined, window, or application specific messages. This range is 0x0400 - 0x7FFF is available for window specific messages. The range 0x8000 - 0xBFFF is available for application specific messages. The keywords WM_USER and WM_APP can be used to identify the bottom of each of these ranges, respectively. In general, it is a good idea to #define your message value so that you have a keyword to use, rather than an integer:

#define WM_NIDAQ_MSG (WM_APP + 20)

For more information on message ranges, please see WM_USER or WM_APP in MSDN.

All Windows messages have associated wParam and lParam data. For the DAQ Event message, the least significant byte of wparam is the device number, and the second least significant byte is a flag indicating whether the operation is complete. lparam contains the scan number at which the DAQ Event occurred. This information can be used inside of the callback function to make decisions on how to handle the event.

The following example is a window procedure: the main message pump associated with a specific window inside the program:

The following is an example WindowProc routine, written in C:

LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsgId, WPARAM wParam, LPARAM
lParam)
{
     static unsigned long int uNIDAQeventCount = 0;
     short DAQeventDevice;
     short doneFlag;
     long scansDone;
     switch (uMsgId)
     {
          case WM_PAINT:
               //..handle this message...
               break;
          case WM_DESTROY:
               //..handle this message...
               break;
          case WM_NIDAQ_MSG:
               //**************************************
               //put your NI-DAQ Message handling here!
               //**************************************
               // increment static counter
               uNIDAQeventCount++;
               DAQeventDevice = (wParam & 0x00FF);
               doneFlag = (wParam & 0xFF00) >> 8;
               scansDone = lParam;
               //..handle this message...
               return 0;
               break;
          default:
               // handle other usual messages...
               return DefWindowProc (hWnd, uMsgId, wParam, lParam);
     }
}





Notice that the switch statement is used to decide which message was received and then to take the appropriate actions. If the WM_NIDAQ_MSG is received, the associated section of the switch executes to handle the DAQ message. Inside this case, all event processing should take place. In this example, bit shifting and bit masking are used on wParam to extract doneFlag and the DAQeventDevice parameters.

MFC Notes
If you are working with Microsoft Visual C++ and Microsoft Foundation Classes, the window procedures are abstracted and effectively hidden. In this case, the ON_MESSAGE macro should be used to sensitize your program to messages generated by DAQ Events. The normal method of doing this is to add a message handler through the ClassWizard. However, because we are adding a user-defined message about which the ClassWizard knows nothing, this is not possible. Therefore, we must manually add the message handler to the message map.

First add a function to your window or dialog class of the format:

CMyAppDlg::OnDAQMessage(WPARAM wParam, LPARAM lParam)
{
//Your code here
}


Then find the message map for the window. It can be identified by the macros BEGIN_MESSAGE_MAP and END_MESSAGE_MAP which signify the start and end of the message map. Inside the message map specify a new message to handle by adding the ON_MESSAGE macro:

BEGIN_MESSAGE_MAP(CMyAppDlg, CDialog)
//{{AFX_MSG_MAP(CMyAppDlg)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_BN_CLICKED(IDC_START, OnStart)
ON_WM_SETCURSOR()
//}}AFX_MSG_MAP

//Add a message handler for the DAQ Event Message
ON_MESSAGE(WM_NIDAQ_MSG, OnDAQMessage)
END_MESSAGE_MAP()


Finally, of course, it is necessary to tell Traditional NI-DAQ (Legacy) to post the message. Call Config_DAQ_Event_Message from the dialog or window's code. For the hwnd parameter, pass the handle of your window which can be acquired by accessing the window classes member variable m_hWnd.

Config_DAQ_Event_Message(1, ... this->m_hWnd, WM_NIDAQ_MSG, NULL);


Your MFC application should now handle the DAQ Event message when it is posted by the Traditional NI-DAQ (Legacy) driver.
Related Links:
NI-DAQ Function Reference Manual for PC Compatibles

NI-DAQ Software Reference Manual for Macintosh

Traditional NI-DAQ User Manual