Reading RPM, and Timer?

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?
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;
}


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.


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
Wed Aug 17, 2016 6:13 pm
So how do i use timers and interrupts? Im sorry its a relatively new concept to me…

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…

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
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 :(

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
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
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
#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++;
}


ahull
Thu Aug 18, 2016 8:48 pm
Maybe I’m mistaken, but is it not…
// RPM = 60 * (1/timediff in seconds)
RPM = 60*(1000000/(micros() - oldTime )) ; // i.e. RPM = 60 seconds divided by (1000000/(timediff uS))

Nutsy
Thu Aug 18, 2016 9:47 pm
How often do millis and micros wrap round?

ahull
Thu Aug 18, 2016 9:56 pm
Nutsy wrote:How often do millis and micros wrap round?

martinayotte
Thu Aug 18, 2016 10:02 pm
it is quite easy to detect the overflow :

if (micros() < oldTime) {
// handle the overflow (or throw away this sample)
}


victor_pv
Tue Aug 30, 2016 4:15 am
I would use a hardware timer in input capture mode.
You can either set the timer to generate an interrupt when it overflows, and compare the time between overflows, or just read the timer value every X milliseconds and compare the timer counter increase in that period. You can also set pre-escalers. It all depends how many RPMs you are measuring and how much variance is between the minimum and the maximum speed.
If you are measuring a high RPM value, polling a pin or serving and interrupt per revolution would consume a lot of cpu time, while with a hardware timer you dont consume any cpu time to count the cicles, only to check the timer value as often as you want, or to server the timer interrupt on overflow, which can be more or less frequently depending on the pre-escaler and the overflow value.

RogerClark
Tue Aug 30, 2016 4:22 am
Welcome back @victor_pv ;-)

I did mention Input Capture in one of my replies to this thread http://www.stm32duino.com/viewtopic.php … 322#p17059 but I think @nutsy hoped he could do it just using an ISR

But it does now sound like the Input Capture method would give a better value that counting over a known time period (as the number of pulses is too few)


Nutsy
Tue Aug 30, 2016 10:48 pm
Sorry for my late reply been busy with all sorts. But not sure anyone noticed. I even put my project on hackaday…

Well the interrupt counting seems to be working well enough currently.

I thought I had a suitable protection transistor to test the real RPM signal but i might have wired it up wrong and pin 12 has died :p

I’ve designed and ordered parts for a opto isolator setup to read the rpm safely… Then ill test again with another pin…


Leave a Reply

Your email address will not be published. Required fields are marked *