Buenas,
Perdonar el retraso...
En mi código genero una interrupción cada 100us lo que nos da una precisión de 0.1ms a la hora de generar un pulso para el servo (1.4, 1.5, 1.7ms, etc.). Cada ese tiempo entro en la interrupción y voy contando el tiempo que servo1 lleva en alto y cuando se alcanza un máximo en el contador lo bajo y lo mismo esta vez en bajo.
He estado midiendo y obtengo una señal un pelín imprecisa, ya que en lugar de ser 1.5ms es más bien 1.6.... Trataré de ver lo que pasa...
Por otra parte he estado haciendo pruebas con otro "algoritmo" para manejar servos... Esta vez manejo 3 servos. Basicamente configuro cada vez que salta la interrupción de timer2 cuando será la siguiente interrupción (programando de nuevo timer2), es decir, entre los 3 servos cual necesita la atención antes. El problema es que la complejidad de hacerlo de esta manera se dispara y he de manejar una constante MARGEN_ERROR para calibrar el error producido entre que se lanza la interrupción y cambio el pulso del servo. Os pego el código por si a alguien le interesa:
#include "16f628a.h" //pic a utilizar
#use delay (clock=4000000) //Fosc=4Mhz
#fuses INTRC_IO,NOWDT,NOMCLR,NOPROTECT,NOLVP,NOBROWNOUT,NOPUT,NOCPD
#byte CMCON = 0x1F
/*Unas 25 instrucciones desde que se genera la interrupcion hasta que se cambia estado
y unas 75 instrucciones desde que se pone en alto hasta que se resetea el timer*/
#define MARGEN_ERROR 160
unsigned long pulso_servo1=1504;
unsigned long pulso_servo2=1800;
unsigned long pulso_servo3=1300;
int atender_contador=1;
unsigned long tiempo1=0;
unsigned long tiempo2=0;
unsigned long tiempo3=0;
void configurar_puertos();
void configurar_interrupciones();
void configurar_timers();
void establecer_interrupcion_para(int);
int get_postscaler(unsigned long);
int menortiempo();
#INT_TIMER2 // Interrupción del Timer2
void timer2_int() { // Timer2 incrementa cada 4us. Genera interrupción cada 100us
switch(atender_contador) {
case(1): //Atendemos servo1
//Actualizamos contadores
if(pulso_servo2!=0) tiempo2=tiempo2 - tiempo1 - MARGEN_ERROR;
if(pulso_servo3!=0) tiempo3=tiempo3 - tiempo1 - - MARGEN_ERROR;
if(input(PIN_B0)==0) {
//Si el pin esta bajo lo subimos
output_bit(PIN_B0, 1);
tiempo1=pulso_servo1;
} else {
//Si el pin esta alto lo bajamos
output_bit(PIN_B0, 0);
tiempo1=20000-pulso_servo1;
} //fin del if
//Cual atendemos ahora?
atender_contador=menortiempo();
establecer_interrupcion_para(atender_contador);
break;
case(2): //Atendemos servo2
//Actualizamos contadores
if(pulso_servo1!=0) tiempo1=tiempo1 - tiempo2 - MARGEN_ERROR;
if(pulso_servo3!=0) tiempo3=tiempo3 - tiempo2 - MARGEN_ERROR;
if(input(PIN_B1)==0) {
//Si el pin esta bajo lo subimos
output_bit(PIN_B1, 1);
tiempo2=pulso_servo2;
} else {
//Si el pin esta alto lo bajamos
output_bit(PIN_B1, 0);
tiempo2=20000-pulso_servo2;
} //fin del if
//Cual atendemos ahora?
atender_contador=menortiempo();
establecer_interrupcion_para(atender_contador);
break;
case(3):
//Atendemos servo3
//Actualizamos contadores
if(pulso_servo1!=0) tiempo1=tiempo1 - tiempo3 - MARGEN_ERROR;
if(pulso_servo2!=0) tiempo2=tiempo2 - tiempo3 - MARGEN_ERROR;
if(input(PIN_B2)==0) {
//Si el pin esta bajo lo subimos
output_bit(PIN_B2, 1);
tiempo3=pulso_servo3;
} else {
//Si el pin esta alto lo bajamos
output_bit(PIN_B2, 0);
tiempo3=20000-pulso_servo3;
} //fin del if
//Cual atendemos ahora?
atender_contador=menortiempo();
establecer_interrupcion_para(atender_contador);
break;
}
} //fin de la interrupcion
void main() {
//Configuramos puertos
configurar_puertos();
//Configuramos los timers
configurar_timers();
//Configuramos interrupciones
configurar_interrupciones();
while(1){}
}
void establecer_interrupcion_para(int mitimer) {
int postscaler, pr2var;
switch(mitimer) {
case(1):
postscaler=get_postscaler(tiempo1-MARGEN_ERROR);
pr2var=((tiempo1-MARGEN_ERROR)/16)/postscaler;
setup_timer_2(T2_DIV_BY_16, pr2var, postscaler);
break;
case(2):
postscaler=get_postscaler(tiempo2-MARGEN_ERROR);
pr2var=((tiempo2-MARGEN_ERROR)/16)/postscaler;
setup_timer_2(T2_DIV_BY_16, pr2var, postscaler);
break;
case(3):
postscaler=get_postscaler(tiempo3-MARGEN_ERROR);
pr2var=((tiempo3-MARGEN_ERROR)/16)/postscaler;
setup_timer_2(T2_DIV_BY_16, pr2var, postscaler);
break;
}
}
int get_postscaler(unsigned long tiempo) {
if(tiempo < 4080)
return 1;
else if(tiempo < 8160)
return 2;
else if(tiempo < 12240)
return 3;
else if(tiempo < 16320)
return 4;
else if(tiempo < 20400)
return 5;
}
int menortiempo() {
if(pulso_servo2==0) return 1;
if(tiempo1 < tiempo2) {
//tiempo1 o tiempo3
if(pulso_servo3==0) return 1;
if(tiempo1 < tiempo3) {
return 1; //tiempo 1 el menor
} else {
//tiempo3 o tiempo2
if(tiempo2 < tiempo3)
return 2; //tiempo 2 el menor
else
return 3; // tiempo3 el menor
}
} else {
//tiempo2 o tiempo3
if(pulso_servo3==0) return 2;
if(tiempo2 < tiempo3)
return 2;
else
return 3;
}
}
void configurar_puertos() {
//Desactivamos comparadores
CMCON = 0x07;
//PORTA como entrada
set_tris_a(0x11);
//PORTB como salida
set_tris_b(0x00);
}
void configurar_timers() {
//TIMER1
setup_timer_1(T1_DISABLED);
//TIMER2
setup_timer_2(T2_DIV_BY_16, 94, 1); //Preescaler a 1:4, PR2 a 124, postscaler a 1:1
set_timer2(0); //TMR2=0
}
void configurar_interrupciones() {
//Habilitamos PIE1.TMR2IE
enable_interrupts(INT_TIMER2);
//Habilitamos Habilitar INTCON.GIE
enable_interrupts(GLOBAL);
}
Creo que seguiré probando formas de manejar servos que se me vayan ocurriendo a ver cual consigo que sea la más precisa. Lo siguiente que haré será programar directamente en ASM usando un timer0 más sencillo y optimizando más... No obstante, veré lo que dices tu juanelete
Si no consigo un resultado exacto cambiaré de pic y usaré algún con 2 modulos PWM (necesito manejar al menos 2 servos) y de esta manera fijo que no falla...
Todavía no he probado bien bien ninguna de las señales con un servo real... Cuando lo haga postearé lo que pasa
Un saludo!