Reading RPM from encoder using Arduino Micro interrupts

Hi,

I'm trying to interface my Arduino micro to this encoder https://www.usdigital.com/products/e4t. The E4T has 500 counts per rev and is at a ratio of 4.706:1 to the output. Therefore 1x revolution of the output will give 2353 counts of the encoder.

I've connected channels A and B of the encoder to pins 0 and 1 of the Arduino Micro and then configured these pins as interrupts. My code is below:

//Output pin mapping
int motorPWMPin = 9;
int motorDirectionPin = A5;

//Encoder variables
unsigned long motorPulseCounter = 0;
int encoderMeasurementInterval = 100; //100ms delay between displaying rpm value

//Timing variable
unsigned long lastTime = 0;

//RPM variable
int rpm = 0;

void setup() {
  Serial.begin(115200);
  delay(2000); // Delay to allow for initialisation of serial

  pinMode(motorPWMPin, OUTPUT);
  pinMode(motorDirectionPin,OUTPUT);

  attachInterrupt(digitalPinToInterrupt(0),motorInterrupt,CHANGE); //Motor encoder channel A attached to interrupt
  attachInterrupt(digitalPinToInterrupt(1),motorInterrupt,CHANGE); //Motor encoder channel B attached to interrupt

  setupTimer3_200mS(); //Setup a timer to trigger every 200ms (5Hz)
}

void loop() 
{
    digitalWrite(motorDirectionPin, 1);
    analogWrite(motorPWMPin, 50);

    while(1)
    {
      if (millis()-lastTime > encoderMeasurementInterval) 
      {
        lastTime += encoderMeasurementInterval;
        measure();
      }
    }
}

void setupTimer3_200mS()
{
  // Disable global interrupts
  cli();

  //Disable timer
  TCCR3A = 0;
  TCCR3B = 0;

  //16000000/1024/5 = 3125
  // Set compare match register to desired timer count - 200ms 5Hz
  OCR3A = 3125;
  
  // Turn on CTC mode WGM32 set CS10 and CS12 bits for Timer3 prescaler of 1024
  TCCR3B = bit (WGM32) | bit (WGM32) | bit (CS30);
    
  // Enable timer compare interrupt
  TIMSK3 |= (1 << OCIE3A);
  
  // Enable global interrupts
  sei();
}

ISR(TIMER3_COMPA_vect) // Timer3 interrupt
{
    rpm = calculateRPM(motorPulseCounter);
    motorPulseCounter = 0;
}

float calculateRPM(int pulses)
{
  //Encoder 500 counts per rev and is at a ratio of 4.706:1 to the output.  
  //Therefore 1x revolution of the output will give 2353 counts of the encoder.
  //pulses / 2353 = number of revs of output shaft in the time the pulses were recorded
  //(pulses / 2353) * scale * 60 = RPM

  float scale = 5.0; //Number of times per second the encoder pulses are read

  float val = ((float)pulses / 2353.0) * scale * 60.0;

  return val;
}

void measure()
{
  Serial.println(rpm);
}

void motorInterrupt()
{
  motorPulseCounter = motorPulseCounter + 1;
}

When I run this I get an output RPM of 0 so something's going wrong! Is it something with my code or is the Arduino micro unable to fire interrupts quickly enough to detect the pulses being sent by the encoder?

Also if I change the function below:

float calculateRPM(int pulses)
{
  //Encoder 500 counts per rev and is at a ratio of 4.706:1 to the output.  
  //Therefore 1x revolution of the output will give 2353 counts of the encoder.
  //pulses / 2353 = number of revs of output shaft in the time the pulses were recorded
  //(pulses / 2353) * scale * 60 = RPM

  //float scale = 5.0; //Number of times per second the encoder pulses are read

  //float val = ((float)pulses / 2353.0) * scale * 60.0;
  float val = (float)pulses;
  return val;
}

I only get a value of either 1 or 2 printed to the serial monitor. This makes me think the Arduino micro can't handle the interrupts quickly enough. Though even if I decrease the PWM to 10 (in code above it was 50) the serial monitor only prints a value of 0 or 1.

When I run this I get an output RPM of 0 so something's going wrong! Is it something with my code or is the Arduino micro unable to fire interrupts quickly enough to detect the pulses being sent by the encoder?

motorPulseCounter variable needs to be declared volatile, otherwise the compiler might do optimizations that don't comply with code called in interrupt context.

Hi @pylon, thanks for your reply! Yes I think you're correct - thanks for the help!

I'm trying to interface my Arduino micro to this encoder https://www.usdigital.com/products/e4t. The E4T has 500 counts per rev and is at a ratio of 4.706:1 to the output. Therefore 1x revolution of the output will give 2353 counts of the encoder.

Using interrupts on both the A and B outputs with CHANGE will give 4 times that count. The typical way a quadrature encoder is described, they will have a possible 4 transitions per count. There are different algorithms for reading them which will count 1,2 or 4 of the transitions available.

What is your application, and what resolution to you need? Why are you using a quadrature encoder? They are usually used when direction of rotation is required, but you are only using a simple count.

This error for clock select was addressed in your other thread. You were taking reading much faster than you intended.

// Turn on CTC mode WGM32 set CS10 and CS12 bits for Timer3 prescaler of 1024
  //TCCR3B = bit (WGM32) | bit (WGM32) | bit (CS30);
 TCCR3B = bit (WGM32) | bit (CS32) | bit (CS30);