Pages: [1]   Go Down
Author Topic: Rotary encoder with interrupts interferes with servo  (Read 771 times)
0 Members and 1 Guest are viewing this topic.
Offline Offline
Newbie
*
Karma: 0
Posts: 4
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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.

Logged

Left Coast, CA (USA)
Online Online
Brattain Member
*****
Karma: 361
Posts: 17259
Measurement changes behavior
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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
Logged

Offline Offline
Newbie
*
Karma: 0
Posts: 4
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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);
}
Logged

New Jersey
Offline Offline
Faraday Member
**
Karma: 65
Posts: 3638
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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.
Logged

Offline Offline
Newbie
*
Karma: 0
Posts: 4
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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

Left Coast, CA (USA)
Online Online
Brattain Member
*****
Karma: 361
Posts: 17259
Measurement changes behavior
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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
Logged

Global Moderator
Netherlands
Offline Offline
Shannon Member
*****
Karma: 211
Posts: 13478
In theory there is no difference between theory and practice, however in practice there are many...
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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:
#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--;
}
Logged

Rob Tillaart

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

Offline Offline
Newbie
*
Karma: 0
Posts: 4
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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.
Logged

Pages: [1]   Go Up
Jump to: