Generate a Continuous Digital Pulse Train With On The Fly Frequency Control

Updated Jun 27, 2023

Environment

Software

  • LabVIEW
  • LabWindows/CVI

Driver

  • NI-DAQmx

  • I am currently using a counter output application that generates a pulse train. I want to know how to change the frequency of this pulse train dynamically in my program.
  • How do I change the frequency output of my pulse train on the fly?
  • How can I generate a continuous pulse train that can dynamically change frequency?

This section will address how to use either LabVIEW or LabWindows™/CVI to accomplish this using the DAQmx driver. If you are using Traditional DAQ, please see the additional information section for that method. 


LabVIEW:

The following example demonstrates the creation of a continuous pulse train output with dynamic frequency variation. A local variable must be utilized to update the frequency property of the DAQmx task. As frequency is a defined configuration property, identical frequency values cannot be sent to the DAQmx Write function in concurrent loop iterations; therefore, a case structure must be implemented to update the frequency value only on value change. In this example, a user may dynamically update the frequency property of the pulse train.

The code for the below example can be found in the Community Example: Generate a Continuous Digital Pulse Train With a Variable Frequency

Often, the correct case logic will not be implemented to handle the frequency update resulting in Error:  -200301. As previously stated, the frequency of the DAQmx task may only be updated as long as it is different from the current property value.

Alternatively, an Event Structure may be implemented to ensure that concurrent frequency values are not passed to the DAQmx task. The example below demonstrates this functionality. This example can be found at the Community Example: Digital Pulse Train with Frequency Update using Event Structure

LabWindows/CVI:

In LabWindows/CVI changing the frequency on-the-fly can be done in a similar manner to the first example above.  After the the Task and channel have been created, the timing configuration set, and the Task started, a loop that takes the current frequency from the UI needs to be inserted to update the pulse generation before the task is finished.  Here is an example portion of this code:
while(status == 0)
    {
        GetCtrlVal(panelId,PANEL_FREQUENCY,&freq);
        Delay(1);                         
        if(freq != temp_freq)
        {    
            DAQmxErrChk(DAQmxSetChanAttribute (gTaskHandle, chan,                  DAQmx_CO_Pulse_Freq, freq, 1000));
            temp_freq = freq;
        }
    }

The code above first checks the status flag which is triggered when the stop button the UI is clicked and if it hasn't been triggered, grabs the current value of frequency on the UI, delays a for a second to prevent error -200301 from happening, and then checks and updates the frequency if it has changed since the last iteration.  Refer to the link below "LabWindows/CVI: Digital Pulse Train with a Variable Frequency" for a full working example.  Note:  In this example it is setup so that the data acquisition runs in a separate thread from the UI so the UI can be updated during acquisition, so some knowledge of using thread pools in CVI is needed to interpret the code.

Additional Information

Traditional DAQ

There are two ways to change the frequency of a pulse train on the fly.
  1. The first method is to setup your counter for Frequency Shift Keying (FSK). This allows you to toggle in hardware between two frequencies. You setup Load Registers A and B on a counter for the first frequency and setup Load Registers C and D for the second frequency. After the counter has been prepared and armed (or programmed), a toggle on the gate pin of the counter will toggle the output from Load Registers A and B to Load Registers C and D and vice versa.
  2. The second method of dynamically changing the frequency of a counter is to use "Switch Cycle." Switch cycle changes the frequency in software by reloading different values into Load Registers A and B (or C and D) and making a call to switch the cycle. While the counter is outputting a pulse train, Switch Cycle causes the counter to load the currently unused Load Registers with the updated value and then causes the counter to use these Load Registers for the pulse train instead. Therefore, if you are outputting a pulse train on Load Registers A and B, a call to switch cycle will load the new values into registers C and D. The counter will then change the target of the pulse train generation registers from A and B to C and D.
How you would perform this in Traditional DAQ is as follows:

LabVIEW

While in a loop for continuous pulse train generation, make two calls to Counter Set Attribute.vi to set the values for "pulse spec 1" (constant 14) and "pulse spec 2" (constant 15). Following these calls you would make a call to Counter Control.vi with the control code set to "switch cycle" (constant 7). The attached LabVIEW programs demonstrate this flow.

LabWindows/CVI (NI-DAQ function calls):

/* Send the new LOW period of squarewave to counter. */
iStatus = GPCTR_Change_Parameter(iDevice, ulGpctrNum, ND_COUNT_1, ulLOWcount);

/* Send the new HIGH period of squarewave to counter. */
iStatus = GPCTR_Change_Parameter(iDevice, ulGpctrNum, ND_COUNT_2, ulHIGHcount);

/* Instruct counter to switch to the new cycle. For NI-TIO based devices, this is the only way to switch frequencies. */
iStatus = GPCTR_Control(iDevice, ulGpctrNum, ND_SWITCH_CYCLE);

Refer to the example that ships with the NI-DAQ driver TIOgenSquareWave.c (if LabWindows/CVI support has been included in installation).