Counter/Timer

cancel
Showing results for 
Search instead for 
Did you mean: 

DAQmxCfgSampClkTiming NICARD 6031 E PCI and Visual C++ 6.0

Solved!
Go to solution

Hi BRAD,

 

Ok I will try that.

 

But what sampling rate should I have? 

 

From the first clock to the second I have about 30us and from the second to the third I have about 50us and then it repeats always 50us between clocks until 129 clocks are finished in total.

I can change the time between first and second clock and between next clocks after that to the value I want. This is not a a problem but I am thinking what sampling rate to use.

 

I already saw data but only once, then it blocked the USER INTERFACE. It gets stuck after my first read.

 

Thanks and regards,

 

Javier.

 

 

 

 

 


0 Kudos
Message 11 of 32
(4,192 Views)
Solution
Accepted by topic author Jaco12b

Because the shortest clock period is 30 us, your sample rate must be at least 1 / 30 us ~= 33334 S/s. Setting the sample rate to 50 kS/s meets this requirement and this happens to be the max 2-channel AI sample rate for the PCI-6031E.

 

To prevent your UI from hanging while the data acquisition, you need to avoid waiting for DAQmxReadAnalogF64() or DAQmxWaitUntilTaskDone() to complete in your UI thread. That means you should not use a timeout of -1 and you should not read samples before they are available. Since you're reading a finite acquisition of 128 samples, it probably makes sense to use DAQmxRegisterDoneEvent() to register a callback function, and call DAQmxReadAnalogF64() in the callback. If you were performing a longer acquisition, DAQmxRegisterEveryNSamplesEvent() or DAQmxGetReadAvailSampPerChan() could also be useful. Doing your own thread management could also work.

 

Brad

---
Brad Keryan
NI R&D
Message 12 of 32
(4,183 Views)

 

Hi Brad, 

 

I reply below your comments: This time I did not get an email when you posted a comment. I prefer to get an email.

 

Because the shortest clock period is 30 us, your sample rate must be at least 1 / 30 us ~= 33334 S/s. Setting the sample rate to 50 kS/s meets this requirement and this happens to be the max 2-channel AI sample rate for the PCI-6031E.

 

Setting 50000 samples per second gives 1/50000 = 20 us. I know that this happens to be the max 2-channel AI sample but I need a clock of 20us for setting 50000. Therefore I do not understand when you say that it meets the requirements for 30us. Am I missing something out? As you say for 30 us I need 33334 at least but 50000 would not work! 

 

To prevent your UI from hanging while the data acquisition, you need to avoid waiting for DAQmxReadAnalogF64() or DAQmxWaitUntilTaskDone() to complete in your UI thread. That means you should not use a timeout of -1

 

I set it to 1 second now instead of -1.

 

and you should not read samples before they are available.

 

Well what happens is that I send a char 'r' and then the MUC sends the clock when it sees the char 'r' and then it receives data. The problem is that I do not know if ,after setting char 'r' and starting the clock, takes faster than for the next instruction of the UI to execute in visual c++ which is READ. 

 

Since you're reading a finite acquisition of 128 samples, it probably makes sense to use DAQmxRegisterDoneEvent() to register a callback function, and call DAQmxReadAnalogF64() in the callback.

 

Never used this one but may be I should have a look in examples or how to use it?I would need an example to implement it. I just want to read the 128 samples from both channels

 

If you were performing a longer acquisition, DAQmxRegisterEveryNSamplesEvent() or DAQmxGetReadAvailSampPerChan() could also be useful. Doing your own thread management could also work.

 

I am not expert in programming so I do not know how to make threads, etc.

 

Thanks.

 

One other thing: I do as you said: FIRST I start the ANALOG task in a INIT procedure and then I start a timer that sends a char 'r' and next instruction I READ but then after one read I stop the task. My mistake was to stop the task. Is it necessary to stop a task? Can I delete the part of stopping the task if it is not needed? I do start it but I was not sure if I had to stop it too or just come out of the application would be enough

So do u strongly recommend DAQmxRegisterDoneEvent()? 

I was thinking if I will be reading all the data when char'r' is sent and then MUC sends the clock and data is received by NICARD.

 

Thanks and regards,

 

Javier

 
0 Kudos
Message 13 of 32
(4,175 Views)

I attach a printscreen of my latest ERROR.

 

I changed  DAQmxWaitUntilTaskDone() to a value of 1 so thta I do not wait forever.

 

