Problema Servo PIC 16F886

Hola, tengo un problema con este código (en proteus sí funciona pero en el circuito físico no),
Ya me he asegurado de que el problema no sea con los componentes electrónicos.

Dependiendo de las frecuencias iniciales el servo se posiciona en la posición correspondiente, pero al momento de utilizar los push button para moverlo simplemente hace un pequeño giro (como 3 grados) y regresa a su posición.

Quiero saber si es necesario implementar algún tipo de interrupción o si pasé por alto algo en el código.


Código:
#include <16f886.h>
#fuses HS,NOWDT,NOPROTECT,NOLVP,XT,INTRC_IO,INTRC,RC_IO,RC,WDT,NOPUT,PUT,NOWRT,NOMCLR
#use delay (internal = 4,000,000)

void main(){

int x=0;
int16 y=0;
int16 uno=1500; //frecuencia 
int16 cero=18500; //frecuencia
int check=1;
   while(TRUE){

//aumento los grados de eje del servo
      if(!input(PIN_B1)){   
         if (uno<2400){            
            uno=uno+100;
            cero=cero-100;
            check=1;

            
         }
      }
//disminución de los grados del eje del servo      
      if(!input(PIN_B2)){
         if (uno<2400){            
            uno=uno+100;
            cero=cero-100;
            check=1;                        
         }
      }
      
//envio de frecuencias al servo
      if(check==1){
      check=0;
      
      for(x=0;x<=25;x++){
         output_high(PIN_B0);
         delay_us(uno);
         output_low(PIN_B0);
         delay_us(cero);
      }
      }    
   }
}
NOTA: PIN B0 es para la linea de control del servo, PIN B1 es para aumentar los grados del eje, PIN B2 es para disminuir los grados del eje.

Saludos.
Gracias.
 
Última edición por un moderador:
Te paso un código que tengo por ahí para el control de un servo con el 16f876a:

#include <pic16f876a.h>
#define LED1 RA0
#define LED2 RA1
#define LED3 RA2
/* ----------------------------------------------------------------------- */
/*

En el control de servos utilizaremos ventanas de 2.5ms
Dicha configuración se realiza mediante:
ticks 39
escaler 64
Rango del servo 0.3ms-2.3
5ticks-36tics
-90 90

*/
#define T_servo 39
#define T0INI_2_5ms 256-39
/* ----------------------------------------------------------------------- */
/* Bits de configuración: adapte los parámetros a su necesidad */
typedef unsigned int word;
word at 0x2007 CONFIG = _XT_OSC & _WDT_OFF & _PWRTE_OFF & _BODEN_ON & _LVP_ON & _CPD_OFF & _WRT_OFF & _DEBUG_OFF & _CP_OFF;

//rutina que carga un retardo de 10ms
void timer0_delay(unsigned char t0ini) {
TMR0=t0ini;
T0IF=0;
while(T0IF==0);
}

void servo_pos(int pos) {
unsigned int ciclos;
unsigned int i;
char Ton;

Ton=(31*pos)/180+41/2;
for (ciclos=0; ciclos<100;ciclos++) {
RB7=1;
timer0_delay(255-Ton);
RB7=0;
timer0_delay(255-T_servo+Ton);
for (i=0;i<7;i++)
timer0_delay(T0INI_2_5ms);
}
}

void main(void)
{
TRISA0=0; TRISA1=0; TRISA2=0;
TRISB7=0;

ADCON1=0x06;
LED1=0;LED2=0;LED3=0;

T0CS=0; PSA=0;
PS2=1; PS1=0; PS0=1;

while(1) {
LED1=0;LED2=1;LED3=0;
servo_pos(0);
LED1=1;LED2=0;LED3=0;
servo_pos(-90);
LED1=0;LED2=1;LED3=0;
servo_pos(0);
LED1=0;LED2=0;LED3=1;
servo_pos(90);
}
}
 
Hola DarkRaven,

El código para aumentar y disminuir los grados del servo es el mismo, creo que puede ser una posible causa.

Por otro lado, las variables uno y cero no están acotadas. La primera crece hasta 2400 y entonces el programa no vuelve a entrar en ninguno de los condicionales, dejando de responder. La segunda variable, cero, podría alcanzar valores negativos, aunque primero uno alcanza su cota y por ello no ocurrirá. En cualquier caso debes reforzarlo por si alcanzara el delay_us con un valor negativo.

Por último, aunque no sé si tendrá relación, pero a delay_us hay que indicarle tiempo y no frecuencia, que son inversos.
 
Es verdad, una disculpa, estaba testeandolo con el debugger y por eso modifiqué el código para que sólo hiciera aumentos de grados en el eje de rotación.

El problema está en que, por ejemplo, si asigno otros valores a "uno" y a "cero", compilo y ejecuto, el servo se posiciona en el lugar que debería estar (o sea, con 600 y 19400 se pone a 0°) pero no obedece a los cambios con ls push buttons (simulado funciona a la perfección y checando con el debugger sí se hacen las operaciones adecuadamente).

El código original:

#include <16f886.h>
#fuses HS,NOWDT,NOPROTECT,NOLVP,XT,INTRC_IO,INTRC,RC_IO,RC,WDT,NOPUT,PUT,NOWRT,NOMCLR
#use delay (internal = 4,000,000)

void main(){

int x=0;
int16 y=0;
int16 uno=1500; //frecuencia
int16 cero=18500; //frecuencia
int check=1;
while(TRUE){

//aumento los grados de eje del servo
if(!input(PIN_B1)){
if (uno<2400){
uno=uno+100;
cero=cero-100;
check=1;


}
}
//disminución de los grados del eje del servo
if(!input(PIN_B2)){
if (uno>600){
uno=uno-100;
cero=cero+100;
check=1;
}
}

//envio de frecuencias al servo
if(check==1){
check=0;

for(x=0;x<=25;x++){
output_high(PIN_B0);
delay_us(uno);
output_low(PIN_B0);
delay_us(cero);
}
}
}
}

Lo del delay_us tengo entendido que depende de la frecuencia que definiste, el delay_ms es para especificar el tiempo en milisegundos.

Gracias!
 
Hola de nuevo,


Me he dado cuenta de que la señal que le estás enviando al servo no es periódica. La bandera check impide enviarle continuamente los pulsos al servo, y yo diría que es vital para que su electrónica detecte el ancho de pulso. Lo que haces es mandar una ráfaga de 26 pulsos (¿ por qué 26 por cierto?) y después, a menos que nadie haya pulsado, no enviar nada. ¿ No deberías mantener la señal constantemente, aunque no haya cambiado?

Un apunte... La función delay_us() toma un argumento en µs, mientras que delay_ms() lo hace en ms, pero ambos toman tiempos. El ancho de pulso a nivel alto que has de generar ha de estar comprendido entre 0'5ms y 2'5ms a una frecuencia de 50Hz. Por ello la frecuencia y el ancho de pulsos que generas son correctos, pues uno+cero suman 20.000µs = 20ms. Ningún servo funciona a 20kHz.


Espero que te sea de ayuda,
¡ Un saludo!
 
Atrás
Arriba