Multifunction DAQ

cancel
Showing results for 
Search instead for 
Did you mean: 

Where are nidaqmx low level functions?

Hi LMP,
 
Sorry you are having problems with your application.   I will see if I can point you in the right direction.
 
First off, there should be some C examples which ship with DAQmx.  These could be located in one of a few places (depending on which version of DAQmx and which version of windows you have installed).  I would recommend that you search your system for WriteDigPort.c.  This is a simple example which will show you the use of DAQmx for doing software timed digital writes, which I assume is what you are attempting for your application.  There should be other examples located very near to this one.
 
(I'd like to be more specific, but based on version of DAQmx and your OS, it could be located in a few different places...
Win XP Pre-DAQmx 8.5: C:\Program Files\National Instruments\NI-DAQ\Examples\DAQmx ANSI C
Win XP Post-DAQmx 8.5: C:\Documents and Settings\All Users\Documents\National Instruments\NI-DAQ\Examples\DAQmx ANSI C
Win VISTA DAQmx 8.6: C:\Public Documents\National Instruments\NI-DAQ\Examples\DAQmx ANSI C)
 
As for documentation, the best I know is probably the NI-DAQmx C reference Help, which should also be installed when you install DAQmx.  On my computer, I can access it from the start menu as Start->Programs->National Instruments->NI-DAQ->NI-DAQmx C Reference help.  This is a good description of DAQmx function by function, however it may not be the best for conveying larger concepts.  However, if you can find the installed examples, you should have a pretty good basis to start from.
 
Hopefully between these example programs and the C Reference Help (I don't know if you've seen that or not), you should be able to progress with your application.  Certainly feel free to post back here with any questions speciffic to your appliation and I will be happy to help.
 
Hope this helps,
Dan
 
PS.. Dennis is correct that starting and stopping your task repeatedly is time-consuming and should not be necessary.  Once you start the task, you should be able to write the digital port repeatedly without stopping or starting the task.
0 Kudos
Message 11 of 62
(2,850 Views)
Hi Mcdan, thanks for your participation.
We have used the Traditional for several years and now we need to change the code to the Nidaqmx, because we at Omnimetra are afraid the E serie boards we use in our equipments will not exist for much time.
 
> As for documentation, the best I know is probably the NI-DAQmx C reference Help,
Yes, that's what we use more.
 
> however it may not be the best for conveying larger concepts. 
The main concepts are understood. Not a big problem. In fact, the Traditional was more hard to figure out and use than the mx. But it had low level functions the mx does not have. THIS is the first point that started this thread: where are the nidaqmx low level functions?
 
The issue is simple. While in Traditional we just use a DIG_Out_Prt(BOARDNUM,0,value) function to write a byte straight, fast and easy, in mx we do not have an equivalent. We need to use the WriteDigitalU8() that needs 8 parameters (!) and uses a BUFFER to write N bytes, not to mention the task control.
 
> I would recommend that you search your system for WriteDigPort.c
 
Yes, we had it. We have all docs and samples. This one is very simple. But have you tested it?
It does not work.

uInt32 data=0xffffffff;
char errBuff[2048]={'\0'};
int32 written;

DAQmxErrChk (DAQmxCreateTask("",&taskHandle));
DAQmxErrChk (DAQmxCreateDOChan(taskHandle,"Dev1/port0","",DAQmx_Val_ChanForAllLines));

DAQmxErrChk (DAQmxStartTask(taskHandle));

DAQmxErrChk (DAQmxWriteDigitalU32(taskHandle,1,1,10.0,DAQmx_Val_GroupByChannel,&data,&written,NULL));

Error:
if( DAQmxFailed(error) ) DAQmxGetExtendedErrorInfo(errBuff,2048);
if( taskHandle!=0 ) {
  DAQmxStopTask(taskHandle);
  DAQmxClearTask(taskHandle);
}
...

It seems natural to use the start() first and then the write() so many times as you need, isn't it?
Yes, that's how it works for reading analog data and I suppose digital data as well. We have done all these programming techniques, continuous reading using callback functions to get the data in real time etc. And they all work.
But to WRITE digital data, it does not work. It raises the error: "Generation cannot be started because the output buffer is empty" !
You need to use write() first, where you set the buffer and the number of bytes to write, and THEN use the starttask() to make it work.
But THEN... you also need to stop() it if you want to start() again... and you are trapped on the bad loop of start/stop.
Or... you can use the write() with the autostart parameter true, what makes the same as if you use the start() / stop() approach. It is weird? Yes it is. But you need to write() first to set the output buffer and then you can use the start(). The result? Horrible timing.

