Reading RPM, and Timer?

What could be included in further releases, or for the forum.
Nutsy
Posts: 241
Joined: Sun Jul 24, 2016 4:26 pm

Reading RPM, and Timer?

Post by Nutsy » Wed Aug 17, 2016 3:48 pm

Well im trying to do the RPM detection code and I wrote something I thought should work. However its not, though it could also be the RPM generator not working....

Rather than show the whole sketch heres the function that does it... or should, anything glaringly wrong?

Code: Select all

void findRPM()
{
  static unsigned long oldTime;
  unsigned long currentTime;
  unsigned long timeDiff;
  unsigned int blah;
  while (digitalRead(12) == LOW) {blah =0;} // pause code waiting for a pulse, theyre regular enough so dont need to wait long

  currentTime = micros();
  for (int i = 0; i < 4; i++)
  {
    while (digitalRead(12) == LOW) {blah =0;} // taking the time difference between 4 rev samples...
  }
  timeDiff = micros() - currentTime;
  Serial.println(timeDiff);
  RPM = 1000000 / (timeDiff / 4) * 60; //uses 4 rev samples to get a fair average between them also creating higher accuracy


  ////// code to calculate RPM here
  //random generator with more realistic increases decreases....
  //  RPM = random((RPM - 1500),(RPM + 1500));
  //  RPM = (RPM <0) ? 0:RPM;
  //  RPM = (RPM > rpmRange) ? rpmRange:RPM;
}
Now one other thing. A guy I used to know who does lots of software and engineering stuff, someone far far smarter than me said I should be using the built in timers to do this as it would be far faster and more accurate. Ive kinda looked up a little and it really does look like some thing beyond me... however... Any ideas how I go about doing this?

User avatar
ahull
Posts: 1630
Joined: Mon Apr 27, 2015 11:04 pm
Location: Sunny Scotland
Contact:

Re: Reading RPM, and Timer?

Post by ahull » Wed Aug 17, 2016 3:54 pm

I would go one stage further.. use a pin to generate an interrupt every revolution (with a hall or optical sensor)... then use the timer to see how much time elapsed since the last interrupt. That gives you your period (one cycle)... you then figure how many times you can divide a minute by your period and you have your RPM... Since you re-calculate for every revolution, it stays accurate, and since you use an interrupt, your STM32 can be off doing something useful for the rest of the time. One refinement is to average your measurement over several revolutions and discard any wildly different measurements, as they are probably missed interrupts, or noise.

To keep your interrupt service routine short (always a very good idea) you can also simply log the timer value at each interrupt to a variable, and do the actual time calculations in your main loop.
- Andy Hull -

Nutsy
Posts: 241
Joined: Sun Jul 24, 2016 4:26 pm

Re: Reading RPM, and Timer?

Post by Nutsy » Wed Aug 17, 2016 3:59 pm

I thought using interrupts paused all the internal timer code when they ran?
Oh and in my code above im doing it via 4 pulse averages...

But hall effect? This is the RPM portion, not the mph side...

Though on my MPH side im going to read it off the MPH cable thats attached to a optical interrupt... IE mouse wheel sensor...

Nutsy
Posts: 241
Joined: Sun Jul 24, 2016 4:26 pm

Re: Reading RPM, and Timer?

Post by Nutsy » Wed Aug 17, 2016 6:13 pm

So how do i use timers and interrupts? Im sorry its a relatively new concept to me...

User avatar
ahull
Posts: 1630
Joined: Mon Apr 27, 2015 11:04 pm
Location: Sunny Scotland
Contact:

Re: Reading RPM, and Timer?

Post by ahull » Wed Aug 17, 2016 8:03 pm

Nutsy wrote:So how do i use timers and interrupts? Im sorry its a relatively new concept to me...
There are a number of examples on this site, take a look at this..

http://www.stm32duino.com/viewtopic.php?t=1207

... and google is your friend... the whole site is indexed by google, so try this for a little more light bedtime reading...

https://www.google.co.uk/search?client= ... 8gfB_ZiwDA

The idea is actually very simple... you create a very short routine, called an interrupt service routine. This is triggered every time a particular interrupt is triggered and .. services that interrupt.

For example if you set up an interrupt to happen every time a certain pin changes state, or every time it goes low. then when that event happens, your interrupt service routine (ISR) will be called automatically by the ARM processor hardware. In your ISR, you might for example place the value of the millis() timer in a certain memory location or variable, or array or whatever, set a memory flag somewhere to indicate you have serviced the routine then return from the routine.

In your main loop, you check to see if the flag has been set, and if so you calculate your rpm based on the difference between the most recent value saved by the ISR, and the previous one. then move the most recent value to the previous value variable, then clear the interrupt flag and continue round your loop doing whatever else you fancy. Since the ISR is triggered by an event outwith the main loop, it needs to return quickly, otherwise there is a risk that you will either get a second interrupt, thus overriding the value from the first, or setting up a situation where your ISR never gets a chance to complete, or you will spend so much time in the ISR that it impacts on your main loop. Hence the mantra.. Always keep your ISR as short as possible.

