Timer interrupt problem

i am using arduino uno to control 2 stepper motors for a self balancing robot, when i give the motors the same signal for some reason the number of steps shifts over time (one of them increases more than the other)
here is a smaller version of the original sketch

int left_motor, throttle_left_motor, throttle_counter_left_motor, throttle_left_motor_memory;
int right_motor, throttle_right_motor, throttle_counter_right_motor, throttle_right_motor_memory;
float pid_output_left, pid_output_right;
long stpL,stpR;
unsigned long loop_timer;
int i;

void setup() {
  // put your setup code here, to run once:

 //To create a variable pulse for controlling the stepper motors a timer is created that will execute a piece of code (subroutine) every 20us
  //This subroutine is called TIMER2_COMPA_vect
  TCCR2A = 0;                                                               //Make sure that the TCCR2A register is set to zero
  TCCR2B = 0;                                                               //Make sure that the TCCR2A register is set to zero
  TIMSK2 |= (1 << OCIE2A);                                                  //Set the interupt enable bit OCIE2A in the TIMSK2 register
  TCCR2B |= (1 << CS21);                                                    //Set the CS21 bit in the TCCRB register to set the prescaler to 8
  OCR2A = 24;                                                               //The compare register is set to 39 => 20us / (1s / (16.000.000MHz / 8)) - 1
  TCCR2A |= (1 << WGM21);                                                   //Set counter 2 to CTC (clear timer on compare) mode
loop_timer = micros() + 15000;
Serial.begin(115200);

}

void loop() {
  // put your main code here, to run repeatedly:
  if(i<400)i++;
 pid_output_left = i;                                             
 pid_output_right = i; 
  //Motor pulse calculations
  ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  //To compensate for the non-linear behaviour of the stepper motors the folowing calculations are needed to get a linear speed behaviour.
  if(pid_output_left > 0)pid_output_left = 405 - (1/(pid_output_left + 9)) * 5500;
  else if(pid_output_left < 0)pid_output_left = -405 - (1/(pid_output_left - 9)) * 5500;

  if(pid_output_right > 0)pid_output_right = 405 - (1/(pid_output_right + 9)) * 5500;
  else if(pid_output_right < 0)pid_output_right = -405 - (1/(pid_output_right - 9)) * 5500;

  //Calculate the needed pulse time for the left and right stepper motor controllers
  if(pid_output_left > 0)left_motor = 400 - pid_output_left;
  else if(pid_output_left < 0)left_motor = -400 - pid_output_left;
  else left_motor = 0;

  if(pid_output_right > 0)right_motor = 400 - pid_output_right;
  else if(pid_output_right < 0)right_motor = -400 - pid_output_right;
  else right_motor = 0;


  //Copy the pulse time to the throttle variables so the interrupt subroutine can use them
  throttle_left_motor = left_motor;
  throttle_right_motor = right_motor;
Serial.println(stpR-stpL);  


while(loop_timer > micros());
loop_timer=loop_timer+15000;
}


ISR(TIMER2_COMPA_vect){
  //Left motor pulse calculations
  throttle_counter_left_motor ++;                                           //Increase the throttle_counter_left_motor variable by 1 every time this routine is executed
  if(throttle_counter_left_motor > throttle_left_motor_memory){             //If the number of loops is larger then the throttle_left_motor_memory variable
    throttle_counter_left_motor = 0;                                        //Reset the throttle_counter_left_motor variable
    throttle_left_motor_memory = throttle_left_motor;                       //Load the next throttle_left_motor variable
    if(throttle_left_motor_memory < 0){                                     //If the throttle_left_motor_memory is negative
      PORTD &= 0b11111011;                                                  //Set output 5 low to reverse the direction of the stepper controller
      throttle_left_motor_memory *= -1;                                     //Invert the throttle_left_motor_memory variable
    }
    else PORTD |= 0b00000100;                                               //Set output 5 high for a forward direction of the stepper motor
  }
  else if(throttle_counter_left_motor == 1)
  {PORTD |= 0b00001000;             //Set output 6 high to create a pulse for the stepper controller
   if(PIND & 0b0000100)stpL++;
   else stpL--;
  }
  else if(throttle_counter_left_motor == 2)PORTD &= 0b11110111;             //Set output 6 low because the pulse only has to last for 20us 
  
  //right motor pulse calculations
  throttle_counter_right_motor ++;                                          //Increase the throttle_counter_right_motor variable by 1 every time the routine is executed
  if(throttle_counter_right_motor > throttle_right_motor_memory){           //If the number of loops is larger then the throttle_right_motor_memory variable
    throttle_counter_right_motor = 0;                                       //Reset the throttle_counter_right_motor variable
    throttle_right_motor_memory = throttle_right_motor;                     //Load the next throttle_right_motor variable
    if(throttle_right_motor_memory < 0){                                    //If the throttle_right_motor_memory is negative
      PORTD &= 0b11011111;                                                   //Set output 3 low to reverse the direction of the stepper controller
      throttle_right_motor_memory *= -1;                                    //Invert the throttle_right_motor_memory variable
    }
    else PORTD |= 0b00100000;                                               //Set output 3 high for a forward direction of the stepper motor
  }
  else if(throttle_counter_right_motor == 1)
   {PORTD |= 0b10000000;             //Set output 6 high to create a pulse for the stepper controller
   if(PIND & 0b00100000)stpR++;
   else stpR--;
  }           
  else if(throttle_counter_right_motor == 2)PORTD &= 0b01111111;            //Set output 4 low because the pulse only has to last for 20us

}

