From Friday, April 19th (11:00 PM CDT) through Saturday, April 20th (2:00 PM CDT), 2024, ni.com will undergo system upgrades that may result in temporary service interruption.

We appreciate your patience as we improve our online experience.

Counter/Timer

cancel
Showing results for 
Search instead for 
Did you mean: 

Can't read data for the same lap from two counters

Solved!
Go to solution

Hi

I am using NI 6624 counter board and building an app in C#.

 

I have one counter (0) which counts edges of Index pulse (Z) from a Angle encoder and triggering on PFI4 on rising edge of Z pulse. Where I set a new lap while motor is spinning. Between each Z pulse there is 360 Degrees (time).

 

Another counter (3) is triggering on PFI0. Around 15 degrees after Z pulse.

 

The problem is that before next Z pulse the counter 3 is not reading its value on time which means it missed the lap. 

 

Can someone please suggest how I can make the two counters synchronize?  

 

I want counter 3 to always read before the next trigger of Counter 0.  

 

Below are my counter 0 & 3 code in C#

 

_myTask = new Task();
// Counter 0 use the Z pulse to count laps
_myTask.CIChannels.CreateCountEdgesChannel("CNT/ctr0", "LapCnt", CICountEdgesActiveEdge.Rising,
(long)0, CICountEdgesCountDirection.Up);
_myTask.Stream.ReadWaitMode = ReadWaitMode.Yield;

_myTask.Timing.ConfigureSampleClock("/CNT/PFI4", 350.0, SampleClockActiveEdge.Rising, SampleQuantityMode.ContinuousSamples, 5);
_myCounterReader = new CounterReader(_myTask.Stream) { SynchronizeCallbacks = true };
_myTask.Start();

_myCounterReader.BeginReadSingleSampleInt32(_asyncDaqmCb, _myTask);

 

Counter 3:

_myTaskBeta = new Task();

_myTaskBeta.CIChannels.CreateAngularEncoderChannel("CNT/ctr3", "AngleCnt3", CIEncoderDecodingType.X4, true, 0.0, CIEncoderZIndexPhase.AHighBHigh, 600, 0.0, CIAngularEncoderUnits.Degrees);
_myTaskBeta.Stream.ReadWaitMode = ReadWaitMode.Poll;

_myTaskBeta.Timing.ConfigureSampleClock("/CNT/PFI0", 350.0, SampleClockActiveEdge.Falling, SampleQuantityMode.ContinuousSamples, 5);
_counterInReaderBeta = new CounterReader(_myTaskBeta.Stream) { SynchronizeCallbacks = true };
_myTaskBeta.Start();
_counterInReaderBeta.BeginReadSingleSampleDouble(_asyncDaqmCbBeta, _myTaskBeta);

 

 

0 Kudos
Message 1 of 12
(4,315 Views)

I know counters, but I'm a LabVIEW guy who's no help on the needed C# syntax.   Let me see what I can decipher though.

 

1. Terminology.  Your description refers to "triggering" but nothing in your config actual configures any kind of trigger.   I've found that sometimes on these forums people use the term "trigger" to refer to what DAQmx considers a "sample clock."   In DAQmx, these are *very* distinct ideas, definitely not synonyms.

   (Further, there's a unique thing about counter input tasks that requires you to configure a special "Arm Start" trigger rather than the normal kind used in other tasks.)

 

2. You say ctr0 counts Z index pulses and also samples the count on each Z index pulse.  This seems a little odd but if you're gonna do it, at least make sure the counting happens on the leading edge and the sampling happens on the trailing edge.   (So you're always sure to sample after the count increments.  When both have the same polarity, you might get unpredictable behavior as to whether counting or sampling happen first).

 

3. ctr3 is using an undefined sample clock from PFI4.  You haven't told us anything about it.  I only kinda sorta get the idea that maybe it should pulse once per rev, roughly 15 degrees after the Z pulse.  So maybe you're trying to measure the *actual* offset angle?

 

4. You start up your tasks and seem to launch callback functions to read the data but you haven't posted those callbacks.

 

5. It appears you want ctr3 to reset to 0 on each Z index pulse.  That makes sense to me.  But if the task happens to start when the encoder is between 1 and 14 degrees, the first measurement will be misleading.

 

What I'd probably do:

 

A. Configure ctr3 to use an "Arm Start" trigger and use the *trailing* edge of PFI4.

B. Also configure ctr3 to use the *leading* edge of PFI4 as its sample clock (as you already do).

