Servo motor interface with PIC - Page 1
Forum section: electronics / general

author: Udayan Sinha
author_rank: Regular Contributor
timestamp: October 03, 2014, 06:47:18 pm
content: I am trying to create a servo motor control library for PIC to control up to 4 servos (standard 180 deg rotation types). I wrote the code for sending the pulse (1-2ms) according to the angle entered and tested it out. The problem is that the servo arm is oscillating (for eg. if I enter 90 deg, the servo keeps moving back and forth all the way from 0 deg to 90 deg).I am using two 16-bit timers(1 for the servo pulse and the other for timing the 20ms delay). I am using a 20Mhz crystal for the PIC. I checked the pulse timing using the PICkit-2 logic tool and it seems perfectly fine so I can't figure out where I have gone wrong. Please help me out.My code://timer values for servo angle controlunion servo1{unsigned char srv1;unsigned int srv1_16;}servo_1;union servo2{unsigned char srv2;unsigned int srv2_16;}servo_2;union servo3{unsigned char srv3;unsigned int srv3_16;}servo_3;union servo4{unsigned char srv4;unsigned int srv4_16;}servo_4;void timer_init(void);void servo_init(unsigned char number_of_servos);void interrupt_init(void);void interrupt isr_h(void);void interrupt low_priority isr_l(void);void timer0_isr(void);void timer3_isr(void);void servo_move(unsigned char angle, unsigned char servo_number);unsigned char iteration_count=0, no_of_servos=1;void main(){servo_init(no_of_servos); //initialize servo controlinterrupt_init(); //initialize interruptsservo_move(180, 1); //rotate servo motor 1 by 90degwhile(1);}//servo motor control initialize functionvoid servo_init(unsigned char number_of_servos){timer_init(); //initialize timer 0//PORTD used for servo motor controlswitch(number_of_servos){case 1:TRISD=0xFE; //1 servo motor is usedbreak;case 2:TRISD=0xFC; //2 servos motors are usedbreak;case 3:TRISD=0xF8; //3 servos motors are usedbreak;case 4:TRISD=0xF0; //4 servos motors are usedbreak;}}//servo motor control functionvoid servo_move(unsigned char angle, unsigned char servo_number){switch(servo_number){case 1:servo_1.srv1_16=60536-(27.77*angle);break;case 2:servo_2.srv2_16=60536-(27.77*angle);break;case 3:servo_3.srv3_16=60536-(27.77*angle);break;case 4:servo_4.srv4_16=60536-(27.77*angle);break;}//start timerTMR3ON=1;TMR0ON=1;}//timer initialize functionvoid timer_init(void){//timer 0T08BIT=0; //timer used in 16-bit modeT0CS=0; //use as timerPSA=1; //prescaler not used//initialize timer 0 registersTMR0H=0xFF;TMR0L=0xFF;TMR0ON=0; //timer 0 is off for now//timer 3T3CON|=0x80; //RD16=1; timer registers accessed together//1:8 prescalerT3CKPS0=1;T3CKPS1=1;TMR3CS=0; //use as timerTMR3ON=0; //timer 3 is off for now//initialize timer 3 registersTMR3H=0xCF;TMR3L=0x2C;//initialize servo angle values to 0degservo_1.srv1_16=0xFFFF;servo_2.srv2_16=0xFFFF;servo_3.srv3_16=0xFFFF;servo_4.srv4_16=0xFFFF;}//interrupt initialize functionvoid interrupt_init(void){IPEN=1; //enable interrupt priorityGIEH=1; //enable high priority interruptsGIEL=1; //enable low priority interruptsTMR0IF=0; //clear interrupt flagTMR0IE=1; //enable timer 0 interruptTMR0IP=0; //low priority interrupt on timer 0TMR3IF=0; //clear interrupt flagTMR3IE=1; //enable timer 3 interruptTMR3IP=1; //high priority interrupt on timer 3}//high priority ISR functionvoid interrupt isr_h(void){asm("CALL _timer3_isr");}//low priority ISR functionvoid interrupt low_priority isr_l(void){asm("CALL _timer0_isr");}//timer 0 ISRvoid timer0_isr(void){TMR0ON=0; //turn off timerTMR0IF=0; //clear interrupt flagif(iteration_count<no_of_servos) //signals need to be sent to servo motors{switch(iteration_count){case 0:TMR0H=servo_1.srv1;TMR0L=servo_1.srv1;iteration_count++;RD0=1;break;case 1:RD0=0;TMR0H=servo_2.srv2;TMR0L=servo_2.srv2;iteration_count++;RD1=1;break;case 2:RD1=0;TMR0H=servo_3.srv3;TMR0L=servo_3.srv3;iteration_count++;RD2=1;break;case 3:RD2=0;TMR0H=servo_4.srv4;TMR0L=servo_4.srv4;iteration_count++;RD3=1;break;}TMR0ON=1; //start timer again}else //all servos motors have been serviced{switch(no_of_servos){case 1:RD0=0;break;case 2:RD1=0;break;case 3:RD2=0;break;case 4:RD3=0;break;}}}//timer 3 ISRvoid timer3_isr(void){TMR3ON=0; //turn off timerTMR3IF=0; //clear interrupt flag//restart processiteration_count=0;//reload timer registersTMR3H=0xCF;TMR3L=0x2C;TMR0H=0xFF;TMR0L=0xFF;//turn on timersTMR3ON=1;TMR0ON=1;}Please let me know if you need any other details.

