fbpx

Expresate

Si además quieres enviarnos un Artículo para el Blog y redes sociales, pulsa el siguiente botón:

Avisos
Vaciar todo

Servos y PWM

40 Respuestas
10 Usuarios
0 Reactions
15.2 K Visitas
ionthas
Respuestas: 153
Topic starter
(@ionthas)
Estimable Member
Registrado: hace 17 años

Buenas a todos.

Últimamente he estado mirándome el tema del PWM estoy intentando controlar un servomotor FUTABA 3003, pero tengo dos problemas.

En primer lugar, la mecánica del servo tiene un ángulo de 360 grados (ya que le quite el tope al trucarlo). Pero controlándolo por PWM el ángulo que puedo recorrer del máximo al mínimo es de menos de 50 grados. Me gustaría saber cómo puedo hacerlo para ampliar ese rango.

En segundo lugar, cuando el servo llega a la posición (media, mínima o máxima), se queda vibrando. ¿Eso es normal? En caso de que no lo sea me gustaría saber cómo puedo evitar que vibre. He comprobado que vibra ya que estoy todo el rato enviándole pulsos. ¿Como lo puedo hacer para que cuando el servo este posicionado donde lo quiero pare de enviar pulsos?

Adjunto el código que utilizo para controlar el servo:


#include <16F876A>
#device adc=8

#FUSES NOWDT //No Watch Dog Timer
#FUSES RC //Resistor/Capacitor Osc with CLKOUT
#FUSES NOPUT //No Power Up Timer
#FUSES NOPROTECT //Code not protected from reading
#FUSES NODEBUG //No Debug mode for ICD
#FUSES BROWNOUT //Reset when brownout detected
#FUSES LVP //Low Voltage Programming on B3(PIC16) or B5(PIC18)
#FUSES NOCPD //No EE protection
#FUSES NOWRT //Program memory not write protected

#use delay(clock=20000000)
#use rs232(baud=9600,parity=N,xmit=PIN_C6,rcv=PIN_C7,bits=8)

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

//Registros
#byte PORTB = 0x06
#byte TRISB = 0x86

//LED
#bit LED = PORTB.1

//SERVOS
#bit servo1 = PORTB.0

//Otros
#define ON 1
#define OFF 0

const int AJUSTE_FINO_DE_RTCC = 61;
const int ticks_PULSO_MINIMO = 62;
const int ticks_PULSO_MEDIO = 93;
const int ticks_PULSO_MAXIMO = 125;

int1 flagRTCC = 0;
int contRTCC = 0;
int1 flagSERVO1 = 0;
int tSERVO1 = ticks_PULSO_MEDIO;
int ValTIMER0;

#int_RTCC
void RTCC_isr()
{

++contRTCC;

if(contRTCC == 4)
{
set_TIMER0(AJUSTE_FINO_DE_RTCC);
}
if(contRTCC == 5)
{
flagRTCC = 1;
contRTCC = 0x00;
}

}

void main(){

//Configuracion
//---------------------------------------------------------------------------

setup_adc_ports(NO_ANALOGS);
setup_adc(ADC_OFF);
setup_spi(SPI_SS_DISABLED);
setup_timer_0(RTCC_INTERNAL|RTCC_DIV_1);
setup_timer_1(T1_DISABLED);
setup_timer_2(T2_DISABLED,0,1);
setup_comparator(NC_NC_NC_NC);
setup_vref(FALSE);

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

setup_counters(RTCC_INTERNAL,RTCC_DIV_16);

// Configura les interrupciones.
enable_interrupts(global);
enable_interrupts(int_RTCC);

// Inicialitza el timer a 0
set_TIMER0(0);

TRISB = 0x00;
PORTB = 0

while(1){

// DISPARO DEL PULSO PWM
if(flagRTCC == 1)
{
flagRTCC = 0;
servo1 = ON;
flagSERVO1 = 1;
}

// CONTROL DE ANCHO DEL PULSO PWM
if(flagSERVO1 == 1)
{
valTIMER0 = get_TIMER0();

if(valTIMER0 > tSERVO1)
{
flagSERVO1 = 0;
servo1 = OFF;
}

}

}

}

Saludos y gracias.

Responder
39 respuestas
dragonet80
Respuestas: 1328
(@dragonet80)
Ardero
Registrado: hace 17 años

Wolfskin, de momento aún no me he mirdo detalladamente el código, pero he visto un error:
Tienes definido el fuse de RC, cuando estás usando el clock a 20MHz. Eso no es posible.

El RC se usa para muy bajas velocidades. Después está el LP para bajo consumo, el XT para media velocidad y HS para alta velocidad. Para 20MHz tienes que usar el HS siempre.

Y si usas un cristal externo para generar los 20MHz, no está de más usar esta directiva:
#use delay(crystal=20000000)

