I am doing a supposedly simple Arduino code on the Nano to measure the RPM. In a nutshell, I am using an interrupt service routine which triggers on falling edge. One it triggers, it measured the periodic time by subtracting the time of the last interrupt with the current time. From there, it is converted to frequency and then to RPM. The variables are declared as shown below. Now, when monitoring the values using serial monitoring, I am noticing discrepancies. For instance, for PT of 21896, I am getting FREQ of 49.27 and RPM of 3154.57. What could be causing this to happen please?
volatile unsigned long PT = 0;
float FREQ = 0;
float RPM_Reading = 0;
volatile unsigned long CurrentTime = 0;
volatile unsigned long longTime = 0;
void ISR()
{
CurrentTime = micros();
PT = CurrentTime - lastTime; // Measure periodic time
FREQ = 1000000.0 / PT; // Measure frequency
RPM_Reading = FREQ * 60; // Convert frequency to RPM
lastTime = micros();
}
FREQ and RPM are not declared as volatile, so who knows what is happening with those variables.
However, as @UKHeliBob pointed out, the only thing you should be doing in the ISR is saving the current time.
Move everything except saving the current time out of the ISR. Turn off interrupts while you do the calculations in loop() and turn them back on again afterwards
Below is the full code. @JohnLincoln I am not convinced that this is the problem. I could be causing incorrect readout for the periodic time, but once this is read, the frequency and RPM should be calculated based on that value, irrelevant what the value is.
@jim-p how can i take PT = currentTime - lastTime; out of the ISR and measure the periodic time please?
On pulse by pulse. I already managed to get RPM once a second using micros(). I also achieved reading every 200ms. My issue is that I need very fast response time, hence why I am trying to calculate the RPM based on the periodic time of every pulse.
I added the TimerOne lib to create interrupts for the ISR (and a number of comments ).
/*
Forum: https://forum.arduino.cc/t/calculating-periodic-time-using-micros-on-arduino-nano/1336853
Wokwi: https://wokwi.com/projects/418610438465724417
2024/12/29
ec2021
*/
// Added to create a digital input for the ISR
// The togglePin switches its state (LOW-> HIGH-> LOW-> ...) every timer1 interrupt.
// As the ISR reacts on a falling edge only
// the measured period equals 2 x timerInterval
#include <TimerOne.h>;
const byte togglePin {3};
const unsigned long timerInterval = 500; //micro seconds !!!
#define INPUT_PIN_1 2
volatile unsigned long PT = 0;
// No need for volatile as FREQ and RPM_Reading are not used in the ISR
float FREQ = 0;
float RPM_Reading = 0;
// Moved to ISR
// volatile unsigned long currentTime = 0;
// volatile unsigned long lastTime = 0;
void setup()
{
// TimerOne
Timer1.initialize(timerInterval);
Timer1.attachInterrupt(toggle);
pinMode(togglePin, OUTPUT);
Serial.begin(115200);
pinMode(INPUT_PIN_1, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(INPUT_PIN_1), InterruptSR, FALLING);
}
void loop()
{
// Copy PT to the variable copyPT which is "outside" of the ISR
noInterrupts();
unsigned long copyPT = PT;
interrupts();
FREQ = 1000000.0 / copyPT; // Calculate frequency in Hz
RPM_Reading = FREQ * 60; // Convert frequency to RPM
// not ms but micro seconds
//Serial.print("periodic time in ms: ");
Serial.print("periodic time in µs: ");
Serial.println(copyPT);
Serial.print("frequency: ");
Serial.println(FREQ);
Serial.print("RPM: ");
Serial.println(RPM_Reading);
}
void InterruptSR()
{
static unsigned long lastTime = 0;
unsigned long currentTime = micros();
PT = currentTime - lastTime;
lastTime = currentTime;
}
void toggle() {
static byte state = LOW;
digitalWrite(togglePin, state);
state = !state;
}
Thanks all for your support. It worked fine with the code provided by @ec2021 . Thanks a lot for taking the time to test.
I tried all the differences between your code and my code to better understand the root cause of the issue. Eventually I ended up with the code below, which is very similar to mine. Then when I removed the last change, which is "unsigned long copyPT = PT;" I started having the same issue again.
I still cannot understand why by just copying the variable to another variable made a difference? What is the issue with using PT directly?
#define INPUT_PIN_1 2
volatile unsigned long PT = 0;
float FREQ = 0;
float RPM_Reading = 0;
volatile unsigned long currentTime = 0;
volatile unsigned long lastTime = 0;
void setup()
{
Serial.begin(9600);
pinMode(INPUT_PIN_1, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(INPUT_PIN_1), InterruptSR, FALLING);
}
void loop()
{
unsigned long copyPT = PT;
FREQ = 1000000.0 / copyPT; // Calculate frequency in Hz
RPM_Reading = FREQ * 60; // Convert frequency to RPM
Serial.print("periodic time in µs: ");
Serial.println(copyPT);
Serial.print("frequency: ");
Serial.println(FREQ);
Serial.print("RPM: ");
Serial.println(RPM_Reading);
}
void InterruptSR()
{
currentTime = micros();
PT = currentTime - lastTime;
lastTime = currentTime;
}