const int encoderPinA = 2;
//const int encoderPinB = 3;
volatile unsigned long pulseDuration; //to store the pulse duration, volatile because can change abruptly
const int interval = 1000;
volatile unsigned long lastTime = 0;
const int PPR = 240; //ppr is 12 and gear ratio 20:1 so eff ppr =12*20
const int numReadings = 5;
int readings[numReadings];// array to store the readdings
int readIndex = 0;
int total = 0;
int average = 0;
void setup() {
Serial.begin(9600);
pinMode(encoderPinA,INPUT);
for (int thisReading = 0; thisReading < numReadings; thisReading++) {
readings[thisReading] = 0; // Initialize the readings array to zero
}
//pinMode(encoderPinB,INPUT);
attachInterrupt(digitalPinToInterrupt(encoderPinA), encoderISR, FALLING);
lastTime = micros();
}
void loop(){
//lastTime = micros();
// noInterrupts();
//lastTime = 0;
total = total - readings[readIndex];// subtracting the last reading from the total
readings[readIndex] = pulseDuration;
total = total + readings[readIndex];
readIndex = readIndex + 1; // readIndex is position so move on to next position,
if (readIndex >= numReadings) {
readIndex = 0;
}
average = total / numReadings;
// Serial.print("pulseduration: ");
//Serial.print(pulseDuration);
//Serial.println("microseconds");
//noInterrupts();
//unsigned long duration = pulseDuration ;
Serial.print("averagepulseduration:");
Serial.print(average);
Serial.println("microseconds");
interrupts();
float speed = (60.0*1000000.0)/(average*(float)PPR);
//interrupts();
Serial.print("speed:");
Serial.print(speed);
Serial.println("RPM");
//interrupts();
//delay(interval);
}
void encoderISR(){
unsigned long currentTime = micros();
pulseDuration = currentTime - lastTime;
lastTime = currentTime;
}
I have wrote this code to measure the rpm of a encoded dc motor by measuring the pulseduration between two consecutive pulses. Is this the correct way to calculate average pulseduration.
No, because it fills the array with pulse durations without waiting for a new pulse to be measured. So you will probably get 20 copies of the same pulse duration in the array.
PS. You are not measuring, and don't want to measure, the pulse durations. You are measuring the pulse period, the time between rising or falling edges of consecutive pulses.
You should update the array only when a new pulse period has been measured.
pulseDuration (which should be called pulsePeriod) is zero when the code begins to run. After the first period has been measured, it will be greater than zero. In loop() you can test for this, and update the array only when the new reading is not zero. You can also set pulseDuration back to zero after reading it so that loop() will know when the next reading has been taken.
When reading or updating a volatile variable in loop() you should disable interrupts, otherwise the reading can get corrupted. Interrupts should be enabled again as quickly as possible, otherwise interrupts could be missed.
noInterrupts();
total = total - readings[readIndex];// subtracting the last reading from the total
readings[readIndex] = pulseDuration;
total = total + readings[readIndex];
readIndex = readIndex + 1; // readIndex is position so move on to next position,
if (readIndex >= numReadings) {
readIndex = 0;
interrupts();
// Computes the average of the last 5 pulse durations.
const int encoderPinA = 2;
//const int encoderPinB = 3;
//variable to store the pulse duration, volatile because it's used in an ISR
volatile unsigned long pulseDuration;
volatile unsigned long lastTime = 0;
volatile bool newPulse = false;
const int interval = 1000;
const int PPR = 240; //ppr is 12 and gear ratio 20:1 so eff ppr =12*20
const int numReadings = 5;
unsigned long readings[numReadings] = {0, 0, 0, 0, 0}; // array to store the readings
int readIndex = 0;
void setup() {
Serial.begin(9600);
pinMode(encoderPinA, INPUT);
//pinMode(encoderPinB,INPUT);
attachInterrupt(digitalPinToInterrupt(encoderPinA), encoderISR, FALLING);
lastTime = micros();
//pinMode(8, OUTPUT); // for DEBUGGING
}
void loop() {
unsigned long total, average;
int i;
//tone (8, 200); // for DEBUGGING
// Check if a new pulseDuration is available
if (newPulse)
{
readings[readIndex] = pulseDuration; // put in array
readIndex = readIndex + 1;
if (readIndex == numReadings) readIndex = 0;
newPulse = false;
}
// Compute the average of the last numReadings
total = 0;
for (i = 0; i < numReadings; i++)
{
total = total + readings[i];
}
average = total / numReadings;
Serial.print("averagepulseduration: ");
Serial.print(average);
Serial.println(" ms");
float speed = (60.0 * 1000000.0) / (average * (float)PPR);
Serial.print("speed: ");
Serial.print(speed);
Serial.println(" RPM");
Serial.println();
}
void encoderISR() {
unsigned long currentTime = micros();
pulseDuration = currentTime - lastTime;
lastTime = currentTime;
newPulse = true;
}