Autor Tema: problema timer2 16F628A controlador servo  (Leído 3901 veces)

0 Usuarios y 1 Visitante están viendo este tema.

Desconectado lqbweb

  • PIC10
  • *
  • Mensajes: 12
problema timer2 16F628A controlador servo
« en: 09 de Enero de 2008, 00:46:33 »
Buenas,

Necesito programar una pequeña controladora de 3 servos en un pic16f628A.... De momento he implementado el de 1 servo, lo he enchufao al osciloscopio y aparentemente me sale la señal decente (posición central 1.5ms de pulso a 50hz). El problema llega cuando conecto el pin RB0 del pic al cable de pulsos del servo y con una fuente alimentación a parte alimento el servo a 5V. El servo ni se inmota, como si no estuviera conectado.... He probado a juntar las masas de la fuente externa y de la ICD2 (la que alimenta el PIC) y tampoco hubo suerte.

Os pego el código:

Código: [Seleccionar]
#include "16f628a.h" //pic a utilizar

#use delay (clock=4000000) //Fosc=4Mhz
#fuses INTRC_IO,NOWDT,NOPROTECT,NOLVP,NOBROWNOUT,NOMCLR,NOPUT,NOCPD
#byte CMCON = 0x1F

void configurar_puertos();
void configurar_interrupciones();
void configurar_timers();
int servo1_high=0;
int servo1_high_max=15; //1.5ms
int servo1_low=0;
int servo1_low_max=185; //18.5ms

#INT_TIMER2                        // Interrupción del Timer2
void timer2_int() {                 // Timer2 incrementa cada 4us. Genera interrupción cada 100us

if(servo1_high<servo1_high_max)
servo1_high++;
else if(servo1_high==servo1_high_max){
output_low(PIN_B0);
servo1_high++;
} else {
if(servo1_low<servo1_low_max) {
servo1_low++;
}else{
output_high(PIN_B0);
servo1_low=0;
servo1_high=0;
}
} // fin IF

} //fin de la interrupcion


void main() {
//Configuramos puertos
configurar_puertos();

//Configuramos los timers
configurar_timers();

//Configuramos interrupciones
configurar_interrupciones();

while(1){

}
}



void setservo1(int pulso) {
servo1_high_max=pulso;
servo1_low_max=200-pulso;
servo1_high=0;
servo1_low=0;
}

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_1, 100, 1); //Preescaler a 1:1, PR2 a 100, 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);
}


Mirar la captura de pantalla del osciloscopio:

Captura_Osciloscopio

Se os ocurre que puede pasar?

Desconectado QIQE

  • PIC18
  • ****
  • Mensajes: 335
Re: problema timer2 16F628A controlador servo
« Respuesta #1 en: 09 de Enero de 2008, 07:19:25 »
Hola nunca realicé el control de un servomotor, es algo pendiente que tengo por hay... , pero siq ue he leido algo sobre ellos mira bien por google por que me parece que estas esperando demasiado tiempo, me a parecido ver en un par de paginas, y posts de otros foros, que la frecuencia debe de ser de 1/11^-3, quiero decir que el tiempo maximo de tu señal debe de ser de 11mS, y tu creo que tienes 20mS.

Algo de Teoria sobre servos

Cuantanos tus avances. un saludo
Muchas gracias por vuestro tiempo.

Saludos desde valencia!!!

Desconectado Nocturno

  • Administrador
  • DsPIC33
  • *******
  • Mensajes: 18310
    • MicroPIC
Re: problema timer2 16F628A controlador servo
« Respuesta #2 en: 09 de Enero de 2008, 08:49:13 »
Cuando unes las dos masas, condición indispensable por otra parte, ¿qué se ve en el osciloscopio?

Desconectado juanelete

  • PIC12
  • **
  • Mensajes: 74
Re: problema timer2 16F628A controlador servo
« Respuesta #3 en: 09 de Enero de 2008, 16:35:12 »
Hola lqbweb

No entiendo muy bien como utilizas la int para enviar los pulsos cad 20 ms.

Por otra parte la funcion setservo() no la llamas nunca.