C. Also configure ctr3 to reset its count on the Z index pulse (as you already seem to do)

D. Plan to do multi-sample buffered reads instead of single point reads.

E. Maybe there's no need for ctr0 to be involved at all.  The *sample #* from the ctr3 data might be sufficient.

 

Why it helps:

- no matter where the encoder is when ctr3 starts, ctr3 won't start tracking position until the Arm Start trigger asserts on the *trailing* edge of PFI4.

- no samples will be taken until the *next* rotation, on the *leading* edge of PFI4

- between these two things, the encoder will pass through the Z-index and properly reset ctr3 to 0.  The "first measurement" issue I mentioned above will be prevented.

 

 

-Kevin P

CAUTION! New LabVIEW adopters -- it's too late for me, but you *can* save yourself. The new subscription policy for LabVIEW puts NI's hand in your wallet for the rest of your working life. Are you sure you're *that* dedicated to LabVIEW? (Summary of my reasons in this post, part of a voluminous thread of mostly complaints starting here).
0 Kudos
Message 2 of 12
(4,292 Views)

Hi Kevin,

Thanks a lot for your reply.

Sorry for missing information.

I will follow you instructions meanwhile I have attached an image of signals which I want to capture. There is a Z pulse and between each Z pulse there is a digital signal. I want to capture timings of digital signal from Z index and for each Z index. I have set three counters. One have sample clock on Z. And two other counters have PFI 4 as an external sample clock which is digital signal in the attached image. 

Can you please guide me what is best way to achieve it?  I have NI DAQ 6624 and PXI 5105 cards. 

 

And Below are my callback functions.

 

//Lap Callback

private void CounterInCallback(IAsyncResult ar)
{
lock (AccessProtection)
{
double tempSpeed = 0.0;

SpeedMeas.Instance.GetCurrentSpeedOnly(out tempSpeed);

// Remove try/catch?
try
{
if ((null != _myTask) && (_myTask == ar.AsyncState) && (null != _myCounterReader))
{
_currentLap = (uint)_myCounterReader.EndReadSingleSampleInt32(ar) - _currentOffset; // Z pulse

if (_currentLap > 10000)
{
_currentLap = 0; //Starting edge
}

if (_currentLap > _localLapNumberCounter)
{
_localLapNumberCounter = _currentLap;

if (null != _storeMeas)
{
_storeMeas.LocalStoreMissedSpark(_configNumber, (int)_localLapNumberCounter, (int) _lapNumber, tempSpeed, (double)_storeMeas.LastSpeed());

_lapNumber++;
}

if (null != _changeSpeedNow)
{
_changeSpeedNow.Set();
}
}

// Retrigger measurement
_myCounterReader.BeginReadSingleSampleInt32(_asyncDaqmCb, _myTask);
}
}
catch (DaqException exception)
{
if (-200284 == exception.Error)
{ // No trigger from counter board, timeout, retrigger measurement
_myCounterReader.BeginReadSingleSampleInt32(_asyncDaqmCb, _myTask);
return;
}
MessageBox.Show(String.Format("Exception message: {0}\nError code: {1}", exception.Message, exception.Error), "LapCounter:CounterInCallback:DAQ exception caught.");
}
catch (Exception exception)
{
MessageBox.Show("LapCounter:CounterInCallback:General exception caught.", exception.Message);
}
}
}

 

// Angle callback

