Multifunction DAQ

Showing results for 
Search instead for 
Did you mean: 

6251 problem with quadrature encoders using nidaqmx

I am writing software to interface the 6251 board using the nidaqmx API in C++ with the latest version of Visual Studio.
At the beginning of my program I create a task and use the DAQmxCreateCILinEncoderChan to create my encoder channel on ctr0.  I can then start the task and pull a single point reading out using DAQmxReadDigitalScalarU32 anytime I want.  The problem occurs when I want to collect analog and encoder data together against the internal clock I have to STOP the encoder task from running before I can call the DAQmxCfgSampClkTiming function (or I get an error back from the API stating I can't change the timing with the task running.  Of course once I do this the encoder's current value is "lost" and the buffered data acuistion starts with the value zero.  "Remembering" the current value and adding it as an offset is not an option as the encoder may be moving at anytime. 
1. Is there a way to do what I need? 
2. Once I have finished reading out the buffered data how do I get the task back to its initial state so I can call the ReadDigitalScalar function to again get instintaneous single point readings whenever I want?  Again, losing the current encoder reading is not an option.
3. Are there any similar "gotcha's" to look out for when I proceed with collecting analog data with the encoder clocking the analog task instead of the internal timer?
Thanks for any help
0 Kudos
Message 1 of 8

Hello Lou_III,

First off, welcome to the NI forum.  From your description, it appears that the problem is with the fact you lose your position information when you try and stop the counter task.  You need to stop the counter task so that you can switch from single point reading to a buffered counter synchronized with an analog input (and back).

Assuming I understand the issue correctly, then I have a couple ideas you can try.  First off, you could use two counters (both wired to the encoder) and have one do single point while the other does buffered.  You could then choose which data is displayed/used/etc depending on what your need is. Since they are both running from the beginning, you would not lose your position when switching.

If that is not an option, then you could just use the buffered acquisition.  When you want the single point, you could read all of the data on the FIFO buffer and only use the last point.  This would be very close to the single point read, but you would just have to parse the data yourself (get the last value in the array).  I tried this in LabVIEW (which I am more fluent in) and it worked very well.  I used a case structure (equivalent to "if, then, else" statements in C) and a Boolean to choose whether to take the last point of the array I read or output the whole array. I did not specify a samples to read so that the function would read all of the samples in the buffer and give me the most recent point.

The trick with this second method is that you must always have an external clock running to latch values into the buffer of the counter.  One way to do this is to have your analog input running continuously (even if you do not need the data).  This would provide your sample clock.  You could then manually control the buffer with the RelativeTo function and the OverWrite Mode Property so you do not get a buffer overwrite error.

I am a little confused as to how the analog input goes with all of this.  Do you want to use the encoder as the sample clock of the analog input? Or are you possibly trying to take encoder values at the same time as your analog input?

If you want to use the encoder signal as your AI sample clock and synchronize with the counter, you could input the encoder signal on a PFI line and use it as the sample clock for both the AI and the counter (you would be counting the edges and latching values of the counter with the same signal). I tried this with my M series card, and it worked.

If you are trying to just use the same sample clock, you can set up the AI sample clock and use it (e.g. Dev1/ai/SampleClock) for both the AI and the counter.

I realize this is a long, wordy post, so please post back with the questions I am sure you will have and I will do my best to clarify.

Neal M.
Applications Engineering       National Instruments
0 Kudos
Message 2 of 8

Hello Neal, thanks for your help on this.  I really appreciate it.

As you guessed, I must support two seperate encoders so using both NI counters for one encoder is not an option.

The software that is being interfaced to is quite flexible so I need to support both "types" of data collection cases.

Case 1. Analog & Encoder & Digital buffered data collected against the NI internal timer (I have this working correctly using the "Dev1/ai/SampleClock" as the second parameter to the DAQmxCfgSampClkTiming function.)

Case 2. Analog & Encoder & Digital buffered data collected against either an Encoder or an Analog value.  (ie, collect data every 10 quadrature pulse or collect data every 1 mv )

I like your idea of always leaving a daq task running but can I change the Rate of the collection without stopping the task?  ie. If I just want single point data can I collect at 10 Hz but then when the user has requested high speed data acquisition I need to get data at 20 000 Hz.  I haven't tried this yet but I think I have to stop the task to change the rate of the internally clocked DAQ.

When it comes to Case 2...I don't understand how to get route the Encoder output to be the sample clock and use a different PFI line or how to get it to collect every say 10th quadrature pulse.  Is there a Divide by N function call somewhere?  If you could steer me to how this type of data collection is setup that would be great.

Message Edited by Lou_III on 11-27-2007 10:24 AM
0 Kudos
Message 3 of 8
Hello again Lou_III,

You are correct in your assumption that you cannot change the sample clock generated by a task while the task is running.  There are several ways to get around this.  The first is to just sample at (or faster than) your maximum sampling rate.  You can then use array manipulation to "divide down" after the fact (e.g. only take every 10th point).  One way to do this would be to use a loop and build a second array of the pertinent data.  You can also use counters (if you had extra) to do a divide down of your sample clock, and you would not have to stop that counter to change the divide down value (you would use the DAQmxGetCOPulseHighTicks and DAQmxGetCOPulseLowTicks functions).

The second method to get around having to stop the task to change sampling rates is to use an external clock.  Our function generators can actually output a square wave and change the frequency on the fly (without stopping the signal).  You could then input this (or something similar) on a PFI line and use it as your sample clock.

The last method to accomplish this would be to use a third (Analog Output) task to create the sample clock for the other tasks.  You could then stop the task and change the rate without loosing the information in the two tasks.

As far as your Case 2 goes (using the encoder as your sample clock), that is relatively more straight forward.  If you hook your encoder signal (for example your A signal on an X2 encoder) to a PFI line, you could specify that line as the sample clock and the counter A signal source (using DAQmxGetCIEncoderAInputTerm).  The only ways I know of dividing that signal (take only one sample for every 10 ticks of the encoder) would be to use that divide down counter or just separate the array like I mentioned before.  There is no function to divide down the sample clock since it is all taken care of in hardware.

I hope this works for you, please post back with any questions.  I am interested in which methods you use so please post back even if you find a solution!
Neal M.
Applications Engineering       National Instruments
0 Kudos
Message 4 of 8

Hi Neal....I have a follow up question related to digital I/O.

If I have a buffered data collection going on to read the digital inputs I have setup on Port0...can I also simultaneously control digital ouputs that I have created on another task using the DAQmxWriteDigitalScalarU32 function?  If not do I have to create my digital inputs & outputs on the same task, could I then do what I want?  Right now the Analog Inputs are on one task, Digital Inputs are on one task, each Encoder has one task, and my Digital Outputs are on another task.  Also, I understand the readback string for the ananlog Outputs is ao0_vs_aognd and ao1_vs_aognd but can I read these back while the AI task is they share the same mux or what are the rules here.



0 Kudos
Message 5 of 8

Hello Lou,

I am going to answer your questions in two "sections" so I can keep things straight.  The first section I will focus on your question about doing synchronous digital input and output.  Then I will go into how the Analog Inputs are read.

Since you are trying to do two separate functions (digital input and digital output), you need two separate tasks.  This is due to how the tasks operate; using different buffers and operations (read v write).  The DAQmxWriteDigitalScalarU32 function is a write function, so it will not work with a digital input.  The way to synchronize these is exactly like how you synchronized the analog and encoder; you must use the same sample clock so that the two tasks read and write out of their respective buffers at the same time.  The catch is that you must provide an external clock signal to do this.

Analog Input:
I am slightly confused about what you mean by "readback string" and reading it back.  It appears to me that you want to read the ao0 and ao1 channels (internally routed to aognd) back with your AI task while it is running.  If this is the case, then you would need to include these channels (_ao0_vs_aognd and _ao1_vs_aognd) in the channel list when configuring the task.  Due to the fact the channel information is used extensively while configuring the task, there is no way to add channels while the task is running.  You would then have to read the data like the rest of the channels.

The only properties of a task you can change while the task is running are listed in the LabVIEW help (see image below).  I am unsure if you have access to this help file, but if you do, you can search for them by searching (with quotes) "Properties Settable at Task Run Time."

I hope I understood your question correctly, post back if there is anything I can clarify.

Message Edited by Neal M on 12-06-2007 07:09 PM
Neal M.
Applications Engineering       National Instruments
0 Kudos
Message 6 of 8

I have physically have one encoder and feed in Phase A & B to ct0 and reverse Phase A & B into ctr1 so that effectively I have two quadrature encoders that always read the same value but one is the negative of the other.  If one reads -27.25 the other reads +27.25   When one counts up the other counts down and vice-versa.  Everything works great when I compile the debug version of my program in MSVC.

However, when I compile the release version the encoders do NOT work correctly.  I have removed the optimizations on the cpp files that contain references to DAQMX C function calls but it still does not work.  The symptom is that ct0  and ctr1 do not count in different directions.  If one moves in the positive direction the other one also moves in the positive diretion but at a different rate (say ctr0 counts faster than ctr1)...when I spin the encoder the opposite way, (instead of counting backwards) both ctr's continue to count up but now ctr1 counts up at a faster rate than ctr0.  Wierd indeed.  Any ides on what is happening here ?

0 Kudos
Message 7 of 8
Hi Lou,

I found some information regarding the difference between the debug and release builds on other discussion forum posts which I will post here. 

Code running in debug mode will run slower than in release mode. There is always an overhead when giving the user the possibility to debug the code.
The best way to avoid this type of problems is not to rely on execution time but to use a timer to control the pace. Use delay functions and keep things executing in sequence.

In debug mode, variables within a function are initialized to 0.  In release mode, memory is in an unknown state.  This means that in debug mode, uninitialized strings will have a terminating null, integers will be 0 when declared, etc and can be used safely.  In release mode, your uninitialized strings will be random values and may not be terminated, integers will be semi-random numbers, etc. 

 I found some KnowledgeBase articles on other websites which shed some light on some things to try when encountering such an error.  Please refer to Why doesn't my project work in release mode? and Why does program work in debug mode, but fail in release mode? for more information on what to do if the solutions above do not solve the problem. 

Best Regards

Hani R.
Applications Engineer
National Instruments
0 Kudos
Message 8 of 8