When dealing with instrument communication, it is very common for the instrument to require service from the controller when the controller is not actually looking at the device. A device can notify the controller via a service request (SRQ), interrupt, or a signal. Each of these is an asynchronous event, or simply an event. In VISA, you can handle these and other events through either callbacks or a software queue.

Handling Events Through Callbacks

Using callbacks, you can have sections of code that are never explicitly called by the program, but instead are called by the VISA driver whenever an event occurs. Due to their asynchronous nature, callbacks can be difficult to incorporate into a traditional, sequential flow program. Therefore, we recommend the queuing method of handling events for new users.

Handling Events Through Queuing

When using a software queue, the VISA driver detects the asynchronous event but does not alert the program to the occurrence. Instead, the driver maintains a list of events that have occurred so that the program can retrieve the information later. With this technique, the program can periodically poll the driver for event information or halt the program until the event has occurred. The following example programs an oscilloscope to capture a waveform. When the waveform is complete, the instrument generates a VXI interrupt, so the program must wait for the interrupt before trying to read the data.

Handling Events Example (C)

Note The following example uses bold text to distinguish lines of code that are different from the other introductory programming examples.
#include "visa.h"
int main(void)
{
    ViStatus     status;            /* For checking errors */
    ViSession    defaultRM, instr;  /* Communication channels */
    ViEvent      eventData;         /* To hold event info */
    ViUInt16     statID;            /* Interrupt Status ID */ 
    /* Begin by initializing the system */
    status = viOpenDefaultRM(&defaultRM);
    if (status < VI_SUCCESS) {
        /* Error Initializing VISA...exiting */
        return -1;
    }
    /* Open communication with VXI Device at Logical Address 16 */
    /* NOTE: For simplicity, we will not show error checking */
    status = viOpen(defaultRM, "VXI0::16::INSTR", VI_NULL, VI_NULL, &instr);

    /* Enable the driver to detect the interrupts */
    status = viEnableEvent(instr, VI_EVENT_VXI_SIGP, VI_QUEUE, VI_NULL);

    /* Send the commands to the oscilloscope to capture the */
    /* waveform and interrupt when done */

    status = viWaitOnEvent(instr, VI_EVENT_VXI_SIGP, 5000, VI_NULL, &eventData);
    if (status < VI_SUCCESS) {

        /* No interrupts received after 5000 ms timeout */
        viClose(defaultRM);
        return -1;
    }
    /* Obtain the information about the event and then destroy the */
    /* event. In this case, we want the status ID from the interrupt. */
    status = viGetAttribute(eventData, VI_ATTR_SIGP_STATUS_ID, &statID);
    status = viClose(eventData);

    /* Your code should read data from the instrument and process it.*/

    /* Stop listening to events */
    status = viDisableEvent(instr, VI_EVENT_VXI_SIGP, VI_QUEUE);

    /* Close down the system */
    status = viClose(instr);
    status = viClose(defaultRM);
    return 0; }

Handling Events Example (VB)

Note The Visual Basic examples in this manual use the VISA data types where applicable. This feature is available only on Windows. To use this feature, select the VISA library (visa32.dll) as a reference from Visual Basic. This makes use of the type library embedded into the DLL.
Private Sub vbMain()
    Dim stat      As ViStatus
    Dim dfltRM    As ViSession
    Dim sesn      As ViSession
    Dim eType     As ViEventType
    Dim eData     As ViEvent
    Dim statID    As Integer 
   Rem Begin by initializing the system
   stat = viOpenDefaultRM(dfltRM)
   If (stat < VI_SUCCESS) Then
    Rem Error initializing VISA...exiting
    Exit Sub
   End If

   Rem Open communication with VXI Device at Logical Address 16
   Rem NOTE: For simplicity, we will not show error checking
   stat = viOpen(dfltRM, "VXI0::16::INSTR", VI_NULL, VI_NULL, sesn)

   Rem Enable the driver to detect the interrupts
   stat = viEnableEvent(sesn, VI_EVENT_VXI_SIGP, VI_QUEUE, VI_NULL)

   Rem Send the commands to the oscilloscope to capture the
   Rem waveform and interrupt when done

   stat = viWaitOnEvent(sesn, VI_EVENT_VXI_SIGP, 5000, eType, eData)
   If (stat < VI_SUCCESS) Then

    Rem No interrupts received after 5000 ms timeout
    stat = viClose (dfltRM)
    Exit Sub
   End If

   Rem Obtain the information about the event and then destroy the
   Rem event. In this case, we want the status ID from the interrupt.
   stat = viGetAttribute(eData, VI_ATTR_SIGP_STATUS_ID, statID)
   stat = viClose(eData)

   Rem Your code should read data from the instrument and process it.

   Rem Stop listening to events
   stat = viDisableEvent(sesn, VI_EVENT_VXI_SIGP, VI_QUEUE)

   Rem Close down the system
   stat = viClose(sesn)
   stat = viClose(dfltRM)
End Sub

Handling Events Example Discussion

Programming with events presents some new functions to use. The first two functions you notice are viEnableEvent() and viDisableEvent(). These functions tell the VISA driver which events to listen for—in this case VI_EVENT_VXI_SIGP, which covers both VXI interrupts and VXI signals. In addition, these functions tell the driver how to handle events when they occur. In this example, the driver is instructed to queue (VI_QUEUE) the events until asked for them. Notice that instr is also supplied to the functions, since VISA performs event handling on a per-communication-channel basis.

Once the driver is ready to handle events, you are free to write code that will result in an event being generated. In the example above, this is shown as a comment block because the exact code depends on the device. After you have set the device up to interrupt, the program must wait for the interrupt. This is accomplished by the viWaitOnEvent() function. Here you specify what events you are waiting for and how long you want to wait. The program then blocks, and that thread performs no other functions, until the event occurs. Therefore, after the viWaitOnEvent() call returns, either it has timed out (5 s in the above example) or it has caught the interrupt. After some error checking to determine whether it was successful, you can obtain information from the event through viGetAttribute(). When you are finished with the event data structure (eventData), destroy it by calling viClose() on it. You can now continue with the program and retrieve the data. The rest of the program is the same as the previous examples.

Notice the difference in the way you can shut down the program if a timeout has occurred. You do not need to close the communication channel with the device, but only with the VISA driver. You can do this because when a driver channel (defaultRM) is closed, the VISA driver closes all I/O channels opened with it. So when you need to shut down a program quickly, as in the case of an error, you can simply close the channel to the driver and VISA handles the rest for you. However, VISA does not clean up anything not associated with VISA, such as memory you have allocated. You are still responsible for those items.