Measurement Studio for VC++

cancel
Showing results for 
Search instead for 
Did you mean: 

helping to understand a c++ code

Hello everybody.

 

I have a problem with an example provided by NI for DAQmx ANSI C programmation. I putted this question here, because I could not find a suitable board for it.

 

About the problem: I'm currently working with a NI PCI-6120 device and therefore I like to connect a trigger signal to PFIO and a AI channel at which the software should acquire data in combination with a trigger event (->PFIO). Serveral times.

 

I found a good example (https://www.ni.com/en/support/documentation/supplemental/21/retriggerable-tasks-in-ni-daqmx.html) which should do the things I'd like to have, but I have problems to understand the code or maybe I don't really understand the way they realized it.

 

Here the code:

#include <string.h>
#include <stdio.h>
#include <NIDAQmx.h>

static TaskHandle  AItaskHandle=0,DItaskHandle=0;


#define DAQmxErrChk(functionCall) if( DAQmxFailed(error=(functionCall)) ) goto Error; else

static int32 GetTerminalNameWithDevPrefix(TaskHandle taskHandle, const char terminalName[], char triggerName[]);

int32 CVICALLBACK EveryNCallback(TaskHandle taskHandle, int32 everyNsamplesEventType, uInt32 nSamples, void *callbackData);
int32 CVICALLBACK DoneCallback(TaskHandle taskHandle, int32 status, void *callbackData);

int main(void)
{
 int32   error=0;
 char    errBuff[2048]={'\0'};
 char    trigName[256];

 /*********************************************/
 // DAQmx Configure Code
 /*********************************************/

 DAQmxErrChk (DAQmxCreateTask("",&AItaskHandle));
 DAQmxErrChk (DAQmxCreateAIVoltageChan(AItaskHandle,"Dev1/ai0","",DAQmx_Val_Cfg_Default,-10.0,10.0,DAQmx_Val_Volts,NULL));
 DAQmxErrChk (DAQmxCfgSampClkTiming(AItaskHandle,"",10000.0,DAQmx_Val_Rising,DAQmx_Val_ContSamps,1000));
 DAQmxErrChk (GetTerminalNameWithDevPrefix(AItaskHandle,"ai/SampleClock",trigName));
 DAQmxErrChk (DAQmxCreateTask("",&DItaskHandle));
 DAQmxErrChk (DAQmxCreateDIChan(DItaskHandle,"Dev1/port0","",DAQmx_Val_ChanForAllLines));
 DAQmxErrChk (DAQmxCfgSampClkTiming(DItaskHandle,trigName,10000.0,DAQmx_Val_Rising,DAQmx_Val_ContSamps,1000));

 DAQmxErrChk (DAQmxRegisterEveryNSamplesEvent(AItaskHandle,DAQmx_Val_Acquired_Into_Buffer,1000,0,EveryNCallback,NULL));
 DAQmxErrChk (DAQmxRegisterDoneEvent(AItaskHandle,0,DoneCallback,NULL));

 /*********************************************/
 // DAQmx Start Code
 /*********************************************/
 DAQmxErrChk (DAQmxStartTask(DItaskHandle));
 DAQmxErrChk (DAQmxStartTask(AItaskHandle));

 printf("Acquiring samples continuously. Press Enter to interrupt\n");
 printf("\nRead:\tAI\tDI\tTotal:\tAI\tDI\n");
 getchar();

Error:
 if( DAQmxFailed(error) )
  DAQmxGetExtendedErrorInfo(errBuff,2048);
 if( AItaskHandle ) {
  /*********************************************/
  // DAQmx Stop Code
  /*********************************************/
  DAQmxStopTask(AItaskHandle);
  DAQmxClearTask(AItaskHandle);
  AItaskHandle = 0;
 }
 if( DItaskHandle ) {
  /*********************************************/
  // DAQmx Stop Code
  /*********************************************/
  DAQmxStopTask(DItaskHandle);
  DAQmxClearTask(DItaskHandle);
  DItaskHandle = 0;
 }
 if( DAQmxFailed(error) )
  printf("DAQmx Error: %s\n",errBuff);
 printf("End of program, press Enter key to quit\n");
 getchar();
 return 0;
}

static int32 GetTerminalNameWithDevPrefix(TaskHandle taskHandle, const char terminalName[], char triggerName[])
{
 int32 error=0;
 char device[256];
 int32 productCategory;
 uInt32 numDevices,i=1;

 DAQmxErrChk (DAQmxGetTaskNumDevices(taskHandle,&numDevices));
 while( i<=numDevices ) {
  DAQmxErrChk (DAQmxGetNthTaskDevice(taskHandle,i++,device,256));
  DAQmxErrChk (DAQmxGetDevProductCategory(device,&productCategory));
  if( productCategory!=DAQmx_Val_CSeriesModule && productCategory!=DAQmx_Val_SCXIModule ) {
   *triggerName++ = '/';
   strcat(strcat(strcpy(triggerName,device),"/"),terminalName);
   break;
  }
 }

Error:
 return error;
}

int32 CVICALLBACK EveryNCallback(TaskHandle taskHandle, int32 everyNsamplesEventType, uInt32 nSamples, void *callbackData)
{
 int32       error=0;
 char        errBuff[2048]={'\0'};
 static int  totalAI=0,totalDI=0;
 int32       readAI,readDI;
 float64     AIdata[1000];
 uInt32      DIdata[1000];

 /*********************************************/
 // DAQmx Read Code
 /*********************************************/
 DAQmxErrChk (DAQmxReadAnalogF64(AItaskHandle,1000,10.0,DAQmx_Val_GroupByChannel,AIdata,1000,&readAI,NULL));
 DAQmxErrChk (DAQmxReadDigitalU32(DItaskHandle,1000,10.0,DAQmx_Val_GroupByChannel,DIdata,1000,&readDI,NULL));

 printf("\t%d\t%d\t\t%d\t%d\r",readAI,readDI,totalAI+=readAI,totalDI+=readDI);
 fflush(stdout);

Error:
 if( DAQmxFailed(error) ) {
  DAQmxGetExtendedErrorInfo(errBuff,2048);
  /*********************************************/
  // DAQmx Stop Code
  /*********************************************/
  if( AItaskHandle ) {
   DAQmxStopTask(AItaskHandle);
   DAQmxClearTask(AItaskHandle);
   AItaskHandle = 0;
  }
  if( DItaskHandle ) {
   DAQmxStopTask(DItaskHandle);
   DAQmxClearTask(DItaskHandle);
   DItaskHandle = 0;
  }
  printf("DAQmx Error: %s\n",errBuff);
 }
 return 0;
}

int32 CVICALLBACK DoneCallback(TaskHandle taskHandle, int32 status, void *callbackData)
{
 int32   error=0;
 char    errBuff[2048]={'\0'};

 // Check to see if an error stopped the task.
 DAQmxErrChk (status);

Error:
 if( DAQmxFailed(error) ) {
  DAQmxGetExtendedErrorInfo(errBuff,2048);
  DAQmxClearTask(taskHandle);
  if( DItaskHandle ) {
   DAQmxStopTask(DItaskHandle);
   DAQmxClearTask(DItaskHandle);
   DItaskHandle = 0;
  }
  printf("DAQmx Error: %s\n",errBuff);
 }
 return 0;
}

 

Here my questions:

 

1: DAQmxErrChk (GetTerminalNameWithDevPrefix(AItaskHandle,"ai/SampleClock",trigName));

I don't get this. Why don't they just put one specific channel name at var: trigName?

 

2.1: DAQmxErrChk (DAQmxRegisterEveryNSamplesEvent(AItaskHandle,DAQmx_Val_Acquired_Into_Buffer,1000,0,EveryNCallback,NULL));
2.2: DAQmxErrChk (DAQmxRegisterDoneEvent(AItaskHandle,0,DoneCallback,NULL));

Why do they setup the event on the AI channel? I thought I have to check for events on the trigger channel?!

 

3: int32 CVICALLBACK EveryNCallback(TaskHandle taskHandle, int32 everyNsamplesEventType, uInt32 nSamples, void *callbackData)

Does this event occur for every N samples acquired?

 

In summary I don't understand how the program is realising the 'wait on trigger' and then starts to acquire the data. In my eyes they just plot the data on both channels without any trigger.

 

Thank you for reading that far. Maybe someone can help me to understand it correctly!

 

Bye

 

Denis

0 Kudos
Message 1 of 12
(8,266 Views)

Hi Denis,

 

if you want to measure a specific amount of samples on multiple triggers while the task is running, this is not the apropriate example!

With the new X-Series, it is very easy to retrigger the acquisition since this is a builtin feature.

With your S-Series Card, you will have to create a CounterPulseOutput-Task

 

int32 DAQmxCreateCOPulseChanFreq (TaskHandle taskHandle, const char counter[], const char nameToAssignToChannel[], int32 units, int32 idleState, float64 initialDelay, float64 freq, float64 dutyCycle);

 

which is generating the SampleClock for your AnalogInput-Task.

 

 

You first start the Counter Output, then start the AI-Task whose Timing is configured to the source terminal "Ctr0InternalOutput"

 

This should do the job.

 

regards

Marco Brauner NIG

0 Kudos
Message 2 of 12
(8,244 Views)

Hello!

 

Thank you for your answer. I have the feeling that maybe I was explaining it a bit wrong.

 

I do have one physical channel coming from an external system which is connected to the device providing real data (on ai0). And I have a physical second channel/line coming from an external system which is connected to the device providing a trigger (on PFIO). This external system is creating several triggers and on/at each trigger event it will send a specific amount of data.

 

So, I start my program and it waits on a number of trigger events. If the trigger is detected by the device it starts to read the data.

The problem is I can't retrigger efficiently with my programmation (see code below).

 

 

Code: 


DAQmxErrChk (DAQmxCreateTask("",&taskHandle));
DAQmxErrChk (DAQmxCreateAIVoltageChan(taskHandle, Channels, "", DAQmx_Val_Cfg_Default, -ChanVolts, ChanVolts, DAQmx_Val_Volts, NULL));
DAQmxErrChk (DAQmxCfgSampClkTiming(taskHandle, "", ChanSR,DAQmx_Val_Falling, DAQmx_Val_FiniteSamps, (int32) ChanSmpPts));   
DAQmxErrChk (DAQmxCfgDigEdgeStartTrig(taskHandle,"PFI0", DAQmx_Val_Rising));   
DAQmxErrChk (DAQmxStartTask(taskHandle));


   
   
for(i=0; i<TrigEvents; i++)
{
        DAQmxErrChk (DAQmxReadAnalogF64(taskHandle, (int32) ChanSmpPts, 5.0, DAQmx_Val_GroupByChannel, buffer, (int32) ChanCount * (int32) ChanSmpPts, &read, NULL));
        DAQmxStopTask(taskHandle);
        DAQmxErrChk (DAQmxCfgDigEdgeStartTrig(taskHandle,"PFI0", DAQmx_Val_Rising));
         
        for(k = 0; k < len; k++)
        {
            result[k + i*len] = buffer[k];  
        }
       
}

 

 

But this is not working. The reinitialisation takes to much time and windows xp sp3 is not deterministic so sometimes it catches the triggerevent and sometimes not - I guess.

 

Greetings,

 

Denis

0 Kudos
Message 3 of 12
(8,239 Views)

Hi Denis, that is exactly what I was describing. With your PCI/PXI-6120 you must use a Counter Pulse Output which can be retriggered as your AI-SampleClock as I have described before.

Try it out.

 

regards

 

Marco Brauner NIG

0 Kudos
Message 4 of 12
(8,236 Views)

Okay I changed my code and it is not working.

I really don't know how to make it the way I want it to work.

 

So far I did the following:

 

- created a AITask

- created AIVoltchan (@AITask, "Dev3/ai0")

- created COTask

- created COPulseChan(@COTask, "Dev3/ctr0")

- configed SampClkTiming(@AITask, "Ctr0InternalOutput")

- started(COTask)

- started(AITask)

 

- looping Nr_Trig_Events

-- cfgDigEdgeTrig(@COTask, "PFI0")

-- ReadAnalogF64(@AITask)

- end loop

 

I really have no clue how this should work.

 

Could you provide me a basic idea how to setup everything? My trigger events are also not with the same delay between each other.

Trigger: _____|____|__|______|

AcqData ____|||___|||_|||______|||

 

Regards

 

Denis

0 Kudos
Message 5 of 12
(8,217 Views)

Hi Denis, thx for tryin it by yourself first. I´ll try to set up an example for you, but I´m not sure if I can do it today.

Tomorrow I´ll be out of office, so most certainly it will be monday.

 

regards

Marco Brauner NIG 

0 Kudos
Message 6 of 12
(8,212 Views)

Hello!

 

I had no time the last days to continue and now the time went away and it is friday - yay ;-). But maybe you can still help me out with this problem the following week. So far, I could not figure out how you might find a way to manage this multitrigger event. My new thought would be to use 'DoneEvents' and 'NSamples'. A 'schematic' would be helpfull so I can try it on my own.

 