I acquire 128 samples per channel and I have two channels (50000 samples/sec max per channel).

 

So therefore do you have an example of how to do what you describe here?: DAQmxRegisterDoneEvent() to register a callback function, and call DAQmxReadAnalogF64() in the callback.

 

Please see the attached printscreen error and suggest FIXES. It looks as if I am reading too late or the ATMEGA128 send too fast for me to pick up values with the NIDAQ READ function.

 

Thanks,

 

Javier.

0 Kudos
Message 14 of 32
(4,141 Views)

Hi Javier,

 

Oops, I must not have noticed the e-mail notification about your previous reply. But there is. Sorry.

 

> Setting 50000 samples per second gives 1/50000 = 20 us. I know that this happens to be the max 2-channel AI sample but I need a clock of 20us for setting 50000. Therefore I do not understand when you say that it meets the requirements for 30us. Am I missing something out? As you say for 30 us I need 33334 at least but 50000 would not work!

 

When using an external clock, you have to tell DAQmx the maximum rate that your clock could go. It is okay if the clock is actually slower than that. If your clock is actually 33 kHz, telling DAQmx that the clock might be as fast as 50 kHz is fine. So the sample rate is probably not why your program does not work.

 

> I set it to 1 second now instead of -1.

 

Now the longest that this call to DAQmxReadAnalogF64() will take is 1 second. If you're calling it from your UI thread, you probably don't want it to wait even that long.

 

> Well what happens is that I send a char 'r' and then the MUC sends the clock when it sees the char 'r' and then it receives data. The problem is that I do not know if ,after setting char 'r' and starting the clock, takes faster than for the next instruction of the UI to execute in visual c++ which is READ.

 

Start the DAQmx task before sending the 'r' to the MCU, so that the DAQ hardware is already reading before the MCU sends any clocks. That way, it will not matter how long it takes to execute the DAQmxReadAnalogF64() function.

 

> Never used this one but may be I should have a look in examples or how to use it?I would need an example to implement it. I just want to read the 128 samples from both channels

 

Many of the DAQmx ANSI C shipping examples demonstrate how to call DAQmxRegisterDoneEvent(). Here's one: C:\Documents and Settings\All Users\Documents\National Instruments\NI-DAQ\Examples\DAQmx ANSI C\Analog In\Measure Voltage\Cont Acq-Ext Clk-Dig Start

 

However, there is no example that does exactly what you want. Keep reading, I've posted some pseudocode at the end of this message.

 

> I am not expert in programming so I do not know how to make threads, etc.

 

Then taking advantage of DAQmx's buffering is the easiest approach.

 

> One other thing: I do as you said: FIRST I start the ANALOG task in a INIT procedure and then I start a timer that sends a char 'r' and next instruction I READ but then after one read I stop the task. My mistake was to stop the task. Is it necessary to stop a task? Can I delete the part of stopping the task if it is not needed? I do start it but I was not sure if I had to stop it too or just come out of the application would be enough

 

You must clear the task, or your app will leak memory (until your app exits, at which point it will be freed). If you do not stop the task, DAQmx will stop it when you clear the task.

 

> I attach a printscreen of my latest ERROR.

 

This error says that you are reading too many samples. If you call DAQmxCfgSampClkTiming(..., DAQmx_Val_FiniteSamps, 128) and then you try to read 129 samples, or you try to read 128 samples twice, or you try to read 1 sample 129 times, you will get this error.

 

> So therefore do you have an example of how to do what you describe here?: DAQmxRegisterDoneEvent() to register a callback function, and call DAQmxReadAnalogF64() in the callback.

 

I don't have an example, but here is some untested C-like pseudocode showing what I'm talking about. Just the basics--no error handling, etc.

 

static TaskHandle task = 0;

static UINT_PTR timer = 0;

 

/* Forward declarations */

int32 CVICALLBACK OnDoneEvent(TaskHandle, int32, void*);

VOID CALLBACK OnTimerExpired(HWND, UINT, UINT_PTR, DWORD);

 

/* This function gets called when a button on the UI is clicked. */