Los ticks no me ha dado tiempo de comprobar si están bien calculados, me tengo que ir a trabajar. Revísalo que al mediodía le daré otro vistazo.

Responder
ionthas
Respuestas: 153
Topic starter
(@ionthas)
Estimable Member
Registrado: hace 17 años

Revisado, continua moviendose menos de 1 grado. :/

Responder
dragonet80
Respuestas: 1328
(@dragonet80)
Ardero
Registrado: hace 17 años

Otra cosa que veo, mirando rápido:

O te has hecho un lío con los Timers o... te has hecho un lío con los timers :|, me explico:

En tu código, usas 2 timers, pero sólo tienes definida la función de interrupción de 1 de ellos. O te falta por definir la otra, o te has confundido al hacer referencia a uno de ellos.

Timers de un pic16:
Timer0: 8 bits (el timer0 y RTCC son LO MISMO)
Timer1: 16 bits
Timer2: 8 bits (este también tiene un post escaler, para alargar el tiempo).

Mírate el datasheet de microchip o la ayuda del ccs, y verás como se usan. Presta atención al tema de divisores (pre-escalers) y post-escalers (Timer2).

Para calcular el tiempo de 1 ciclo de instrucción, tienes que tener en cuenta:
* Velocidad del cristal u oscilador empleado (interno o externo).
* Las instrucciones tardan 4 pulsos de reloj en completarse.

Ésto llámalo como quieras (velocidad de Tick o lo que sea) y cuando quieras generar una interrupción, tienes que tener en cuenta esto:
* Los contadores empiezan a contar de 0. Si quieres que empiecen con otro número, tienes que cargarles ese valor. Por ejemplo en ccs: set_timer1(valor);
* Cuentan hasta el máximo dependiendo de su longitud (8 bits = 255; 16 bits = 65536). Entonces, por ejemplo, el Timer0 contará 256 ticks si no le has puesto precarga, y contará 156 ticks si le has puesto una precarga de 100. Eso quiere decir que al ponerle una precarga más grande, tarda menos en llegar al máximo.
* Al llegar al máximo generan una interrupción. En ccs, ese es el momento que se ejecuta la función que tú hayas definido. Por ejemplo:
#int_RTCC
void RTCC_mi_funcion()
{
// aquí hago lo que yo quiero
}

-> Tema divisores:
Tal como lo tienes tú definido arriba, cada ciclo de instrucción (recuerda 20000000/4 = 5000000) el Timer0 (RTCC) cuenta o se incrementa en 1. Eso es así porque le has definido el divisor a 1 (RTCC_DIV_1). A veces este tiempo es muy corto, y necesitas algo más largo, entonces te conviene cambiar el divisor. Imagina que pones el RTCC_DIV_4, entonces el micro tarda 4 "ticks" en incrementar en 1 el contador del Timer0, o lo que es lo mismo, tarda 4 veces más.
Te vuelvo a repetir, toda esta información viene detallada en los datasheets. Y para programar en ccs, un sito indispensable a visitar siempre, es el fichero que pones en el "device", en tu caso sería el 16F876A.h, que está dentro del directorio PICCDevices

-> Tema post-escalers:
El Timer2, además, tiene un post-escaler. Eso quiere decir que cuando su contador llega al máximo, no genera inmediatamente una interrupción, sino que esa cuenta tiene que repetirse el número de veces que ponga el post-escaler. Con el post-escaler a 1, la cuenta sólo se realiza una vez (igual que en los otros timers), con el post-escaler a 2 se realizan 2 cuentas y después genera interrupción, etc, etc.

Mi consejo es que antes de meterte en el contro de un servo por interrupciones hagas el control de parpadeo de un led. Coje lápiz y papel (una hoja de cálculo mejor todavía) y haz los cálculos para hacer parpadear un led cada segundo exacto. Haz lo mismo para diferentes tiempos (0,5 s; 2 s; etc). Hazlo para el Timer0, para el Timer1 después y para el Timer2 finalmente. Monta ese circuito y comprueba, cronómetro en mano, que todo funciona bien.

Cuando ya tengas eso dominado, pasa al tema del servo. Tienes que llevar la cuenta de 3 valores. Tiempo de repetición de la señal (50 ms para todos los servos), tiempo en ON y tiempo en OFF (esto ya depende de cada fabricante del servo, pero todos tienen un mínimo y un máximo que debes respetar).

Responder
ionthas
Respuestas: 153
Topic starter
(@ionthas)
Estimable Member
Registrado: hace 17 años

Muchas gracias dragonet80. Después de clase me pongo ha hacerlo con un LED ^^.

Responder
dragonet80
Respuestas: 1328
(@dragonet80)
Ardero
Registrado: hace 17 años

enga, que esta Campus tienes que traer un robot velocista y otro robotracker para competir !!

Responder
Página 4 / 8
Compartir: