Rotary Encoder Interrupt routine interfering with Servo.h

I have developed some basic code to drive a motor and read the attached encoder. The Servo.h code works by itself, but not with the interrupt routine used to read the encoder. The interrupt routine is very simple, but causes significant changes in the PWM signal sent to the motor resulting in sporadic speed. I have tried several different servo libraries, but could not get any others to work.

#include <Servo.h>

#define resolution 5120

Servo motor;

int ReadPWM = 2;
unsigned long duration;
int EncA = 3;
int EncB = 4;
int indicate_pin = 13;
volatile float count = 0;
long angle = 0;

int motorPWM = 6;
int motorSpeed = 90;
int accel = 8;
int decel = 9;

float velocity = 0;
unsigned long V_start = 0;
unsigned long V_elapse = 0;
float V_diff = 0;

// the setup routine runs once when you press reset:
void setup() {
  // initialize serial communication at 9600 bits per second:
  Serial.begin(9600);
  // make the pushbutton's pin an input:
  pinMode(ReadPWM, INPUT);
  pinMode(EncA, INPUT);
  pinMode(EncB, INPUT);
  pinMode(accel, INPUT);
  pinMode(decel, INPUT);
  motor.attach(motorPWM);
  attachInterrupt(digitalPinToInterrupt(EncA), Quadrature, RISING);
}

// the loop routine runs over and over again forever:
void loop() {

  motor.write(motorSpeed);
  if (digitalRead(accel) == HIGH){
    motorSpeed += 1;
    delay(50);
  }
    
  if (digitalRead(decel) == HIGH){
    motorSpeed -= 1;
    delay(50);
  }

  
  duration = pulseIn(ReadPWM, HIGH);
  angle = (count/resolution)*360;
  V_start = micros();
  delay(100);
  V_diff = angle-((count/resolution)*360);
  V_elapse = micros()-V_start;
  velocity = (V_diff/V_elapse)*1000000;
  
  Serial.print(motorSpeed);
  Serial.print("  ");
  Serial.println(duration);
  
}

void Quadrature() {
  if (digitalRead(EncB) == HIGH)
  {
    --count;
  }
  else
  {
    ++count;
  }

}

Not sure what you are hoping for here. Yes the two interrupt routines will interfere with each other. The only chance is to incorporate the two functions into one interrupt routine using a state machine, but it is a bit of a kludg.

The other thing is to make the servo signal with external hardware like a PCA9685.

It sounds like Servo.h will not work for me in this application because it also relies on interrupts. Some of the other Servo libraries claim to not use interrupts, but I was unable to get any of them to work for me. they all appear to be rather dated as well. Are there any new Servo libraries that I may have missed?

Servo uses a timer interrupt and your code uses a hardware pin interrupt. I don't see why the two would conflict.

I read the pulse width to get an idea of what is happening. at servo.write(90) the pulse width is as expected, but as the position is lowered the pulse width decreases, but also, the period decreases dramatically until the following results:

(values are in microseconds)
(Low)(High)

1349 186
819 46
1188 12
949 99
825 100
68 13
1679 41
1400 35
324 11
949 86
1372 89
1177 79
216 37
505 38
886 112
1676 196
731 28
1190 77
926 120
1347 136
1006 119
455 14
// increasing the command position
904 35
1787 140
1783 131
1932 114
2827 293
3991 380
4422 457
4954 489
5644 492
6592 609
7079 638
7606 703
8391 755
8165 764
9049 800
9607 873
10144 929
10736 959
11516 985
11704 1070
12305 1069
12923 1149
13559 1215
14660 1255
14964 1209
15206 1306
15188 1270
15188 1281
15770 1330

The worst glitching starts happening around servo.write(50) or so which corresponds with a period dropping below 2 milliseconds which is the minimum period recognized by the motor controller.

oddly enough the pulse widths behave similarly when the command position is increased. I suspect there is a lot of noise introduced in the PWM signal. Perhaps the hardware interrupt is causing a low in the PWM signal for a brief moment?

Perhaps the hardware interrupt is causing a low in the PWM signal for a brief moment?

no it is not, why would it?

The only problem you can get is time jitter, because when an ISR is running then you can't interrupt it and the second ISR has to wait until the first one has finished.

my thought on the momentary low attempts to explain why the pulsein() function would read the same pulse width when the servo position is >90 and <90 but the motor controller still interprets the signal as a forward and reverse signal. Without an O-scope it is a bit of a guessing game.

the problem “njgedr” describes in http://forum.arduino.cc/index.php?topic=67187.0 is similar but I think a different cause. he describes a noisy PWM signal as soon as the encoder is hooked up. I think that is happening here, but my ISR is much cleaner. The only way I can think to streamline my ISR is to bit mask the io register directly instead of reading the pin, but I don’t think the length of the ISR is a major factor.

Can you post the code that gave the output in #4?

all I changed from the code above was the addition of a second unsigned long variable for the lOW pulse length.
When i initially found this problem, I commented out the pulseIn() line because I thought it might be messing with the PWM output timing, but the effect did not go away.

unsigned long Hduration;
unsigned long Lduration;
...
Hduration = pulseIn(ReadPWM, HIGH);
Lduration = pulseIn(ReadPWM, LOW);

Delta_G:
Can you post the code that gave the output in #4?

So that will be a no then?

As I said, there was very little changed. The addition of the second pulseIn() line did not affect the behavior.

#include <Servo.h>

#define resolution 5120

Servo motor;

int ReadPWM = 2;
unsigned long Hduration;
unsigned long Lduration;
int EncA = 3;
int EncB = 4;
int indicate_pin = 13;
volatile float count = 0;
long angle = 0;

int motorPWM = 6;
int motorSpeed = 90;
int accel = 8;
int decel = 9;

float velocity = 0;
unsigned long V_start = 0;
unsigned long V_elapse = 0;
float V_diff = 0;

// the setup routine runs once when you press reset:
void setup() {
  // initialize serial communication at 9600 bits per second:
  Serial.begin(9600);
  // make the pushbutton's pin an input:
  pinMode(ReadPWM, INPUT);
  pinMode(EncA, INPUT);
  pinMode(EncB, INPUT);
  pinMode(accel, INPUT);
  pinMode(decel, INPUT);
  motor.attach(motorPWM);
  attachInterrupt(digitalPinToInterrupt(EncA), Quadrature, RISING);
}

// the loop routine runs over and over again forever:
void loop() {

  motor.write(motorSpeed);
  if (digitalRead(accel) == HIGH){
    motorSpeed += 1;
    delay(50);
  }
    
  if (digitalRead(decel) == HIGH){
    motorSpeed -= 1;
    delay(50);
  }

  
  Hduration = pulseIn(ReadPWM, HIGH);
  Lduration = pulseIn(ReadPWM, LOW);
  angle = (count/resolution)*360;
  V_start = micros();
  delay(100);
  V_diff = angle-((count/resolution)*360);
  V_elapse = micros()-V_start;
  velocity = (V_diff/V_elapse)*1000000;
  
  Serial.print(Lduration);
  Serial.print("  ");
  Serial.println(Hduration);
  
}

void Quadrature() {
  if (digitalRead(EncB) == HIGH)
  {
    --count;
  }
  else
  {
    ++count;
  }

}

Grumpy_Mike:
The other thing is to make the servo signal with external hardware like a PCA9685.

I think that is my best option. for now I am using an Arduino Nano to drive the motor.