Go Down

Topic: Rotary encoder with interrupts interferes with servo (Read 984 times) previous topic - next topic

njgedr

I am using an uno board to control a servo and read a 64 cpr rotary encoder on 131:1 motor at the same time. I originally had the encoder working using interrupt pins 2 and 3. It worked great until I tried to attach a servo to one of the other pins, I tried all of them. When the arduino read the rotary encoders square wave, it messed up the pwm wave sent to the servo. I hooked a scope up to the servo signal wire and confirmed that whenever I attached the encoder, specifically pin 3, the servo signal would turn to noise. I gave up and switched to a different rotary encoder reading method that didn't use interrupts, and the servo worked fine. Now the encoder cant keep up with motor at higher speeds.

I am building a 1:5 scale car that autonomously parks for Virginia Tech.


retrolefty

The thing to keep in mind when using user interrupt routines is that while your servicing your ISR function all other interrupts are disabled. So the rule is to keep ISR functions as simple and short as possible. As the servo function will automatically try and keep updating the servo pulse every 20msec or so, you must try and prevent missing servo updates because of another ISR routine taking too much time.

Perhaps if you posted the two ISR functions you wrote to service pins 2 and 3 we can see if there is room for improvement.

Lefty

njgedr

Ignore all the comments
This is when the encoder was running off two interrupts. I think I am going to try to do it with just one interrupt.


#include <Servo.h>

Servo myservo;  // create servo object to control a servo
//int val;    // variable to read the value from the analog pin 



#define encoder0PinA 2
#define encoder0PinB 3
//int D2 = 5;
//int EN = 6; 
int D1 = 5;
int D2 = 6; ///previously inv pin 7
//int invertswitch = 8;
//int motorPin2 = 8;
//Servo Pin 9
//int motorPin1 = 10;

//analog pins
//int Pot = 0; //MOTOR
//int potpin = 1; //SERVO

volatile unsigned int encoder0Pos = 0;
//const int inc = 1;
//volatile int encoder0Pos = 0.0;
//const int inc = 1.0;
 
void setup()
{

  myservo.attach(9);  // attaches the servo on pin 9 to the servo object 
  pinMode(encoder0PinA, INPUT);
// digitalWrite(encoder0PinA, HIGH);
  pinMode(encoder0PinB, INPUT);
// digitalWrite(encoder0PinB, HIGH);
// encoder pin on interrupt 0 (pin 2)
  attachInterrupt(0, doEncoderA, CHANGE);
// encoder pin on interrupt 1 (pin 3)
  attachInterrupt(1, doEncoderB, CHANGE); 
  Serial.begin (115200);
  //pinMode (EN, OUTPUT);
// pinMode (motorPin1, OUTPUT);
  //pinMode (motorPin2, OUTPUT);
  //pinMode (D2, OUTPUT);
  pinMode (D1, OUTPUT);
  pinMode (D2, OUTPUT); // previously INV
  //pinMode (Pot, INPUT);
  //pinMode (invertswitch, INPUT);
}
  //int getPot() {           //MOTOR
  //int v;                   //MOTOR
// v = analogRead(Pot);     //MOTOR
// v /= 4;                  //MOTOR
// v = max(v,0);            //MOTOR
// v = min(v,255);          //MOTOR
// return v;                //MOTOR
                 }
void loop(){
  //val = analogRead(potpin);            // SERVO reads the value of the potentiometer (value between 0 and 1023)
  //val = map(val, 0, 1023, 0, 179);     // SERVO scale it to use it with the servo (value between 0 and 180)
  myservo.write(90);                  // SERVO CONTROL 0 TO 90 ...should be val SERVO sets the servo position according to the scaled value
  //delay(15);                         // SERVO waits for the servo to get there
  //digitalWrite (motorPin1, HIGH);
  //digitalWrite (motorPin2, LOW);
  //digitalWrite (D2, HIGH);
  analogWrite  (D1, 127);  //should be GETPOT MOTOR CONTROL PWM
  analogWrite (D2, 127); // MOTOR CONTROL PWM
 
//  if (digitalRead(invertswitch)==HIGH)
//  {
//      digitalWrite (INV, LOW);
//    }
//  else
//  {
//   digitalWrite (INV, HIGH)                                                                              ;
// }

  //digitalWrite (EN,HIGH);
 


}

