DC motor control and Incremental Rotary Encoder Speed Control

Hi,

I am planning to monitor the speed of a DC motor (Maxon DCX 12V) with an incremental rotary encoder welded onto the shaft (1024PPR). I am using the VNH5019 Pololu Dual motor driver shield and pin 3 as my encoder input. I am trying to use TIMER2 as a timer interrupt to calculate the speed of my motor (I can't use TIMER1 since pins 9 and 10 are used by the shield to drive the motor). I can get a reading from 'count', however, the speed returns a value of 0.

I feel like the motor is spinning too fast for my code even at a low speed because I keep getting random values for trigger_counter (i.e. the trigger_counter can't keep up with the rotor speed). I have also attached the datasheets for the motor, encoder and the shield. I appreciate your help.

#include "DualVNH5019MotorShield.h"
DualVNH5019MotorShield md;
volatile int count = 0;
volatile int trigger_counter = 0;
volatile int pulse_width = 0;
volatile int previous_count = 0;
volatile double speed = 0;
volatile double pwm_val = 0;
const uint16_t timer_load = 0;
const uint16_t timer_compare = 128000;


void setup() {
 Serial.begin (115200);
   pinMode(3, INPUT);
   attachInterrupt(1, motor_speed, RISING);
   md.init();

  // initialize timer2 
 TCCR2A = 0;
 
 TCCR2B  |= (1 << CS22);   // 1024 prescalar set to 1,1,1 for timer 2 to 
 TCCR2B  |= (1 << CS21);   // increment prescalar every 128000 counts
 TCCR2B |= (1 << CS20); 

   
 TCNT2 = timer_load;   // preload timer 2 with the value 0
 OCR2A = timer_compare;   // store the matched value into the timer 2 register
 
 TIMSK2 = (1 << OCIE1A);   // enable the compare interrupt
 
 sei(); //enable global interrupts   

}

void loop() {
 md.setM1Speed(50); // set the speed of motor (0-400)
Serial.print("\t Speed = ");
Serial.println(speed);
}

ISR(TIMER2_COMPA_vect){ //add 1 to the value of count with each increment of the prescalar
   TCNT2 = timer_load;
   count = count + 1;
  
}
void motor_speed(void){
   if(trigger_counter < 50) trigger_counter = trigger_counter + 1; //calculates speed every 50 triggers of the encoder
   else{
       pulse_width = count - previous_count;
       previous_count = count;
       speed = pulse_width * (1024/50) * 60 / 1000000;

       trigger_counter = 0;
       } 
   }

Maxon DCX 32L.pdf (204 KB)

Pololu Dual VNH5019 Motor Driver Shield User's Guide.pdf (1010 KB)

Why are you trying to use a timer? You have the encoder connected to an external interrupt pin. Attach an interrupt handler, and trigger an interrupt when the encoder outputs a pulse.

Count the pulses in the interrupt handler, and, using millis() and the blink without delay technique, in loop(), calculate the RPM based on the number of pulses and the time during which those pulses arrived.

Welcome to the forum. When considering counting anything, you must decide whether you want continuous or burst data. If continuous and you will be performing other tasks as well, you must share time between them. If the tasks are complex or slow, it doesn't leave much time to service interrupts for counting. If counting is the priority, it doesn't leave much time for doing anything with the data. Always a trade-off. If what you want is RPM then burst data or data for a longer period but intermittent will typically suffice.

With an encoder 1024PPR, your magic number is 58,593.75. For simplicity call it 60,000. So divide 60k by whatever RPM you're attempting to measure and the result is how many microseconds you have between pulses. That's your window for doing whatever you need to do with the data. If your motor speed is 300RPM, you've got 200us between pulses (minus a couple to service the interrupt). Now you can do a fair bit in that period of time, unless it involves division or almost anything with a float. As the speed increases, the window closes. If however, you can get by with measuring a finite number of pulses, or counting pulses for a prescribed period of time, going away to do all the math stuff and then starting again, you can log data very quickly into a buffer, limited only by the memory. For pure logging purposes, you could capture speeds in the 10,000 to 15,000 RPM range. So, give it some thought and choose a path. We can go forward from there.