Regards,

 

Denis

0 Kudos
Message 7 of 12
(8,125 Views)

Hi Denis, finally, here is an example for what I meant in my posts before.

It is AI with a retriggerable sampleclock which you can use with you´re card.

 

 

Marco Brauner NIG

0 Kudos
Message 8 of 12
(8,061 Views)

Hello!

 

Thank you very much! I am going to test it right now and hopefully I can give you a positiv response these days. I have to modify it a bit because I dont use a interface. We will see if I can manage it!

 

Regards,

 

Denis

0 Kudos
Message 9 of 12
(8,046 Views)

Hello,

 

it is me again. I modified the code you provided to me. So far I can say it is working 😃

But there are some things I like to know and maybe you can explain it to me.

Right now my code acquires data from two channels. I removed the BufferInput-function, because he was not able to trigger or run corretly if I have serveral scans/triggers.

 

Questions:

Why does it run if I dont use a specific InputBufferSize?

 

// ---------------------------------------- code snippet ----------------------------------------------

 

// ChanSR - SampleRate

// ChanSmpPts - SamplesPerChan

// Channels = \Dev3\ai0:1 (2 channels)

 

// -- create Tasks

DAQmxErrChk (DAQmxCreateTask("", &AItaskHandle));
DAQmxErrChk (DAQmxCreateTask("", &COtaskHandle));

