Driver Development Kit (DDK)

cancel
Showing results for 
Search instead for 
Did you mean: 

M-series analog input performance degradation

I'm working with the M-series PCI-6229 card.  Recently, I noticed the data acquisition rate of the card was lower than expected.  Below around 50-70 kHz, there is no issue. From ~50 kHz to 250kHz reading only one analog input, the acquisition rate is as expected, but when using 2 or more analog inputs the acquisition rate is limited to 50-70kHz.  I suspect this is because of the convert period divisor or something similar.  I'm using DMA transfers, but the same code using the FIFO buffer instead yields the same results.  Additionally, I tried this with NI-VISA on Windows instead of InTime to see if there were any OS specific problems, but the results were the same.

Here is the relevant code (based off of the DDK example code):

for(u16 i=0; i<m_numInputs; i++)
{
    aiConfigureChannel(board,
        m_aiInputs[i],  // channel number
        aiVRange,  // gain -- check ai.h for allowed values
        tMSeries::tAI_Config_FIFO_Data::kAI_Config_PolarityBipolar,
        tMSeries::tAI_Config_FIFO_Data::kAI_Config_Channel_TypeRSE,
        // was tMSeries::tAI_Config_FIFO_Data::kAI_Config_Channel_TypeDifferential,
        (i == m_numInputs-1)?kTrue:kFalse); // last channel?
}
aiSetFifoRequestMode(board);   
aiEnvironmentalize(board);
aiHardwareGating(board);
aiTrigger(board,
        tMSeries::tAI_Trigger_Select::kAI_START1_SelectPulse,
        tMSeries::tAI_Trigger_Select::kAI_START1_PolarityRising_Edge,
        tMSeries::tAI_Trigger_Select::kAI_START2_SelectPulse,
        tMSeries::tAI_Trigger_Select::kAI_START2_PolarityRising_Edge);
aiSampleStop(board,
            (m_numInputs > 1)?kTrue:kFalse); // multi channel?
aiNumberOfSamples(board,  
                numberOfSamples, // posttrigger samples
                0,               // pretrigger samples
                continuous);     // continuous?
aiSampleStart(board,
            samplePeriodDivisor,
            3,
            tMSeries::tAI_START_STOP_Select::kAI_START_SelectSI_TC,
            tMSeries::tAI_START_STOP_Select::kAI_START_PolarityRising_Edge);
aiConvert(board,
        280,     // convert period divisor
        3,       // convert delay divisor
        kFalse); // external sample clock?

I've changed different parameters in the code to see if it would help without any luck so far.
If anyone sees the problem in my setup for multiple analog inputs, I would appreciate the help.  Thanks.

Aaron
0 Kudos
Message 1 of 15
(11,434 Views)
Additionally, I'm doing a continuous hardware acquisition, numberOfSamples is 1, and samplePeriodDivisor is calculated as 20,000,000/desired frequency and is 80 to acquire at 250 kSa/sec.  If I acquire 2 analog inputs, I realize I can only acquire each at a maximum of 125 kSa/sec/input, but I am only acquiring at ~30 kSa/sec/input for 2 analog inputs.  Thanks.

Aaron
0 Kudos
Message 2 of 15
(11,432 Views)

Hi Aaron-

I suspect the bottleneck you're running into is the fact that the AI Convert Clock is running at too slow of a rate.  From your example:

aiConvert(board,
        280,     // convert period divisor
        3,       // convert delay divisor
        kFalse); // external sample clock?

is setting the aiConvert rate to be 20M/280 = 71428.6kHz.  The AI Convert Clock is the background clock that advances the AI multiplexor and actually initiates each ADC capture.  The AI timing engine on M Series (and other MIO boards) is designed to mask additional aiSampleStart (aka ai/SampleClock) pulses until an aiSampleStop occurs.  So, the sequence of events is required to be ai/SampleClock -> ai/ConvertClock -> ai/ConvertClock -> ... -> ai/ConvertClock -> ai/SampleStop ->ai/SampleClock -> ...