the only reason i can think of is that the timer interrupt happens between these two lines

  throttle_left_motor = left_motor;
  throttle_right_motor = right_motor;

so one value of them is updated while the other hasn't, is there a way to make sure the 2 lines are executed together? (in other words how to prevent the timer interrupt from executing if only the first line is executed)

i am not even sure that this is what's causing the problem.... TIA

You could try disabling interrupts for those two lines to see if it makes a difference, ie:

  noInterrupts();
  throttle_left_motor = left_motor;
  throttle_right_motor = right_motor;
  interrupts();

unfortunately it doesn't work i guess i was wrong , will try to figure it out thanks though

If you switch the motors over does the issue stay with that motor, or with the channel?

the motors are not connected right now i am using serial monitor to see number of steps
image

for some time the number of steps in both motors are the same then as you can see left motor steps starts to increase more than the right motor even though both are given same signal
image

Where did you set the pins as output? You never touch DDRD. So they're inputs and you're reading noise at this line in the cases where the PORT bit isn't set enabling the pullup.

1 Like

i totally forgot when i was writing this version of the sketch but i did set them as outputs in the original sketch, still that's not the problem since it happens in the main sketch as well.

something very strange is happening i wrote

if(stpL!=stpR)
Serial.println(10);

in the interrupt section and it didn't print anything, it seems like number of steps of both motors are equal in the interrupt part and it only changes in the void loop ... does anyone have explanation ?
both steppers steps counter variables (stpR and stpL) are defined as long ,thought its worth mentioning.

If you think so. I think you should give that a try. Because it is definitely not going to work right in the code you posted. It will cause the two motors to occasionally count differently. Isn't that the problem you see?

ISR(TIMER2_COMPA_vect){
  //Left motor pulse calculations
  throttle_counter_left_motor ++;                                           //Increase the throttle_counter_left_motor variable by 1 every time this routine is executed
  if(throttle_counter_left_motor > throttle_left_motor_memory){             //If the number of loops is larger then the throttle_left_motor_memory variable
    throttle_counter_left_motor = 0;                                        //Reset the throttle_counter_left_motor variable
    throttle_left_motor_memory = throttle_left_motor;                       //Load the next throttle_left_motor variable
    if(throttle_left_motor_memory < 0){                                     //If the throttle_left_motor_memory is negative
      PORTD &= 0b11111011;                                                  //Set output 5 low to reverse the direction of the stepper controller
      throttle_left_motor_memory *= -1;                                     //Invert the throttle_left_motor_memory variable
    }
    else PORTD |= 0b00000100;                                               //Set output 5 high for a forward direction of the stepper motor
  }
  else if(throttle_counter_left_motor == 1)
  {PORTD |= 0b00001000;             //Set output 6 high to create a pulse for the stepper controller
   if(PIND & 0b0000100)stpL++;
   else stpL--;
  }
  else if(throttle_counter_left_motor == 2)PORTD &= 0b11110111;             //Set output 6 low because the pulse only has to last for 20us 
  
  //right motor pulse calculations
  throttle_counter_right_motor ++;                                          //Increase the throttle_counter_right_motor variable by 1 every time the routine is executed
  if(throttle_counter_right_motor > throttle_right_motor_memory){           //If the number of loops is larger then the throttle_right_motor_memory variable
    throttle_counter_right_motor = 0;                                       //Reset the throttle_counter_right_motor variable
    throttle_right_motor_memory = throttle_right_motor;                     //Load the next throttle_right_motor variable
    if(throttle_right_motor_memory < 0){                                    //If the throttle_right_motor_memory is negative
      PORTD &= 0b11011111;                                                   //Set output 3 low to reverse the direction of the stepper controller
      throttle_right_motor_memory *= -1;                                    //Invert the throttle_right_motor_memory variable
    }
    else PORTD |= 0b00100000;                                               //Set output 3 high for a forward direction of the stepper motor
  }
  else if(throttle_counter_right_motor == 1)
   {PORTD |= 0b10000000;             //Set output 6 high to create a pulse for the stepper controller
   if(PIND & 0b00100000)stpR++;
   else stpR--;
  }           
  else if(throttle_counter_right_motor == 2)PORTD &= 0b01111111;            //Set output 4 low because the pulse only has to last for 20us

}

All the variables modified within the isr should be declared as volatile.

For example

//int  throttle_counter_left_motor;
volatile int throttle_counter_left_motor;
1 Like

I would suggest you move most of the calculations out of the ISR routine. Long ISR's are not recommended.

May be overkill but I would disable interrupts when you are setting

  //Copy the pulse time to the throttle variables so the interrupt subroutine can use them
  throttle_left_motor = left_motor;
  throttle_right_motor = right_motor;

also look through your other code for places that would disrupt your counts if the ISR were to be run at that location.

It might be beneficial for you to purchase one of the low cost logic analyzers from eBay. You could use unused output pins to measure what is going on.

1 Like

it worked ! thanks a lot

i actually tried right after i read your reply but unfortunately with no success , it turns out to be the way i was defining the variable didn't know that i should use volatile before long for variables used in both ISR and void loop, thanks for your time though.