As we see, there are two different issues here:
1) the simple low level I/O write of the Traditional that we do not have in the Mx. THIS should be the right solution.
2) how to make the task approach less cumbersome and time consuming just to write a byte!
 
From the time point of view, critical in real time applications, it is an absurd that to write a single byte to a hardware port, we need to use a buffer, a block of bytes, a clocking timing generation (that also seems not to work as expected) and a 8 parameter function!
 
LMP
 
0 Kudos
Message 12 of 62
(2,840 Views)

LMP,

Unfortunately I am at home and do not have any hardware with me to test the example now (nor do I have Traditional DAQ installed, so I am without reference for that at the moment).  I will say that I use the LabVIEW version of this example quite frequently and have not had any problems with it.  There is only one line which looks suspicious to me which is the following:

DAQmxErrChk (DAQmxWriteDigitalU32(taskHandle,1,1,10.0,DAQmx_Val_GroupByChannel,&data,&written,NULL));

I am not sure why they 'autoStart' argument is set to '1' since the task was already started.  I would think this should be set to '0' (though according to the documentation this should have no effect for a task which was already started).  At this point you should be able to write repeatedly.  However this does not seem to be in line with the behavior you are seeing.  Let me ask a few questions so that I can clarify what you are trying to accomplish in my mind.

>> It raises the error: "Generation cannot be started because the output buffer is empty" !
     You need to use write() first, where you set the buffer and the number of bytes to write, and THEN use the starttask() to make it work.

When you originally wrote that you were writing an application to replace an E-Series device, I assumed that you were performing software timed writes (essentially every call to the write function updates the value output on your digital port.  However, this error would seem to indicate that you have explicitly set up a buffer (and possibly hardware timing?) as part of your task.  If you have configured timing, then essentially then data will need to be written before that task is started such that there is data available to the hardware before the clock responsible for updating the port occurs.  However, I don't believe that E-Series devices had this functionality, so I was assuming that this was not what you were attempting.  I would imagine you would get such an error if you called DAQmxCfgInputBuffer or DAQmxCfgSampClkTiming.

>>  As we see, there are two different issues here:
      1) the simple low level I/O write of the Traditional that we do not have in the Mx. THIS should be the right solution.
      2) how to make the task approach less cumbersome and time consuming just to write a byte!

To address point 1, DAQmx is task based.  Therefore, the key is to configure the task to behave the way you would like it to.  There really aren't any low level functions that should be necessary to make this happen.  Ideally, you would create your channels, do any configuration necessary (for example setting up any timing parameters or triggers), start the task, then read or write the task as required.  Once the task is started, you should be able to call DAQmxWriteDigitalXX just as you would have DIG_Out_Prt, and DAQmx should update your hardware with the new value you have written.

>> We need to use the WriteDigitalU8() that needs 8 parameters (!) and uses a BUFFER to write N bytes, not to mention the task control

To address this and point number 2 above, the idea behind the highly parameterized DAQmxWriteDigitalXX functions I believe was to present a consistent interface to write data regardless of 'application' or task configuration.  The parameters of write and the configuration of the task tell the driver how to behave (This in effect presents a consistent interface across functionality, and eliminates the need for 'low' level functions).  In a software timed case, the driver will write the data in 'writeArray' to the hardware as quickly as possible.  In a hardware timed, buffered case the driver will write the data in 'writeArray' to the buffer from which the hardware reads as quickly as possible.

Hopefully this information is at least informative, and at most helpful.  At this point, I would suspect from the errors that you are seeing that something in your task's configuration is not quite correct for what you are trying to do.  I'll see about giving the example a try when I get to work tomorrow and have some hardware available to me.  In the mean time, please let me know if you have questions about any of that, or if I am still not understanding what you need to accomplish.

Dan

Message 13 of 62
(2,832 Views)
Hi Mcdan,

>> I am not sure why they 'autoStart' argument is set to '1' since the task was already started.