You'll need to adjust your convert clock to fit (at least) <# of channels> * <ai/SampleClock rate> within each sample clock period.  So, for a 125kHz scan of two channels, aiSampleStart should be set to a divisor of (20M/160) = 125kHz and aiConvert should be set to a divisor of (20M/80) = 250kHz.

Hopefully this helps-

Tom W
National Instruments
Message 3 of 15
(11,408 Views)
As I expected, but I think the only other number I tried for the period divisor number was zero.  It works as expected now.  Thanks.

Aaron
0 Kudos
Message 4 of 15
(11,401 Views)
I've suddenly discovered some troubling behavior for my 62xx cards during analog input. I've submitted this snippet before, but here's the code again:

// Analog Input
-( BOOL )aiSetup {
UInt32 i;
UInt32 scanInterval = ( UInt32 )floor( 20000000. / _aiSampleRate );
UInt16 convert = 280;

BOOL continuous = FALSE;

[ self initMITE ];
[ self initialize ];
[ self configureTimebase ];
[ self resetPLL ];
[ self resetAnalogTrigger ];

[ self aiReset ];
[ self aiPersonalize:_aiNIConvertOutputSelect ];
[ self aiClearFifo ];
[ self resetADC ];
[ self aiDisarm ];
[ self aiClearConfigurationMemory ];

for( i = _aiFirstChannel; i < ( _aiFirstChannel + _aiChannelsToRecord ); i++ )
[ self aiConfigureChannel:i :_aiGainIndex :kBiPolar :_aiChannelType 😞 i == ( ( _aiFirstChannel + _aiChannelsToRecord ) - 1 ) ) ? TRUE : FALSE ];

[ self aiSetFIFORequestMode ];
[ self aiEnvironmentalize ];
[ self aiHardwareGating ];
[ self aiTrigger:kAISTART1SelectPulse :kAISTART1PolarityRisingEdge :kAISTART2SelectPulse :kAISTART2PolarityRisingEdge ];
[ self aiSampleStop:( _aiChannelsToRecord > 1 ) ? TRUE : FALSE ];
[ self aiNumberOfSamples:_aiFramesPerBuffer :0 :continuous ];
[ self aiSampleStart:scanInterval :3 :kAISTARTSelectSITC :kAISTARTPolarityRisingEdge ];
[ self aiConvert:convert :3 :FALSE ];
[ self aiClearFifo ];

return TRUE;
}

// Sample!
while( _aiSampling ) {
[ _device lockRegisters ];


int i = 0;
[ _device aiStartScan ];
while( ( i < numSamples ) && _aiSampling ) {
if( [ _device aiDataAvailable ] ) {
_aiInputBuffer[ i ] = ( double )( ( SInt32 )[ _device aiGetNextValue ] ) / normalizescale;
//NSLog( @"aiSample: read:%+5.5f", _aiInputBuffer[ i ] );
i++;
} else {
[ _device unlockRegisters ];
//NSLog( @"[ %@ aiSample ]: Waiting for data!", [ _device description ] );
env_waitforeventticks( 2 );

[ _device lockRegisters ];
}
}

...
}

The samples I collect from the fifo appear correct, however, instead of starting with the first channel's data, the FIFO queue always has the *last* channel's data as the first value. I hadn't noticed the behavior because I'd only been doing single channel recording up to this point. I can't think of anything that would cause the the final channel to be the first one sampled, unless aiSampleStart had a very long delay. Can you see anything in my setup code (which was ported directly from the examples) that would cause this wonky behavior?

R
----
Rob Dotson
Asst. Research Scientist
Center for Neural Science
New York University
0 Kudos
Message 5 of 15
(11,007 Views)

Hi Rob-

One thing that jumps out at me immediately is your call to resetADC() (which I assume is an implementation similar to adcReset() from the M Series MHDDK). 