// -- create voltage chan, configure timing and optional the buffer size   
DAQmxErrChk (DAQmxCreateAIVoltageChan(AItaskHandle, Channels, "", DAQmx_Val_Cfg_Default, -ChanVolts, ChanVolts, DAQmx_Val_Volts, NULL));
DAQmxErrChk (DAQmxCfgSampClkTiming(AItaskHandle, "Ctr0InternalOutput", ChanSR, DAQmx_Val_Falling, DAQmx_Val_ContSamps, (int32) ChanSmpPts));
//DAQmxErrChk (DAQmxSetBufferAttribute (AItaskHandle, DAQmx_Buf_Input_BufSize, ChanSmpPts*2));
    

// -- create the clock for the acquistion
DAQmxErrChk (DAQmxCreateCOPulseChanFreq (COtaskHandle, "Dev3/ctr0", "", DAQmx_Val_Hz, DAQmx_Val_Low, 0., ChanSR, 0.5));
DAQmxErrChk (DAQmxCfgImplicitTiming (COtaskHandle, DAQmx_Val_FiniteSamps, ChanSmpPts));

// -- create trigger and define the edge        
DAQmxErrChk (DAQmxCfgDigEdgeStartTrig(COtaskHandle,"PFI0", DAQmx_Val_Rising));    
DAQmxErrChk (DAQmxSetTri
gAttribute (COtaskHandle, DAQmx_StartTrig_Retriggerable, TRUE));       

// -- start the tasks
DAQmxErrChk (DAQmxStartTask(COtaskHandle));
DAQmxErrChk (DAQmxStartTask(AItaskHandle));

// -- knowing the numbers of triggers we repeat the acquisition
for(i=0; i<TrigEvents; i++)
{
    DAQmxErrChk (DAQmxReadAnalogF64(AItaskHandle, (int32) ChanSmpPts, 2.0, DAQmx_Val_GroupByChannel, buffer, 
                                      (int32) (ChanCount * ChanSmpPts), &read, NULL));       
    for(k = 0; k < len; k++) 
    {   
         result[k + i*len] = buffer[k]; // just plot/save the data
    }

}

Cleanup();

// ----------------------------------------------------------------------------------------------

Bye

Denis

0 Kudos
Message 10 of 12
(8,032 Views)