private void CounterInCallback(IAsyncResult ar)
{

lock (AngelAccessProtectionBeta)
{
_localLapNumberBeta = LapCounter.Instance.GetCurrentLap();

try
{
if ((null != _myTaskBeta) && (_myTaskBeta == ar.AsyncState) && (null != _counterInReaderBeta))
{
_localAngleBeta = _counterInReaderBeta.EndReadSingleSampleDouble(ar) - _angleZeroOffsetBeta;

if (null != _storeMeasBeta)
{
double angleAlpha = 0.0;
uint lapAlpha = 0;
AngleMeasIndexPulsePositiveSlopePositionAlpha.Instance.GetLastAngle(out angleAlpha, out lapAlpha);
_storeMeasBeta.LocalStoreAngleAlphaBeta(_configNumberBeta, (int)lapAlpha, angleAlpha, (int)_localLapNumberBeta, _localAngleBeta, _normalRotationDirectionBeta);

}
else
{
if (IndexPulseBetaAngleEvent != null)
{
_localAngleBeta %= 360.0;
_localAngleBeta += (-180.0 > _localAngleBeta) ? 360.0 : 0.0;
_localAngleBeta -= (180.0 < _localAngleBeta) ? 360.0 : 0.0;
_localAngleBeta *= _normalRotationDirectionBeta;

IndexPulseBetaAngleEvent(this, new IndexPulseAnglesEventArgs(_localAngleBeta));
}
}

// Retrigger measurement
_counterInReaderBeta.BeginReadSingleSampleDouble(_asyncDaqmCbBeta, _myTaskBeta);
}
}
catch (DaqException exception)
{
if (-200284 == exception.Error)
{
_localAngleBeta = 0.0;
// No trigger from counter board, timeout, retrigger measurement
_counterInReaderBeta.BeginReadSingleSampleDouble(_asyncDaqmCbBeta, _myTaskBeta);
return;
}
MessageBox.Show(String.Format("Exception message: {0}\nError code: {1}", exception.Message, exception.Error), "AngleMeasIndexPulseNegativeSlopePositionBeta:CounterInCallback:DAQ exception caught.");
}
catch (Exception exception)
{
MessageBox.Show("AngleMeasIndexPulseNegativeSlopePositionBeta:CounterInCallback:General exception caught.", exception.Message);
}
}
}

 

 

0 Kudos
Message 3 of 12
(4,264 Views)

I really don't understand what you're trying to do here.  If you need delta times between each Z pulse and each "digital signal", you could get that with a single counter task configured for two-edge separation measurement.  If you need the encoder position value sampled at each "digital signal" while also resetting to 0 at each Z pulse, then the stuff I described previously would be the better approach.  Both approaches only need 1 counter rather than 2 or 3.

 

Without referring to specific counters and PFI pins that you've been using so far, please describe the meaning of this "digital signal" and exactly what characteristic(s) of your system you want to measure.

 

 

-Kevin P

CAUTION! New LabVIEW adopters -- it's too late for me, but you *can* save yourself. The new subscription policy for LabVIEW puts NI's hand in your wallet for the rest of your working life. Are you sure you're *that* dedicated to LabVIEW? (Summary of my reasons in this post, part of a voluminous thread of mostly complaints starting here).
0 Kudos
Message 4 of 12
(4,257 Views)

Hi,

Let me explain my problem.

 

I want to measures rising (lets say Alpha Angle) and falling (Beta) angles of my digital signal for each Z index (one revolution).  Because we are not sure that digital signal will always appear so we want to set separate counters to save the Alpha and Beta. Then at the end we compare if we really don't get signal for a specific revolution. 

 

After your suggestion I had started Arm Trigger. It does correct revolutions now on Z index.

 

The problem is that when Z index pulse is saving its value, then sometime, other counters do not get enough time to save their values and Z goes to next revolution. Any suggestion to solve this?

 

There are three counters

 

Counter 0 looking Z index and increasing revolutions. (Yeild)

Counter 2 is looking for Alpha angle on digital signal's rising edge  

Counter 3 is looking for Beta angle on digital signal's falling edge (Polling)

All three counters should save their values for each revolution. 

 

Problem is that the Counter 2  & 3 does not get enough time to save their values for each Z index revolution when motor is spinning at high speed  . Please see the attached image for Z index and digital signal. 

 

0 Kudos
Message 5 of 12
(4,253 Views)
Solution
Accepted by topic author ArshadAwan

Thanks for the description, I think I can give more definitive advice now.  I'm still no particular help for the C# syntax, but I can at least describe the concepts.

 

Because the "digital signal" isn't reliable, I now understand why you want to count Z index pulses as your master count of rotations (what you've called "laps").  A new problem now arises.  If your encoder tasks use the Z index pulse to reset to 0 each rotation, you won't be sure how to correlate the position readings to the rotation #.  For example, suppose over the 1st 10 rotations, you get 8 digital signal pulses.  Which 8 did you get?  There's no way to know...

 

So, now it'll be important *NOT* to reset the encoders on the z index pulse, but to still make sure both reference the z index pulse position as 0 degrees.   You'll then measure a continuously increasing angle.  Dividing this angle by 360 with the "Quotient & Remainder" function will yield both the rotation # and the relative angle within that rotation.  When a digital signal doesn't happen, you'll be able to know which rotation had the miss because the rotation # will jump by 2 instead of 1.

 

Take a deep breath, we're getting there.  Here are things I'd do:

 