There is one thing I did not mention and explains many things.
The simple code using the sequence: create, start, write does not work when you use DAQmxCfgSampClkTiming().
If you use clkTiming(), then you cannot start() before setting the buffer using write(). (I was not able to do it, it gives the error mentioned about buffer not set).
If you do not use clkTiming(), then you can start() and then use write() inside a loop or anything else without start()/stops() and the autostart parameter has no effect; it works the same with autostart true or false.
Thus, the most fast approach is not using the clkTiming() but using create(), start() and then use the write() at will as in the simple example of c code.
And, of course, in the write() you need to set the buffer and the number of bytes each time.
For software timed outputs, I think this approach is the better one.

One thing that was not clear to me is how I can be sure the write() is done. Because if I do not use clkTiming(), I cannot use waituntiltaskdone(). If a second write() comes very soon, how be sure the first one was completed to avoid "writting overlap"? It would work if the write() only returns after its completion.

Also, I was not able to see the clkTiming() working. If you set 1KHz as output generation clock and write 1000 bytes, shouldn't the task take 1 second to complete and output the bytes (that could be a waveform of a custom function generator) at 1 byte each 1 ms ? This did not work. It spends 10 to 15 ms to complete the task using the waitUntilTaskDone() (however this was not checked using an osciloscope in the output pins).

>>
However, I don't believe that E-Series devices had this functionality,

We are trying to use the nidaqmx in the 6220M series board.
We use the 6023E with the Traditional.

Among other things (multithreaded stuff), we use one output digital port to send a custom generated waveform to an external hardware including three DACs to feed a cell used in electro chemistry to make pulse techniques measurements. Currently (E series + traditional nidaq) we use software timed generation. With the M series boards and MX nidaq, we could maybe use the hardware timing generation and get faster output, IF the clkTiming() worked.

🙂



0 Kudos
Message 14 of 62
(2,824 Views)
LMP,
 
I had a long post written, and seem to have lost it all, so I'll try to write it again...
 
I did test the software timed example, and had did get it to work as I expected.  I modified it to write multiple samples after start and that seemed to work just fine.  Here is the meat of the modified example:

DAQmxErrChk (DAQmxCreateTask("",&taskHandle));
DAQmxErrChk (DAQmxCreateDOChan(taskHandle,"Dev1/port0","",DAQmx_Val_ChanForAllLines));
DAQmxErrChk (DAQmxStartTask(taskHandle));
for(index = 0; index < 1000; index++)
{
   DAQmxErrChk (DAQmxWriteDigitalU32(taskHandle,1,1,10.0,DAQmx_Val_GroupByChannel,&index,&written,NULL));
   printf("Current Sample %X\n", index);
}
DAQmxStopTask(taskHandle);
DAQmxClearTask(taskHandle);
 
This code should behave much like the software timed code traditional DAQ code you were using with your E-Series device.  Basically after you call start, you should be able to write over and over again until you stop the task.  Note that I have not configured any time or buffer parameters.  For software timed write such as this, when the write call returns, you can be assured that the sample written has been output to hardware.
 
If you want to use hardware timing then I suggest using writeDigChan-ExtClk and ContWriteDigPort-ExtClk as your starting points.  Basically I took parts of both of these examples and put them together to demonstrate hardware timed port writes.  The meat of that code follows:

DAQmxErrChk (DAQmxCreateTask("",&taskHandle));
DAQmxErrChk (DAQmxCreateDOChan(taskHandle,"Dev1/port0","",DAQmx_Val_ChanForAllLines));
DAQmxErrChk (DAQmxCfgSampClkTiming(taskHandle,"/Dev1/PFI0",1000.0,DAQmx_Val_Rising,DAQmx_Val_FiniteSamps,1000));

for(;i<1000;i++)
{
   data[i] = (uInt8)(i%255);
}
DAQmxErrChk (DAQmxWriteDigitalU32(taskHandle,1000,0,10.0,DAQmx_Val_GroupByChannel,data,&returnData,NULL));
DAQmxErrChk (DAQmxStartTask(taskHandle));
DAQmxErrChk (DAQmxWaitUntilTaskDone(taskHandle,10.0));
DAQmxStopTask(taskHandle);
DAQmxClearTask(taskHandle);

