VM3616A Programming Examples Outputting Static Voltages: Immediate/Triggered The most basic function of the VM3616A is that of a static digital to analog converter (DAC). Each of the 16 channels can be programmed independently. All of the outputs are referenced to a common ground. If an isolated DAC is required, please refer to the VM3618. Program Example 1 describes the sequence of SCPI commands to be sent to the device to configure the first four channels at four different voltage levels. The output range of each channel can be configured independently or as a group to +/- 10 or +/-20. Setting the range will affect the output resolution PROGRAM EXAMPLE 1 sour:volt:rang 10v, (@1:4) /* all channels set to 10v range sour:volt:lev 1.5, (@1) /*set channel 1 output to 1.5 volt sour:volt:lev 2.5, (@2) /*set channel 2 output to 2.5 volt sour:volt:lev 3.5, (@3) /*set channel 3 output to 3.5 volt sour:volt:lev 4.5, (@4) /*set channel 4 output to 4.5 volt These commands will cause the DAC's to update immediately. If it is desired to update the DAC's based on a trigger input, such as the backplane TTLT0, then the commands in program example 2 must be sent. In this example the outputs will not be updated until a rising edge trigger on TTLT0 occurs. PROGRAM EXAMPLE 2 trig:sour ttlt0 /*configure trigger source as TTLT0 trig:slop pos /* trigger slope is positive The plug&play driver includes application and configuration functions that perform similar tasks. The application function to setup and write to the DAC can be called from the .dll per program example 3. PROGRAM EXAMPLE 3 #include "vtvm3616A.h" static ViSession instrHndl; char instrDesc[50]; //A valid example for this variable would be VXI0::4::INSTR, where 4 is the logical address of the device. static ViInt16 channelList1[1] = {1}; */ initialize the VM3616A vtvm3616_init (instrDesc, vtvm3616_INIT_ID_QUERY, vtvm3616_INIT_RESET, &instrHndl); /* setup channel 1 to output 1.5V upon receipt of a rising edge trigger on TTLT0 vtvm3616_setupAndWriteToDAC (instrHndl, vtvm3616_TRIG_SRC_TTLTRG0, vtvm3616_SLOPE_POSITIVE, 1.5, channelList, 1); Outputting Voltages from Scan Tables Each DAC channel supports scan lists of up to 512 entries long. The scan list is predefined and loaded into RAM prior to use. All DACs update synchronously and can be advanced through the list upon receipt of a front panel input or TTL trigger. The scan list can be programmed to loop back to the beginning, or halt once the end of the list has been reached. The scan lists cannot use the internal oscillator for pacing the output. Utilize the ARB mode if internal pacing is desired. To setup the DACs to operate in the scan mode, refer to program example 4. In this case, channels 5 and 6 have the scan mode set to ON and channels 7 and 8 have the scan mode set to LOOP. The trigger source is will be defined as backplane TTLT0. PROGRAM EXAMPLE 4 scan off */ensure that no invalid mode transitions will occur scan on, (@5:6) */set the scan mode to ON for channels 5 and 6. These channels will not loop back once the list is completed. scan loop, (@7:8) */set the scan mode to LOOP for channels 7 and 8. These channels will loop back to start of list. trig:sour ttlt0 */TTLT0 will be used to advance through the scan list. scan:tabl 5,0,5,0,5,0 /* alternating off/on pattern. After the 5th trigger, this channel outputs 0V indefinitely. scan:tabl 6,5,5,0,0,5 /* alternating off/on pattern. After the 5th trigger, this channel outputs 5V indefinitely. scan:tabl 7,0,1,2,3,4 /* step function. After the 5th trigger, this channel repeats the pattern. scan:tabl 8,0,2,0,4 /* step function. After the 4th trigger, this channel repeats the pattern. Outputting Standard Waveforms Using Arb Mode One of the more powerful features of the VM3616A is the arbitrary waveform mode. This mode makes use of an internal timing clock to pace the output of the data as fast as 10us intervals at 100ns resolution. Standard waveforms at lower frequencies can be generated using the arb mode by loading the waveform data into memory, which is separate from that used for the scan lists, and pacing the output using the internal oscillator. In arb mode, the channels share up to 1MB of memory. There is only one oscillator per module, therefore all channel outputs are updated simultaneously. However, each channel can be programmed with different waveform data allowing for independent waveforms. Programming example 5 illustrates how two channels can be programmed to output continuous sine waves at different frequencies, in this case the desire is to have channel 1 output a sine wave at 193 Hz and channel 2 at 130Hz (Figure E-1). There are many different ways that the sine wave data can be generated including software packages that generate 16-bit binary waveform data files which can be used by the VM3616A. Also, some application development environments (ADE's) have built in analysis tools that create waveform data that can be ported directly into the device. FIGURE E-1. PROGRAM EXAMPLE 5 sour:volt:rang 10v, (@1:2) /* Set the voltage range across the two channels trig:sour none /* Set trigger source to NONE. Output will begin as soon as an INIT command is sent. scan arb, (@1:3) /* Set channels to ARB mode trace:def 0, 1536 /* Define the size of a segment sour:volt:form 0 /* Define whether waveform data will be loaded as signed or unsigned binary sour:rosc:sour tim /* Set the internal timer to be the pacer for the output data. trig:tim 1e-5 /* Set the internal timing interval. This ultimately determines the frequency of the output. /* Define trace data within the segment for each channel (1536 data points/channel) trace:lev 0,1, <1536 points of sine wave data, 3 cycles> // Segment 0, Ch1 trace:lev 0,2, <1536 points of sine wave data, 2 cycles> //Segment 0, Ch2 source:seq:list (@ 0) /* Define the sequence to contain segment 0 sour:seq:start auto,(@0) /* sequence will loop and output will be continuous init /* Begin outputting data The frequency of the sine wave output is determined by the timing clock interval (10us) and the number of data points per cycle. For channel 1, this works out to 1/(512*0.00001) = 195.3 Hz. Channel 2 = 1/(768*0.00001) = 130.2 Hz. Different frequencies on each channel can be achieved by varying the internal timing interval, or the number of waveform cycles within a segment. For example a sine wave can be defined by 128 data points, 16 cycles, effectively quadrupling the frequency of the output on channel 1. Loading Arb Data Using Register-Based Commands The previous example uses word serial (or SCPI) commands to download the waveform data to the VM3616A. This is a very time intensive process when large blocks of data are downloaded to the device via this method. While the VM3616A is a message-based device, the registers can be accessed to speed up the data upload process. Figure E-2 shows the valid register map for the ARB mode. 0x26 Waveform Data 0x24 RAM Request 0x22 Channel Number 0x20 Segment Number FIGURE E-2: A16 register map, ARB mode The flow for loading data begins with the segment number and finishes with the 16-bit data. Refer to the flow chart (Figure E-3) below. In the previous example, this sequence of register-based calls would have been performed twice, one for each active channel. Load Segment Number 0x20 Load Channel Number 0x22 Request Ram <write a '0' to 0x24> Load 16-bit data value 0x26 TRUE More data for this channel? FALSE TRUE More channels or segments? FALSE DONE FIGURE E-3: Loading arb data via registers The plug&play driver for the VM3616A includes a function that embeds the register-based calls when loading arb data. This function can be called from any ADE that can utilize dynamically linked libraries (.dll). The data is loaded as an array and the following function call is used. vtvm3616_arbLoadData (ViSession instrHndl, int segmentNumber, int channel, ViInt16 voltageDataBuffer[], ViInt32 buffSize); Creating Arbitrary Waveform Patterns on Multiple Channels It is important to become familiar with the concept of traces, segments and sequences to understand the arb mode operation of the VM3616A. A trace is the smallest building block of the arbitrary waveform (e.g. 3 cycle sine wave). A segment defines the output of all channels set to arb mode for a specified length in time. The length in time is determined by the number of data points assigned to the segment multiplied by the internal timer (pacing clock) interval. The sequence defines the order in which the segments will be called to create the output pattern. For example, if three channels are set to the arb mode, three different blocks of trace data can be loaded into memory and assigned to a particular segment. Figure E-4 illustrates the concept of traces and segments. FIGURE E-4 To create an arbitrary waveform from the segments described in the above example, a sequence must be created to define the order of segment calls which ultimately defines the arbitrary pattern. Programming example 6 shows how to build arbitrary patterns across multiple channels. PROGRAM EXAMPLE 6 trig:sour none /* Set trigger source to NONE. Output will begin as soon as an INIT command is sent. scan arb, (@1:3) /*Set all three channels to ARB mode trace:def 1, 1000 /*Define the size of segment 1 trace:def 2, 500 /*Define the size of segment 2 sour:volt:form 0 /*Define whether waveform data will be loaded as signed or unsigned binary sour:rosc:sour tim /* Set the internal timer to be the pacer for the output data. trig:tim 1e-5 /*Set the internal timing interval. This ultimately determines the frequency of the output. /*Define trace data within each segment. Avoid undefined segment data as it will result in a random output. trace:lev 1,1, <1000 points of sine wave data, 3 cycles> Segment 1, Ch1 trace:lev 1,2, <1000 points of sine wave data, 2 cycles> Segment 1, Ch2 trace:lev 1,3, <1000 points of triangle wave data, 2 cycles> Segment 1, Ch3 trace:lev 1,1, <500 points of square wave data, 2 cycles> Segment 2, Ch1 trace:lev 1,2, <500 points of sine wave data, 1 cycle> Segment 2, Ch2 trace:lev 1,3, <500 points of gated sine data, 1 cycle> Segment 2, Ch3 source:seq:list (@ 1, 2, 2) /* Define the sequence /*Define how each segment will begin outputting data within the sequence (i.e. auto or triggered) sour:seq:start auto,(@0:2) */new segment begins as soon as previous segment is complete. Waveform loops until an abort is issued. init /*Begin outputting data VXIplug&play Programming example 7 achieves the identical waveform using the VXIplug&play driver supplied with the product PROGRAM EXAMPLE 7 #include "vtvm3616A.h" static ViSession instrHndl; char instrDesc[50]; //A valid example for this variable would be VXI0::4::INSTR, where 4 is the logical address of the device. static ViInt16 channelList[3] = {1,2,3}; static ViInt16 seqList[3] = {1,2,2}; double ThreeCycleSine[1000], TwoCycleSine[1000], Triangle[1000], OneCycleSine[500], TwoCycleSquare[500], GatedSine[500]; vtvm3616_init (instrDesc, vtvm3616_INIT_ID_QUERY, vtvm3616_INIT_RESET, &instrHndl); vtvm3616_configTriggerParams (instrHndl, vtvm3616_TRIG_SRC_NONE, vtvm3616_SLOPE_POSITIVE); vtvm3616_setupScanMode (instrHndl, vtvm3616_SCAN_MODE_ARB, channelList,3); vtvm3616_arbDefineSegment (instrHndl, 1, 1000) vtvm3616_arbDefineSegment (instrHndl, 1, 500) vtvm3616_setupDataFormat (instrHndl, vtvm3616_FORMAT_UNSIGNED) vtvm3616_setupClock (instrHndl, vtvm3616_ROSC_TIMER, vtvm3616_POSITIVE) vtvm3616_setupTimer (instrHndl, 0.00001) vtvm3616_arbLoadData (instrHndl, 1, 1, ThreeCycleSine, 1000); vtvm3616_arbLoadData (instrHndl, 1, 2, TwoCycleSine, 1000); vtvm3616_arbLoadData (instrHndl, 1, 3, Triangle, 1000); vtvm3616_arbLoadData (instrHndl, 2, 1, TwoCycleSquare, 500); vtvm3616_arbLoadData (instrHndl, 2, 2, OneCycleSine, 500); vtvm3616_arbLoadData (instrHndl, 2, 3, GatedSine, 500); vtvm3616_arbDefineSequence (instrHndl, seqList, 3); vtvm3616_arbSetSeqParms (instrHndl, 0, vtvm3616_SEQ_AUTO_START, vtvm3616_SEQ_ASYNC_ADVANCE, 1, vtvm3616_SEQ_MARKER_OFF); vtvm3616_arbSetSeqParms (instrHndl, 1, vtvm3616_SEQ_AUTO_START, vtvm3616_SEQ_ASYNC_ADVANCE, 1, vtvm3616_SEQ_MARKER_OFF); vtvm3616_arbSetSeqParms (instrHndl, 2, vtvm3616_SEQ_AUTO_START, vtvm3616_SEQ_ASYNC_ADVANCE, 1, vtvm3616_SEQ_MARKER_OFF); vtvm3616_commandArb (instrHndl, vtvm3616_INITIATE); As soon as the init command is issued, the channels will begin to output the data. Because each segment is defined in the 'auto' mode, the DACs will produce a continuous output. The resultant waveform generated by the previous code is illustrated in Figure E-5. FIGURE E-5 Single Shot Mode and Triggering All previous examples exhibited continuous waveforms that were triggered as soon as the 'init' command was issued to the device. If the outputs of the DAC are to be in quiescent state until a single shot waveform is desired, then some slight modifications to the code must be made. As an example, the front panel external input can be used to trigger the arbitrary waveform pattern defined in the previous example. trig:sour ext /* Set trigger source to EXT. External trigger will cause DAC to begin outputting data However, as currently defined, the DACs will begin outputting the data as soon as the 'init' command is issued. To correct this, it is recommended to set up an initial or quiescent segment, detailed in programming example 8, at a DC level that has its sequence start parameter set to triggered. This will cause the output of the DAC to hold at the quiescent level until the trigger is received. PROGRAM EXAMPLE 8 trac:def 0, 2 /*define the size of segment 0 at two elements trac:lev 0,1,0,0 /*quiescent voltage level for all three channels set to 0VDC trac:lev 0,2,0,0 trac:lev 0,3,0,0 sour:seq:list (@0,1,2,2) sour:seq:star trig, (@0) /*Segment 0 (sequence index 0) will execute after init is issued and then holds until trigger is issued sour:seq:star auto, (@1:3) /* the rest of the pattern will be output after trigger is received. Single shot output. Streaming Data: Using the FIFO Mode The VM3616A series has an on-board FIFO (first-in first-out) that is useful for outputting waveform patterns that require more memory than is contained on the card. A typical application is the playback of recorded data such as audio signals. Data files that exceed the 1MB memory limitation of the VM3616A can still be converted to an analog waveform by using the FIFO mode. The internal timer that is used in ARB mode is the same timer that is used in the FIFO mode. The FIFO is emptied at a rate that is equivalent to the settings of the timer. Multiple channels can be set to the FIFO mode and in this case, the data is loaded into the FIFO interleaved. The supplied soft front panel illustrates use of the FIFO mode. Three 16-bit binary data files are also included for example purposes. It is recommended that the user familiarize him/herself with the soft front panel. In addition, detailed examples are listed below. Single Channel FIFO Mode The VM3616A converts 16-bit binary data into analog voltages. A common audio application plays back data at a rate of 43-44kb/s. Refer to programming example #9 which shows how to playback a 16-bit audio file out of channel 1. The name of the file is music.b16 and is stored in the root (c:\) drive. PROGRAM EXAMPLE 9 #include "vtvm3616A.h" static ViSession instrHndl; char instrDesc[50]; //A valid example for this variable would be VXI0::4::INSTR, where 4 is the logical address of the device. char FileName[] = {"C:\ music.b16"}; int FileHandle, dataBuffer, SampleCount = 50000; static ViInt16 channelList[1] = {1}, fifoStatus; vtvm3616_init (instrDesc, vtvm3616_INIT_ID_QUERY, vtvm3616_INIT_RESET, &instrHndl); vtvm3616_setupScanMode (instrHndl, vtvm3616_SCAN_MODE_OFF, channelList,1); /* must first be off to transition to FIFO vtvm3616_configTriggerParams (instrHndl, vtvm3616_TRIG_SRC_NONE, vtvm3616_SLOPE_POSITIVE); vtvm3616_setupScanMode (instrHndl, vtvm3616_SCAN_MODE_FIFO, channelList,1); vtvm3616_setupClock (instrHndl, vtvm3616_ROSC_TIMER, vtvm3616_POSITIVE); vtvm3616_setupTimer (instrHndl, 0.0000227); /*44.0Khz output vtvm3616_setupDataFormat (instrHndl, vtvm3616_FORMAT_UNSIGNED) /* Store 50000 samples to a data buffer. These samples will be 'pre-loaded' into the FIFO FileHandle =OpenFile (FileName, VAL_READ_ONLY, VAL_OPEN_AS_IS, VAL_BINARY); read(FileHandle,(unsigned char *)dataBuffer, SampleCount); /* pre-load the FIFO vtvm3616_loadFIFO_Data (ViSession instrHndl, dataBuffer[], SampleCount); vtvm3616_commandFifo (instrHndl, vtvm3616_INITIATE); /*FIFO starts to empty /* Read next 50000 samples to data buffer. Then load to FIFO. This is a repetitive process that ensures that the FIFO doesn't /*underrun (run out of data). A loop should be set up such that the FIFO status is also checked and overrun conditions are also /*avoided. read(FileHandle,(unsigned char *)dataBuffer, SampleCount); vtvm3616_loadFIFO_Data (ViSession instrHndl, dataBuffer[], SampleCount); vtvm3616_GetFifoStatus (instrHndl, *fifoStatus); In most high speed applications where the timer is set at fast update rates, the risk of underrun is higher than an overrun. Data tends to be emptied out of the FIFO at a faster pace than the host controller can read the file into the buffer and pass the data to the FIFO over the VXIbus interface. In slow speed applications, it is possible that the FIFO can be filled up at a faster rate than it is emptied. In these cases, the risk of a FIFO overflow is greater. To avoid an overflow condition, simply check the FIFO status. As long as the status indicates that the FIFO is not full, another sample of data can be uploaded. Multiple Channel FIFO mode: A Register-Based example Each sample of converted data is a 16-bit integer. When multiple channels are stored in FIFO, the data must be interleaved and with every tick of the internal timer, each channel set to FIFO mode is updated with new data. Therefore, if 'n' channels are set to FIFO mode, and the internal timer is set to 't' seconds, then the rate (in bytes) at which the data flows out of the FIFO is calculated using the following equation: (n*2)/t For example, if three channels are set to the FIFO mode, the data in FIFO memory is stored per Fig. E-6. If the timer is set to the minimum interval of 10us, then data is being extracted from the FIFO at a rate of 600kb/s. Slower host controllers may not be able to refill the FIFO at a rate equivalent to which it is being emptied. Therefore, for very large files, an underrun condition is probable (FIFO is empty and waiting for data) and the timer interval may need to be extended if possible. 0x(6n-2) Channel 3 (Sample n) 0x(6n-4) Channel 2 (Sample n) 0x(6n-6) Channel 1 (Sample n) 0x4 Channel 3 (Sample 1) 0x2 Channel 2 (Sample 1) 0x0 Channel 1 (Sample 1) FIGURE E-6 Interleaved data in FIFO Program Example 10 (written in LabWindows®/CVI) illustrates how to output multiple channels of FIFO data using register-based access. In this example, channel 1 is outputting a constant square wave pulse, while channel 2 is outputting either a sine wave (sine.b16) or triangle wave (tri.b16) depending on a selection that the user makes from a GUI (figure E-7). Both 16-bit binary files are included with the plug and play driver. The plug and play driver function, vtvm3616_loadFIFO_Data can be used with multiple channels as well, however, the programmer must be sure that the buffer contains interleaved data. Register 0x20 in A16 space is the FIFO data register. Writing interleaved data to this register loads the FIFO. As soon as the FIFO initiate command is sent, data from the FIFO is offloaded to the DACs and the conversions begin to take place at the rate at which the timer is set. As in the single channel example, it is the program's responsibility to ensure that the FIFO does not run out of data, or conversely, overflows. For the purpose of clarity, this example does not check the FIFO status prior to loading data to the FIFO. FIGURE E-7 PROGRAM EXAMPLE 10 #include "vtvm3616A.h" #define SamplesPerChannel 1000 #define ValidChannels 2 #define TotalNumOfBytesPerChannel 1000 #define TotalNumOfChannels 2 char FileName3[] = {"C:\\VXIPNP\\Win95\\Vtvm3616a\\Sine.b16"}; //file supplied with the driver program char FileName4[] = {"C:\\VXIPNP\\Win95\\Vtvm3616a\\Tri.b16"}; ViSession instrHndl; ViInt16 Channel_List [2] = {1, 2}; ViUInt16 fifoStatus, Filebuffer[2000], checkingarrayforMove[2000], InitialFiFoData[TotalNumOfChannels*TotalNumOfBytesPerChannel], DataForValidChannels[TotalNumOfChannels][TotalNumOfBytesPerChannel], FiFoFinalData[TotalNumOfChannels*TotalNumOfBytesPerChannel], UpdatedFiFoData[TotalNumOfChannels*TotalNumOfBytesPerChannel]; void FIFOdataForCH1() { /* The following for loop is used to assign pulse data for channel 1in FIFO mode.*/ /* DataForValidChannels[][] is the two dimensional array containing all 16-bit channel data*/ for(DataByteCounter = 0; DataByteCounter < TotalNumOfBytesPerChannel; DataByteCounter +=2) { DataForValidChannels[1][DataByteCounter] = 32767; /* 0V DataForValidChannels[1][DataByteCounter+1] = 40959; /*5V } } void InitialFIFOdataForCH2() /* Loads sine.b16 data into DataForValidChannels Array handle =OpenFile (FileName3, VAL_READ_ONLY, VAL_OPEN_AS_IS, VAL_BINARY); status = read(handle,(unsigned char *)Filebuffer,FileBytescount); i=0; for(DataByteCounter = 0; DataByteCounter < TotalNumOfBytesPerChannel; DataByteCounter ++) { DataForValidChannels[2][DataByteCounter] = Filebuffer[i]; i+=1; } } void UpdatedFIFOdataForCH2() /* Loads tri.b16 data into DataForValidChannels Array { /* This function opens tri.b16 and reads into DataFromValidChannelArray handle =OpenFile (FileName4, VAL_READ_ONLY, VAL_OPEN_AS_IS, VAL_BINARY); status = read(handle,(unsigned char *)Filebuffer,FileBytescount); i=0; for(DataByteCounter = 0; DataByteCounter < TotalNumOfBytesPerChannel; DataByteCounter ++) { DataForValidChannels[2][DataByteCounter] = Filebuffer[i]; i+=1; } { void InitialFIFOdataForAllValidChs() /*This function interleaves the pulse (ch1) and sine (ch2) data into InitialFiFoData array*/ { FIFOdataForCH1(); //Function call to load data for CH#1. InitialFIFOdataForCH2(); //Function call to load data(sine wave) for CH#2. FiFoDataCounter = 0; for(DataByteCounter = 0; DataByteCounter < TotalNumOfBytesPerChannel; DataByteCounter++) { for (ChannelCounter = 1; ChannelCounter < 3; ChannelCounter++) { InitialFiFoData[FiFoDataCounter] = DataForValidChannels[ChannelCounter][DataByteCounter]; FiFoDataCounter +=1; } } } void UpdatedFIFOdataForAllValidChs() /*This function interleaves pulse (ch1) and triangle (ch2) into UpdatedFiFoData array*/ { UpdatedFIFOdataForCH4(); //Function call to load data(triangle Wave) for CH#2. FiFoDataCounter = 0; for(DataByteCounter = 0; DataByteCounter < TotalNumOfBytesPerChannel; DataByteCounter++) { for (ChannelCounter = 1; ChannelCounter < 3; ChannelCounter++) { UpdatedFiFoData[FiFoDataCounter] = DataForValidChannels[ChannelCounter][DataByteCounter]; FiFoDataCounter +=1; } } TotalBytesToTransmit = FiFoDataCounter; FiFoDataCounter = 0; } /*The above steps show how to read in data from a file and then interleave multiple channel data into arrays. In this example, a toggle switch will determine which of two data arrays is uploaded to the FIFO. The next steps set up the VM3616A */ /*Setup channels 1 and 2 for FIFO mode*/ vtvm3616_init (instrDesc, vtvm3616_INIT_ID_QUERY, vtvm3616_INIT_RESET, &instrHndl); vtvm3616_setupScanMode (instrHndl, vtvm3616_SCAN_MODE_OFF, Channel_List,2); vtvm3616_configTriggerParams (instrHndl, vtvm3616_TRIG_SRC_NONE, vtvm3616_SLOPE_POSITIVE); vtvm3616_setupScanMode (instrHndl, vtvm3616_SCAN_MODE_FIFO, Channel_List,2); /*setup internal timer (60us) and data format as unsigned. vtvm3616_setupDataFormat (instrHndl, vtvm3616_FORMAT_UNSIGNED); vtvm3616_setupTimer (instrHndl, 0.00006); vtvm3616_commandFifo (instrHndle, vtvm3616_INITIATE); /*Random data until FIFO is loaded with known data*/ /*map the A16 address space to a pointer, offset 0x20 is the FIFO data register viMapAddress (VM3616A, VI_A16_SPACE, 0x20, 0x20, VI_FALSE, VI_NULL, &A16Ptr ); /* The next functions are called from actions on the GUI. A timer tick will cause data to be uploaded. The toggle switch determines which timer is active and ultimately which data is uploaded to the FIFO.*/ int CVICALLBACK Timer1 (int panel, int control, int event, void *callbackData, int eventData1, int eventData2) /* Timer1 tick will cause 1000 more sine wave samples to be loaded to the FIFO. { switch (event) { case EVENT_TIMER_TICK: /*disable the timer until all 1000 samples have been loaded status = SetCtrlAttribute (panelHandle, PANEL_TIMER1, ATTR_ENABLED, 0); for(i = 0; i < 2; i++) { for(DataByteCounter = 0; DataByteCounter < TotalBytesToTransmit; DataByteCounter++) { viPoke16 (instrHndle, A16Ptr, InitialFiFoData[DataByteCounter]);// for sinewave data to fifo } } /*enable the timer so the next tick will bring more data status = SetCtrlAttribute (panelHandle, PANEL_TIMER1, ATTR_ENABLED, 1); break; } return 0; } int CVICALLBACK Timer2 (int panel, int control, int event, void *callbackData, int eventData1, int eventData2) /* Timer2 tick will cause 1000 more triangle samples to be loaded to the FIFO. { switch (event) { case EVENT_TIMER_TICK: /*disable the timer until all 1000 samples have been loaded status = SetCtrlAttribute (panelHandle, PANEL_TIMER2, ATTR_ENABLED, 0); for(i = 0; i < 2; i++) { for(DataByteCounter = 0; DataByteCounter < TotalBytesToTransmit; DataByteCounter++) { viPoke16 (instrHndle, A16Ptr, InitialFiFoData[DataByteCounter]);// for triangle data to fifo } } /*enable the timer so the next tick will bring more data status = SetCtrlAttribute (panelHandle, PANEL_TIMER2, ATTR_ENABLED, 1); break; } return 0; } /*This call is associated with the front panel toggle. Toggling the switch results in one of the timers being disabled, and the other one will be enabled. This determines which data file (sine or triangle wave), gets loaded into the FIFO for Channel 2 output. int CVICALLBACK SineTriControlSwitch (int panel, int control, int event, void *callbackData, int eventData1, int eventData2) { switch (event) { case EVENT_COMMIT: status = GetCtrlVal (panelHandle, PANEL_SINETRICONTROLSWITCH, &ControlValue); if (ControlValue == 0) { SetCtrlVal(panelHandle,PANEL_WAVETEXTBOXCH_4,FileName3); status = SetCtrlAttribute (panelHandle, PANEL_TIMER1, ATTR_ENABLED, 1); //Enable timer1 status = SetCtrlAttribute (panelHandle, PANEL_TIMER2, ATTR_ENABLED, 0); //Disable timer2 } else { SetCtrlVal(panelHandle,PANEL_WAVETEXTBOXCH_4,FileName4); status = SetCtrlAttribute (panelHandle, PANEL_TIMER2, ATTR_ENABLED, 1); //Enable timer2 status = SetCtrlAttribute (panelHandle, PANEL_TIMER1, ATTR_ENABLED, 0); //Disable timer1 break; } return 0; } A more detailed variation and listing of example #10 (fifo3616.zip) can be found on the VXI Technology ftp site by connecting through www.vxitech.com