void doEncoderA()
{
  // look for a low-to-high on channel A
  if (digitalRead(encoder0PinA) == HIGH) {
    // check channel B to see which way encoder is turning
    if (digitalRead(encoder0PinB) == LOW) { 
      encoder0Pos = encoder0Pos + 1;         // CW
    }
    else {
      encoder0Pos = encoder0Pos - 1;         // CCW
    }
  }
  else   // must be a high-to-low edge on channel A                                       
  {
    // check channel B to see which way encoder is turning 
    if (digitalRead(encoder0PinB) == HIGH) {   
      encoder0Pos = encoder0Pos + 1;          // CW
    }
    else {
      encoder0Pos = encoder0Pos - 1;          // CCW
    }
  }
  Serial.println (encoder0Pos, DEC);         
  // use for debugging - remember to comment out
}
           

void doEncoderB(){
  // look for a low-to-high on channel B
  if (digitalRead(encoder0PinB) == HIGH) {   
   // check channel A to see which way encoder is turning
    if (digitalRead(encoder0PinA) == HIGH) { 
      encoder0Pos = encoder0Pos + 1;         // CW
    }
    else {
      encoder0Pos = encoder0Pos - 1;         // CCW
    }
  }
  // Look for a high-to-low on channel B
  else {
    // check channel B to see which way encoder is turning 
    if (digitalRead(encoder0PinA) == LOW) {   
      encoder0Pos = encoder0Pos + 1;          // CW
    }
    else {
      encoder0Pos = encoder0Pos - 1;          // CCW
    }
  }
  Serial.println(encoder0Pos, DEC);
}

wildbill

Get rid of the print statements in the interrupt routines - serial comms is slow. If you need to see the values for debug, store them in a global & set a flag in the interrupt that you can check in loop to see if it's time to print.

njgedr

That helped alot, Still cant run the motor at full speed though

retrolefty


That helped alot, Still cant run the motor at full speed though


Well you two pwm commands in the sketch you posted are only commanding for 50% speed:

Quote

analogWrite  (D1, 127);  //should be GETPOT MOTOR CONTROL PWM
analogWrite (D2, 127); // MOTOR CONTROL PWM

robtillaart

Simplify the reading of the encoder without losing too much. You don't need to have two IRQ's.

The encoder below does measure BOTH direction and the position by comparing the state of both pins with each other.
For position the code uses a signed long so one has a distance/direction indication since start.
e.g. if pos = -1000, we know the sytem did more CCW as CW since start.

This encoder is very small and will interfere minimal with your servo, give it a try..

(code compiles but not tested)
Code: [Select]

#define encoder0PinA 2
#define encoder0PinB 3

volatile long pos = 0;         // long can hold far more pulses than int; signed to know direction since start.
volatile boolean CW = true;    // direction if false it is CCW

void setup()
{
 // SERIAL COM
 Serial.begin (115200);
 Serial.println("Start app");

 // CONFIG IRQ
 pinMode(encoder0PinA, INPUT);
 attachInterrupt(0, doEncoder, RISING);    // CHANGE will work too but gives you twice the amount of irq's! Do you need that much?
}

void loop()
{
 // OUTPUT ENCODER INFO
 Serial.print("pos:  ");
 Serial.println(pos);
 Serial.print("direction 0=CCW   1=CW:  ");  
 Serial.println(CW);

 delay(250);  // simulate other activities.
}

void doEncoder()
{
 // determine direction
 CW = (digitalRead(encoder0PinB) != digitalRead(encoder0PinA));  

 // update position
 if (CW)  pos++;
 else pos--;
}
Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

njgedr

I ended up reading the encoder off just one interrupt (rising), and this fixed the problem. For direction I am just using an if statement with my motor controller input. Trying to send the serial data back to the computer was what was causing the delay. Everything is working well now, thanks for the help.

Go Up