1. All 3 counter tasks would be configured to use an "Arm Start" digital edge trigger.  This is distinct from the regular kind of start trigger available for all kinds of DAQmx tasks.   Only counters support the Arm Start trigger, and only an Arm Start trigger is supported for counter measurement tasks.

   I would have all 3 tasks be arm start triggered off the trailing edge of the Z index pulse, likely a falling edge.

 

2. All tasks would continue to use hardware timing and buffering, but I would read multiple samples at a time from the buffer.  You seem to be setup to read 1 sample at a time and your software isn't keeping up with your rotation rates. 

   Reading N samples at a time instead of just 1 is pretty trivial in the LabVIEW API.  However, I don't know the syntax needed to accomplish the same thing via callback functions.

 

3. ctr0 would sample on the leading edge (likely rising) of the Z index pulse.  I would have it count the trailing edges of the Z index pulse.

   This gives you a slight signal-level race condition between triggering and counting on the same edge of the same signal.  The first value sampled will probably usually be 0, but it's conceivable that it could be a 1 (if the same edge that arm-start-triggers the task is also seen by the count-increment circuitry after that circuitry is triggered/enabled).  This is something you'll need to explore.  There's a workaround possible with a 4th counter, but let's not complicate things yet if not needed.

 

4. ctr2 and ctr3 will perform position measurement with Z-index reset disabled.  They will use opposite edges of your digital signal on PFI4 as their respective sample clocks.

   Crucial question: do ctr2 and ctr3 capture angular position of the same encoder at the two edges of PFI4?  Or do they capture angular positions from different encoders?   If same, then my advice is about done.  If different, well, we've got a new problem.

 

5. The problem is that position will start at 0 and begin to increment at the moment the arm start trigger is asserted.  The first encoder is at the z-index pulse position at that moment, which is exactly what you *want* to call 0 position.  A second encoder could be anywhere relative to its own z-index reference position.  Wherever it happens to be will be identified as 0 for that test run, but the data won't be able to reference that point to something physically unambiguous (like a z-index pulse).

   A workaround I can think of is to have the 2nd encoder task ENABLE z-index reset and plan to do some post-processing.   In this approach, the 1st encoder data tells you which rotation #'s skipped a digital signal pulse on PFI4.  The 2nd encoder data always falls between 0 and 360 relative to its *own* z-index pulse, and knowledge of which rotation #'s were missed must be borrowed from analysis of the 1st encoder data.

 

 

-Kevin P

CAUTION! New LabVIEW adopters -- it's too late for me, but you *can* save yourself. The new subscription policy for LabVIEW puts NI's hand in your wallet for the rest of your working life. Are you sure you're *that* dedicated to LabVIEW? (Summary of my reasons in this post, part of a voluminous thread of mostly complaints starting here).
Message 6 of 12
(4,244 Views)

Hi Thanks a lot for your help and reply.

Many things have been cleared to me as I am very new to NI.

Just little corrections below.

I am following your step 1 to 3.

I have connected Z signal to PFI4 which is external sample clock for Ctr0. I thought I have to set external sample clock to trigger/ read Z position on rising/falling edge? How I can set the triggering (reading encoder position) on rising & falling edges of a digital signal without external clock?

 

Ctr2 & Ctr3 have PFI0 as their external sample clock. I assume external sample clock also set the triggers for example on rising or falling edge?

Ctr2 & Ctr3 reading the same encoder position but for rising and falling edges respectively. My digital signal comes after few revolutions of Z index also. As digital signal does not pop up until we reach at a specific speed of motor.

 

After you suggestions, I think I am very near to solution because I saw the problem is as below which you had figured out. But I am still investigating today and would update you. As I think C# is too slow to re-trigger the call back function. Hardware counters have correct values but I could not read them for the same lap as the ctr2 & 3 callbacks too slow to re-trigger to read next sample.  

 

"

2. All tasks would continue to use hardware timing and buffering, but I would read multiple samples at a time from the buffer.  You seem to be setup to read 1 sample at a time and your software isn't keeping up with your rotation rates. 

   Reading N samples at a time instead of just 1 is pretty trivial in the LabVIEW API.  However, I don't know the syntax needed to accomplish the same thing via callback functions."

"

0 Kudos
Message 7 of 12
(4,235 Views)

I think we have a bit of a terminology problem here.  This comes up quite a bit on the forum, nothing new.  DAQmx uses pretty clear, distinct, and consistent terminology when it comes to clocks and triggers.  Other kinds of equipment (that many posters have familiarity with) may use the same terminology but with a different meaning.

 

All my talk is based on DAQmx terminology.

 

