PIC Servo Controller
From Mech
Code
#include <18f4520.h>
#device ADC=10 // set ADC to 10 bit accuracy
#fuses HS,NOLVP,NOWDT,NOPROTECT
#use delay(clock=40000000)
#use rs232(baud=19200, UART1) // hardware uart much better; uses RC6/TX and RC7/RX
#define PanTorqueMax 650 // 650 corresponds to 100% duty cycle for driving the motor full on in one dirction
#define PanTorqueMin -650 // -650 corresponds to 0% duty cylce for driving the motor full on in the other direction
#define pGain 0 // This is the Proportional coefficient, adjust this first
#define iGain 0 // This is the Integral coefficient, adjust this second
#define dGain 0 // This is the Derivative coefficient, adjust this last
signed int32 rise,fall,pulse_width;
int32 msecs=0;
signed int32 PanTarget = 0;
signed int32 PanActual = 0;
signed int32 PanError, PanErrorLastTime=0, PanIntegral, PanDerivative, PanTorque=0;
int16 value;
int i=0;
#int_ccp1 // This is the CCP capture interrupt, this allows you to use one pin (CCP1) to capture both
// the rising edge and falling edge of the servo PWM signal, which leaves the other CCP pin
// open for drving the servo motor
void isr()
{
if (i==0) {output_high(PIN_A1);
rise = CCP_1;
setup_ccp1(CCP_CAPTURE_FE);
i=1;}
else if (i==1) {output_low(PIN_A1);
fall = CCP_1;
pulse_width = fall - rise;
setup_ccp1(CCP_CAPTURE_RE);
i=0;}
}
#INT_TIMER2 // designates that this is the routine to call when timer2 overflows
void Timer2isr() {
msecs++; // keep track of time
if ((msecs & 7) == 0) { // servo routine every 8ms is plenty. 125x/sec
output_high(PIN_B0);
PanTarget=pulse_width;
PanActual=read_adc();
PanActual=25*PanActual;
PanError = PanTarget - PanActual; // position error
PanIntegral += PanError;
PanDerivative = PanError - PanErrorLastTime;
PanErrorLastTime = PanError;
PanTorque = pGain * PanError + iGain * PanIntegral + dGain * PanDerivative;
TorqueShifted=PanTorque>>16;
if (PanTorque < PanTorqueMin) PanTorque = PanTorqueMin;
if (PanTorque > PanTorqueMax) PanTorque = PanTorqueMax;
if (PanActual<PanTarget){
output_low(PIN_C0);
set_pwm2_duty(PanTorque);
}
else if (PanActual>PanTarget){
output_high(PIN_C0);
set_pwm2_duty(650 + PanTorque);
}
output_low(PIN_B0);
}
}
void main() {
setup_timer_2(T2_DIV_BY_4, 156, 16); // clock at 16KHz, interrupt every 4*25nS * 4 * 156 * 16 = 1.0mS
setup_timer_1(T1_INTERNAL); //start timer 1
setup_adc_ports(AN0); // Enable analog inputs; This will read the ADC on pin AN0
setup_adc(ADC_CLOCK_INTERNAL);
setup_ccp2(CCP_PWM); // PWM output on CCP1/RC2, pin 17
setup_ccp1(CCP_CAPTURE_RE);
enable_interrupts(INT_CCP1);
enable_interrupts(INT_TIMER2);
enable_interrupts(GLOBAL);
//set_pwm2_duty(value); // h-bridge for pan motor from CCP2 (pin 16, RC1) to C0 (pin 16)
//output_high(PIN_C3);
while (TRUE) {
//This is useful for debugging using the RS-232 serial communication
printf("pulse width: %Lu Target: %Ld Actual: %Ld Torque: %Ld \r\n", pulse_width,PanTarget,PanActual,PanTorque);
}
}