In an earlier post you indicated that you were using a 622x device, but the adcReset() call is only applicable to and should only be called when a 625x device is in use.  Can you please confirm which hardware you are using and what functionality your resetADC() function entails?

Tom W
National Instruments
0 Kudos
Message 6 of 15
(11,000 Views)
I have multiple M-Series devices, each running on machines with different PCI architectures. The 6221 is running on a PCI-X machine, and the 6251 on a PCI-e machine. The NI class is further subclassed for specific machines. The resetADC function does nothing on the 6221 machine.

The thing is, the values read in from the ADC are correct, only it starts recording the last channel first, then loops back around and starts from the first channel again.
----
Rob Dotson
Asst. Research Scientist
Center for Neural Science
New York University
0 Kudos
Message 7 of 15
(10,989 Views)
Tom,

Things have been working pretty flawlessly for quite a while now, but recently we started reading more inputs and have again had some lower than expected acquisition rates.

I'm still using DMA transfers, but now the task is basically recording the data for 200 us and the task is restarted every 250 us.  The card is a PCI-6229 and the acquistion rate is 250 kSa/sec.  Here are the results of my testing:
2 inputs, 250 kSa/sec, 25 Sa/input/main loop, convert period divisor = 80, samples measured per sec = 50 (CORRECT because 25 Sa*2 inputs = 50 samples per main loop)
4 inputs, 250 kSa/sec, 12 Sa/input/main loop, convert period divisor = 80, samples measured per sec = 37 (WRONG because 12 Sa*4 inputs = 48 samples per main loop)
4 inputs, 245 kSa/sec, 12 Sa/input/main loop, convert period divisor = 81, samples measured per sec = 48 (CORRECT because 12 Sa*4 inputs = 48 samples per main loop)

The number of samples/input/main loop is calculated so that each input is measured the same number of times as all the other inputs.  This problem can be repeated for 5, 6, 7, ... inputs with the necessary sample rate reducing by 5 kSa/sec for each added one.  The one that is wrong above actually only does about 30 in 200 us but 37 in 250 us, I believe.  I'm not aware of a limitation such as this, but I could definitely be wrong.  Here is the code I think would be relevant:

aiNumberOfSamples(board,  
    numPointsPerInputPerLoop,
   // posttrigger samples
    0,                
// pretrigger samples
    kFalse);          
// continuous?
aiSampleStart(board,
    convertPeriodDivisor*numInputs,
    3,
    tMSeries::tAI_START_STOP_Select::kAI_START_SelectSI_TC,
    tMSeries::tAI_START_STOP_Select::kAI_START_PolarityRising_Edge);
aiConvert(board,
    convertPeriodDivisor,
//80,     // convert period divisor
    3,      
// convert delay divisor
    kFalse);
// external sample clock?

The setup is currently set to keep the acquisition rate constant at whatever value is chosen 245 kSa/sec, 250 kSa/sec, etc.  The point is to record as many samples as possible in less than or equal to 200 us.  numPointsPerInputPerLoop is what limits the scan to this time for a given acquisition rate, but the unexpectedly low acquistion rate means the task is not completed even within 250 us.  I look forward to anybody's insight in to my problem.  Thanks.

Aaron
0 Kudos
Message 8 of 15
(10,559 Views)
I still haven't resolved my dilemna and wondered if anyone could point me in the right direction.  Thanks.

Aaron
0 Kudos
Message 9 of 15
(10,312 Views)
Hi Aaron,
 
I am going to be working on reproducing your issue in order to narrow down the limitation that you are experiencing. I have found a KnowledgeBase article that may improve the speed of your measurements if you are using an internal clock. The ability to remove a clock tick from each Convert Clock could improve performance by being several microseconds faster for each iteration. Please let me know if you see any improvement in your measurement and if this applies to your situation.
 
How Can I Improve My Single Point AI RT Performance with M Series
http://digital.ni.com/public.nsf/allkb/E2C2E5FF9F53C2ED86256FB70073D822?OpenDocument
Steve B

0 Kudos
Message 10 of 15
(10,272 Views)