Designing interrupt based solutions can sometimes be a bit of a head scratcher, but I'm sure if you look through the examples, you will pick up the idea fairly easily.
- Andy Hull -

User avatar
RogerClark
Posts: 7171
Joined: Mon Apr 27, 2015 10:36 am
Location: Melbourne, Australia
Contact:

Re: Reading RPM, and Timer?

Post by RogerClark » Wed Aug 17, 2016 9:46 pm

I would have a global to store the number of pulses.

Then then in your display code, read that value and convert to RPM ( as your display code runs at a fixed interval)
then zero the global value

In your ISR just increment that gobal

Nutsy
Posts: 241
Joined: Sun Jul 24, 2016 4:26 pm

Re: Reading RPM, and Timer?

Post by Nutsy » Thu Aug 18, 2016 8:49 am

Hi Roger, yeah i was thinking along that lines.... Now im just trying to work out how to use interrupts and the timer :(

User avatar
RogerClark
Posts: 7171
Joined: Mon Apr 27, 2015 10:36 am
Location: Melbourne, Australia
Contact:

Re: Reading RPM, and Timer?

Post by RogerClark » Thu Aug 18, 2016 9:47 am

See the old leaflabs docs

http://docs.leaflabs.com/static.leaflab ... rrupt.html

I think the only thing to be aware of, is how often your display RPM function is called versus how often the RPM interrupt is triggered.

e.g. at 1500 rpm, you will only get 25 pulses per second. and if your display function is called every 100ms you will only have 2 or 3 pulses between display updates.

This causes a lot of inaccuracy. (but you can average out the 2 or 3 pulse problem)

Or even better use a longer sample window, i.e actually get the pulse count every 250ms or 500ms and animate your RPM display to the new value over 250 or 500mS

There is also a very technical way to get the STM32 to measure the pulse width (or distance between pulses) called Timer Input Capture mode.
Someone did a blog post of how to do it with bare metal coding of the STM32

http://embedded-lab.com/blog/stm32-timers/

This allows very accurate measurement of incoming pulses, as I think it runs a counter (from a divider off the main CPU clock). But I"m not sure if it has a mode where the counter is started by one pulse and stopped by another - it seems like a feature it should have, but you'd need to read the master programming doc (which is somewhat hard going)


Personally, I'd get it working with the ISR method and perhaps upgrade to the Timer Input Capture at a later date.

Nutsy
Posts: 241
Joined: Sun Jul 24, 2016 4:26 pm

Re: Reading RPM, and Timer?

Post by Nutsy » Thu Aug 18, 2016 1:42 pm

Yeah I managed to make a quick simple interrupt based scetch to blink the led per rpm pulse off the generator... That worked...

Going to convert that to rpm counting and measuring like you said averaging out over so many samples. once i got all that tied down ill add it to the main project code.

I think the guy i used to know meant that timer based way of doing things very accurately... But yeah I think that might be a tinsy bit out my league

Nutsy
Posts: 241
Joined: Sun Jul 24, 2016 4:26 pm

Re: Reading RPM, and Timer?

Post by Nutsy » Thu Aug 18, 2016 5:28 pm

Well i got an interupt working counting the pulses and then using the scheduler to then do a sum to find the rpm... Its kinda working...

Trouble is its not really accurate. Unless the pulse generator isn't working quite right and is werbling a little even though its set to 1000...

Ive tried fiddling with the code to produce slightly sharper timing and its not made any difference... 1000rpm is coming up as around 1900-2500 rpm
5000 rpm is coming up at around 4800-5200 ish.

Now what is odd is I got the numbers on serial print and theres a pattern to the inaccuracies... Its not randomly inaccurate.
Even when I got the RPM calculator running once a second the numbers are still inaccurate by quite a noticeable amount. Its close but not there yet...

Heres the sketch im using as a test

Code: Select all

#include <Thread.h>
#include <ThreadController.h>// new scheduler
ThreadController controll = ThreadController();
Thread ReadRPM = Thread();
long rpmSample = 0;
unsigned long oldTime = 0;
unsigned long timeDiff = 0;
long RPM;


void setup() {
  Serial.begin(9600);
  
  pinMode(33, OUTPUT);
  pinMode(12, INPUT);

  ReadRPM.onRun(findRPM);
  ReadRPM.setInterval(100);

  controll.add(&ReadRPM);

  attachInterrupt(12, sampleRPM, RISING);
  oldTime = micros();
}

void findRPM() {
  RPM = 1000000 / ((micros() - oldTime) / rpmSample) * 60;
  rpmSample = 0;
  //oldTime = micros();
  Serial.println(RPM);
}
void loop() {
  
  controll.run();
}
void sampleRPM() {
  //This if sets the oldTime, this was in the findRPM loop but I put it here in case the oldtime 
  //varible wasnt capturing the correct microsecond on the first sample, dunno. Didnt make a difference.
  if (rpmSample == 0) { 
    oldTime = micros();
  }
  
  rpmSample++;
}
UPDATE... Silly me double read the sketch for the generator, it cant show bellow 2000rpm... in any case ignoring that, the results are still inaccurate and in a pattern...

Post Reply