void OnStartButtonClick() {

   DAQmxCreateTask(..., &task);

   DAQmxCreateAIVoltageChan(task, ...);

   DAQmxCfgSampClkTiming(task, "PFI7", 50000, DAQmx_Val_Rising, DAQmx_Val_FiniteSamps, 128);

 

   /* When/if this task finishes acquiring 128 samples, OnDoneEvent() will get called. Since this is a UI app, you might want to replace the 2nd parameter (0) with DAQmx_Val_SynchronousEventCallbacks to have DAQmx use Windows messages instead of a separate thread. */

   DAQmxRegisterDoneEvent(task, 0, OnDoneEvent, NULL);

 

   /* If something goes wrong, this task might never finish. Set a Win32 timer to execute in 20 seconds, in case that happens. Since this is a UI app, you might want to have the timer send a Windows message instead of calling a callback, but I showed the callback because it's clearer. */

   timer = SetTimer(..., 20000, OnTimerExpired);

 

   /* Start the DAQmx task before sending the command to the MCU, so that the PCI-6031E is already waiting for sample clocks when the MCU starts sending them. */

   DAQmxStartTask(task);

   SendCommandToMCU('r');

}

 

/* If this function gets called, then there are 128 samples to read, and calling DAQmxReadAnalogF64() should not have to wait at all. */

int32 CVICALLBACK OnDoneEvent(TaskHandle, int32 status, void *) {

   KillTimer(..., timer);

   if(status < 0) ReportErrorToUser();

   DAQmxReadAnalogF64(task, 128, 0.0, DAQmx_Val_GroupByScanNumber, buffer, buffer size, &samplesRead, NULL);

   DAQmxStopTask(task);

   DAQmxClearTask(task);

   DisplayBuffer(buffer, samplesRead);

   return 0;

}

 

/* If this function gets called, then the DAQmx task did not finish in 20 seconds. */

VOID CALLBACK OnTimerExpired(HWND, UINT, UINT_PTR, DWORD) {

   KillTimer(..., timer);

   DAQmxClearTask(task);

   DisplayError("Task didn't finish in 20 seconds! Something is wrong.");

}

 

Brad
---
Brad Keryan
NI R&D
Message 15 of 32
(4,106 Views)
   

When using an external clock, you have to tell DAQmx the maximum rate that your clock could go. It is okay if the clock is actually slower than that. If your clock is actually 33 kHz, telling DAQmx that the clock might be as fast as 50 kHz is fine. So the sample rate is probably not why your program does not work.

 

I set it to 50000 Khz.

 

 

Now the longest that this call to DAQmxReadAnalogF64() will take is 1 second. If you're calling it from your UI thread, you probably don't want it to wait even that long.

 

I left to 1 second but I thought it read it as fast as it could and then continued. Is this right?

 

 

Start the DAQmx task before sending the 'r' to the MCU, so that the DAQ hardware is already reading before the MCU sends any clocks. That way, it will not matter how long it takes to execute the DAQmxReadAnalogF64() function.

 

Ok. I am doing that like this. I created separate routine for start, read and stop so I start a timer and call the following:

 