author: weky
author_rank: Newbie
timestamp: October 15, 2014, 11:53:15 pm
content: Hi! Can you tell me how much time is between two pulses? I was working on some project with RC servos so I can help.... But it's too late for me to go thru code now....

author: Udayan Sinha
author_rank: Regular Contributor
timestamp: October 16, 2014, 04:59:27 am
content: Quote from: weky on October 15, 2014, 11:53:15 pmPulses must be sent every 20ms. Please do let me know if you get a working code.

author: dannyf
author_rank: Super Contributor
timestamp: October 16, 2014, 10:31:47 am
content: QuoteWhat does that mean?QuoteThe issue is fairly simple: articulate what you expect (why you think your code will produce the expected results) and show what you actually got.

author: Udayan Sinha
author_rank: Regular Contributor
timestamp: October 16, 2014, 11:39:27 am
content: Quote from: dannyf on October 16, 2014, 10:31:47 amQuoteAlready done that. Read description carefully.

author: Udayan Sinha
author_rank: Regular Contributor
timestamp: October 17, 2014, 10:01:58 am
content: Got it working guysGuess what the problem was: a faulty connection on a crusty breadboard

author: dannyf
author_rank: Super Contributor
timestamp: October 17, 2014, 03:00:50 pm
content: Here is my implementation - a multi-channel, synchronized pwm generator using just 1 timer (timer0 in this case).In this particular case, I implemented four independent pwm channels (PB0, 1, 7 and 6), over a 20ms period.It can be easily expanded to generate additional channels (or fewer if you want). Very little overhead - the glitches in PB4 are due to when the timer isr is being serviced - 20us in the pro mode and slightly higher in the free mode.Very handy for low-end pics that lack a hardware pwm module.Invoking it is easy:Code:pwm_init(TMR0_PS_2x, 160);//reset the pwm period, roughly 2 * 160 * 256 * 1/4Mhz=20mspwm0_setdc(1<<0, 6);//pwm ch0, on pin P0, dc=6/160pwm1_setdc(1<<1, 7);//pwm ch1, on pin P1, dc=7/160pwm2_setdc(1<<7, 8);//pwm ch2, on pin P7, dc=8/160pwm3_setdc(1<<6, 18);//pwm ch3, on pin P6, dc=18/160After that, everything is done in the background and fully transparent to the user.