sample clock is a completely different thing functionally than a trigger.  A given task can use either one, both, or neither.  A sample clock is the thing that causes signals to be physically sampled and transferred to a buffer.  Tasks will generally react to many sample clock edges and buffer many samples as they run.  A trigger (especially the most common type, the digital edge trigger) marks a single specific instant in the life of the task.  It's often the start.  It can be some other reference point.  Tasks will generally mark or react to exactly 1 digital edge trigger as it runs.

 

I don't think I have a full understanding of the physical layout of your wiring in order to comment accurately about specific PFI pins.  I'll just have to refer to the functional *meaning* of wires and let you work out the physical wiring implications.  I'll address your further questions inline with my responses in red.

 

I have connected Z signal to PFI4 which is external sample clock for Ctr0. I thought I have to set external sample clock to trigger/ read Z position on rising/falling edge? How I can set the triggering (reading encoder position) on rising & falling edges of a digital signal without external clock?

    This phrasing is confusing to me.  I thought Ctr0 was your lap counter but you refer to "reading encoder position". I'll try to answer clearly according to standard DAQmx terminology because I'm not sure I can accurately interpret your meaning.

   I've recommended that you use the same Z index signal for many distinct purposes.

  1. The trailing edge (probably falling) should be an "Arm Start" digital edge trigger for all 3 tasks.
  2. The trailing edge should *also* be the edge count terminal for the Ctr0 task, your lap counter.
  3. The leading edge (probably rising) of the same signal should be the external sample clock for the Ctr0 task.

Ctr2 & Ctr3 have PFI0 as their external sample clock. I assume external sample clock also set the triggers for example on rising or falling edge?

   Ctr2 & Ctr 3 should use the "digital signal" as their external sample clock.  They will use opposite edges of it though.  Your "trigger" terminology probably doesn't belong here.

 

Ctr2 & Ctr3 reading the same encoder position but for rising and falling edges respectively. My digital signal comes after few revolutions of Z index also. As digital signal does not pop up until we reach at a specific speed of motor.

   So Ctr2 & Ctr3 should be configured *NOT* to use z-index reset on their positions so that you'll be able to correlate their measurements to the correct "lap #". 

 

One final caveat:  internally, the counters increment a 32-bit integer count register.  During very long test runs, it's possible this value will roll over back to 0.  I typically read this 32-bit value from the counter directly and have to be aware of this effect.  You're reading the scaled angular position value in floating point and I just don't happen to know whether DAQmx handles this issue internally for you.  I'd predict it probably does, but in case you do long test runs and get a weird anomaly at some point far into the data, I wanted to give you some forewarning.  

   From your code, I'm guessing the 600 value is the # quadrature cycles per rev and your X4 decoding means you get 2400 counts per rev.  You'll need almost 1.8 million revs to roll the count value over.  Even at a high speed like 10000 RPM, that would take almost 3 hours.  Anything short of that and you won't run into it.

 

 

-Kevin P

 

CAUTION! New LabVIEW adopters -- it's too late for me, but you *can* save yourself. The new subscription policy for LabVIEW puts NI's hand in your wallet for the rest of your working life. Are you sure you're *that* dedicated to LabVIEW? (Summary of my reasons in this post, part of a voluminous thread of mostly complaints starting here).
Message 8 of 12
(4,227 Views)

Hi Kevin,

Thanks a lot for the great help. I have learned & corrected many things from your answer but the major problem was as in red text below and solution in green.

 

2. All tasks would continue to use hardware timing and buffering, but I would read multiple samples at a time from the buffer.  You seem to be setup to read 1 sample at a time and your software isn't keeping up with your rotation rates

   Reading N samples at a time instead of just 1 is pretty trivial in the LabVIEW API.  However, I don't know the syntax needed to accomplish the same thing via callback functions.

 

And now I am synchronizing laps by not resetting Z index.  

 

Ctr2 & Ctr3 should be configured *NOT* to use z-index reset on their positions so that you'll be able to correlate their measurements to the correct "lap #".

Message 9 of 12
(4,190 Views)

Hi Kevin,

Thanks a lot for your help. I have corrected the laps synchronizing by avoiding not resetting Z index.

But now I have another problem. I have NI PXI 5105 which does not trigger on few laps. Can you please help me in this regard? 

 

Analog signal is connected with Channel 0.

Sample Rate="10000000"

Sample Length="3000"

Trigger Level="-0.545"

Trigger Slope= "Negative edge"

 

Vertical range 20 V

0 Kudos
Message 10 of 12
(4,173 Views)