DA FunctionGenerator
Edit this on GitLab
DA FunctionGenerator Sample Application (SSK 1.x)
Overview
The DA FunctionGenerator sample application demonstrates how to generate analog waveforms by computing sample data and loading it into a DA module’s FIFO buffer. Once triggered, the hardware plays the waveform in repeat mode at a fixed output rate — no further software intervention is required.
The application supports five waveform types:
-
Constant voltage — a steady DC level on the output.
-
Square wave — alternates between two voltage levels.
-
Triangle wave — linearly ramps up and down.
-
Sawtooth wave — linearly ramps up, then resets.
-
Sine wave — a standard sinusoidal output.
Each waveform is configurable by frequency (approximately 15 Hz to 15 kHz), amplitude (up to +/-10 V), and DC offset (up to +/-10 V). The application computes the required number of FIFO data points based on the requested frequency and the module’s fixed output data rate, fills the FIFO, and issues a software trigger to start playback.
Supported Modules
This sample supports the following DA module types:
-
GEN3 (16-bit FIFO): F1, F3, F5, J3, J5, J8
-
GEN5 (32-bit FIFO): DA1, DA2
The application calls SupportsDAFuncGeneration() at runtime to verify that the selected module supports FIFO-based waveform generation.
Prerequisites
Before running this sample, make sure you have:
-
An NAI board with a supported DA module installed (see list above).
-
SSK 1.x installed on your development host.
-
The sample applications built. Refer to the SSK 1.x build instructions for your platform if you have not already compiled them.
How to Run
Launch the DA_FunctionGenerator executable from your build output directory. On startup the application looks for a configuration file (default_DAFuncGen.txt). On the first run, this file will not exist — the application will present an interactive board menu where you configure a board connection, card index, and module slot. You can save this configuration so that subsequent runs skip the menu and connect automatically. Once connected, the application prompts for a DA channel and then presents a command menu for selecting waveform types.
Board Connection and Module Selection
|
Note
|
This startup sequence is common to all NAI sample applications. The board connection and module selection code shown here is not specific to DA. |
The main() function follows a standard SSK 1.x startup flow:
-
Call
naiapp_RunBoardMenu()to load a saved configuration file (if one exists) or present the interactive board menu. The configuration file (default_DAFuncGen.txt) is not included with the SSK — it is created when the user saves their connection settings from the board menu. On the first run, the menu will always appear. -
Query the user for a card index with
naiapp_query_CardIndex(). -
Query for a module slot with
naiapp_query_ModuleNumber(). -
Retrieve the module ID with
naibrd_GetModuleID()and pass it toRun_DA_FunctionGenerator().
#if defined (__VXWORKS__)
int32_t DA_FunctionGenerator(void)
#else
int32_t main(void)
#endif
{
bool_t stop = FALSE;
int32_t cardIndex;
int32_t moduleCnt;
int32_t module;
uint32_t moduleID = 0;
int8_t inputBuffer[80];
int32_t inputResponseCnt;
if (naiapp_RunBoardMenu(CONFIG_FILE) == TRUE)
{
while (stop != TRUE)
{
/* Query the user for the card index */
stop = naiapp_query_CardIndex(naiapp_GetBoardCnt(), 0, &cardIndex);
if (stop != TRUE)
{
check_status(naibrd_GetModuleCount(cardIndex, &moduleCnt));
/* Query the user for the module number */
stop = naiapp_query_ModuleNumber(moduleCnt, 1, &module);
if (stop != TRUE)
{
moduleID = naibrd_GetModuleID(cardIndex, module);
if ((moduleID != 0))
{
Run_DA_FunctionGenerator(cardIndex, module, moduleID);
}
}
}
printf("\nType Q to quit or Enter key to restart application:\n");
stop = naiapp_query_ForQuitResponse(sizeof(inputBuffer), NAI_QUIT_CHAR, inputBuffer, &inputResponseCnt);
}
}
printf("\nType the Enter key to exit the program: ");
naiapp_query_ForQuitResponse(sizeof(inputBuffer), NAI_QUIT_CHAR, inputBuffer, &inputResponseCnt);
naiapp_access_CloseAllOpenCards();
return 0;
}
|
Important
|
Common connection errors you may encounter at this stage:
|
Program Structure
Entry Point
The program entry point is main() on most platforms and DA_FunctionGenerator() on VxWorks. After the board connection and module selection described above, main() delegates to Run_DA_FunctionGenerator(), which handles all waveform generation logic.
Module Detection and Channel Selection
Run_DA_FunctionGenerator() first calls SupportsDAFuncGeneration() to confirm the selected module supports FIFO-based DA output. If the check passes, it retrieves the channel count with naibrd_DA_GetChannelCount() and prompts the user to select a single DA channel. All subsequent waveform operations target that channel.
Menu-Driven Operation
Once a channel is selected, the application loads a command table and presents a menu with five waveform commands:
| Command | Description |
|---|---|
CONST VOLT |
DA Generate Constant Voltage |
SQUARE |
DA Generate Square Wave |
TRIANGLE |
DA Generate Triangle Wave |
SAWTOOTH |
DA Generate Saw Tooth Wave |
SINE WAVE |
DA Generate Sine Wave |
The command table is defined in the source as follows:
naiapp_cmdtbl_params_t DA_FuncGenMenuCmds[] = {
{"CONST VOLT", "DA Generate Constant Voltage", DA_FUNCGEN_CMD_CONSTANT_VOLT, DA_GenerateConstantVoltage},
{"SQUARE ", "DA Generate Square Wave", DA_FUNCGEN_CMD_SQUARE_WAVE, DA_GenerateSquareWave},
{"TRIANGLE ", "DA Generate Triangle Wave", DA_FUNCGEN_CMD_TRIANGLE_WAVE, DA_GenerateTriangleWave},
{"SAWTOOTH ", "DA Generate Saw Tooth Wave", DA_FUNCGEN_CMD_SAWTOOTH_WAVE, DA_GenerateSawToothWave},
{"SINE WAVE ", "DA Generate Sine Wave", DA_FUNCGEN_CMD_SINE_WAVE, DA_GenerateSineWave},
};
|
Note
|
After a waveform is triggered, the hardware plays it autonomously in repeat mode — no further CPU involvement is needed. The user can select a new waveform at any time from the menu; the application will clear the FIFO, load the new sample data, and re-trigger the output. |
FIFO Playback Model
The DA module’s FIFO plays samples at a fixed hardware output rate that is determined by the module generation. The application controls the output waveform frequency by varying the number of samples loaded into the FIFO for one complete cycle.
Output Data Rates
| Module Generation | Sample Period | Output Rate |
|---|---|---|
GEN3 (F1, F3, F5, J3, J5, J8) |
2.56 µs ( |
390.625 kHz ( |
GEN5 / DA1 |
2.50 µs ( |
400.000 kHz ( |
Sample Count Formula
The number of FIFO samples needed for one waveform cycle is:
Num_Samples = Output_Rate / Desired_Frequency
More samples per cycle means a lower output frequency; fewer samples means a higher frequency. Because the sample count must be an integer, the actual output frequency will differ slightly from the requested value. The actual frequency is:
Actual_Frequency = 1 / (Sample_Period × Num_Samples)
The function GetNumberOfDAFifoDataPoints() implements this calculation:
static void GetNumberOfDAFifoDataPoints(double dReqFreq, unsigned int *punNumFifoDataPoints, double *pdActualFrequency, uint32_t modid)
{
unsigned int unNumOfFifoSamples;
float64_t outputFrequency;
float64_t outputDateRate;
if (modid == NAI_MODULE_ID_DA1)
{
outputFrequency = DA_FIFO_GEN5_OUTPUT_FREQUENCY;
outputDateRate = DA_FIFO_GEN5_OUTPUT_DATARATE;
}
else
{
outputFrequency = DA_FIFO_GEN3_OUTPUT_FREQUENCY;
outputDateRate = DA_FIFO_GEN3_OUTPUT_DATARATE;
}
unNumOfFifoSamples = (unsigned int)(outputFrequency / dReqFreq);
if (unNumOfFifoSamples > DA_MAX_FIFO_SAMPLES)
{
unNumOfFifoSamples = DA_MAX_FIFO_SAMPLES;
}
*punNumFifoDataPoints = unNumOfFifoSamples;
*pdActualFrequency = (double)(1.0 / ((double)outputDateRate * (double)unNumOfFifoSamples));
}
Worked Example
For a 1 kHz sine wave on a GEN3 module:
-
Num_Samples = 390,625 / 1,000 = 390(truncated to integer). -
Actual_Frequency = 1 / (2.56 µs × 390) ≈ 1.0016 kHz.
The slight deviation from exactly 1 kHz is an inherent consequence of the integer sample count — the hardware cannot output a fractional number of samples.
Constraints
-
Maximum FIFO depth —
DA_MAX_FIFO_SAMPLESis defined as 26,213. If the calculated sample count exceeds this value, the function clamps it to 26,213. This sets the minimum achievable frequency at approximately 15 Hz. -
User-selectable range — the application limits the requested frequency to a maximum of 15,000 Hz.
-
Clamping behavior — when clamping occurs, the actual output frequency will be higher than the requested frequency because fewer samples per cycle means a shorter period.
One-Shot FIFO Load
The application computes one complete waveform cycle, loads the sample data into the FIFO, and issues a single software trigger. The hardware then replays the FIFO contents indefinitely in repeat mode — there is no real-time loop or continuous data feed from the host. This design keeps the CPU free after the initial load and eliminates any timing jitter that a software loop would introduce.
|
Note
|
Refer to the module manual for your specific DA module for exact output data rates and FIFO depth specifications. |
FIFO Initialization and Hardware Configuration
Before any waveform data is loaded, the application calls Initialize_DAFifo() to configure the DA channel’s FIFO engine. This function sets the output rate, FIFO control mode, trigger source, output range, and initial voltage — all of which must be correct before data is written. The configuration differs between GEN3 and GEN5 modules, so the function branches on the module ID.
The relevant constants are defined at the top of the source file:
#define DAFUNCGEN_FIFO_BUF_CTRL_GEN3_DA (NAI_DA_GEN3_FIFO_CTRL_ENABLE | NAI_DA_GEN3_FIFO_CTRL_MODE) /* 0x0003 */
#define DAFUNCGEN_FIFO_BUF_CTRL_GEN5_DA (NAI_DA_GEN5_FIFO_CTRL_ENABLE | NAI_DA_GEN5_FIFO_CTRL_REPEAT_MODE)
#define DAFUNCGEN_FIFO_TRIG_CTRL_GEN3_DA (NAI_DA_GEN3_FIFO_TRIG_SOFT | NAI_DA_GEN3_FIFO_TRIG_ENABLE) /* 0x0022 */
#define DAFUNCGEN_FIFO_TRIG_CTRL_GEN5_DA (NAI_DA_GEN5_FIFO_TRIG_SOFT) /* 0x0130 */
#define DAFUNCGEN_FIFO_RATE_GEN3_DA 0x0001
#define DAFUNCGEN_FIFO_RATE_GEN5_DA 0x61A80 /* 400,000 */
#define DAFUNCGEN_FIFO_DELAY 0x0000
The full initialization function:
static void Initialize_DAFifo(int32_t cardIndex, int32_t module, int32_t channel, uint32_t modid)
{
check_status(naibrd_DA_ClearFIFO(cardIndex, module, channel));
if (modid == NAI_MODULE_ID_DA1)
{
check_status(naibrd_DA_SetRange(cardIndex, module, channel, NAI_DA_DATA_VOLTAGE, NAI_DA_GEN5_RANGE_MODE_BIPOLAR, 10.0));
check_status(naibrd_DA_SetFIFOTrigCtrl(cardIndex, module, channel, DAFUNCGEN_FIFO_TRIG_CTRL_GEN5_DA));
check_status(naibrd_DA_SetFIFORate(cardIndex, module, channel, DAFUNCGEN_FIFO_RATE_GEN5_DA));
check_status(naibrd_DA_SetFIFOCtrl(cardIndex, module, channel, DAFUNCGEN_FIFO_BUF_CTRL_GEN5_DA));
}
else
{
check_status(naibrd_DA_SetFIFOTrigCtrl(cardIndex, module, channel, DAFUNCGEN_FIFO_TRIG_CTRL_GEN3_DA));
check_status(naibrd_DA_SetFIFORate(cardIndex, module, channel, DAFUNCGEN_FIFO_RATE_GEN3_DA));
check_status(naibrd_DA_SetFIFOCtrl(cardIndex, module, channel, DAFUNCGEN_FIFO_BUF_CTRL_GEN3_DA));
}
check_status(naibrd_DA_SetOutputTrigger(cardIndex, module, channel, NAI_DA_OUT_TRG_CONST_UPDATE));
check_status(naibrd_DA_SetFIFOSize(cardIndex, module, channel, 0x0000));
check_status(naibrd_DA_SetFIFODelay(cardIndex, module, channel, DAFUNCGEN_FIFO_DELAY));
/* Initialize Data Registers to Zero */
check_status(naibrd_DA_SetData(cardIndex, module, channel, NAI_DA_DATA_VOLTAGE, 0.0));
}
Initialization Steps
The setup sequence proceeds as follows:
-
Clear the FIFO —
naibrd_DA_ClearFIFO()flushes any stale data from the FIFO buffer. This is always the first step, regardless of module generation. -
Set the output range (GEN5 only) —
naibrd_DA_SetRange()configures the DA1 for bipolar +/-10 V operation (NAI_DA_GEN5_RANGE_MODE_BIPOLAR, 10.0). GEN3 modules do not require an explicit range call in this function. -
Configure the trigger source —
naibrd_DA_SetFIFOTrigCtrl()selects a software trigger. GEN3 usesNAI_DA_GEN3_FIFO_TRIG_SOFT | NAI_DA_GEN3_FIFO_TRIG_ENABLE(0x0022); GEN5 usesNAI_DA_GEN5_FIFO_TRIG_SOFT(0x0130). -
Set the FIFO output rate —
naibrd_DA_SetFIFORate()programs the sample playback rate. GEN3:0x0001; GEN5:0x61A80(400,000). -
Enable FIFO with repeat mode —
naibrd_DA_SetFIFOCtrl()enables the FIFO engine and selects repeat mode so the waveform plays continuously. GEN3:NAI_DA_GEN3_FIFO_CTRL_ENABLE | NAI_DA_GEN3_FIFO_CTRL_MODE(0x0003); GEN5:NAI_DA_GEN5_FIFO_CTRL_ENABLE | NAI_DA_GEN5_FIFO_CTRL_REPEAT_MODE. -
Set the output trigger mode —
naibrd_DA_SetOutputTrigger()selects constant update mode (NAI_DA_OUT_TRG_CONST_UPDATE) so the output updates on every sample clock. -
Set FIFO size —
naibrd_DA_SetFIFOSize()sets the buffer size to0x0000. The actual sample count is configured later when waveform data is loaded. -
Set FIFO delay —
naibrd_DA_SetFIFODelay()sets the output delay to zero (DAFUNCGEN_FIFO_DELAY = 0x0000), meaning playback begins immediately after the trigger. -
Initialize the output voltage —
naibrd_DA_SetData()writes 0.0 V to the data register so the output starts at a known level before the FIFO takes over.
|
Important
|
|
Waveform Generation
All five waveform generators follow the same structure:
-
Query parameters via
QueryForDAFuncGeneratorParameters()— frequency, amplitude, offset, and (for the square wave) initial output state. -
Calculate the FIFO sample count via
GetNumberOfDAFifoDataPoints(). -
Call
naibrd_DA_SetFIFOSize()to size the FIFO buffer accordingly. -
Loop over samples: compute the voltage for each point, convert it to raw format with
naibrd_DA_ConvertToDataRaw16(), and store the result in an array. -
Load the FIFO:
naibrd_DA_SetFIFORaw32()for DA1 modules ornaibrd_DA_SetFIFORaw16()for GEN3 modules. -
Verify the write count matches the expected sample count.
-
Trigger playback:
naibrd_DA_SoftwareTrigger()for most waveforms, ornaibrd_DA_SoftwareTriggerByChannel()for sine.
The following snippet from the square wave generator is representative of the voltage-to-raw conversion and FIFO load that all five generators share. Note the bipolar vs. unipolar handling and the 16-bit vs. 32-bit FIFO write paths:
naibrd_DA_ConvertToDataRaw16(modid, NAI_DA_DATA_VOLTAGE, damode, (nai_da_range_t)darange, dDAVoltage, &rawdata);
if (damode == NAI_DA_GEN3_RANGE_MODE_UNIPOLAR) /* Unipolar voltage */
{
hexVoltages16[j] = rawdata;
hexVoltages32[j] = rawdata;
}
else /* Bipolar Voltage*/
{
hexVoltages16[j] = rawdata;
hexVoltages32[j] = (uint32_t)((int16_t)rawdata);
}
For unipolar modules, the 16-bit raw value is stored directly into both arrays. For bipolar modules, the 16-bit value is sign-extended to 32 bits via the (int16_t) cast before being stored in the 32-bit array. After the loop, the appropriate FIFO write function is called based on the module generation:
if (modid == NAI_MODULE_ID_DA1)
check_status(naibrd_DA_SetFIFORaw32(cardIndex, module, channel, unNumFifoDataPoints, hexVoltages32, &unDataCnt));
else
check_status(naibrd_DA_SetFIFORaw16(cardIndex, module, channel, unNumFifoDataPoints, hexVoltages16, &unDataCnt));
Constant Voltage
The constant voltage generator outputs a steady DC level. Every sample in the FIFO is set to the same value — the requested amplitude. The FIFO is loaded with only three samples (the minimum needed for repeat-mode playback).
naibrd_DA_ConvertToDataRaw16(modid, NAI_DA_DATA_VOLTAGE, damode, (nai_da_range_t)darange, dDAAmplitude, &rawdata);
Because every sample is identical, the output is a flat DC voltage equal to dDAAmplitude. No frequency, offset, or initial-state parameters are used.
Square Wave
The square wave alternates between two voltage levels with a 50% duty cycle. The first half of the FIFO holds one level and the second half holds the other. At the midpoint, bOutputInitHigh is toggled to switch levels:
if (j == (int32_t)(unNumFifoDataPoints / 2))
/* Change the voltage level to output a square wave */
bOutputInitHigh = !bOutputInitHigh;
if (bOutputInitHigh)
dDAVoltage = dDAAmplitude - dDAOffset;
else
dDAVoltage = 0 - dDAOffset;
When bOutputInitHigh is true the output is amplitude - offset; when false it is 0 - offset. The bOutputInitHigh parameter (set by the user at startup) controls which phase comes first — setting it to true starts the waveform at the high level, while false starts at the low level.
Triangle Wave
The triangle wave consists of two linear ramps. The first half of the cycle descends from the peak and the second half ascends back up:
if (j < (int32_t)(unNumFifoDataPoints / 2))
dDAVoltage = (dDAAmplitude - 4.0*dDAAmplitude*((double)j / (double)unNumFifoDataPoints)) - dDAOffset;
else
dDAVoltage = (4.0*dDAAmplitude*((double)j / (double)unNumFifoDataPoints) - 3.0*dDAAmplitude) - dDAOffset;
In the first half (j < N/2), the voltage decreases linearly from +amplitude toward -amplitude. In the second half, it increases linearly back toward +amplitude. The factor of 4.0 ensures the full peak-to-peak swing is covered within each half-cycle. The DC offset is subtracted from both halves.
Sawtooth Wave
The sawtooth wave is a single linear ramp that sweeps from -amplitude to +amplitude over one full cycle:
/* Amp*(x/WaveCountMax) will adjust for frequency */
dDAVoltage = ((2.0*dDAAmplitude*((double)j / (double)unNumFifoDataPoints)) - dDAAmplitude) - dDAOffset;
At j = 0 the voltage is -amplitude - offset, and at j = N-1 the voltage approaches +amplitude - offset. When the FIFO repeats, the output snaps back to -amplitude, producing the characteristic sawtooth shape.
Sine Wave
The sine wave computes each sample using the C sin() function:
/* 360*(x/nNumFifoDataPoints) will spread out Sine wave to adjust for Frequency */
dDARadians = (360.0 * j * PI / 180.0) / (double)unNumFifoDataPoints;
dDAVoltage = (dDAAmplitude*sin(dDARadians)) - dDAOffset;
The expression 360.0 * j * PI / 180.0 converts the sample index to radians over one full cycle (0 to 2*PI). Dividing by unNumFifoDataPoints spreads the cycle across the entire FIFO buffer. The amplitude scales the sine output and the DC offset is subtracted.
|
Note
|
The sine wave generator uses naibrd_DA_SoftwareTriggerByChannel() to trigger playback on the specific channel, whereas all other waveform generators use the module-level naibrd_DA_SoftwareTrigger(). The sine generator also calls naibrd_DA_SetUseFifo() for GEN3 modules before triggering.
|
|
Important
|
|
Troubleshooting Reference
This table summarizes common errors and symptoms covered in the sections above. For detailed context on each entry, refer to the relevant section. Consult your module’s manual for hardware-specific FIFO specifications.
Diagnosing DA Waveform Output Issues
When a DA waveform doesn’t appear or looks wrong on the output, check three things:
-
FIFO was cleared before loading. Stale data from a previous waveform will corrupt the output. Call
naibrd_DA_ClearFIFO()before each new waveform load. -
FIFO control mode includes both enable and repeat. Without repeat mode, the FIFO plays once and stops. Verify the control word includes both the enable and repeat mode flags for your module generation.
-
Software trigger was issued after loading. The FIFO does not begin playback automatically. Call
naibrd_DA_SoftwareTrigger()(ornaibrd_DA_SoftwareTriggerByChannel()for sine) after loading all samples.
If the waveform appears but at the wrong frequency or amplitude, verify the sample count calculation matches the desired frequency and that the voltage range is set correctly for your amplitude + offset combination. Consult your module’s manual for FIFO hardware specifications and output rate details.
| Error / Symptom | Possible Causes | Suggested Resolution |
|---|---|---|
No board found or connection timeout |
Board not powered, missing configuration file, network issue |
Verify hardware and configuration. If file doesn’t exist, configure and save from board menu. |
Module not supported for function generation |
Module is not F1, F3, F5, J3, J5, J8, DA1, or DA2. |
Verify module type at the selected slot. |
No output on channel |
FIFO not enabled, trigger not issued, or FIFO empty |
Verify |
FIFO not cleared before loading |
Stale data from previous waveform corrupts output |
Call |
Output stops after one cycle |
Repeat mode not enabled in FIFO control word |
Verify control word includes repeat mode flag for your module generation (GEN3 vs GEN5). |
Waveform clipped or distorted |
Amplitude + offset exceeds configured voltage range |
Ensure the sum of amplitude and offset stays within the range set by |
Frequency doesn’t match requested |
Integer rounding in sample count calculation |
The actual frequency is reported after calculation. Use more samples (lower frequency) for better accuracy. |
FIFO write count mismatch |
Fewer samples written than expected |
Verify FIFO size was set correctly before loading. Check return count from |
Wrong data format for module |
Using 16-bit FIFO calls on DA1 (needs 32-bit) or vice versa |
DA1 (GEN5) uses |
Trigger not starting playback |
Wrong trigger function for waveform type |
Sine wave uses |
Full Source
The complete source for this sample is provided below for reference. The sections above explain each part in detail. This sample is entirely self-contained — all waveform generation is implemented inline with no external utility files.
Full Source — DA_FunctionGenerator.c (SSK 1.x)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <math.h>
#include <ctype.h>
/* Common Sample Program include files */
#include "include/naiapp_boardaccess_menu.h"
#include "include/naiapp_boardaccess_query.h"
#include "include/naiapp_boardaccess_access.h"
#include "include/naiapp_boardaccess_display.h"
#include "include/naiapp_boardaccess_utils.h"
/* naibrd include files */
#include "nai.h"
#include "naibrd.h"
#include "functions/naibrd_da.h"
#include "advanced/nai_ether_adv.h"
static const int8_t *CONFIG_FILE = (const int8_t *)"default_DAFuncGen.txt";
/* Function prototypes */
static int32_t Run_DA_FunctionGenerator(int32_t cardIndex, int32_t module, int32_t ModuleID);
static nai_status_t DA_GenerateConstantVoltage(int32_t paramCount, int32_t* p_params);
static nai_status_t DA_GenerateSquareWave(int32_t paramCount, int32_t* p_params);
static nai_status_t DA_GenerateTriangleWave(int32_t paramCount, int32_t* p_params);
static nai_status_t DA_GenerateSawToothWave(int32_t paramCount, int32_t* p_params);
static nai_status_t DA_GenerateSineWave(int32_t paramCount, int32_t* p_params);
static bool_t QueryForDAFuncGeneratorParameters(bool_t bQueryFreq, bool_t bQueryAmplitude, bool_t bQueryOffset, bool_t bQueryInitState,
float64_t *pdDAFrequency, float64_t *pdDAAmplitude, float64_t *pdDAOffset, bool_t *pbOutputInitialHigh, uint32_t modid);
static void Initialize_DAFifo(int32_t cardIndex, int32_t module, int32_t channel, uint32_t modid);
static void GetNumberOfDAFifoDataPoints(double dReqFreq, unsigned int *punNumFifoDataPoints, double *pdActualFrequency, uint32_t modid);
static bool_t SupportsDAFuncGeneration(uint32_t moduleID);
/****** Command Table *******/
enum dafuncgen_commands
{
DA_FUNCGEN_CMD_CONSTANT_VOLT,
DA_FUNCGEN_CMD_SQUARE_WAVE,
DA_FUNCGEN_CMD_TRIANGLE_WAVE,
DA_FUNCGEN_CMD_SAWTOOTH_WAVE,
DA_FUNCGEN_CMD_SINE_WAVE,
DA_FUNCGEN_CMD_COUNT
};
/****** Command Tables *******/
naiapp_cmdtbl_params_t DA_FuncGenMenuCmds[] = {
{"CONST VOLT", "DA Generate Constant Voltage", DA_FUNCGEN_CMD_CONSTANT_VOLT, DA_GenerateConstantVoltage},
{"SQUARE ", "DA Generate Square Wave", DA_FUNCGEN_CMD_SQUARE_WAVE, DA_GenerateSquareWave},
{"TRIANGLE ", "DA Generate Triangle Wave", DA_FUNCGEN_CMD_TRIANGLE_WAVE, DA_GenerateTriangleWave},
{"SAWTOOTH ", "DA Generate Saw Tooth Wave", DA_FUNCGEN_CMD_SAWTOOTH_WAVE, DA_GenerateSawToothWave},
{"SINE WAVE ", "DA Generate Sine Wave", DA_FUNCGEN_CMD_SINE_WAVE, DA_GenerateSineWave},
};
#define DAFUNCGEN_FIFO_CLEAR 0x0000
#define DAFUNCGEN_FIFO_BUF_CTRL_GEN3_DA (NAI_DA_GEN3_FIFO_CTRL_ENABLE | NAI_DA_GEN3_FIFO_CTRL_MODE) /* 0x0003 */
#define DAFUNCGEN_FIFO_BUF_CTRL_GEN5_DA (NAI_DA_GEN5_FIFO_CTRL_ENABLE | NAI_DA_GEN5_FIFO_CTRL_REPEAT_MODE)
#define DAFUNCGEN_FIFO_TRIG_CTRL_GEN3_DA (NAI_DA_GEN3_FIFO_TRIG_SOFT | NAI_DA_GEN3_FIFO_TRIG_ENABLE) /* 0x0022 */
#define DAFUNCGEN_FIFO_TRIG_CTRL_GEN5_DA (NAI_DA_GEN5_FIFO_TRIG_SOFT) /* 0x0130 */
#define DAFUNCGEN_FIFO_RATE_GEN3_DA 0x0001
#define DAFUNCGEN_FIFO_RATE_GEN5_DA 0x61A80 /*400,000 */
#define DAFUNCGEN_FIFO_DELAY 0x0000
#define DAFUNCGEN_FIFO_SOFT_TRIG 0x00FF
/************************************************************************
GEN 2 / GEN 3
-------------
The DA Fifo Size is 26,213
The DA Fifo Output Data Rate is 2.56 usec.
Thus the DA Fifo Output Frequency is (1/2.56 usec) or 390.625 kHz.
The Maximum Output Frequency is:
Max Request Frequency = DA Fifo Output Frequency/Fifo Size
= 390.625 kHz/26213
= 14.902 kHz
GEN 5
-----
The DA Fifo Size is 26,213
The DA Fifo Output Data Rate is 2.50 usec.
Thus the DA Fifo Output Frequency is (1/2.50 usec) or 400 kHz.
The Maximum Output Frequency is:
Max Request Frequency = DA Fifo Output Frequency/Fifo Size
= 400 kHz/26213
= 15.260
************************************************************************/
#define DA_MAX_FIFO_SAMPLES 26213
static const float64_t DA_FIFO_GEN3_OUTPUT_DATARATE = 2.56E-6;
static const float64_t DA_FIFO_GEN3_OUTPUT_FREQUENCY = 390.625E+3;
static const float64_t DA_FIFO_GEN5_OUTPUT_DATARATE = 2.50E-6;
static const float64_t DA_FIFO_GEN5_OUTPUT_FREQUENCY = 400.000E+3;
static const float64_t PI = 3.1415926536;
/**************************************************************************************************************/
/**
<summary>
DA_FunctionGenerator illustrates setting up the D/A FIFO to generate the following types of outputs:
1) Constant Voltage
2) Square Wave
3) Triangle Wave
4) Sawtooth Wave
5) Sine Wave
The following system configuration routines from the nai_sys_cfg.c file are called to assist with the configuration
setup for this program prior to calling the naibrd DA routines.
- ClearDeviceCfg
- QuerySystemCfg
- DisplayDeviceCfg
- GetBoardSNModCfg
- SaveDeviceCfg
</summary>
*/
/**************************************************************************************************************/
#if defined (__VXWORKS__)
int32_t DA_FunctionGenerator(void)
#else
int32_t main(void)
#endif
{
bool_t stop = FALSE;
int32_t cardIndex;
int32_t moduleCnt;
int32_t module;
uint32_t moduleID = 0;
int8_t inputBuffer[80];
int32_t inputResponseCnt;
if (naiapp_RunBoardMenu(CONFIG_FILE) == TRUE)
{
while (stop != TRUE)
{
/* Query the user for the card index */
stop = naiapp_query_CardIndex(naiapp_GetBoardCnt(), 0, &cardIndex);
if (stop != TRUE)
{
check_status(naibrd_GetModuleCount(cardIndex, &moduleCnt));
/* Query the user for the module number */
stop = naiapp_query_ModuleNumber(moduleCnt, 1, &module);
if (stop != TRUE)
{
moduleID = naibrd_GetModuleID(cardIndex, module);
if ((moduleID != 0))
{
Run_DA_FunctionGenerator(cardIndex, module, moduleID);
}
}
}
printf("\nType Q to quit or Enter key to restart application:\n");
stop = naiapp_query_ForQuitResponse(sizeof(inputBuffer), NAI_QUIT_CHAR, inputBuffer, &inputResponseCnt);
}
}
printf("\nType the Enter key to exit the program: ");
naiapp_query_ForQuitResponse(sizeof(inputBuffer), NAI_QUIT_CHAR, inputBuffer, &inputResponseCnt);
naiapp_access_CloseAllOpenCards();
return 0;
}
/**************************************************************************************************************/
/**
<summary>
</summary>
*/
/**************************************************************************************************************/
static int32_t Run_DA_FunctionGenerator(int32_t cardIndex, int32_t module, int32_t ModuleID)
{
bool_t bQuit = FALSE;
bool_t bContinue = TRUE;
bool_t bCmdFound = FALSE;
int32_t MaxChannel;
uint32_t moduleID;
int32_t cmd;
int32_t channel;
int8_t inputBuffer[80];
int32_t inputResponseCnt;
naiapp_AppParameters_t da_params;
p_naiapp_AppParameters_t da_funcgen_params = &da_params;
da_funcgen_params->cardIndex = cardIndex;
da_funcgen_params->module = module;
da_funcgen_params->modId = ModuleID;
if (!bQuit)
{
/* Get the number of D/A channels on the module */
moduleID = naibrd_GetModuleID(cardIndex, module);
if (SupportsDAFuncGeneration(moduleID))
{
MaxChannel = naibrd_DA_GetChannelCount(moduleID);
/* Get the DA channel */
printf("\nD/A Setup\n");
printf("---------------------\n");
printf("D/A Channel Selection:");
bQuit = naiapp_query_ChannelNumber(MaxChannel, 1, &channel);
da_funcgen_params->channel = channel;
bContinue = FALSE;
}
else
printf("ERROR: Module selected does not support DA Functionality\n");
}
else
bContinue = FALSE;
if (bQuit)
bContinue = FALSE;
else
bContinue = TRUE;
naiapp_utils_LoadParamMenuCommands(DA_FUNCGEN_CMD_COUNT, DA_FuncGenMenuCmds);
while (bContinue)
{
naiapp_display_ParamMenuCommands((int8_t *)"DA Function Generator Menu");
printf("\nType DA command or %c to quit : ", NAI_QUIT_CHAR);
bQuit = naiapp_query_ForQuitResponse(sizeof(inputBuffer), NAI_QUIT_CHAR, inputBuffer, &inputResponseCnt);
if (!bQuit)
{
if (inputResponseCnt > 0)
{
bCmdFound = naiapp_utils_GetParamMenuCmdNum(inputResponseCnt, inputBuffer, &cmd);
if (bCmdFound)
{
switch (cmd)
{
case DA_FUNCGEN_CMD_CONSTANT_VOLT:
case DA_FUNCGEN_CMD_SQUARE_WAVE:
case DA_FUNCGEN_CMD_TRIANGLE_WAVE:
case DA_FUNCGEN_CMD_SAWTOOTH_WAVE:
case DA_FUNCGEN_CMD_SINE_WAVE:
DA_FuncGenMenuCmds[cmd].func(APP_PARAM_COUNT, (int32_t*)da_funcgen_params);
break;
default:
printf("Invalid command entered");
break;
}
}
else
printf("Invalid command entered\n");
}
}
else
bContinue = FALSE;
}
return cardIndex;
}
static nai_status_t DA_GenerateConstantVoltage(int32_t paramCount, int32_t* p_params)
{
bool_t bQuit = FALSE;
uint32_t modid;
float64_t dDAFrequency = 0.0;
float64_t dDAAmplitude = 0.0;
float64_t dDAOffset = 0.0;
bool_t bOutputInitHigh = FALSE;
p_naiapp_AppParameters_t da_funcgen_params = (p_naiapp_AppParameters_t)p_params;
int32_t cardIndex = da_funcgen_params->cardIndex;
int32_t module = da_funcgen_params->module;
int32_t channel= da_funcgen_params->channel;
uint32_t unNumFifoDataPoints; /* Number of Data Points to fill in the fifo */
uint32_t unDataCnt;
nai_da_range_mode_t damode;
float64_t darange;
uint16_t rawdata;
uint16_t hexVoltages16[1024];
uint32_t hexVoltages32[1024];
int32_t j;
#if defined (WIN32)
UNREFERENCED_PARAMETER(paramCount);
#endif
modid = naibrd_GetModuleID(cardIndex, module);
bQuit = QueryForDAFuncGeneratorParameters(FALSE, TRUE, FALSE, FALSE,
&dDAFrequency, &dDAAmplitude, &dDAOffset, &bOutputInitHigh, modid);
if (!bQuit)
{
Initialize_DAFifo(cardIndex, module, channel, modid);
/* Fill the DA Fifo with the Constant Voltage Waveform Data */
unNumFifoDataPoints = 3;
check_status(naibrd_DA_SetFIFOSize(cardIndex, module, channel, unNumFifoDataPoints));
check_status(naibrd_DA_GetRange(cardIndex, module, channel, NAI_DA_DATA_VOLTAGE, &damode, &darange));
for (j = 0; j < (int32_t)unNumFifoDataPoints; j++)
{
naibrd_DA_ConvertToDataRaw16(modid, NAI_DA_DATA_VOLTAGE, damode, (nai_da_range_t)darange, dDAAmplitude, &rawdata);
if (damode == NAI_DA_GEN3_RANGE_MODE_UNIPOLAR) /* Unipolar voltage */
{
hexVoltages16[j] = rawdata;
hexVoltages32[j] = rawdata;
}
else /* Bipolar Voltage*/
{
hexVoltages16[j] = rawdata;
hexVoltages32[j] = (uint32_t)((int16_t)rawdata);
}
}
if (modid == NAI_MODULE_ID_DA1)
check_status(naibrd_DA_SetFIFORaw32(cardIndex, module, channel, unNumFifoDataPoints, hexVoltages32, &unDataCnt));
else
check_status(naibrd_DA_SetFIFORaw16(cardIndex, module, channel, unNumFifoDataPoints, hexVoltages16, &unDataCnt));
if (unDataCnt != unNumFifoDataPoints)
printf("Problem with writing to FIFO - Elements in Fifo: Expected %d, Actual %d\n", unNumFifoDataPoints, unDataCnt);
else
printf("FIFO Data Population is complete.\n");
printf("\nDA channel %d for Module %d has been programmed to output %f volts.\n\n",
channel, module, dDAAmplitude);
/* Trigger the FIFO to start outputting the voltage */
if (modid == NAI_MODULE_ID_DA1)
check_status(naibrd_DA_SetFIFOTrigCtrl(cardIndex, module, channel, NAI_DA_GEN5_FIFO_TRIG_ENABLE | DAFUNCGEN_FIFO_TRIG_CTRL_GEN5_DA));
check_status(naibrd_DA_SoftwareTrigger(cardIndex, module));
}
return (bQuit) ? NAI_ERROR_UNKNOWN : NAI_SUCCESS;
}
static nai_status_t DA_GenerateSquareWave(int32_t paramCount, int32_t* p_params)
{
bool_t bQuit = FALSE;
uint32_t modid;
float64_t dDAFrequency = 0.0;
float64_t dDAAmplitude = 0.0;
float64_t dDAOffset = 0.0;
bool_t bOutputInitHigh = FALSE;
p_naiapp_AppParameters_t da_funcgen_params = (p_naiapp_AppParameters_t)p_params;
int32_t cardIndex = da_funcgen_params->cardIndex;
int32_t module = da_funcgen_params->module;
int32_t channel= da_funcgen_params->channel;
float64_t dDAActualFrequency = 0.0, dDAVoltage;
uint32_t unNumFifoDataPoints; /* Number of Data Points to fill in the fifo */
uint32_t unDataCnt;
nai_da_range_mode_t damode;
float64_t darange;
uint16_t rawdata;
uint16_t hexVoltages16[DA_MAX_FIFO_SAMPLES];
uint32_t hexVoltages32[DA_MAX_FIFO_SAMPLES];
int32_t j;
#if defined (WIN32)
UNREFERENCED_PARAMETER(paramCount);
#endif
modid = naibrd_GetModuleID(cardIndex, module);
bQuit = QueryForDAFuncGeneratorParameters(TRUE, TRUE, TRUE, TRUE,
&dDAFrequency, &dDAAmplitude, &dDAOffset, &bOutputInitHigh, modid);
if (!bQuit)
{
Initialize_DAFifo(cardIndex, module, channel, modid);
/* Fill the DA Fifo with one iteration of the Square Waveform Data */
GetNumberOfDAFifoDataPoints(dDAFrequency, &unNumFifoDataPoints, &dDAActualFrequency, modid);
check_status(naibrd_DA_SetFIFOSize(cardIndex, module, channel, unNumFifoDataPoints));
check_status(naibrd_DA_GetRange(cardIndex, module, channel, NAI_DA_DATA_VOLTAGE, &damode, &darange));
printf("\nPlease wait while FIFO data is being populated for channel %d...\n", channel);
for (j = 0; j < (int32_t)unNumFifoDataPoints; j++)
{
if (j == (int32_t)(unNumFifoDataPoints / 2))
/* Change the voltage level to output a square wave */
bOutputInitHigh = !bOutputInitHigh;
if (bOutputInitHigh)
dDAVoltage = dDAAmplitude - dDAOffset;
else
dDAVoltage = 0 - dDAOffset;
naibrd_DA_ConvertToDataRaw16(modid, NAI_DA_DATA_VOLTAGE, damode, (nai_da_range_t)darange, dDAVoltage, &rawdata);
if (damode == NAI_DA_GEN3_RANGE_MODE_UNIPOLAR) /* Unipolar voltage */
{
hexVoltages16[j] = rawdata;
hexVoltages32[j] = rawdata;
}
else /* Bipolar Voltage*/
{
hexVoltages16[j] = rawdata;
hexVoltages32[j] = (uint32_t)((int16_t)rawdata);
}
}
if (modid == NAI_MODULE_ID_DA1)
check_status(naibrd_DA_SetFIFORaw32(cardIndex, module, channel, unNumFifoDataPoints, hexVoltages32, &unDataCnt));
else
check_status(naibrd_DA_SetFIFORaw16(cardIndex, module, channel, unNumFifoDataPoints, hexVoltages16, &unDataCnt));
if (unDataCnt != unNumFifoDataPoints)
printf("Problem with writing to FIFO - Elements in Fifo: Expected %d, Actual %d\n", unNumFifoDataPoints, unDataCnt);
else
printf("FIFO Data Population is complete.\n");
printf("\nDA channel %d for Module %d have been programmed to output a %f Hz square wave\n with an amplitude of %f volts offset by %f volts.\n\n",
channel, module, dDAActualFrequency, dDAAmplitude, dDAOffset);
/* Trigger the FIFO to start outputting the voltage */
if (modid == NAI_MODULE_ID_DA1)
check_status(naibrd_DA_SetFIFOTrigCtrl(cardIndex, module, channel, NAI_DA_GEN5_FIFO_TRIG_ENABLE | DAFUNCGEN_FIFO_TRIG_CTRL_GEN5_DA));
check_status(naibrd_DA_SoftwareTrigger(cardIndex, module));
}
return (bQuit) ? NAI_ERROR_UNKNOWN : NAI_SUCCESS;
}
static nai_status_t DA_GenerateTriangleWave(int32_t paramCount, int32_t* p_params)
{
bool_t bQuit = FALSE;
uint32_t modid;
float64_t dDAFrequency = 0.0;
float64_t dDAAmplitude = 0.0;
float64_t dDAOffset = 0.0;
bool_t bOutputInitHigh = FALSE;
p_naiapp_AppParameters_t da_funcgen_params = (p_naiapp_AppParameters_t)p_params;
int32_t cardIndex = da_funcgen_params->cardIndex;
int32_t module = da_funcgen_params->module;
int32_t channel= da_funcgen_params->channel;
float64_t dDAActualFrequency = 0.0, dDAVoltage;
uint32_t unNumFifoDataPoints; /* Number of Data Points to fill in the fifo */
uint32_t unDataCnt;
nai_da_range_mode_t damode;
float64_t darange;
uint16_t rawdata;
uint16_t hexVoltages16[DA_MAX_FIFO_SAMPLES];
uint32_t hexVoltages32[DA_MAX_FIFO_SAMPLES];
int32_t j;
#if defined (WIN32)
UNREFERENCED_PARAMETER(paramCount);
#endif
modid = naibrd_GetModuleID(cardIndex, module);
bQuit = QueryForDAFuncGeneratorParameters(TRUE, TRUE, TRUE, FALSE,
&dDAFrequency, &dDAAmplitude, &dDAOffset, &bOutputInitHigh, modid);
if (!bQuit)
{
Initialize_DAFifo(cardIndex, module, channel, modid);
/* Fill the DA Fifo with one iteration of the Square Waveform Data */
GetNumberOfDAFifoDataPoints(dDAFrequency, &unNumFifoDataPoints, &dDAActualFrequency, modid);
check_status(naibrd_DA_SetFIFOSize(cardIndex, module, channel, unNumFifoDataPoints));
check_status(naibrd_DA_GetRange(cardIndex, module, channel, NAI_DA_DATA_VOLTAGE, &damode, &darange));
printf("\nPlease wait while FIFO data is being populated for channel %d...\n", channel);
for (j = 0; j < (int32_t)unNumFifoDataPoints; j++)
{
if (j < (int32_t)(unNumFifoDataPoints / 2))
dDAVoltage = (dDAAmplitude - 4.0*dDAAmplitude*((double)j / (double)unNumFifoDataPoints)) - dDAOffset;
else
dDAVoltage = (4.0*dDAAmplitude*((double)j / (double)unNumFifoDataPoints) - 3.0*dDAAmplitude) - dDAOffset;
naibrd_DA_ConvertToDataRaw16(modid, NAI_DA_DATA_VOLTAGE, damode, (nai_da_range_t)darange, dDAVoltage, &rawdata);
if (damode == NAI_DA_GEN3_RANGE_MODE_UNIPOLAR) /* Unipolar voltage */
{
hexVoltages16[j] = rawdata;
hexVoltages32[j] = rawdata;
}
else /* Bipolar Voltage*/
{
hexVoltages16[j] = rawdata;
hexVoltages32[j] = (uint32_t)((int16_t)rawdata);
}
}
if (modid == NAI_MODULE_ID_DA1)
check_status(naibrd_DA_SetFIFORaw32(cardIndex, module, channel, unNumFifoDataPoints, hexVoltages32, &unDataCnt));
else
check_status(naibrd_DA_SetFIFORaw16(cardIndex, module, channel, unNumFifoDataPoints, hexVoltages16, &unDataCnt));
if (unDataCnt != unNumFifoDataPoints)
printf("Problem with writing to FIFO - Elements in Fifo: Expected %d, Actual %d\n", unNumFifoDataPoints, unDataCnt);
else
printf("FIFO Data Population is complete.\n");
printf("\nDA channel %d for Module %d have been programmed to output a %f Hz triangle wave\n with an amplitude of %f volts offset by %f volts.\n\n",
channel, module, dDAActualFrequency, dDAAmplitude, dDAOffset);
/* Trigger the FIFO to start outputting the voltage */
if (modid == NAI_MODULE_ID_DA1) /*if its a DA1 you need to enable the trigger to allow outputting*/
check_status(naibrd_DA_SetFIFOTrigCtrl(cardIndex, module, channel, NAI_DA_GEN5_FIFO_TRIG_ENABLE | DAFUNCGEN_FIFO_TRIG_CTRL_GEN5_DA));
check_status(naibrd_DA_SoftwareTrigger(cardIndex, module));
}
return (bQuit) ? NAI_ERROR_UNKNOWN : NAI_SUCCESS;
}
static nai_status_t DA_GenerateSawToothWave(int32_t paramCount, int32_t* p_params)
{
bool_t bQuit = FALSE;
uint32_t modid;
float64_t dDAFrequency = 0.0;
float64_t dDAAmplitude = 0.0;
float64_t dDAOffset = 0.0;
bool_t bOutputInitHigh = FALSE;
p_naiapp_AppParameters_t da_funcgen_params = (p_naiapp_AppParameters_t)p_params;
int32_t cardIndex = da_funcgen_params->cardIndex;
int32_t module = da_funcgen_params->module;
int32_t channel= da_funcgen_params->channel;
float64_t dDAActualFrequency = 0.0, dDAVoltage;
uint32_t unNumFifoDataPoints; /* Number of Data Points to fill in the fifo */
uint32_t unDataCnt;
nai_da_range_mode_t damode;
float64_t darange;
uint16_t rawdata;
uint16_t hexVoltages16[DA_MAX_FIFO_SAMPLES];
uint32_t hexVoltages32[DA_MAX_FIFO_SAMPLES];
int32_t j;
#if defined (WIN32)
UNREFERENCED_PARAMETER(paramCount);
#endif
modid = naibrd_GetModuleID(cardIndex, module);
bQuit = QueryForDAFuncGeneratorParameters(TRUE, TRUE, TRUE, FALSE,
&dDAFrequency, &dDAAmplitude, &dDAOffset, &bOutputInitHigh, modid);
if (!bQuit)
{
Initialize_DAFifo(cardIndex, module, channel, modid);
/* Fill the DA Fifo with one iteration of the Square Waveform Data */
GetNumberOfDAFifoDataPoints(dDAFrequency, &unNumFifoDataPoints, &dDAActualFrequency, modid);
check_status(naibrd_DA_SetFIFOSize(cardIndex, module, channel, unNumFifoDataPoints));
check_status(naibrd_DA_GetRange(cardIndex, module, channel, NAI_DA_DATA_VOLTAGE, &damode, &darange));
printf("\nPlease wait while FIFO data is being populated for channel %d...\n", channel);
for (j = 0; j < (int32_t)unNumFifoDataPoints; j++)
{
/* Amp*(x/WaveCountMax) will adjust for frequency */
dDAVoltage = ((2.0*dDAAmplitude*((double)j / (double)unNumFifoDataPoints)) - dDAAmplitude) - dDAOffset;
naibrd_DA_ConvertToDataRaw16(modid, NAI_DA_DATA_VOLTAGE, damode, (nai_da_range_t)darange, dDAVoltage, &rawdata);
if (damode == NAI_DA_GEN3_RANGE_MODE_UNIPOLAR) /* Unipolar voltage */
{
hexVoltages16[j] = rawdata;
hexVoltages32[j] = rawdata;
}
else /* Bipolar Voltage*/
{
hexVoltages16[j] = rawdata;
hexVoltages32[j] = (uint32_t)((int16_t)rawdata);
}
}
if (modid == NAI_MODULE_ID_DA1)
check_status(naibrd_DA_SetFIFORaw32(cardIndex, module, channel, unNumFifoDataPoints, hexVoltages32, &unDataCnt));
else
check_status(naibrd_DA_SetFIFORaw16(cardIndex, module, channel, unNumFifoDataPoints, hexVoltages16, &unDataCnt));
if (unDataCnt != unNumFifoDataPoints)
printf("Problem with writing to FIFO - Elements in Fifo: Expected %d, Actual %d\n", unNumFifoDataPoints, unDataCnt);
else
printf("FIFO Data Population is complete.\n");
printf("\nDA channel %d for Module %d have been programmed to output a %f Hz saw-tooth wave\n with an amplitude of %f volts offset by %f volts.\n\n",
channel, module, dDAActualFrequency, dDAAmplitude, dDAOffset);
/* Trigger the FIFO to start outputting the voltage */
if (modid == NAI_MODULE_ID_DA1)
check_status(naibrd_DA_SetFIFOTrigCtrl(cardIndex, module, channel, NAI_DA_GEN5_FIFO_TRIG_ENABLE | DAFUNCGEN_FIFO_TRIG_CTRL_GEN5_DA));
check_status(naibrd_DA_SoftwareTrigger(cardIndex, module));
}
return (bQuit) ? NAI_ERROR_UNKNOWN : NAI_SUCCESS;
}
static nai_status_t DA_GenerateSineWave(int32_t paramCount, int32_t* p_params)
{
bool_t bQuit = FALSE;
uint32_t modid;
float64_t dDAFrequency = 0.0;
float64_t dDAAmplitude = 0.0;
float64_t dDAOffset = 0.0;
bool_t bOutputInitHigh = FALSE;
p_naiapp_AppParameters_t da_funcgen_params = (p_naiapp_AppParameters_t)p_params;
int32_t cardIndex = da_funcgen_params->cardIndex;
int32_t module = da_funcgen_params->module;
int32_t channel= da_funcgen_params->channel;
float64_t dDAActualFrequency = 0.0, dDAVoltage, dDARadians;
uint32_t unNumFifoDataPoints; /* Number of Data Points to fill in the fifo */
uint32_t unDataCnt;
nai_da_range_mode_t damode;
float64_t darange;
uint16_t rawdata;
uint16_t hexVoltages16[DA_MAX_FIFO_SAMPLES];
uint32_t hexVoltages32[DA_MAX_FIFO_SAMPLES];
int32_t j;
#if defined (WIN32)
UNREFERENCED_PARAMETER(paramCount);
#endif
modid = naibrd_GetModuleID(cardIndex, module);
bQuit = QueryForDAFuncGeneratorParameters(TRUE, TRUE, TRUE, FALSE,
&dDAFrequency, &dDAAmplitude, &dDAOffset, &bOutputInitHigh, modid);
if (!bQuit)
{
Initialize_DAFifo(cardIndex, module, channel, modid);
/* Fill the DA Fifo with one iteration of the Square Waveform Data */
GetNumberOfDAFifoDataPoints(dDAFrequency, &unNumFifoDataPoints, &dDAActualFrequency, modid);
check_status(naibrd_DA_SetFIFOSize(cardIndex, module, channel, unNumFifoDataPoints));
check_status(naibrd_DA_GetRange(cardIndex, module, channel, NAI_DA_DATA_VOLTAGE, &damode, &darange));
printf("\nPlease wait while FIFO data is being populated for channel %d...\n", channel);
for (j = 0; j < (int32_t)unNumFifoDataPoints; j++)
{
/* 360*(x/nNumFifoDataPoints) will spread out Sine wave to adjust for Frequency */
dDARadians = (360.0 * j * PI / 180.0) / (double)unNumFifoDataPoints;
dDAVoltage = (dDAAmplitude*sin(dDARadians)) - dDAOffset;
naibrd_DA_ConvertToDataRaw16(modid, NAI_DA_DATA_VOLTAGE, damode, (nai_da_range_t)darange, dDAVoltage, &rawdata);
if (damode == NAI_DA_GEN3_RANGE_MODE_UNIPOLAR) /* Unipolar voltage */
{
hexVoltages16[j] = rawdata;
hexVoltages32[j] = rawdata;
}
else /* Bipolar Voltage*/
{
hexVoltages16[j] = rawdata;
hexVoltages32[j] = (uint32_t)((int16_t)rawdata);
}
}
if (modid == NAI_MODULE_ID_DA1)
check_status(naibrd_DA_SetFIFORaw32(cardIndex, module, channel, unNumFifoDataPoints, hexVoltages32, &unDataCnt));
else
check_status(naibrd_DA_SetFIFORaw16(cardIndex, module, channel, unNumFifoDataPoints, hexVoltages16, &unDataCnt));
if (unDataCnt != unNumFifoDataPoints)
printf("Problem with writing to FIFO - Elements in Fifo: Expected %d, Actual %d\n", unNumFifoDataPoints, unDataCnt);
else
printf("FIFO Data Population is complete.\n");
printf("\nDA channel %d for Module %d have been programmed to output a %f Hz sine wave\n with an amplitude of %f volts offset by %f volts.\n\n",
channel, module, dDAActualFrequency, dDAAmplitude, dDAOffset);
/* Trigger the FIFO to start outputting the voltage */
if (modid == NAI_MODULE_ID_DA1)
check_status(naibrd_DA_SetFIFOTrigCtrl(cardIndex, module, channel, NAI_DA_GEN5_FIFO_TRIG_ENABLE | DAFUNCGEN_FIFO_TRIG_CTRL_GEN5_DA));
else
check_status(naibrd_DA_SetUseFifo(cardIndex, module, channel, TRUE));
check_status(naibrd_DA_SoftwareTriggerByChannel(cardIndex, module, channel));
}
return (bQuit) ? NAI_ERROR_UNKNOWN : NAI_SUCCESS;
}
static void Initialize_DAFifo(int32_t cardIndex, int32_t module, int32_t channel, uint32_t modid)
{
check_status(naibrd_DA_ClearFIFO(cardIndex, module, channel));
if (modid == NAI_MODULE_ID_DA1)
{
check_status(naibrd_DA_SetRange(cardIndex, module, channel, NAI_DA_DATA_VOLTAGE, NAI_DA_GEN5_RANGE_MODE_BIPOLAR, 10.0));
check_status(naibrd_DA_SetFIFOTrigCtrl(cardIndex, module, channel, DAFUNCGEN_FIFO_TRIG_CTRL_GEN5_DA));
check_status(naibrd_DA_SetFIFORate(cardIndex, module, channel, DAFUNCGEN_FIFO_RATE_GEN5_DA));
check_status(naibrd_DA_SetFIFOCtrl(cardIndex, module, channel, DAFUNCGEN_FIFO_BUF_CTRL_GEN5_DA));
}
else
{
check_status(naibrd_DA_SetFIFOTrigCtrl(cardIndex, module, channel, DAFUNCGEN_FIFO_TRIG_CTRL_GEN3_DA));
check_status(naibrd_DA_SetFIFORate(cardIndex, module, channel, DAFUNCGEN_FIFO_RATE_GEN3_DA));
check_status(naibrd_DA_SetFIFOCtrl(cardIndex, module, channel, DAFUNCGEN_FIFO_BUF_CTRL_GEN3_DA));
}
check_status(naibrd_DA_SetOutputTrigger(cardIndex, module, channel, NAI_DA_OUT_TRG_CONST_UPDATE));
check_status(naibrd_DA_SetFIFOSize(cardIndex, module, channel, 0x0000));
check_status(naibrd_DA_SetFIFODelay(cardIndex, module, channel, DAFUNCGEN_FIFO_DELAY));
/* Initialize Data Registers to Zero */
check_status(naibrd_DA_SetData(cardIndex, module, channel, NAI_DA_DATA_VOLTAGE, 0.0));
}
static bool_t QueryForDAFuncGeneratorParameters(bool_t bQueryFreq, bool_t bQueryAmplitude, bool_t bQueryOffset, bool_t bQueryInitState,
float64_t *pdDAFrequency, float64_t *pdDAAmplitude, float64_t *pdDAOffset, bool_t *pbOutputInitialHigh, uint32_t modid)
{
bool_t bQuit = FALSE;
bool_t bContinue = TRUE;
bool_t bInvalidEntry = FALSE;
float64_t DA_MIN_REQUEST_FREQ;
float64_t dValue = 0.0;
int32_t nValue;
int8_t inputBuffer[80];
int32_t inputResponseCnt;
if (modid == NAI_MODULE_ID_DA1)
DA_MIN_REQUEST_FREQ = (float64_t)(DA_FIFO_GEN5_OUTPUT_FREQUENCY / DA_MAX_FIFO_SAMPLES);
else
DA_MIN_REQUEST_FREQ = (float64_t)(DA_FIFO_GEN3_OUTPUT_FREQUENCY / DA_MAX_FIFO_SAMPLES);
/* Initialize the output values */
*pdDAFrequency = 0.0;
*pdDAAmplitude = 0.0;
*pdDAOffset = 0.0;
if (bQueryFreq)
{
bContinue = TRUE;
while (bContinue)
{
bInvalidEntry = FALSE;
printf("\nPlease enter Waveform Frequency (Valid Value: %4.0f Hz to 15000 Hz) : ", DA_MIN_REQUEST_FREQ);
bQuit = naiapp_query_ForQuitResponse(sizeof(inputBuffer), NAI_QUIT_CHAR, inputBuffer, &inputResponseCnt);
if (!bQuit)
{
if (inputResponseCnt > 0)
{
dValue = atof((const char *)inputBuffer);
if ((dValue >= DA_MIN_REQUEST_FREQ) && (dValue <= 15000.0))
{
*pdDAFrequency = dValue;
bContinue = FALSE;
}
else
bInvalidEntry = TRUE;
}
else
bInvalidEntry = TRUE;
if (bInvalidEntry)
printf("ERROR: Invalid entry\n");
}
else
bContinue = FALSE;
}
}
if (!bQuit && bQueryAmplitude)
{
bContinue = TRUE;
while (bContinue)
{
bInvalidEntry = FALSE;
printf("\nPlease enter Waveform Amplitude Voltage (Valid Value: -10V to 10V) : ");
bQuit = naiapp_query_ForQuitResponse(sizeof(inputBuffer), NAI_QUIT_CHAR, inputBuffer, &inputResponseCnt);
if (!bQuit)
{
if (inputResponseCnt > 0)
{
dValue = atof((const char *)inputBuffer);
if ((dValue >= -10) && (dValue <= 10.0))
{
*pdDAAmplitude = dValue;
bContinue = FALSE;
}
else
bInvalidEntry = TRUE;
}
else
bInvalidEntry = TRUE;
if (bInvalidEntry)
printf("ERROR: Invalid entry\n");
}
else
bContinue = FALSE;
}
}
if (!bQuit && bQueryOffset)
{
bContinue = TRUE;
while (bContinue)
{
bInvalidEntry = FALSE;
printf("\nPlease enter Waveform Offset Voltage (Valid Value: -10V to 10V) : ");
bQuit = naiapp_query_ForQuitResponse(sizeof(inputBuffer), NAI_QUIT_CHAR, inputBuffer, &inputResponseCnt);
if (!bQuit)
{
if (inputResponseCnt > 0)
{
dValue = atof((const char *)inputBuffer);
if ((dValue >= -10.0) && (dValue <= 10.0))
{
*pdDAOffset = dValue;
bContinue = FALSE;
}
else
bInvalidEntry = TRUE;
}
else
bInvalidEntry = TRUE;
if (bInvalidEntry)
printf("ERROR: Invalid entry\n");
}
else
bContinue = FALSE;
}
}
if (!bQuit && bQueryInitState)
{
bContinue = TRUE;
while (bContinue)
{
bInvalidEntry = FALSE;
printf("\nPlease enter Waveform Initial Output State (Valid Value: 1 (High) or 0 (Low)) : ");
bQuit = naiapp_query_ForQuitResponse(sizeof(inputBuffer), NAI_QUIT_CHAR, inputBuffer, &inputResponseCnt);
if (!bQuit)
{
if (inputResponseCnt > 0)
{
nValue = atol((const char *)inputBuffer);
if ((nValue == 0) || (nValue == 1))
{
if (nValue == 0)
*pbOutputInitialHigh = FALSE;
else
*pbOutputInitialHigh = TRUE;
bContinue = FALSE;
}
else
bInvalidEntry = TRUE;
}
else
bInvalidEntry = TRUE;
if (bInvalidEntry)
printf("ERROR: Invalid entry\n");
}
else
bContinue = FALSE;
}
}
return bQuit;
}
static void GetNumberOfDAFifoDataPoints(double dReqFreq, unsigned int *punNumFifoDataPoints, double *pdActualFrequency, uint32_t modid)
{
/*
FOR ALL GEN 3 D/A modules
-------------------------
Compute the number of data points to fill in the fifo based on
the requested frequency.
The DA Fifo Output Data Rate is 2.56 usec.
Thus the DA Fifo Output Frequency is (1/2.56 usec) or 390.625 kHz.
The number of data points is determined by:
NumFifoDataPoints = DA Fifo Output Freq/Requested Frequency
For example, for a 1kHz wave:
NumFifoDataPoints = 390.625 kHz/1.0 kHz = 390
The actual frequency of the waveform is determined as follows:
ActualFreq = 1/(DA Fifo Output Data Rate * NumFifoDataPoints)
For example, for a 1kHz wave:
ActualFreq = 1/(2.56 usec * 390) = 1.0016 kHz
FOR THE DA1
-----------
Fifo Output Frequency = 400 Khz
The Fifo Output Data rate is 2.5 usec
*/
unsigned int unNumOfFifoSamples;
float64_t outputFrequency;
float64_t outputDateRate;
if (modid == NAI_MODULE_ID_DA1)
{
outputFrequency = DA_FIFO_GEN5_OUTPUT_FREQUENCY;
outputDateRate = DA_FIFO_GEN5_OUTPUT_DATARATE;
}
else
{
outputFrequency = DA_FIFO_GEN3_OUTPUT_FREQUENCY;
outputDateRate = DA_FIFO_GEN3_OUTPUT_DATARATE;
}
unNumOfFifoSamples = (unsigned int)(outputFrequency / dReqFreq);
if (unNumOfFifoSamples > DA_MAX_FIFO_SAMPLES)
{
unNumOfFifoSamples = DA_MAX_FIFO_SAMPLES;
}
*punNumFifoDataPoints = unNumOfFifoSamples;
*pdActualFrequency = (double)(1.0 / ((double)outputDateRate * (double)unNumOfFifoSamples));
}
static bool_t SupportsDAFuncGeneration(uint32_t moduleID)
{
bool_t bSupportDAFunc = FALSE;
switch (moduleID)
{
case NAI_MODULE_ID_F1:
case NAI_MODULE_ID_F3:
case NAI_MODULE_ID_F5:
case NAI_MODULE_ID_J3:
case NAI_MODULE_ID_J5:
case NAI_MODULE_ID_J8:
case NAI_MODULE_ID_DA1:
case NAI_MODULE_ID_DA2:
bSupportDAFunc = TRUE;
break;
default:
bSupportDAFunc = FALSE;
break;
}
return bSupportDAFunc;
}