Issue with micros() in an ISR

Status
Not open for further replies.

KrisKasprzak

Well-known member
All,

I have a data logger that is being used to measure wheel RPM (0 to about 500 RPM) in and electric go cart. I'm using an infrared sensor and for the most part it works very well. When the throttle "on/off button" is on, everything is fine--sensor works well and RPM is very accurate, upon release of the throttle, I sometimes get very wrong results (RPM is reported as say 100000). My calculations are every second (so the driver has a good view of current speed).

My approach is NOT to measure pulses per second and compute rpm based on that, but get The delta in micros() between first and last pulse (within the 1 second update time) the compute RPM based on number of pulses and elapsed micros() between the start and end pulse. I'm adopted this approach to give more accurate RPM's--especially at low speeds

Anything look error prone in my ISR? All variables are assigned as volatile? Any bad practices here?

My code is 2500 lines long but I can probably strip it down if that would help.

Code:
void inc_rpm() {

  PulseStartTime = micros();

  if ((PulseStartTime - PulseDebounce) > RPMDebounceTime) {
    PulseDelta =  PulseStartTime - PulseOldTime;
    PulseOldTime = PulseStartTime;
    PulseDebounce = PulseStartTime;
    wRPM = 60000000L / PulseDelta;
    PulseCount++;
    TotalRevolutions++;
    LapPulses++;
  }

}

Snippet of the errors

Point ,RPM ,Speed ,Total Rev.
4105 ,1042 ,11.3 ,20687
4106 ,990 ,10.74 ,20690
4107 ,933 ,10.12 ,20693
4108 ,896 ,9.72 ,20696
4109 ,834 ,9.04 ,20700
4110 ,782 ,8.48 ,20702
4111 ,724 ,7.86 ,20704
4112 ,14954 ,162.11 ,20708
4113 ,12566 ,136.22 ,20712
4114 ,9349 ,101.35 ,20714
4115 ,1100 ,11.93 ,20716
4116 ,260 ,2.83 ,20717
4117 ,969 ,10.51 ,20719
4118 ,0 ,0 ,20719
 
Did you solve your issue?

I'm slightly confused with your approach to this. You are using an interrupt to catch every pulse from the sensor and recomputing the time between pulses during the interrupt? What do you think will happen if the interrupts pile up (fire while doing the calculations)? Interrupts should be on the short nano-seconds of time. You're doing maths during your interrupt which will block the teensy for a relatively large amount of time for a simple interrupt. This is likely what is causing your issues, your interrupt should ideally be a short counter, not doing long handled maths.

Also how do you know that your "RPM" is accurate?

If i were tackling this I'd do the following;

Code:
**Note Untested**
void inc_rpm() { //Increment counters. You may need to look into a debounce scheme for pin change interrupts
	PulseCount++;
	TotalRevolutions++;
    LapPulses++;
  }
  
int rpm_Calc()
{
   static unsigned long RPM_1SecDelay = millis(); //Timer for 1 second delay
   static long wRPM = 0;
   
   if(millis() - RPM_1SecDelay > 1000UL) //1 second delay between updates
   {
	   unsigned long pulseIn = PulseCount; //grab current pulse count
	   PulseCount = 0; //Reset pulse count
	   wRPM = 60000000L / pulseIn; //calculate RPM (this may be wrong as I'm too lazy to do the math right now)
	   RPM_1SecDelay = millis(); //Reset 1 second timer   
   }
	return wRPM; //returns current RPM - only changes on 1 second update but can be called as often as you like
}

}
 
JWScotSat, thanks for chiming in.

Our v1.0 used your approach, the issue was that speed resolution based on our wheel diameter is 3.31 MPH (too low for our needs)--this is with a 1 second update time. We tried to increase the update time to 4 or 5 seconds, which increased resolution but the driver sees delayed speed updates. The issue there is if the race car is over 8.0 MPH in the pit we get penalized, hence accurate and quick updates are needed.

We then came up with the scheme where we measure time between pulses. Note max time between pulses is around 100 ms so we figured we could measure start and end time between each rise (or fall). Also we notice severe bounce issues due to the reflective tape to trigger the opto sensor. Note we tried magnets but they have to be around 1/8 from the sensor--very hard to set that distance.

The issue we see is not reliability of sensing at steady state, the car on a lift can run for a long time and the speed never errors. The issue is when the diver deactivates the motor (on / off switch), then the sensor can error--but not always. We think it's some EMF issue or voltage / current spike--but we have no idea how to debug, we have caps, shielded cables, etc. but turning on / off motor seems to cause the problem.

Our latest crazy fix is to look at current pulses and if it's like +/- 50% different, use the previous speed. (car can't start or slow that fast). So far that seems the best.