He hecho varios proyectos con servos (hasta 8 ) moviedose simultaneamente, utilizando el timer de una forma un poco diferente a como lo haces tu.

Por si te interesa en este post encontraras explicaciones y ejemplos de como hacerlo.

http://www.todopic.com.ar/foros/index.php?topic=17052.msg114026#msg114026

Saludos a to@s

Desconectado lqbweb

  • PIC10
  • *
  • Mensajes: 12
Re: problema timer2 16F628A controlador servo
« Respuesta #4 en: 10 de Enero de 2008, 07:54:27 »
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:

Código: [Seleccionar]
#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!

Desconectado juanelete

  • PIC12
  • **
  • Mensajes: 74
Re: problema timer2 16F628A controlador servo
« Respuesta #5 en: 10 de Enero de 2008, 10:19:08 »
Hola lqbweb

Perdona que insista, pero con el metodo que te digo, apenas con 7 u 8 lineas de codigo puedes manejar 8 servos,
con una precision extrema (0,0002ms) y con una sencillez pasmosa. Ademas se le quita al procesador el minimo tiempo imprescindible
para poner el servo en alto o en bajo.

Estas dos interrupciones hacen casi todo el trabajo:
Código: [Seleccionar]
#int_timer1                     // Esta int se encarga de poner en alto el servo correspondiente
void handle_timer1_int()  // Se activa cada 2,5 ms (cada 2,5ms porque como son 8 servos.   8x2,5=20ms) 
{
    flag_timer1=TRUE;
    set_timer1(VAL_TIMER1);          // VAL_TIMER1=12536, porque 65536-53000=12536*0,0002 = 2,5 ms (utilizando 20 Mhz, timer de 16 bits y sin preeescaler)
}


#int_ccp2
void handle_ccp2_int()             //Esta int se encarga de poner en bajo el servo correspondiente
{
    bit_clear(*6,servo);              // igual que output_low(PIN_Bx);  -> *6=PORTB  -> Pone a 0 el bit indicado en servo (pongo en bajo el servo)
}


Este codigo incluido en main(), se encarga de poner en alto cada servo secuencialmente, y de configurar la int_ccp2 con el tiempo requerido
para situar cada servo en la posiscion deseada

Código: [Seleccionar]
    do
    {
        if(flag_timer1==TRUE)              // Cuando se activa la int_timer1 -> cada 2,5 ms pongo en alto cada servo secuencialmente
        {
            servo++;                            // Siguiente servo
            if (servo>=9) servo=1;       
            bit_set(*6,servo);              // igual que output_high(PIN_Bx);  -> *6=PORTB  -> Pone a 1 el bit indicado en servo (pongo en alto el servo)
            CCP_2 = VAL_TIMER + pos_servo(servo);         // Configuro la int con el tiempo que tiene que estar en alto el servo, cuando se active pondra el servo en bajo

            // La funcion pos_servo() debeis implementarla vosotros, teniendo en cuenta que debe devolver el tiempo (los ticks de reloj) que quereis que el servo este en alto
            // sabiendo que 1 tick = 0,0002 ms, osea que 12536*0,0002 = 2,5 ms 

            flag_timer1=FALSE;       
        }
    }
    while (TRUE);

Creo que observaras que es bastante sencillo y eficiente, porque en ningun momento se pierde tiempo en las interrupciones, y tampoco se pierde tiempo comprobando continuamente si ha trasncurrido tal o cual tiempo.

En este post encontraras mas informacion
http://www.todopic.com.ar/foros/index.php?topic=17052.msg114026#msg114026

Saludos a tod@s

Desconectado lqbweb

  • PIC10
  • *
  • Mensajes: 12
Re: problema timer2 16F628A controlador servo
« Respuesta #6 en: 11 de Enero de 2008, 23:35:51 »
juanelete Te he hecho caso jejeje y he implementado un código similar al tuyo con 2 servos.... Pero tengo unos problemas (quiza no lo sea?). Va todo bien excepto porque mido con el osciloscopio y en lugar de salir un pulso alto de 1.5ms sale 1.7ms :S aquí mismo os pego la gráfica.... El tema es que mi osciloscopio (si a eso se le puede llamar así) es una especie de pic18 y he pensado que el error sea producido por este.... A ver si alguién pudiera revisar el código y/o probarlo. Se admiten también sugerencias de optimización :P

