Im trying to understand how the Arduino ISR keeps track of timing between pulses stored in a variable, if that variable is latter used.
Say, for example, you have a magnet mounted on a circular object, that passes a reed switch. Using this to trigger the ISR that calls a function that would have something like counts++ in it, then storing that value in a variable.
If that varible is used latter to turn on a motor, for example, according to the count capture, how does the arduino MCU know the freq or timing to output the count or pulses of the magnet passing the reed switch, at the same frequency that the counts were received? For example, a magnet mounted on a 2 inch dia shaft would turn slower past a reed switch than one mounted on a .5 inch shaft, or one could vary the speed of the magnet past the reed as they wish. So how does counter++ keep track of this freq? is it reading the attachinterupt pin (or is the function that is being called, keeping track) or does the ISR time stamp each pulse it receives and store that interval in the declared variable? so when the variable is used again, it has this interval stored in it?
To be more clear, if it takes one second for the magnet to complete one revolution, how does the MCU know it took one second and store this, along with the counts of the revolutions?
Thanks for any help
1 Answer 1
All the interrupt does is count. It has no concept of time. For instance:
volatile uint32_t revolutions = 0;
void count() {
revolutions++;
}
....
attachInterrupt(0, count, FALLING);
will just count up 1 every time the interrupt triggers.
To be able to convert that count into something meaningful you have to manually introduce the concept of time - that is, the count of "revolutions" (for example) between two points in time. And it's up to your sketch to implement that.
The simplest way may be:
// For anything more than an 8 bit value on an 8 bit machine
// make a critical section to save and reset the count
cli();
uint32_t savedResolutions = revolutions;
revolutions = 0;
sei();
Serial.print(savedRevolutions);
Serial.println(F(" revolutions per second"));
// Introduce the concept of time - this will
// make it get the count once a second.
delay(1000);
Of course, instead of delay()
you can use millis()
in much the same way as the BlinkWithoutDelay
example in the IDE.
It is possible (as long as you're not counting too fast) to incorporate the time element into the interrupt routine by keeping track of the time in a variable:
volatile uint32_t rps = 0;
void count() {
static uint32_t revolutions = 0;
static uint32_t timestamp = millis();
revolutions++;
if (millis() - timestamp >= 1000) {
timestamp = millis();
rps = revolutions;
revolutions = 0;
}
}
....
attachInterrupt(0, count, FALLING);
Then you can just reference the rps
variable at any time to get the revolutions per second. Again it should really be queried inside a critical section to prevent the value being changed while you're actively reading it...
cli();
uint32_t myRPS = rps;
sei();
Serial.print(myRPS);
Serial.println(F(" revolutions per second"));
-
Ive seen people use a variable++ or +1 in a function called by the ISR, without any millis or time reference, then use that variable against another variable to capture a peak value, use that to turn something on for that duration, for example.Jim– Jim2018年01月08日 11:39:48 +00:00Commented Jan 8, 2018 at 11:39
-
Sure, if that's what they are doing requires them to do...Majenko– Majenko2018年01月08日 12:04:26 +00:00Commented Jan 8, 2018 at 12:04
-
"like that they introduce the concept of time at a later stage" could you explain what you mean by this perhaps with an example? I dont quite get itJim– Jim2018年01月08日 12:08:30 +00:00Commented Jan 8, 2018 at 12:08
-
You mean besides the one I already show in my answer?Majenko– Majenko2018年01月08日 12:09:29 +00:00Commented Jan 8, 2018 at 12:09
-
1they were comparing 2 variables and saving the larger count, then using that condition to turn on a motor, so it wasnt timed to anything, just when one became larger than the other, save that, and that condition became true, turn on the motor... As for nested "if" statements, if you have an if then a nested if, that is equilivant to an AND, correct? but an if, then an if else is equilivant to a NOT, correct?Jim– Jim2018年01月18日 03:20:25 +00:00Commented Jan 18, 2018 at 3:20
millis()
value. Even better would be to have the ISR (locally) store the millis() value of the last time the ISR was called. Then when the ISR is called the next time, it could calculate the time that has passed since the last ISR call. If the motor is running faster, this method can get a bit less accurate. In that case you could have the mainloop
measure the time it takes for the counter to increase by e.g. 20 (i.e. 20 revolutions). The it can calculate the average RPM.