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: 

What is the correct way to measure RPM using Visual Basic?

I have something that works now, but I want to know if there is a better way. I am using a 6023E, I have a Hall Effect switch that goes low then high each rev when a magnet goes by. I have the signal connected to the GATE pin on counter0. I poll the card for the RPM over and over using code based closely on the example STCsinglePeriodMeasure. While this works, I'm not sure exactly what this code is doing. Do I need to call the entire thing every time? The problem is I need to reset and arm the counter each time before starting the counter, and calling everything was the only way I could get it to work. I'm looking for a way to either trim this down, or maybe there is another way altogether that is more apporpriate. Here's the code:

iDevice% = 1
ulGpctrNum& = ND_COUNTER_0
ulArmed& = ND_YES
ulTCReached& = ND_NO
iYieldON% = 1

iStatus% = GPCTR_Control(iDevice%, ulGpctrNum&, ND_RESET)
iRetVal% = NIDAQErrorHandler(iStatus%, "GPCTR_Control/RESET", iIgnoreWarning%)
iStatus% = GPCTR_Set_Application(iDevice%, ulGpctrNum&, ND_SINGLE_PERIOD_MSR)
iRetVal% = NIDAQErrorHandler(iStatus%, "GPCTR_Set_Application", iIgnoreWarning%)
iStatus% = GPCTR_Change_Parameter(iDevice%, ulGpctrNum&, ND_SOURCE, ND_INTERNAL_100_KHZ)
iRetVal% = NIDAQErrorHandler(iStatus%, "GPCTR_Change_Parameter/SOURCE", iIgnoreWarning%)
iStatus% = GPCTR_Change_Parameter(iDevice%, ulGpctrNum&, ND_GATE, ND_DEFAULT_PFI_LINE)
iRetVal% = NIDAQErrorHandler(iStatus%, "GPCTR_Change_Parameter/GATE", iIgnoreWarning%)
iStatus% = GPCTR_Change_Parameter(iDevice%, ulGpctrNum&, ND_INITIAL_COUNT, ulCount&)
iRetVal% = NIDAQErrorHandler(iStatus%, "GPCTR_Change_Parameter/INITCOUNT", iIgnoreWarning%)
iStatus% = GPCTR_Control(iDevice%, ulGpctrNum&, ND_PROGRAM)
iRetVal% = NIDAQErrorHandler(iStatus%, "GPCTR_Control/PROGRAM", iIgnoreWarning%)
' Loop until 'ulGpctrNum' is no longer armed, or run window is closed.
Do
iStatus% = GPCTR_Watch(iDevice%, ulGpctrNum&, ND_ARMED, ulArmed&)
DoEvents
Loop While ((ulArmed& = ND_YES) And (iStatus% = 0)) And RunWindow.ExitStatus = "Running"
If RunWindow.ExitStatus = "Running" Then
'Run window wasn't closed, so read data.
iRetVal% = NIDAQErrorHandler(iStatus%, "GPCTR_Watch/ARMED", iIgnoreWarning%)
iStatus% = GPCTR_Watch(iDevice%, ulGpctrNum&, ND_COUNT, ulCount&)
iRetVal% = NIDAQErrorHandler(iStatus%, "GPCTR_Watch/COUNT", iIgnoreWarning%)
iStatus% = GPCTR_Watch(iDevice%, ulGpctrNum&, ND_TC_REACHED, ulTCReached&)
If (ulTCReached& = ND_YES) Then
MsgBox "Counter reached terminal count! RPM value may be incorrect"
Else
'ulCount is the period in microseconds, RPM = 1/val*(100000ms/s)*(60s/min)

RPM = 6000000 / ulCount&
' Debug.Print RPM
RunWindow.RPM.Text = Str(RPM)
End If
0 Kudos
Message 1 of 5
(3,934 Views)
Try looking at the example: "STCsingleBufPeriodMeasure" instead. This example counts the pulses of the internal timer of the DAQ board at the source, and each time a pulse is sensed at the gate (your signal) it latches the current count into a buffer and then resets the counter to 0. This way, since you know the frequency of the internal timer, the period is determined by viewing the number of pulses at the source which happen in between each gate pulse.

The purpose of the buffered period measurement is so that your program can be continuous. Using a buffered measurement would be a much better way to create your program.
0 Kudos
Message 2 of 5
(3,934 Views)
I'm sorry, this code IS based on STCsingleBufPeriodMeasure, not STCsinglePeriodMeasure like I said above. My mistake.

Doug
0 Kudos
Message 3 of 5
(3,934 Views)
Finally getting around to this again. My previous post is incorrect, as the code I am using is based on the single period measure (it's right there in the code). I am having a problem with the values returned with this method. Occasionally, the card will return a lower than expected count, resulting in a very large (and incorrect) RPM value. I'm thinking this must have something to do with arming the counter at some unfortunate and coincidental point on the gate signal, resulting in a short count. It must be an unlikely event, because I get thousands of good, consistent values for every one bad one.

To resolve this, I am thinking of going to the single buffered period measurement as suggested above. My idea is to set up a buffer with two values,
and always take the second one (to avoid coincidental arming and short counts). I could also set it up to return more values, and the application could choose the most likely one (ie return three values, throw out the largest and smallest, etc.)

I would rather just use the buffer in continuous mode, since the RPM varies much more slowly than the counter counts, and any snapshot of the buffer at any time would be sufficient, even in the presence of overwriting, etc. This would allow me to arm the counter once and poll it on the fly.

My concern is that in continuous mode, the counter will be throwing a lot of errors which will interefere with the program flow. Can I just set the counter to suppress all errors when I do a buffered read? Worst case, I get a value as it is being written to by the counter, which will have some garbage value (hopefully a recognizable signature) which I can throw out in favor of the other values.

Thanks,
Doug
0 Kudos
Message 4 of 5
(3,934 Views)

Update. In the end I was never able to get RPM read reliably with the above method. I purchased an industrial frequency to voltage box instead and sent the slowly varying voltage output to the 6023E. Works great.

 

Bear in mind that the problem I was having may have been due to noise or something on the line. I didn't have a portable o-scope at the time.

 

Doug

0 Kudos
Message 5 of 5
(2,978 Views)