Código: [Seleccionar]
#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

/*
Se ha debugeado y se ha visto que desde que salta la interrupción
timer1 hasta que se pone en alto el servo y se reinicia el timer hay
un delay de 55 instrucciones */
#define MARGEN_ERROR1 55
#define MARGEN_ERROR2 55

#define PORTA = 0x05
#define PORTB 0x06

int8 flag_timer1;
int1 servo_sel=0;
int16 pulso_servo1=1500; //pulso por defecto del servo1
int16 pulso_servo2=1500; //pulso por defecto del servo2

#int_TIMER1
void timer1_int() {
//subimos el servo
flag_timer1=1;
}

#int_CCP1
void ccp1_int() {
//bajamos el servo
flag_timer1=2;
}


void main() {
delay_ms(350); //Esperamos la inicialización del micro

// --------------- CONFIGURACIÓN PUERTOS
CMCON = 0x07; //Desactivamos comparadores
set_tris_a(0x11); //PORTA como entrada
set_tris_b(0x00); //PORTB como salida
//--------------------------------------

// --------------- CONFIGURACIÓN TIMERS
setup_timer_1(T1_INTERNAL|T1_DIV_BY_1); //TIMER1
setup_timer_2(T2_DISABLED,0,1); //TIMER2
setup_ccp1(CCP_COMPARE_INT); //Configuramos el CCP
set_timer1(55535-MARGEN_ERROR1); //Generamos interrupción cada 10ms en timer1

//-------------------------------------

// --------------- CONFIGURACIÓN INTERRUPCIONES
enable_interrupts(INT_TIMER1); //Habilitamos PIE1.TMR1IE
enable_interrupts(INT_CCP1); //Habilitamos interrupcion CCP1
enable_interrupts(GLOBAL); //Habilitamos Habilitar INTCON.GIE
//-------------------------------------


do{
if(flag_timer1==1) {
//subimos el servo
servo_sel=!servo_sel; // o servo 0 o 1

bit_set(*PORTB,servo_sel); //Ponemos el servo en Alto
set_timer1(55535-MARGEN_ERROR1); // 55535 - margen_error

if(servo_sel==0)
CCP_1 = 55535-MARGEN_ERROR2+pulso_servo1; //55535 - error. configuramos cuando vamos a bajar a servo1
else
CCP_1 = 55535-MARGEN_ERROR2+pulso_servo2; //55535 - error. configuramos cuando vamos a bajar a servo2

//----- Debajo de aquí en este if, se puede añadir lo que se quiera


flag_timer1=0;

} else if(flag_timer1==2){
flag_timer1=0;
bit_clear(*PORTB,servo_sel);

//----- Debajo de aquí en este if, se puede añadir lo que se quiera
}

} while(1);
}


La captura la podeis ver aqui

Gracias de antemano!

Añado: Si en el código de arriba cambio MARGEN_ERROR1 por 200 y MARGEN_ERROR2 por 300, entonces la onda me sale perfecta. pero esto ya no tiene sentido teórico :S
« Última modificación: 12 de Enero de 2008, 00:17:20 por lqbweb »

Desconectado juanelete

  • PIC12
  • **
  • Mensajes: 74
Re: problema timer2 16F628A controlador servo
« Respuesta #7 en: 12 de Enero de 2008, 06:52:59 »
Hola

Asi, a bote pronto....

No tendrias que sumar el margen de error, en vez de restarlo ??

Ademas yo pondria bit_clear(*PORTB,servo_sel) dentro de la int_CCP1, para ganar algo de tiempo.

Saludos

Desconectado lqbweb

  • PIC10
  • *
  • Mensajes: 12
Re: problema timer2 16F628A controlador servo
« Respuesta #8 en: 12 de Enero de 2008, 11:41:17 »
tienes razón, el bit_clear va dentro.... lo meto dentro, ajusto margenes y pruebo....


 

anything