void CDisplayDlg::OnTimer(UINT nIDEvent)
{

 

 //Start task
    m_niCard.Start();

    mySerialPort.Write("r");

    //read values
    m_niCard.Read();

    //Stop and clear task
    m_niCard.StopClear();

 

You must clear the task, or your app will leak memory (until your app exits, at which point it will be freed). If you do not stop the task, DAQmx will stop it when you clear the task.

 

I stop and clear the task after reading.

 

I don't have an example, but here is some untested C-like pseudocode showing what I'm talking about. Just the basics--no error handling, etc.

 

static TaskHandle task = 0;

static UINT_PTR timer = 0;

 

/* Forward declarations */

int32 CVICALLBACK OnDoneEvent(TaskHandle, int32, void*);

VOID CALLBACK OnTimerExpired(HWND, UINT, UINT_PTR, DWORD);

 

/* This function gets called when a button on the UI is clicked. */

void OnStartButtonClick() {

   DAQmxCreateTask(..., &task);

   DAQmxCreateAIVoltageChan(task, ...);

   DAQmxCfgSampClkTiming(task, "PFI7", 50000, DAQmx_Val_Rising, DAQmx_Val_FiniteSamps, 128);

 

   /* When/if this task finishes acquiring 128 samples, OnDoneEvent() will get called. Since this is a UI app, you might want to replace the 2nd parameter (0) with DAQmx_Val_SynchronousEventCallbacks to have DAQmx use Windows messages instead of a separate thread. */

   DAQmxRegisterDoneEvent(task, 0, OnDoneEvent, NULL);

 

   /* If something goes wrong, this task might never finish. Set a Win32 timer to execute in 20 seconds, in case that happens. Since this is a UI app, you might want to have the timer send a Windows message instead of calling a callback, but I showed the callback because it's clearer. */

   timer = SetTimer(..., 20000, OnTimerExpired);

 

   /* Start the DAQmx task before sending the command to the MCU, so that the PCI-6031E is already waiting for sample clocks when the MCU starts sending them. */

   DAQmxStartTask(task);

   SendCommandToMCU('r');

}

 

/* If this function gets called, then there are 128 samples to read, and calling DAQmxReadAnalogF64() should not have to wait at all. */

int32 CVICALLBACK OnDoneEvent(TaskHandle, int32 status, void *) {

   KillTimer(..., timer);

   if(status < 0) ReportErrorToUser();

   DAQmxReadAnalogF64(task, 128, 0.0, DAQmx_Val_GroupByScanNumber, buffer, buffer size, &samplesRead, NULL);

   DAQmxStopTask(task);

   DAQmxClearTask(task);

   DisplayBuffer(buffer, samplesRead);

   return 0;

}

 

/* If this function gets called, then the DAQmx task did not finish in 20 seconds. */

VOID CALLBACK OnTimerExpired(HWND, UINT, UINT_PTR, DWORD) {

   KillTimer(..., timer);

   DAQmxClearTask(task);

   DisplayError("Task didn't finish in 20 seconds! Something is wrong.");

}

 

I had a look at your code but I am not sure I am able to program well enough what you suggest. Are the lines in ur code in order? I do not see mysefl capable of coding what you say in ur lastest comment above. I prefer to keep it simple and simply read. It would also add extra code which is not absolutely needed? If it is really needed, then may be u can paste what I should put and I can just copy paste into my code? However, I did see already a nice related acquired value (-0.025) but just once as you can see in the printscreen attached.

 

I ATTACH in this reply the latest ERROR message which complains about the TASK being invalid. I managed however to see a reasonable value on the screen after one acquisition (-0.025). ALMOST there I think. Please help. 

 

I also attach the routines which I call in the TIMER as a TXT file.

 

Thanks.

 

Javier.

 

 


0 Kudos
Message 16 of 32
(4,057 Views)

I left to 1 second but I thought it read it as fast as it could and then continued. Is this right?

Yes, this means that your UI will stop responding for up to one second if you execute that code in the UI thread.

 

I had a look at your code but I am not sure I am able to program well enough what you suggest.

If that's the case and you can get your current solution to work, then don't worry about it.

 

Are the lines in ur code in order?

The lines of code in each function are in order, and the functions are arranged in the order that they are expected to be called in.

 

It would also add extra code which is not absolutely needed?

The pseudocode I posted performs an asynchronous read without blocking the UI, and displays a timeout error if a trigger isn't received in a set amount of time, which is what I thought you wanted to do. But if you are willing to let it block the UI for a second at a time, then you don't have to do it that way. And you could simplify it by removing the timeout error, but the DAQ board would keep waiting forever in that case.

 

I ATTACH in this reply the latest ERROR message which complains about the TASK being invalid.

This error sounds like you are trying to reuse the task after clearing it. You can't do that. You can create a new task each time you need it (i.e. combine configuration and starting), or you can avoid clearing the task until you're completely done with it (i.e. separate stopping and clearing).

 

I also attach the routines which I call in the TIMER as a TXT file. 

Does the timer execute in the UI thread or a separate thread? If it's in a separate thread, then it shouldn't block the UI.

 

Brad

---
Brad Keryan
NI R&D
Message 17 of 32
(4,037 Views)

I left to 1 second but I thought it read it as fast as it could and then continued. Is this right?

Yes, this means that your UI will stop responding for up to one second if you execute that code in the UI thread.

 

 

I did not know that this thing stopped for one sec. Then I should not use at all this function below. I might as well delete it. I do not know why I am using it. What is the minimum value that this function can be set to? From -1 (forever) to microseconds perhaps? I think this is the reason why my code is not working!

 

    nErrorCode = DAQmxWaitUntilTaskDone(m_hAnalogTask, 1);
    if(nErrorCode!=0) //error encountered
    {
        DAQmxGetErrorString(nErrorCode, szErrorMsg, 1024);
        AfxMessageBox(szErrorMsg);
        return;
    }

 

 

I had a look at your code but I am not sure I am able to program well enough what you suggest.

If that's the case and you can get your current solution to work, then don't worry about it.

 

Yes, I prefer my current solution but I am trying to find out what the heck is not working! I think that it is just waituntiltaskdone. I am not sure though.

I just wanted to simply read the 128 values per channel to the buffer and that was it! I did not know that it was going to be such a pain

 

 

It would also add extra code which is not absolutely needed?

The pseudocode I posted performs an asynchronous read without blocking the UI, and displays a timeout error if a trigger isn't received in a set amount of time, which is what I thought you wanted to do. But if you are willing to let it block the UI for a second at a time, then you don't have to do it that way. And you could simplify it by removing the timeout error, but the DAQ board would keep waiting forever in that case.

 

 The external clock from the microcontroller definately arrives ( i checked it with oscilloscope so I do not think I need all this.

 

In order not to let it block one sec i think i should take out waituntiltaskdone and leave the code as it was intially.

 

 

 

I ATTACH in this reply the latest ERROR message which complains about the TASK being invalid.

This error sounds like you are trying to reuse the task after clearing it. You can't do that. You can create a new task each time you need it (i.e. combine configuration and starting), or you can avoid clearing the task until you're completely done with it (i.e. separate stopping and clearing).

 

Ok. before I had everything together, but I guess separating them did not work. Ok so I guess I will start the task before I start the timer, then I start the timer and every time the timer executes I read the values and I do not stop the task at all.

 

Another option is that I start the timer and then I start the task, read and stop every time the timer executes and then when the UI closes I suppose everything will clear anyway.  I AM CONFUSED a little bit. May be it is easier for you to have a look at my Visual C++ code. I wanted to keep it simple and I am thinking that WAITUNTILTASKDONE is not really needed. 

 

I also attach the routines which I call in the TIMER as a TXT file. 

Does the timer execute in the UI thread or a separate thread? If it's in a separate thread, then it shouldn't block the UI.

 

I do not really know what is a thread. -) I mean I understand it but I never created one so that is why I do not know exaclty what it is. I just initialise a dialog and then call a timer when I press a button. Simple programming which is what I am able to do just to get my things working.

 

All the action starts with INITDIALOG which means, when the dialog User interface shows up. Then I press a READ button and this sets the timer and the code in the timer is executed. 

 

In the INITDIALOG I initialise the NICARD and call the START task code (initial code). Then the timer starts when I PRESS a READ button and the READanalogf64 is called and then again the timer executes over and over again. I have the feeling this waituntiltaskdone is ruining everything making the UI stop and block but I am not sure. May be the best is that you look at my code. Let me know.

 

Bear in mind that I do not know how to program that well and so I can do the basics and so my code is also basic.

 

Thanks and regards,

 

Javier.

 

 

0 Kudos
Message 18 of 32
(4,034 Views)

By "up to one second" I meant "between 0 seconds and 1 second". If the task is done in less than one second, the function returns sooner.

 

Why don't you go back to something else I suggested here? You can use DAQmxGetReadAvailSampPerChan to find out how many samples are in DAQmx's buffer. Call it in your timer callback to determine when to call DAQmxReadAnalogF64. When there are enough samples in DAQmx's buffer, then you call DAQmxReadAnalogF64, but not before. And don't call DAQmxWaitUntilDone because its purpose is to wait.

 

Brad

 

---
Brad Keryan
NI R&D
0 Kudos
Message 19 of 32
(4,016 Views)

 

Hi Brad.

 

I attach a WORDPAD file where I put my new comments. It did not fit here in the email so I attach this WORDPAD file.

 

Please have a look.

 

In my new code I declare functions in an NIWrapper.h file and I code in an NIWrapper.cpp file.

 

So when I try compiling int 32 CVICALLBACK CNIWrapper::OnDoneEvent(TaskHandle, int32 status, void *) 

I get a compile error:

error C2664: 'DAQmxRegisterDoneEvent' : cannot convert parameter 3 from
'long (void)' to '

Most functions in these files are declared as void so this int32 CVICALLBACK is a bit weird to include in my NIWRAPPER.

 

Then from my main dialog cpp file I call everything; start task, etc... but I cannot call int32 CVICALLBACK because it does not compile. 

 

Thanks.

 

Javi.

 

 

 

0 Kudos
Message 20 of 32
(3,937 Views)