Please note that the digital lines on M-Series devices do not have any timing mechanism dedicated to them.  Therefore you will need to provide an external clock to it.  For my testing purposes, I used Counter 0 to output a pulse train to use as the sample clock for  my digital task.  I simply wired this to PFI0, which I used as a sample clock source.  Here, write will complete as soon as DAQmx has been able to transfer your requested samples into a buffer on your PC.  Because of this, I use the DAQmxWaitUntilTaskDone call to wait for all of my sample to be output before I continue.

Hopefully these modified examples will guide you in the right direction.  Please let me know if you have any other questions.
Hope this helps,
Dan

Message 15 of 62
(2,802 Views)

>> Please note that the digital lines on M-Series devices do not have any timing mechanism dedicated to them. 

Ok, but it accepts the clkTiming() with an internal clock source for generation of the output waveform with no errors or warnings. It accepts the parameter but it seems it does not use it or obey it.

On the other stuff, we are in synch.

Thanks for your help,

🙂

 

0 Kudos
Message 16 of 62
(2,793 Views)
>> Ok, but it accepts the clkTiming() with an internal clock source for generation of the output waveform with no errors or warnings. It accepts the parameter but it seems it does not use it or obey it.

Can you expand on this statement?  What string do you use for you source?
You can use internal singnals such as "/Dev1/ai/sampleClock", an internal counter output, etc.  However for this to work, you would have to have a task running on one of these other subsystems which would generate a clock signal.  The digital task cannot actually cause these clocks to run.
 
Might this be the issue you are running into?
Dan
0 Kudos
Message 17 of 62
(2,789 Views)
>> Can you expand on this statement?  What string do you use for you source?
 
DAQmxCreateTask('',@task5);
DAQmxCreateDOChan(task5,'Dev1/port0','',DAQmx_Val_ChanForAllLines);
DAQmxCfgSampClkTiming(task5,'20MHzTimebase',DIGFREQ,DAQmx_Val_Rising,DAQmx_Val_FiniteSamps,DIGSIZE);
(digfreq = 1E3, digsize = 1000, the @task5 is Pascal equivalent for &task5)
 
The 6220M accepts 20MHzTimebase and 100KhzTimebase. But the timing is not as expected.
Depending on the board, as in the small USB 6009, all clock sources return error.
If I use OnbardClock I get the error 'no source clock in the device'.
If I use NULL (as says the doc), I get the error 'use external clock for this application'.
Maybe I am not understanding well the documentation (my native language is not English).
 
LMP

 
0 Kudos
Message 18 of 62
(2,780 Views)
M-Series devices do not have their own digital timing engine.  Timed digital is "correlated" which means it needs an external timing source.  External just means external to the digital hardware, as you've seen it can come from the 100kHz timebase, ai/SampleClock, counters, etc.

Other devices (such as the USB-6008) return errors when you attempt to configure timing for digital tasks because they do not support correlated digital. 

The behavior for Write is slightly different if you are doing a correlated task (timing configured) or not.  In the correlated case, you are required to write before starting the task to prevent immediate underflow of the FIFO.  In the software timed case (no timing configured) you do not have to write before start.  Call start, write as many times as you like, and call stop.

Hope that helps.
0 Kudos
Message 19 of 62
(2,773 Views)

Hi LMP,

Here is an instance where DAQmxCfgSampClkTiming may not behave exactly how you are expecting.  The error you receive that states that there is no clock source in the device when you attempt to use on-board clock is correct.  That is because M-Series devices do not have timing controllers dedicated to them like AI or AO would.  With AI and AO have some dedicated timers which can be used to create clock signals for those subsystems.  If you use the onboardClock option with these subsystems and set a sample rate, hardware will get programmed which causes these counters to create a clock signal with the specified rate, and will then route this signal to your DAC or ADC.  Things are a bit different for Digital input or Digital Output.

These subsystems lack the ability to create their own clock.  Therefore they must use a source which is external to the subsystem.  There are several multiple signals which are possible to use as a sample clock for DI or DO.  Dev1/100KhzTimebase is routable to the do/sampleClock.  If you configure this route, DO will update at the rate of this clock, in this instance 100 kHz.  The rate input to DAQmxCfgSampClkTiming will only be used by the driver as a sanity check for what you are attempting, not to control the the frequency of the sample clock.  This is why I had tested my earlier example by using a counter to provide a clock for my digital output.

Hope this helps,
Dan

Message 20 of 62
(2,772 Views)