Servo Jerks during sweep of servo values

I currently have a PWM generator plugged into pin 2 on my Arduino and I am attempting to read the value of the PWM signal and output a value of the same duty cycle out to a servo motor.

Currently I have an interrupt for when the signal of the PWM generator goes high, I then read the value from TCNT1 and wait till the signal goes low, then read the value of TCNT1 again to get the high time of the PWM wave. I have calculated the 100% duty cycle value and the 0% duty cycle value and use that to calculate the current % duty cycle.

This code currently works and smoothly sweeps, but every 17 seconds on the nose, it freezes for a few seconds and then jumps to the current value. At first I thought maybe it was something interrupting but I disable all interrupts after the PWM interrupt triggers. I'm guessing it has to do somehow with the way my functions are working together (there isnt anything in my loop). Any help will be awesome, here is my interrupt function:

#include "mavlink.h"
#include <Servo.h>

Servo ServoOUT;

//PWM variables
volatile float start_time = 0;
volatile float final_time = 0;
volatile double duty_cycle = 0;

void setup() {
  
  TCCR1A = 0;//timer1 in normal mode
  TCCR1B = 0;//timer1 in normal mode
  TCCR1B |= (1<< CS10);//start the timer with no prescaler, running at system speed of 16MHz
  pinMode(2, INPUT);
  ServoOUT.attach(11,900,2100);
  attachInterrupt(0, servoDriver, RISING); // attachInterrupt allows rising signal from pin 2 to cause an interrupt in the processor, this allows the PWM signal to be read
}

void loop() {

}

void servoDriver() {

  cli(); //disable all interrupts
  start_time = TCNT1; //read high start
  while(digitalRead(2)); //wait till PWM goes LOW
  final_time = TCNT1; // read high end
  duty_cycle = final_time - start_time; // calc high time
  duty_cycle = (duty_cycle - 14342)/(33580-14342); // calc duty cycle %
 if (duty_cycle < 0){} //filter out bad values
 else if ( duty_cycle > 100){} // filter out bad values
else {
    ServoOUT.writeMicroseconds(duty_cycle * 1000 + 1000); //write to servo
 }
  TCNT1 = 0x0000; //reset timer
  sei(); //enable interrupts


}
void servoDriver() {

  cli(); //disable all interrupts

Interrupts are already disabled.

else {
    ServoOUT.writeMicroseconds(duty_cycle * 1000 + 1000); //write to servo
 }

The servo library relies on interrupts being enabled.

  sei(); //enable interrupts


}

When an ISR ends, interrupts are re-enabled automatically.

Thank you very much! If the servo library relies on interrupts how can I make sure nothing interrupts the servo in the middle of its sweeping? :slight_smile:

I think you may have missed the fact that servo signals are specialised (not real PWM) and work with duty cycles of roughly 5 to 10 percent. (1-2ms in 20ms cycle). They never get anywhere near 0 or 100 percent.

Also you don't want to do any serious work in an ISR. ISRs must be as short as possible. Use them to save times and maybe set event flags then have some main code in loop() do the real work based on them.

Steve

Some useful rules for using interrupts:

  1. Interrupt routines should be as short and fast as possible

  2. Don't do calculations or I/O (other than setting an output pin state) in an interrupt routine

  3. Do not wait for anything in an interrupt

  4. Communicate with the main routine using the smallest integer variable possible, not floats

  5. Use digitalPinToInterrupt() in attachInterrupt()

  6. Multbyte variables shared with interrupt routines must be protected from corruption by making a copy, with the interrupts turned off(*)

  7. Since the external interrupts can respond to RISING or FALLING, use that feature.

(*) In main, if needed

cli();
interrupt_variable_copy = shared_interrupt_variable;
sei();