09-03-2013 05:16 PM
I am using a cDAQ-9174 chassis with a 9474 and 9411 modules. I don't believe it matters but they are the old NI cRIO-XXXX modules that came with a test setup that was distributed to early adopters. I am using DAQmx tasks in an application (C libraries) to read a quadrature encoder (angular position) and to drive a motor directly with PWM current (pulse output). To satisfy various other requirements, my setup of the tasks is as follows:
[DAQmx] MajorVersion = 9
MinorVersion = 2
[DAQmxChannel GenV 9411 Rotary Encoder Input/AngularPosition]
CI.AngEncoder.PulsesPerRev = 500
CI.AngEncoder.InitialAngle = 0
CI.Encoder.ZIndexVal = 0
CI.Encoder.ZIndexPhase = A High B Low
CI.Encoder.ZIndexEnable = 0
ChanType = Counter Input
CI.MeasType = Position:Angular Encoder
CI.AngEncoder.Units = Ticks
PhysicalChanName = cDAQ1Mod2/ctr2
CI.Encoder.DecodingType = X4
[DAQmxChannel GenV 9474 PWM Output/PulseOutput]
CO.Pulse.IdleState = Low
ChanType = Counter Output
CO.OutputType = Pulse:Time
CO.Pulse.HighTime = 5.0000000000000004E-006
CO.Pulse.LowTime = 5.0000000000000002E-005
CO.Pulse.Time.InitialDelay = 0
CO.Pulse.Time.Units = Seconds
PhysicalChanName = cDAQ1Mod1/ctr3
[DAQmxTask GenV 9411 Rotary Encoder Input]
Channels = GenV 9411 Rotary Encoder Input/AngularPosition
SampQuant.SampMode = Continuous Samples
SampClk.ActiveEdge = Rising
SampQuant.SampPerChan = 100000
SampClk.Rate = 100000
SampTimingType = Sample Clock
SampClk.src=/cDAQ1/100kHzTimebase
[DAQmxTask GenV 9474 PWM Output]
Channels = GenV 9474 PWM Output/PulseOutput
SampQuant.SampMode = Continuous Samples
SampQuant.SampPerChan = 100000
SampTimingType = Implicit
RegenMode = Allow Regeneration
[DAQmxCDAQChassis cDAQ1
] ProductType = cDAQ-9174
DevSerialNum = 0x18B3EC0
[DAQmxCDAQModule cDAQ1Mod1]
ProductType = NI 9474
DevSerialNum = 0xDEDF40
CompactDAQ.ChassisDevName = cDAQ1
CompactDAQ.SlotNum = 1
[DAQmxCDAQModule cDAQ1Mod2]
ProductType = NI 9411
DevSerialNum = 0xDEDB24
CompactDAQ.ChassisDevName = cDAQ1
CompactDAQ.SlotNum = 2
Each task works fine on its own (i.e., without the other). The problem is that if I start the encoder task first and then the PWM task, the latter causes an error:
Error -89137 occurred at DAQ Assistant
Possible Reason(s):
Specified route cannot be satisfied, because it requires resources that are currently in use by another route.
Source Device: cDAQ1
Source Terminal:
80MHzTimebase
Destination Device: cDAQ1
Destination
Terminal: Ctr3Source
Required Resources in Use bySource
Device: cDAQ1
Source Terminal: 100kHzTimebase
Destination
Device: cDAQ1
Destination Terminal: Ctr2SampleClock
Task Name: _unnamedTask<61>
I don't know why that is but if I start the PWM task first and then the encoder task, it also works. I should also mention that initally I was using counter 0 for the encoder, which caused a redirection of 100kHzTimebase to Ctr0SampleClock, which, according to the 9411 device routes, is not supported; yet it worked (by itself). I wonder if what is actually happening under the hood is not quite what is being shown.
What exactly is the conflict and what can I do to avoid it? The reasons for needing to use the specific modes and parameters chosen (e.g., the "continuous samples" with the 100kHzTimebase clock) are rooted in various performance and optimization requirements that have been established in a previous version of our software, so I'd prefer not taking a completely different route, if some small changes would lead us to correcting the problem.
I appreciate your assistance.
Kamen
Solved! Go to Solution.
09-03-2013 07:16 PM
I should also mention that there are no other tasks running on the chassis, so there is nothing else that might be interfering.
Why isn't there a way to edit a message? it is very inconvenient to have to post replies when you simply want to add or correct one simple thing.
Kamen
09-03-2013 08:08 PM
I guess, I was desperate and tried a few things, like moving the tasks to different counters. One source who was getting the same error, suggested they could only get it to work if both tasks used the same counter. That did not work (I got another error, suggesting the resource was taken). I tried a few other combinations until I finally moved the PWM output to counter 0, while the encoder input stayed on counter 2 and... lo and behold, it worked! And now it doesn't matter in what order the tasks are started, it works both ways, so this seems to be a solution.
I have no idea what the problem was before and why it is working now, so if someone provides a plausible explanation, I'd be happy to award them the solution. But I am satisfied with the way it is working, so - no worries.
Kamen
09-04-2013 09:43 AM
Hi Kamen,
The 100 kHz timebase doesn't have a direct route to the counter sample clocks, the device actually uses one of the other counters to complete the route (the routing table is a little misleading here as it shows Counter 2 as the one always making the route--in reality it will be any available counter):
So in your case, when you start the encoder task, it uses one of the other available counters to complete the configured route (100 kHz timebase to ctr2 sample clock). Evidently it chose counter 3.
Some possible workarounds (it sounds like you've already found one yourself):
1. Start the PWM task before the encoder task--if the PWM task starts first the counter is already reserved and the encoder task would choose another available counter to complete its route.
2. Explicitly reserve the PWM task before starting the encoder task (if you do need to start the encoder task first).
3. Use cDAQ1/_freqout to generate the 100 kHz sample clock signal and use this instead of routing the 100 kHz timebase to the counter sample clock.
Changing the counters around should also work, but I'm not 100% sure how the device picks which counter to use for the routing (I wouldn't expect it to change in the future, but if it's not explicitly spec'ed somewhere then I wouldn't take my chances)--if it were me I would pick one of the other above options.
Best Regards,
09-04-2013 10:20 AM
Thank you, John, your explanation makes sense. At this time, the setup I'm putting together is for testing, so I'm not worried about future-proofing it (e.g., upgrades to DAQmx drivers). But for production purposes, it would require some deeper digging. As far as the workarounds you suggested, controlling the order of task execution or reserving tasks (not sure if I'm understanding this one correctly) are not very appealing. The software that uses those tasks is a product that should be agnostic of the idiosyncrasies of the hardware and, thus, would have no way of knowing which task to start first or to reserve. Your third workaround seems very promising, though, since it would be implemented at the configuration stage, which can take into account any special requirements imposed by the hardware. I'm just not sure how to do that - I tried all combinations in MAX and was getting errors, timing out or not working. I'd hate to take much more of your time but if you could explain how I could use the FrequencyOutput to generate the sample clock, I'd appreciate that.
Kamen
09-04-2013 11:07 AM - edited 09-04-2013 11:15 AM
You can program the Frequency Output similar to a counter output task (Continuous Implicit Timing is the default/only timing option though). For example:
DAQmxCreateTask("",&taskHandle); DAQmxCreateCOPulseChanFreq(taskHandle,"cDAQ1/_freqout","",DAQmx_Val_Hz,DAQmx_Val_Low,0.0,100000.0,0.50); DAQmxStartTask(taskHandle);
The sample clock source for the encoder task would become "/cDAQ1/FrequencyOutput" (instead of "/cDAQ1/100kHzTimebase").
I like this solution since you're freeing up an extra full-featured counter at the expense of the frequency generator (which is essentially a limited counter output that most people don't use or even know about).
Best Regards,
09-04-2013 11:20 AM
Thanks, but what I meant was how to setup the frequency output so that it can be used in the encoder and PWM tasks. I can't have this setup in the C code because those are only device interfaces, each knows nothing about the others. What I was imagining was all of this setup in MAX, with both tasks (encoder and PWM) setup and running so that the software can pick up its encoder input and PWM output, respectively (it is plug-and-play).
Kamen
09-04-2013 11:59 AM
The frequency output is only going to be used by the encoder task, why not put the code for it contained within the encoder task implementation?
The way you have it currently, the encoder implementation uses 2 counters (the one you specify and then some other one to facilitate the routing of the 100 kHz timebase). This is undesirable because the second counter is picked by the driver and might correspond to one that you need for some other purpose later on. You could in effect choose the 2nd counter by explicitly configuring it as a counter output at 100 kHz, but why not just use the Freq Out instead which is capable of what you need and can't be used for as many other useful purposes.
Best Regards,
09-04-2013 12:02 PM
I think I understand. I'll have to talk to my coworker, who had implemented the encoder instrument driver for our previous software; he had some considerations for using the 100kHz timebase. I'll ask him whether we can do what you suggest, instead.
Thanks for everything!
Kamen
07-30-2015 10:35 PM
I'm sorry to resurrect this old thread, but I recently upgraded to DAQmx 14.5.1 and my old kludgy arrangement is not working anymore. I still don't fully understand what I'm doing but I tried to follow John's suggestion (with the help of my coworker) - in my code I add the creation of an addtional task (whenever the measurement is of the encoder type and the supporting hardware - a cDAQ chassis), which task starts a CO pulse task with a channel for generating the 100kHz time-base I need, and in MAX I configure my encoder task to use the FrequencyOutput clock source. Again, I don't fully understand how this all works, but it does work with the exact setup that I have. I also read the actual chassis device name, so at least the code is not vulnerable to someone renaming the device. Here's the code:
if(pTasks[TskIdx].Devices[0].pcDAQmxChassisName != NULL) // If this counter is supported by a cDAQ chassis (counters are on the chassis, not on the modules); <* this assumes a single hardware device, [0] *> { char* pcCounterName; StrSize = strnlen(pTasks[TskIdx].Devices[0].pcDAQmxChassisName, 512) + strnlen("/_freqout", 512) + 1; // Enough for the name and the terminating null character but no more than 1K characters pcCounterName = new char[StrSize]; // Allocate enough space strcpy_s(pcCounterName, StrSize, pTasks[TskIdx].Devices[0].pcDAQmxChassisName); // Start with the device name (the chassis) strcat_s(pcCounterName, StrSize, "/"); // Add the separator strcat_s(pcCounterName, StrSize, "_freqout"); // Add the special channel physical name (<-* should be determined programmatically *->) pTasks[TskIdx].AuxCurrError = DAQmxCreateTask("Auxiliary Encoder clock-source routing Task", &pTasks[TskIdx].hAuxTaskHandle); // Create the auxiliary task for signal routing (connecting the sample clock source of the encoder task to the freqeuncy generator of the device) pTasks[TskIdx].AuxCurrError = DAQmxCreateCOPulseChanFreq(pTasks[TskIdx].hAuxTaskHandle, pcCounterName, "", DAQmx_Val_Hz, DAQmx_Val_Low, 0.0, pTasks[TskIdx].SampleClockRate, 0.5); pTasks[TskIdx].AuxCurrError = DAQmxStartTask(pTasks[TskIdx].hAuxTaskHandle); delete[] pcCounterName; }
Considering that our software is a product and we want it to be as robust as possible, there are many probelms with this approach. One of them is the inability to obtain the exact name of that channel, or whatever "cDAQ1/_freqout" happens to be ("a circuit within the device", says NI). For now, I'd be very happy to get started with the answer to that question: how to programmatically obtain the specific name of that "thing"? Or is each DAQmx device that supports encoders so quirky that it'd be a lost cause trying to automate this, anyway?
Kamen