Si además quieres enviarnos un Artículo para el Blog y redes sociales, pulsa el siguiente botón:
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.
Vale, luego tienes que procurar no pasarte de los 180º que suelen tener! Normalmente, si cuando lo llevas al límite derecho o izquierdo te vibra es que te estás pasando con el pulso, ya sea por corto o por largo.
Pásate por la página de Picmania que tiene un estupendo ejemplo completo:
http://picmania.garcia-cuervo.com/PICC2.php#SERVO1
Ah, y si lo quieres como servo, ni se te ocurra quitar la electrónica o el potenciómetro, o te dejará de funcionar ❗
El código que he puesto está basado en el ejemplo de la página que me pasaste dragonet80, con los mismos parámetros y en teoría el mismo servo.
Voy a intentar calibrar mejor los ticks y los parámetros.
Saludos.
Por cierto, hace un par de semanas estuve por Manresa. Pero sólo fue por la noche después de cenar (cena re-encuentro de ex-alumnos de la facultad, 10 años después de acabar la carrera :)). De estar más tiempo te habría llamado. Estuvimos por algún bareto que no conocía y después en el Cielu...
Qué recuerdos! Poco ha cambiado, a parte del edifico-parking al lado de la catedral y algún que otro edificio nuevo cerca del río...
dragonet80, yo fui al sielu la semana pasada, "casi" coincidimos! 😛
Weno tema servo, esa es toda la info sobre los timings que tengo que aplicar en mi caso:
tick = 5 / 256 = 0.019 ms.
4 RTCC = 4 * 5ms = 20ms
Precarga del Timer = 40536t
Precarga para cada interrupcion = 40536t / 4 = 10134t
tickMedio = 1.5ms / 0.0002ms = 7500t
tickMin = 0.5ms / 0.0002ms = 2500t
tickMax = 2.5ms / 0.0002ms = 12500t
flagRTCC = 4 RTCC completas.
Y eso es el código que tengo de momento:
#include <16F876A>
#device adc=8
#priority timer1,rda
#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 standard_io(B)
#use rs232(baud=9600,parity=N,xmit=PIN_C6,rcv=PIN_C7,bits=8)
//------------------------------------------------------------------------------
// Registros
#byte PORTB = 0x06
#byte TRISB = 0x86
// SERVOS
#bit servo1 = PORTB.7
// Otros
#define ON 1
#define OFF 0
// Funcions
void disparoPWM();
void controlPWM();
// Constants
const int precarga = 10134; //Divido la precarga por 4 ya que sino se pasa de los 20ms
const int tickMedio = 7500;
const int tickMin = 2500;
const int tickMax = 12500;
// Variables Globals
int flagRTCC = 0;
int contRTCC = 0;
int flagSERVO1 = 0;
int tSERVO1 = tickMax;
int ValTIMER1;
#int_RTCC
void RTCC_isr()
{
++contRTCC;
set_TIMER1(precarga);
if(contRTCC == 4)
{
flagRTCC = 1;
contRTCC = 0;
}
}
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(RTCC_INTERNAL|RTCC_DIV_1);
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 la precarga per tal de que duri 5ms
set_TIMER1(precarga);
TRISB = 0b00000001;
PORTB = 0;
while(TRUE){
disparoPWM();
controlPWM();
}
}
// DISPARO DEL PULSO PWM
void disparoPWM()
{
if(flagRTCC == 1)
{
flagRTCC = 0;
servo1 = ON;
flagSERVO1 = 1;
}
}
// CONTROL DE ANCHO DEL PULSO PWM
void controlPWM()
{
if(flagSERVO1 == 1)
{
valTIMER1 = get_TIMER1();
if(valTIMER1 > tSERVO1)
{
flagSERVO1 = 0;
servo1 = OFF;
}
}
}
Yo creo que el problema está en ese IF:
if(valTIMER1 > tSERVO1)
{
flagSERVO1 = 0;
servo1 = OFF;
}
El servo se mueve un poco (ni 1 grado) y se para.
Si alguien me puede ayudar.
Saludos y gracias.