Hello, I am running motor and printing rpm, but the output at 120 rpm varies 12 rpm. I've narrowed it down to loss of pulses during the serial print. The time between pulses should be 157 usec at 120 rpm. Any ideas on how to fix this?
volatile unsigned long timenew;
volatile unsigned long timeold;
volatile unsigned long difference;
int pwm_value = 255; // set PWM between 0 and 255
double rpm;
void setup()
{
pinMode(2, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(2), counter, CHANGE);
Serial.begin(9600);
pinMode(5, OUTPUT);
rpm = 0;
timeold = 0;
analogWrite(5, pwm_value);
}
void loop()
{
rpm = 1000000 / (difference) * 60 / 288; //3 magnets (6 poles) and 48:1 gearbox
if (rpm < 1000) // first rpm is very high and screws up the plotter
{
Serial.print(" rpm: "); Serial.println(rpm);
}
}
void counter()
{
timenew = micros();
difference = timenew - timeold;
timeold = timenew;
}
Fault number one! As difference is an unsigned long, 32 bits, 4 bytes, and the controller is an 8 bit device then an interrupt can change difference while the execution is on the way to read that long.
In science it's named "critical region", a part of code that can't be interrupted, the reading of four bytes.
I suggest this coding:
Krank up the baudrate for Serial. I use 115200. My initial feeling is that the Serial.print will be overloaded.
Also add a timer that only enters and prints the RPM every 100 mS. No human eye will read that high speed Serial.print.
Measure and print alternately, by flushing the output buffer after you write to it. As it is now, serial interrupts are being called "behind your back" while you go on and make measurements. The two interrupts are taking whatever time they like to complete.
By calling 'flush()' you can be sure the output buffer is empty, so no serial interrupts will be called until you write something again.