PDA

View Full Version : Discussion Problems controlling servo with 16F 877A


JF1980
Feb 25, 2009, 11:20 AM
Hi all,

I've been trying to move some servos around with a PIC 16F877A. I understand the principles of driving a servo but my code doesn't seem to work -- being a 'C' newbie I suspect I may have made a few coding errors although the program compiles. I'm using two CCP modules to generate servo on/off interrupts.

Hopefully someone can tell me what I've done wrong:

/*
* Project name:
MoveServo1
* Description:
Move Servo with TMR0/1 Interrupts.
* Test configuration:
MCU: PIC16F877A
Dev.Board: EasyPic4
Oscillator: HS, 08.000 MHz
Ext. Modules: -
SW: mikroC v7.0
* NOTES:

Drive up to 8 servos on PortB using CCP1, CCP2 and TMR1

Configure TMR1 for 1us ticks
CCP1 is set up for special event on match with TMR1
CCP1 is loaded with servo cycle length = 20000us / number of servos; 2500us for 8 servos. It triggers an interrupt and resets TMR1.
CCP1 triggers every servo cycle resetting TMR1, moves program to next servo via single left bitshift, turns turns that servo on.

CCP2 is set up for a normal event which does not reset the timer.
CCP2 value is loaded from the servo position array when the servo is switched on via CCP1 interrupt service.
CCP2 triggers when the servo-on cycle has been completed.
CCP2 interrupt service switches off servo output (all of PORTB) and clears the CCPIR2.0 interrupt flag.




*/


// Define variables

#define ServoOnIntEnable PIE1.F2
#define ServoOnFlag PIR1.F2
#define ServoOffIntEnable PIE2.F0
#define ServoOffFlag PIR2.F0
//#define ServoPeriod CCPR1L
//#define ServoDuty CCPR2L

unsigned int volatile ServoPeriod absolute 0x0015; // CCPR1L
unsigned int volatile ServoDuty absolute 0x001B; // CCPR2L
unsigned int Servo[8]; // servo position array
unsigned int xx ; // var for test loop
unsigned short Servo_Output; // Shadow register for Output
unsigned short sn ; // Servo number, to ref Servo array

// Define functions

void init(void) ;

// Main

void main(void) {

init();

while (1) {
for (xx = 350; xx <= 2400; xx++){ // cycle servos
Servo[0] = 500;
Servo[1] = xx;
Servo[2] = xx;
Servo[3] = xx;
Servo[4] = xx;
Servo[5] = xx;
Servo[6] = xx;
Servo[7] = 2400;
Delay_us(5500);
}
}
}


// Setup CPU

void init(void) {

PORTB = 0 ; // servo port is portB
TRISB = 0 ; // set portB as output
T1CON = 0b00010000 ; // prescaler = 2 = 1us ticks ; timer1 off
TMR1L = 0 ; // Timer1L starts at 0
TMR1H = 0 ; // Timer1H starts at 0
CCP1CON = 0b00001011 ; // CCP1 triggers special compare event on Timer1 match
CCP2CON = 0b00001010 ; // CCP2 normal compare event on Timer1 match
ServoPeriod = 2500 ; // special event interrupt every 2500us; jumps to ServoON ISR
Servo_Output = 0b00000001 ; // servo outputs start at PORTB.F0, then shift and rotate
sn = 0 ; // start with servo(0)
INTCON = 0b11000000 ; // enable GIE and PEIE
ServoOnFlag = 0 ;
ServoOffFlag = 0 ;
ServoOnIntEnable = 1 ;
ServoOffIntEnable = 1 ;
T1CON.F0 = 1; // start Timer1
}


// Interrupt

void interrupt(void) {

if (ServoOnFlag == 1) { // CCP1 Special Compare Event Interrupt
PortB = Servo_Output ; // Turn On Next Servo
ServoDuty = Servo[sn] ; // Active Servo Value loaded in CCPR2
Servo_Output = Servo_Output << 1 ; // Prepare Servo_Output for next cycle with bitshift
sn++ ;
//sn = sn And 7 ;
sn = sn & 7 ;
ServoOnFlag = 0 ;
}
else { // CCP2 Normal Compare Event Interrupt
PORTB = 0 ; // Turn Off Active Servo
ServoOffFlag = 0 ;
}
}

JF1980
Feb 25, 2009, 10:20 PM
Forgot to reset Servo_Output after each cycle!

vpatron
Feb 26, 2009, 01:40 AM
Glad you figured it out! I've found that it helps to get really good at using the simulator including setting up stimulus and all that.

I had to develop code for the 8-pin PIC which didn't have enough pins for in-circuit debugging, and didn't really get good at using the simulator until now when I had to really rely on it.

-Vince

JF1980
Feb 26, 2009, 04:59 AM
Yeah, very frustrating but working now and moving my first servo is a milestone.

I did use the Mikro debugger but I mustn't have gone through a complete cycle. The debugger requires you to manually make the interrupt happen by setting the required interrupt flag and then telling it to jump to the interrupt routine.

Proteus is a good sim but it takes such a long time to set up the virtual circuit.