05-12-2025 07:43 AM
I using NI-DAQmx C API to create a task and then create 4 counters in that task. The task is not started at this point. Later, I call DAQmxWriteCtrFreq to write the frequency and duty cycle samples, 1 sample per channel, so each array (frequency and duty cycle) has 4 float64 positions. The write function is returning error -50175 (The specified mathematical operation results in an overflow). Running the same code in a simulated hardware works. If I run the same code, but instead of 4 counters I create just one in the task, it works.
My hw setup is a CCA,CDAQ-9185 ENET CDAQ, 4-SLOT, and a MODULE ASSY, NI 9474
This is a simple code to reproduce the error:
#include "pch.h"
#include "NIDAQmx.h"
static const char* counters[] =
{
#if !defined _DEBUG
"cDAQ9185-23617DBMod3/ctr0",
"cDAQ9185-23617DBMod3/ctr1",
"cDAQ9185-23617DBMod3/ctr2",
"cDAQ9185-23617DBMod3/ctr3",
#else
"cDAQ1Mod6/ctr0",
"cDAQ1Mod6/ctr1",
"cDAQ1Mod6/ctr2",
"cDAQ1Mod6/ctr3",
#endif
};
static const float64 frequencies[] = { 2000, 2000, 2000, 1000 };
static const float64 dutyCycles[] = { 99, 99, 99, 1 };
static void showError( int rc );
int main()
{
int rc;
TaskHandle task;
std::cout << "Creating task..." << std::endl;
if ( !DAQmxFailed( rc= DAQmxCreateTask("", &task) ) )
{
std::cout << "Task created, creating channels..." << std::endl;
for ( int i = 0; !DAQmxFailed( rc ) && i < ( sizeof( counters ) / sizeof( counters[ 0 ] ) ); i++ )
{
if ( !DAQmxFailed( rc = DAQmxCreateCOPulseChanFreq(
task, // The task handle
counters[ i ], // The name of the physical channel to use for generating the PWM pulse.
"", // The name(s) to assign to the created virtual channel(s). ("" = NI-DAQmx uses the physical channel name as the virtual channel name).
DAQmx_Val_Hz, // The units in which to specify frequency.
DAQmx_Val_Low, // The resting state of the output terminal.
0, // The amount of time in seconds to wait before generating the first pulse.
frequencies[ i ], // The frequency at which to generate pulses.
dutyCycles[ i ] / 100.0 ) ) ) // The width of the pulse divided by the pulse period. NI-DAQmx uses this ratio, combined with frequency, to determine pulse width and the interval between pulses.
{
// Specifies that the task generates samples continuously. NI-DAQmx uses the last parameter to determine the buffer size.
rc = DAQmxCfgImplicitTiming( task, DAQmx_Val_ContSamps, 1 );
}
}
if ( !DAQmxFailed( rc ) )
{
std::cout << "Retrieving current counter parameters from the task..." << std::endl;
uint32_t numberOfChannels = 0;
if ( !DAQmxFailed( rc = DAQmxGetTaskNumChans( task, ( uInt32* )&numberOfChannels ) ) )
{
std::vector<float64> frequencySamples( numberOfChannels );
std::vector<float64> dutyCycleSamples( numberOfChannels );
for ( uint32_t i = 0; i < numberOfChannels && !DAQmxFailed( rc ); i++ )
{
uint32_t index = i + 1; // For DAQmx functions the first channel starts in 1, hence the i + 1.
size_t len = DAQmxGetNthTaskChannel( task, index, nullptr, 0 );
if ( len > 0 )
{
std::vector<char> ch( len, 0 ); // Allocate the required size (the null terminator is included)
if ( !DAQmxFailed( DAQmxGetNthTaskChannel( task, index, &ch[ 0 ], len ) ) ) // The first call was just to get the required string length. This second call will actually return the channel name.
{
DAQmxGetCOPulseFreq( task, &ch[ 0 ], &frequencySamples[ i ] );
DAQmxGetCOPulseDutyCyc( task, &ch[ 0 ], &dutyCycleSamples[ i ] );
}
}
}
DAQmxResetBufOutputBufSize( task );
if ( !DAQmxFailed( rc ) )
{
rc = DAQmxWriteCtrFreq(
task,
1,
true,
DAQmx_Val_WaitInfinitely,
DAQmx_Val_GroupByChannel,
&frequencySamples[ 0 ],
&dutyCycleSamples[ 0 ],
nullptr,
nullptr );
}
}
}
DAQmxStopTask( task );
DAQmxClearTask( task );
}
if ( DAQmxFailed( rc ) )
{
showError( rc );
}
}
static void showError( int rc )
{
size_t len = DAQmxGetErrorString(
rc, // The NIDAQmx API error code
nullptr, // Buffer that will have the converted error message. Passing nullptr will cause the function to return the required buffer size.
0 ); // The size, in bytes, of the buffer passed in the errorString. Passing 0 will cause the function to return the required buffer size.
if ( len > 0 )
{
std::vector<char> errorDescription( len + 1, 0 ); // Allocate the required size. Make sure we have the string terminator.
if ( DAQmxGetErrorString( rc, &errorDescription[ 0 ], errorDescription.size() ) == 0 ) // Retrieve the error message.
{
std::cout << "DAQmx error: " << rc << " - " << &errorDescription[ 0 ] << std::endl;
}
}
}