DA PatternGenerator
Edit this on GitLab
DA PatternGenerator Sample Application (SSK 1.x)
Overview
The DA PatternGenerator sample application demonstrates how to load analog output waveform data from external pattern files and play it back through a DA module’s pattern generation RAM. Unlike the DA FunctionGenerator sample — which computes waveforms algorithmically at runtime (sine, square, triangle, etc.) — this application reads arbitrary voltage sample data from .ptrn files on disk and writes it into the module’s pattern RAM for continuous looped playback.
This file-based approach is useful when you need to reproduce a specific recorded waveform, replay captured sensor data, or output complex patterns that are difficult to express as simple mathematical functions. You prepare the pattern data offline (in a spreadsheet, MATLAB, Python script, or similar tool), save it to a .ptrn file with the appropriate header parameters, and the application handles loading it into hardware and starting playback.
The pattern generation hardware works as follows: the module contains a dedicated pattern RAM region for each channel. Your application writes voltage sample data into this RAM, sets start and end address pointers to define the active region, configures a sample rate (FIFO rate) that controls how fast the hardware steps through the data, and then enables looped playback. The hardware autonomously cycles through the RAM region at the configured rate, converting each sample to an analog voltage on the output — no further software intervention is required once playback starts.
For basic DA channel configuration (range, polarity, static voltage output), see the DA BasicOps sample. For algorithmically generated waveforms (sine, square, triangle, sawtooth), see the DA FunctionGenerator sample.
Supported Modules
This sample supports the following DA module types:
-
DA3
-
DA4
The application calls SupportsDAFuncGeneration() at startup to verify that the selected module supports pattern generation. If you select a module that is not DA3 or DA4, the application prints an error and returns to the module selection prompt.
Prerequisites
Before running this sample, make sure you have:
-
An NAI board with a DA3 or DA4 module installed.
-
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.
-
One or more
.ptrnpattern files placed in the same directory as the executable (see Pattern File Format for the file structure).
How to Run
Launch the DA_PatternGenerator executable from your build output directory. On startup, the application looks for a configuration file (default_DAPatternGen.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, lists available .ptrn files in the current directory, and loads the selected pattern into the module’s pattern RAM for playback.
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. For details on board connection configuration, see the First Time Setup Guide. |
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_DAPatternGen.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 verify it supports DA pattern generation viaSupportsDAFuncGeneration().
#if defined (__VXWORKS__)
int32_t DA_PatternGenerator(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)
{
stop = naiapp_query_CardIndex(naiapp_GetBoardCnt(), 0, &cardIndex);
if (stop != TRUE)
{
check_status(naibrd_GetModuleCount(cardIndex, &moduleCnt));
stop = naiapp_query_ModuleNumber(moduleCnt, 1, &module);
if (stop != TRUE)
{
moduleID = naibrd_GetModuleID(cardIndex, module);
if ((moduleID != 0))
{
if (SupportsDAFuncGeneration(moduleID))
{
Run_DA_PatternGenerator(cardIndex, module, moduleID);
}
else
{
printf("ERROR: Module selected does not support DA Functionality\n");
}
}
}
}
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;
}
The SupportsDAFuncGeneration() function checks the module ID against the known list of modules that support pattern generation:
static bool_t SupportsDAFuncGeneration(uint32_t moduleID)
{
bool_t bSupportDAFunc = FALSE;
switch (moduleID)
{
case NAI_MODULE_ID_DA3:
case NAI_MODULE_ID_DA4:
bSupportDAFunc = TRUE;
break;
default:
bSupportDAFunc = FALSE;
break;
}
return bSupportDAFunc;
}
|
Important
|
Common Connection Errors
|
Program Structure
After board connection, the application flow is:
-
Floating-point mode query — if the module supports hardware floating-point conversion, the user is asked whether to enable it. Hardware floating-point offloads voltage-to-raw conversions to the module FPGA, which is faster and less CPU-intensive than software conversion.
-
Channel selection — the user selects which DA output channel to use.
-
Pattern file discovery — the application scans the current directory for
.ptrnfiles and lists them. -
Pattern file loading — the selected file is parsed for configuration parameters (sample rate, range, polarity) and voltage data points.
-
Pattern generation — the data is written to the module’s pattern RAM and playback is enabled.
The application tracks pattern configuration through a PatternInfo structure:
typedef struct _PatternInfo
{
char patternFileName[MAX_FILE_NAME_SIZE];
uint32_t sampleRate;
uint32_t range;
uint32_t polarity;
uint32_t dataLength;
float64_t *data;
} PatternInfo;
These fields map directly to the pattern file parameters and the API calls used to configure the hardware:
-
sampleRate— the FIFO output rate in microseconds, passed tonaibrd_DA_SetFIFORate(). -
range— the voltage range in volts, passed tonaibrd_DA_SetRange(). -
polarity— 0 for unipolar, 1 for bipolar, passed tonaibrd_DA_SetRange(). -
dataLength— the number of voltage samples, used to calculate the pattern RAM end address. -
data— dynamically allocated array offloat64_tvoltage values, passed tonaibrd_DA_SetPatternGenData().
Floating-Point Mode
If your DA module supports hardware floating-point conversion, the application queries whether to enable it. This is a module-level setting that affects how voltage values are translated to raw DAC codes.
naibrd_GetFloatingPointModeCapability(cardIndex, module, &floatingPointCapable);
if (FALSE != floatingPointCapable)
{
printf("This module supports H/W floating point conversion. Do you want to enable it?\n");
/* ... user input handling ... */
naibrd_SetFloatingPointModeEnable(cardIndex, module, TRUE);
naibrd_GetRunningInFloatingPointMode(cardIndex, module, &floatingPointMode);
if (FALSE == floatingPointMode)
{
printf("**Floating point mode is disabled!**\n");
}
else
{
printf("**Floating point mode is enabled.**\n");
}
}
To enable hardware floating-point in your own application, call naibrd_GetFloatingPointModeCapability() first to check if the module supports it, then call naibrd_SetFloatingPointModeEnable() to turn it on. After enabling, call naibrd_GetRunningInFloatingPointMode() to confirm the setting took effect. With hardware floating-point enabled, API calls like naibrd_DA_SetPatternGenData() accept voltage values directly as float64_t and the module FPGA handles the conversion. With it disabled, the SSK library performs software conversion on the host, which works on all modules but uses more CPU time.
Pattern File Format
Pattern data is stored in .ptrn files — plain text files with a simple key-value header followed by comma-separated voltage data. The application searches the current working directory for all files with the .ptrn extension.
A pattern file has this structure:
SAMPLE_RATE = 100 POLARITY = BIPOLAR RANGE = 10 DATA = 0.0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5, 5.0, ...
The parameters are:
-
SAMPLE_RATE — the FIFO output rate in microseconds. This controls how fast the hardware steps through the pattern data. A value of 100 means each sample is output for 100 microseconds, giving a 10 kHz sample rate.
-
POLARITY — either
BIPOLAR(positive and negative voltages) orUNIPOLAR(positive voltages only). -
RANGE — the voltage range in volts. Consult your module’s manual for supported range values.
-
DATA — comma-separated floating-point voltage values. The parser counts commas to determine the data length, dynamically allocates memory, and parses each value as a
float64_t.
Lines beginning with / or * are treated as comments and skipped during parsing.
How the File is Loaded
The LoadPatternFile() function reads the file line by line, extracting each parameter using GetParamFromLine(). When it encounters the DATA keyword, it calls GetDataFromPatternFile(), which:
-
Skips whitespace and the
=sign after the DATA keyword. -
Makes a first pass through the remaining file data, counting commas to determine how many data points exist.
-
Allocates a
float64_tarray of that size usingmalloc(). -
Seeks back to the start of the data and parses each comma-separated value into the array.
static int32_t LoadPatternFile( char patternFileName[], PatternInfo *patternInfo )
{
char paramValue[20];
FILE *file;
int32_t retVal = 0;
strcpy( patternInfo->patternFileName, patternFileName );
file = fopen( patternInfo->patternFileName, "r" );
if ( NULL != file )
{
char line[MAX_LINE_SIZE];
while ( (0 == retVal) && (fgets(line, MAX_LINE_SIZE, file)) )
{
if ( '/' != line[0] && '*' != line[1] )
{
if ( 0 == GetParamFromLine(line, "SAMPLE_RATE", paramValue) )
sscanf( paramValue, "%u", &(patternInfo->sampleRate) );
else if ( 0 == GetParamFromLine(line, "POLARITY", paramValue) )
{
if ( 0 == strcmp(paramValue, "BIPOLAR") )
patternInfo->polarity = 1;
else if ( 0 == strcmp(paramValue, "UNIPOLAR") )
patternInfo->polarity = 0;
else
{
printf( "\nIllegal value specified for the POLARITY parameter (%s?).\n",
paramValue );
retVal = -1;
}
}
else if ( 0 == GetParamFromLine(line, "RANGE", paramValue) )
sscanf( paramValue, "%u", &(patternInfo->range) );
else if ( NULL != strstr(line, "DATA") )
retVal = GetDataFromPatternFile(file, &(patternInfo->dataLength),
&(patternInfo->data));
}
}
fclose(file);
}
return retVal;
}
|
Important
|
Common Pattern File Errors
|
Pattern Generation
This is the core of the application. Once the pattern file is loaded and the PatternInfo structure is populated, the GeneratePattern() function configures the DA channel hardware and loads the pattern data into RAM for playback.
Configuring the Channel
The function sets up the channel in this order:
-
Set the FIFO rate — controls how fast the hardware steps through the pattern data.
-
Set the operation mode — switches the channel to voltage pattern generation mode.
-
Set range and polarity — configures the output voltage range and whether it is unipolar or bipolar.
/* Set Sample Rate */
check_status(naibrd_DA_SetFIFORate(cardIndex, module, channel, patternInfo->sampleRate));
/* Set Operation Mode */
check_status(naibrd_DA_SetOpMode(cardIndex, module, channel, NAI_DA_DATA_VOLTAGE_PATTERN_GEN));
/* Set Polarity and Range */
check_status(naibrd_DA_SetRange(cardIndex, module, channel, NAI_DA_DATA_VOLTAGE_PATTERN_GEN,
patternInfo->polarity, patternInfo->range));
To configure a DA channel for pattern generation in your own application:
-
Call
naibrd_DA_SetFIFORate()with the sample period in microseconds. This value directly controls the output update rate. For example, a value of 100 gives a 10 kHz update rate (one sample every 100 microseconds). -
Call
naibrd_DA_SetOpMode()withNAI_DA_DATA_VOLTAGE_PATTERN_GENto put the channel in pattern generation mode. This is distinct from normal static voltage output mode. -
Call
naibrd_DA_SetRange()with the operation mode, polarity (0 = unipolar, 1 = bipolar), and range in volts. The range and polarity must match what was specified in the pattern file, or the output voltages will be incorrect.
Understanding the FIFO Rate and Output Frequency
The FIFO rate determines the time between successive output samples. The relationship between the FIFO rate, data length, and output waveform frequency is:
Output Frequency = 1 / (FIFO Rate * Data Length)
For example, with a FIFO rate of 2.50 microseconds and 26,213 data points (the maximum FIFO size on Gen5 modules), the maximum output frequency is approximately 15.26 Hz. With fewer data points or a faster FIFO rate, you can achieve higher output frequencies.
On Gen5 DA modules, the FIFO size is 26,213 entries and the minimum FIFO output period is 2.50 microseconds, giving a maximum output data rate of 400 kHz. Consult your module’s manual for the specific FIFO size and timing constraints of your hardware.
Setting Pattern RAM Addresses
Each channel has a dedicated region in pattern RAM. The start and end addresses define which portion of the RAM contains valid data.
/* Set the Start and End Addresses */
startAddress = 0x20000 * channel;
check_status(naibrd_DA_SetPatternGenStartAddr(cardIndex, module, channel, startAddress));
endAddress = startAddress + ((patternInfo->dataLength - 1) * sizeof(uint32_t));
check_status(naibrd_DA_SetPatternGenEndAddr(cardIndex, module, channel, endAddress));
The start address is calculated as 0x20000 * channel, giving each channel a 128 KB (0x20000 bytes) region of pattern RAM. The end address is the start address plus the byte offset of the last data sample: (dataLength - 1) * 4 bytes (since each sample is stored as a 32-bit value).
In your own application, call naibrd_DA_SetPatternGenStartAddr() and naibrd_DA_SetPatternGenEndAddr() to define the active region before writing data. The hardware will loop through addresses from start to end during playback.
Writing Pattern Data to RAM
After configuring the addresses, the application writes the voltage sample array to the pattern RAM:
check_status(naibrd_DA_SetPatternGenData(cardIndex, module, channel,
patternInfo->dataLength, patternInfo->data));
free(patternInfo->data);
Call naibrd_DA_SetPatternGenData() with the channel, the number of data points, and a pointer to a float64_t array of voltage values. The API handles converting the floating-point voltages to the appropriate raw DAC codes (using either hardware or software floating-point conversion, depending on the mode you selected earlier). After the write completes, the sample frees the dynamically allocated data array.
Enabling and Disabling Playback
Once data is loaded, the application enters a loop that toggles pattern output on and off with each press of Enter:
do
{
check_status(naibrd_DA_SetPatternGenCtrl(cardIndex, module, channel,
NAI_DA_CTRL_PATTERN_ENABLE, patternEnable));
if ( NAI_DA_ENABLE == patternEnable )
{
printf( "Pattern is ENABLED.\n" );
patternEnable = NAI_DA_DISABLE;
}
else
{
patternEnable = NAI_DA_ENABLE;
printf( "Pattern is DISABLED.\n" );
}
printf("Hit ENTER to toggle output, Type 'q' to quit\n" );
for (key = getchar(); ('q' != key) && ('Q' != key) && ('\n' != key); key = getchar());
} while ( ('q' != key) && ('Q' != key) );
/* Disable the output before exiting */
check_status(naibrd_DA_SetPatternGenCtrl(cardIndex, module, channel,
NAI_DA_CTRL_PATTERN_ENABLE, NAI_DA_DISABLE));
To control pattern playback in your own application, call naibrd_DA_SetPatternGenCtrl() with:
-
NAI_DA_CTRL_PATTERN_ENABLEas the control type. -
NAI_DA_ENABLEto start playback orNAI_DA_DISABLEto stop it.
When enabled, the hardware continuously loops through the pattern RAM from the start address to the end address at the configured FIFO rate. When disabled, the output holds at its last value. Always disable the pattern output before exiting your application to leave the channel in a known state.
|
Important
|
Common Pattern Generation Errors
|
Pattern Generation vs. Function Generation
The DA PatternGenerator and DA FunctionGenerator samples both produce analog waveforms on DA output channels, but they use different approaches:
| Feature | PatternGenerator | FunctionGenerator |
|---|---|---|
Data source |
External |
Algorithmically computed at runtime |
Waveform types |
Any arbitrary waveform — limited only by file contents |
Five built-in types: constant, square, triangle, sawtooth, sine |
Hardware mechanism |
Pattern RAM with start/end address pointers, looped playback |
FIFO buffer with software trigger |
Use case |
Replaying recorded data, complex custom waveforms, waveforms from external tools |
Standard test signals with configurable frequency, amplitude, and offset |
Runtime flexibility |
Must reload a new file to change the waveform |
Can switch waveform type and parameters from the command menu |
Choose the PatternGenerator when you need to output waveform shapes that are not simple mathematical functions, or when you want to replay data captured from another source. Choose the FunctionGenerator when you need standard test waveforms with easy runtime parameter adjustment.
Troubleshooting Reference
The following table summarizes errors and symptoms you may encounter. Consult your module’s manual for hardware-specific diagnostics.
| Error / Symptom | Possible Causes | Suggested Resolution |
|---|---|---|
No board found |
Board not powered, not physically connected, or driver not loaded. |
Verify power, physical connection, and that the appropriate driver is installed for your connection type. |
Connection timeout |
Ethernet-connected board is unreachable. |
Check network settings, IP configuration, and firewall rules. |
"Module selected does not support DA Functionality" |
The module in the selected slot is not a DA3 or DA4. |
Select a slot containing a DA3 or DA4 module. |
"No pattern files found!" |
No |
Copy your pattern files to the same directory as the executable, or launch from the directory containing them. |
"Error loading pattern file!" |
Pattern file has invalid syntax or unrecognized parameter values. |
Verify the file follows the expected format: |
"Illegal value specified for the POLARITY parameter" |
The POLARITY field is not exactly |
Correct the spelling in the pattern file. The values are case-sensitive. |
No output signal after enabling |
Channel not in pattern generation mode, or start/end addresses not set. |
Verify |
Output voltage is clipped or incorrect |
Range or polarity mismatch between the pattern file settings and the data values. |
Ensure the data values fall within the configured range and polarity. For bipolar mode, data can span negative to positive; for unipolar, data should be non-negative. |
Unexpected output frequency |
FIFO rate or data length is different from expected. |
Output frequency = 1 / (FIFO rate * data length). Adjust the SAMPLE_RATE in the pattern file or change the number of data points. |
|
API call not supported on the selected module type. |
Verify you are using a DA3 or DA4 module. Other DA modules do not support pattern generation. |
Full Source
Full Source — DA_PatternGenerator.c (SSK 1.x)
#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <math.h>
#if WIN32
#include <io.h>
#else
#include <dirent.h>
#endif
/* 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"
#include "boards/naibrd_gen5.h"
static const int8_t *CONFIG_FILE = (const int8_t *)"default_DAPatternGen.txt";
#define MAX_NUM_PATTERN_FILES 20
#define MAX_FILE_NAME_SIZE 100
#define MAX_LINE_SIZE 256
typedef struct _PatternInfo
{
char patternFileName[MAX_FILE_NAME_SIZE];
uint32_t sampleRate;
uint32_t range;
uint32_t polarity;
uint32_t dataLength;
float64_t *data;
} PatternInfo;
/* Function prototypes */
static int32_t Run_DA_PatternGenerator(int cardIndex, int module, uint32_t moduleID);
static int32_t GeneratePattern( int32_t cardIndex, int32_t module, int32_t channel, PatternInfo *patternInfo );
static int32_t GetPatternFileList( char patternFileNames[][MAX_FILE_NAME_SIZE], int32_t *numPatternFiles );
static int32_t LoadPatternFile( char patternFileName[], PatternInfo *patternInfo );
static int32_t GetParamFromLine( char line[], char paramName[], char paramValue[] );
static int32_t GetDataFromPatternFile( FILE *file, uint32_t *dataLength, float64_t **patternData );
static bool_t SupportsDAFuncGeneration(uint32_t moduleID);
/************************************************************************
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
************************************************************************/
/**************************************************************************************************************/
/**
<summary>
DA_PatternGenerator illustrates setting up the D/A Pattern Generator to generate various types of outputs. Output
data is read in from a file, loaded into the DA Pattern RAM, and then set to loop the pattern.
</summary>
*/
/**************************************************************************************************************/
#if defined (__VXWORKS__)
int32_t DA_PatternGenerator(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))
{
if (SupportsDAFuncGeneration(moduleID))
{
Run_DA_PatternGenerator(cardIndex, module, moduleID);
}
else
{
printf("ERROR: Module selected does not support DA Functionality\n");
}
}
}
}
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_PatternGenerator(int cardIndex, int module, uint32_t moduleID)
{
bool_t bQuit = FALSE;
int maxChannel = -1, channel = -1;
char patternFileNames[MAX_NUM_PATTERN_FILES][MAX_FILE_NAME_SIZE];
int32_t numPatternFiles;
int8_t inputBuffer[80];
int32_t inputResponseCnt;
bool_t floatingPointMode = FALSE;
bool_t floatingPointCapable = FALSE;
/* If this module supports H/W floatingPoint, then query the user to enable it. */
naibrd_GetFloatingPointModeCapability(cardIndex, module, &floatingPointCapable);
if (FALSE != floatingPointCapable)
{
printf("This module supports H/W floating point conversion. Do you want to enable it? If not, software converting will\n");
printf(" be used (slower and processor intensive) (default: Y)");
inputResponseCnt = 0;
bQuit = naiapp_query_ForQuitResponse(sizeof(inputBuffer), NAI_QUIT_CHAR, inputBuffer, &inputResponseCnt);
if (!bQuit)
{
if ((toupper(inputBuffer[0]) == 'Y') || (inputResponseCnt == 0))
{
naibrd_SetFloatingPointModeEnable(cardIndex, module, TRUE);
}
else
{
naibrd_SetFloatingPointModeEnable(cardIndex, module, FALSE);
}
/* Make sure FloatingPoint was enabled/disabled */
naibrd_GetRunningInFloatingPointMode(cardIndex, module, &floatingPointMode);
if (FALSE == floatingPointMode)
{
printf("**Floating point mode is disabled!**\n");
}
else
{
printf("**Floating point mode is enabled.**\n");
}
}
}
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);
if (!bQuit)
{
/* Get a list of available pattern files */
GetPatternFileList( patternFileNames, &numPatternFiles );
/* Prompt user for the Pattern File */
if ( 0 < numPatternFiles )
{
PatternInfo patternInfo;
int32_t patternFileIndex = 0;
if ( 1 == numPatternFiles )
{
printf("1 Pattern File found. ");
}
else
{
printf( "List of Pattern Files:\n\n");
for( patternFileIndex = 0; patternFileIndex < numPatternFiles; patternFileIndex++ )
{
printf( "%d) %s\n", patternFileIndex + 1, patternFileNames[patternFileIndex] );
}
do
{
printf( "\n\nPlease select a Pattern File (1 - %d):\n", numPatternFiles );
bQuit = naiapp_query_ForQuitResponse(sizeof(inputBuffer), NAI_QUIT_CHAR, inputBuffer, &inputResponseCnt);
patternFileIndex = inputBuffer[0] - 0x31;
} while ( ((patternFileIndex < 0) || (patternFileIndex >= numPatternFiles)) && !bQuit);
}
if (!bQuit)
{
/* Load the pattern file */
printf( "Loading Pattern file %s...", patternFileNames[patternFileIndex] );
if ( 0 == LoadPatternFile(patternFileNames[patternFileIndex], &patternInfo) )
{
printf("Done.\n");
/* Load the pattern into the D/A and output the signal. */
GeneratePattern(cardIndex, module, channel, &patternInfo);
}
else
{
printf("\nError loading pattern file! Can not generate output. Press ENTER to quit.\n");
getchar();
}
}
}
else
{
printf("No pattern files found!\n");
}
}
return 0;
}
static int32_t GeneratePattern( int32_t cardIndex, int32_t module, int32_t channel, PatternInfo *patternInfo )
{
nai_da_enable_t patternEnable = NAI_DA_ENABLE;
int key;
const char szPolarity[][10] = { "UniPolar", "BiPolar" };
uint32_t startAddress = 0, endAddress = 0;
/* Set Sample Rate */
printf("Setting Sample Rate to %u\n", patternInfo->sampleRate);
check_status(naibrd_DA_SetFIFORate(cardIndex, module, channel, patternInfo->sampleRate));
/* Set Operation Mode */
printf("Setting Operation Mode to VOLTAGE_PATTERN_GEN.\n");
check_status(naibrd_DA_SetOpMode(cardIndex, module, channel, NAI_DA_DATA_VOLTAGE_PATTERN_GEN));
/* Set Polarity and Range */
printf("Setting Range to %uv, Polarity to %s.\n", patternInfo->range, szPolarity[patternInfo->polarity]);
check_status(naibrd_DA_SetRange(cardIndex, module, channel, NAI_DA_DATA_VOLTAGE_PATTERN_GEN,
patternInfo->polarity, patternInfo->range));
/* Set the Start and End Addresses. */
startAddress = 0x20000 * channel;
printf("Setting START Address to 0x%4X.\n", startAddress );
check_status(naibrd_DA_SetPatternGenStartAddr(cardIndex, module, channel, startAddress));
endAddress = startAddress + ((patternInfo->dataLength - 1) * sizeof(uint32_t));
printf("Setting END Address to 0x%4X.\n", endAddress);
check_status(naibrd_DA_SetPatternGenEndAddr(cardIndex, module, channel, endAddress));
printf("Amount of data to write: (%u items x 4 bytes/item) = %u (0x%04X) total bytes.\n", patternInfo->dataLength,
patternInfo->dataLength * 4, patternInfo->dataLength * 4);
/* Write the pattern to the D/A and free allocated memory*/
printf("Writing pattern data to RAM..");
check_status(naibrd_DA_SetPatternGenData(cardIndex, module, channel, patternInfo->dataLength, patternInfo->data));
free(patternInfo->data);
printf("Done.\n");
/* Toggle the Pattern output Enable and Disable based on user input. */
do
{
check_status(naibrd_DA_SetPatternGenCtrl(cardIndex, module, channel, NAI_DA_CTRL_PATTERN_ENABLE, patternEnable));
if ( NAI_DA_ENABLE == patternEnable )
{
printf( "Pattern is ENABLED.\n" );
patternEnable = NAI_DA_DISABLE;
}
else
{
patternEnable = NAI_DA_ENABLE;
printf( "Pattern is DISABLED.\n" );
}
printf("Hit ENTER to toggle output, Type 'q' to quit\n" );
for (key = getchar(); ('q' != key) && ('Q' != key) && ('\n' != key); key = getchar());
} while ( ('q' != key) && ('Q' != key) );
/* Disable the output before exiting. */
check_status(naibrd_DA_SetPatternGenCtrl(cardIndex, module, channel,NAI_DA_CTRL_PATTERN_ENABLE, NAI_DA_DISABLE));
return 0;
}
#if WIN32
static int32_t GetPatternFileList( char patternFileNames[][MAX_FILE_NAME_SIZE], int32_t *numPatternFiles )
{
intptr_t hFile;
struct _finddata_t finddata;
int32_t done;
/* Search the directory for files with the .ptrn extension */
*numPatternFiles = 0;
hFile = _findfirst( "*.ptrn", &finddata );
done = ( 0 >= hFile );
while(!done)
{
strcpy( patternFileNames[*numPatternFiles], finddata.name );
(*numPatternFiles)++;
done = _findnext(hFile, &finddata);
}
_findclose(hFile);
return 0;
}
#else
static int32_t GetPatternFileList( char patternFileNames[][MAX_FILE_NAME_SIZE], int32_t *numPatternFiles )
{
DIR *dir;
struct dirent *entry;
int32_t done;
/* Search the directory for files with the .ptrn extension */
*numPatternFiles = 0;
dir = opendir( "./" );
done = ( NULL == dir );
while(!done)
{
entry = readdir(dir);
if (NULL != entry)
{
if (NULL != strstr(entry->d_name, ".ptrn"))
{
strcpy( patternFileNames[*numPatternFiles], entry->d_name );
(*numPatternFiles)++;
}
}
else
done = TRUE;
}
closedir(dir);
return 0;
}
#endif
static int32_t LoadPatternFile( char patternFileName[], PatternInfo *patternInfo )
{
char paramValue[20];
FILE *file;
int32_t retVal = 0;
strcpy( patternInfo->patternFileName, patternFileName );
/* Open Pattern File for reading */
file = fopen( patternInfo->patternFileName, "r" );
if ( NULL != file )
{
char line[MAX_LINE_SIZE];
/*** Load Parameters ***/
while ( (0 == retVal) && (fgets(line, MAX_LINE_SIZE, file)) )
{
if ( '/' != line[0] && '*' != line[1] )
{
if ( 0 == GetParamFromLine(line, "SAMPLE_RATE", paramValue) )
sscanf( paramValue, "%u", &(patternInfo->sampleRate) );
else if ( 0 == GetParamFromLine(line, "POLARITY", paramValue) )
{
if ( 0 == strcmp(paramValue, "BIPOLAR") )
patternInfo->polarity = 1;
else if ( 0 == strcmp(paramValue, "UNIPOLAR") )
patternInfo->polarity = 0;
else
{
printf( "\nIllegal value specified for the POLARITY parameter (%s?). Please check the pattern file!\n",
paramValue );
retVal = -1;
}
}
else if ( 0 == GetParamFromLine(line, "RANGE", paramValue) )
sscanf( paramValue, "%u", &(patternInfo->range) );
else if ( NULL != strstr(line, "DATA") )
retVal = GetDataFromPatternFile(file, &(patternInfo->dataLength), &(patternInfo->data));
}
}
/*** END Load Parameters ***/
fclose(file);
}
return retVal;
}
static int32_t GetParamFromLine( char line[], char paramName[], char paramValue[] )
{
int32_t retVal = -1;
if ( NULL != strstr(line, paramName) )
{
char *lineData = strstr(line, "=");
char *newLine;
(lineData)++;
if ( NULL != lineData )
{
while ( ' ' == *lineData )
lineData++;
newLine = strrchr(lineData, '\r');
if (NULL != newLine)
*newLine = '\0';
else
{
newLine = strrchr(lineData, '\n');
if (NULL != newLine)
*newLine = '\0';
}
strcpy(paramValue, lineData);
retVal = 0;
}
}
return retVal;
}
static int32_t GetDataFromPatternFile( FILE *file, uint32_t *dataLength, float64_t **patternData )
{
int32_t retVal = 0;
char lineData;
long fpos;
uint32_t patternDataIndex = 0;
bool_t doneReadingFile = FALSE;
*dataLength = 0;
do
{
lineData = (char)fgetc(file);
} while ( '=' == lineData || ' ' == lineData || '\n' == lineData );
/* Store this location, we'll need to come back after counting the ',' */
fpos = ftell(file);
fpos--;
/* Count the ',' to determine how much data is in the file. */
do
{
lineData = (char)fgetc(file);
if ( (char)EOF == lineData || ',' == lineData )
(*dataLength)++;
} while ( (char)EOF != lineData );
/* Allocate memory based on the count */
*patternData = (float64_t*)malloc(*dataLength * sizeof(float64_t));
/* Read the data */
fseek(file, fpos, SEEK_SET);
while (!doneReadingFile)
{
char data[20];
int32_t dataIndex = 0;
bool_t dataItemDone = FALSE;
/* Parse a datum */
do
{
data[dataIndex] = (char)fgetc(file);
if ((char)EOF == data[dataIndex])
{
/* Finished parsing last datum.. finished with file. */
data[dataIndex] = '\0';
dataItemDone = TRUE;
doneReadingFile = TRUE;
}
else if (',' == data[dataIndex])
{
/* Finished parsing a datum.. still more data to parse. */
data[dataIndex] = '\0';
dataItemDone = TRUE;
}
else
dataIndex++;
} while (!dataItemDone);
/* Store this datum as pattern data. */
sscanf( data, "%lf", &(*patternData)[patternDataIndex] );
patternDataIndex++;
}
return retVal;
}
static bool_t SupportsDAFuncGeneration(uint32_t moduleID)
{
bool_t bSupportDAFunc = FALSE;
switch (moduleID)
{
case NAI_MODULE_ID_DA3:
case NAI_MODULE_ID_DA4:
bSupportDAFunc = TRUE;
break;
default:
bSupportDAFunc = FALSE;
break;
}
return bSupportDAFunc;
}