PPM read and move servo without jitter

I’d just like to share some code to read a PPM signal and move a servo without jitter like other methods do. In this all signal processing is done with hardware interrupts and the result is a clean servo movement like it’s soupposed to be. The pulseIn() and servo library don’t produce such good results. My solution combines two codes, one I found here http://diydrones.com/profiles/blogs/705844:BlogPost:39393?id=705844%3ABlogPost%3A39393, and some from Arducopter source code. I made it for atmega328. The downside is we can use only one servo on pin 10 due to the limitations of atmega328.

unsigned int serinp[8]; //servo positions
int pulse = 0;

void setup_timer1(){
  //WGM -> Fast PWM
  //Prescaler -> 1/8
  //Compare Output Mode - > non-inverting mode
  //Input capture interrupt enable
  TIMSK1 &= ~( _BV(TOIE1) | _BV(ICIE1) | _BV(OCIE1A) | _BV(OCIE1B));
  TCCR1B &= ~(_BV(ICNC1));
  TCCR1A |= _BV(WGM11) | _BV(WGM10) | _BV(COM1A1) | _BV(COM1B1);
  TCCR1B |= _BV(WGM12) | _BV(WGM13) | _BV(ICES1);
  TCCR1B |= _BV(CS11);
  TCCR1B &= ~( _BV(CS12) | _BV(CS10) );
  TCCR1A &= ~( _BV(COM1A0) | _BV(COM1B0));
  TIMSK1 |= (1<<ICIE1);
  OCR1A = 0xFFFF;
  OCR1B = 0x0;
}

void setup()
{
  pinMode(8, INPUT); //ppm input
  pinMode(10, OUTPUT);  //servo output
  setup_timer1();
  Serial.begin(38400);
}

void loop()
{
  pulse = serinp[2]/2;  //PPM channel2. divide by 2 to get real pulsewidth
  setServoPulse(pulse);
  
  for(int i = 0; i < 8; i++)    //display all channels
   {
     Serial.print((serinp[i]/2));
     Serial.print("  ");
   }
   Serial.println();
}
 
 void setServoPulse(int pulseWidth)
{
   pulseWidth *= 2;
   if(pulseWidth > 1800 && pulseWidth < 4200)  //servo pulse 900-2100
   {  
     OCR1B = pulseWidth;    //pin D10(PB2)
   }
}
 
ISR(TIMER1_CAPT_vect){
   
  static unsigned int lasticr; //icr at last caputre
  static unsigned char cserinp; //current input servo

  unsigned int licr;

  licr = ICR1 - lasticr;
  lasticr = ICR1;
 
  if(licr > 5000)
  { //pulse too long, means start of new frame
    cserinp=0;
  }
  else if(licr > 1000)
  { 
    //pulse good, take reading, go to next channel
    serinp[cserinp]=licr;
    if(cserinp < 8)
    {
      cserinp++;
    }
  }
  else{
    //too short, nothing to see here
  }
 
}

Great. Just what I was looking for. i am using an ar6100e rexeiver and an freeduino with an atmel 328 chip.

But i think my timer is a little bit out :-/ i get only valuse in the "2000" area . Have you found any references to how to set these correctly ?

Vidar

Your code works great on the ATMega 328, but any chance you could change the timers/interrupts to make it work on the Arduino Mega? I've had a look myself, but it's a bit over my head at the mo... ;)