Our telemetry is in a very noisy environment and my debugging tools and skills is very limited.

Another race coming up, i'll let you know if these "fixes" helped.

On a side note: we were having significant white screen issues with our SPI based 2.4" TFT display. We move from RS232 cable to shielded ethernet (shield ground to Teensy via a 1M resistor) and ground/data on a twisted pair, and the display functions for the duration of the race--a first ever. We're getting there but a very painful process.
 
Hi Kris,

A racing project!? Excellent, hope it's going well.

To further discuss your issues. Can you describe your expected pulses per second? I get why you increased the update rate to increase the amount of pulses and better get the speed. However, I might better understand the situation if you can provide your code and physical setup more.

You're using reflective tape on the wheel with an optical sensor. How many pieces of tape are you using? More tape will give you a higher resolution.

For your noise issues it seems you've solved the SPI screen with better shielding and grounding. Can you provide the datasheet for your sensor and also a connection diagram? It may well be noise, and if it's only when the motor is powering down it may be due to back EMF while the motor is coasting. How is the speed output under braking?



Edit - Just to add to this, here is a method of wheel speed reading using a holed disc on the wheel to cut a sensor. Like my suggestion with the tape, it has many holes to give you a good resolution - https://www.brainy-bits.com/speed-sensor-with-arduino/

Also, perhaps you can look at an ABS sensor for tracking your wheel speed? - https://www.reed-sensor.com/applications/automotive/anti-lock-braking-system-abs/
 
Last edited:
I don't see a note on which Teensy is in use? If not a T_LC - the T_3.1-T_3.6 have a cycle counter that increments each clock cycle based on F_CPU.

This should get you going - or find other posts. Reading the ARM_DWT_CYCCNT is a 'simple' read, calling micros() is costly and not simple.

Assuming all vars involved are uint32_t it compares just like micros() but instead of 1 MHz it will be F_CPU of 96 or 256 MHz for better resolution - it will wrap that much sooner but that is 16+ seconds even at 256 MHz.
Code:
  ARM_DEMCR |= ARM_DEMCR_TRCENA; // Assure Cycle Counter active - do this is setup() once
  ARM_DWT_CTRL |= ARM_DWT_CTRL_CYCCNTENA;

  RPMDebounceTime =  F_CPU/????; // based on F_CPU decide how many counts is the desired Debounce

Code:
void inc_rpm() {

  PulseStartTime = ARM_DWT_CYCCNT;

  if ((PulseStartTime - PulseDebounce) > RPMDebounceTime) {
    PulseDelta =  PulseStartTime - PulseOldTime;
    PulseOldTime = PulseStartTime;
    PulseDebounce = PulseStartTime;
    wRPM = 60000000L / PulseDelta;
    PulseCount++;
    TotalRevolutions++;
    LapPulses++;
  }

}
 
Thanks to all,

We're using a Teensy 3.2. defragster--will look at that code tonight. All my time var are volatile unsigned longs . I've been looking for a direct access to the chips timer as it "feels" more reliable--this may be it.

I'll report back after some testing.

here's a link to my speed sensor. Back when we used magnets, we had 6 mounted on the sprocket. Better resolution but very difficult to set the correct distance to the sensor.

https://www.amazon.com/gp/product/B01I57HIJ0/ref=ppx_yo_dt_b_asin_title_o00_s00?ie=UTF8&psc=1


I'm a coach for a high-school electric race car team. Students design, build and a race a single seater. Powered by 2 x 12 volt batteries, and the team that goes the farthest in 90 minutes wins. We've been very successful over the last 4 years. While it's an after school club, one thing I do with my team is try to run it like a business--especially around IP protection. Students are required to sign and NDA, so I have to be a bit careful in what I disclose--such as a wiring diagram or complete code. I know it makes getting help tough, but I try to lead by example.
 
KrisK - good luck - I've used CycCnt for a couple things to good end - timing of GPS PPS and gps message start watching for UART Serial start bit - and updated micros() for the Teensy beta in progress as it was using a slow millisecond clock and micros resolution was 10 us. Need to look into porting that micros() code back to T_3.x. It uses an 'atomic' has interrupt happened feature that exists on T_3.6 - so it should work on T_3.2 but have yet to confirm. Not sure if that would let some of the calc code in the _isr be moved to loop() when the RPM is actually checked at time of display update.
 
A quick google shows that others have had issue with your sensor module picking up noise. Perhaps this may be of use to you; http://irsensor.wizecode.com/ - They mention adding a cardboard tube in order to block out outside interference. I would also question the update rate of your sensor. I wonder if you could potentially be missing "reads" depending on the speed of the cart. I'd be interested to see how your next test goes. Good luck.
 
Status
Not open for further replies.
Back
Top