# Codigo C Control de servomotor (Grados) con PIC16F84A



## kurt cobain (Ene 20, 2013)

Hola a todos, tengo un proyecto el cual con el pic16f84a se tiene que controlar las grados de un servomotor a Rb0 por ejemplo: si presiono pulsador en Ra0 el servo se va a 0º y si vuelvo a presionar el mismo pulsador se vaya a 30º y así hasta 180º y con otro pulsador en Ra1 vaya de 180º a 150º y asi hasta 0º.

Tengo esta programación (c) pero lo que hace es ir de 135º y -135º sin parar, agradecería mucho su ayuda en la programación la lograr lo postulado anteriormente, un saludo.


```
#include <16F84a.h>
#use delay(clock=4000000)
#fuses NOWDT,NOPROTECT,
int retardo; 
void main() 
{ retardo=5; 
WHILE(input(PIN_a0))  
 {
   output_high(PIN_B0);
   output_high(PIN_B1);
 
 }
do{
if(!input(pin_a1)&&(input(pin_a1)))      

{
   OUTPUT_HIGH(PIN_B0);
   OUTPUT_LOW(PIN_B1);
   delay_ms(500); }
   if(input(pin_a0)&&(!input(pin_a1)))
   
{ 
   OUTPUT_LOW(PIN_B0);
   OUTPUT_HIGH(PIN_B1);
  
   delay_ms(retardo); }}while(true); 
}
```

Simulación:
Ver el archivo adjunto Servomotor.zip


----------



## Pec (Ene 22, 2013)

Bueno, mirando tu codigo, estas haciendolo bastante poco optimizado. te recomiendo que averigues como usar el timer0 y el timer1 para conformar el PWM para el servo, asi te va a ser muchisimo mas facil.


----------



## kurt cobain (Ene 27, 2013)

Seguí tu consejo, ya había leído sobre el PWM pero no muy a fondo y pues ahora tengo entendido los pulsos que le tengo que dar al servomotor para que vaya a los diferentes grados bueno la mayoría van de 0.5ms a 2ms, 0º-180º (espero no tener información errónea  ) bueno a base de eso intente hacer parte del programa que necesito, pero no funciona, no soy muy bueno en esto  espero alguien me pueda decir en que estoy mal ...


```
# include <16F84A.h>
# use delay(clock=4000000)
# fuses XT,NOWDT
# byte puerto_b=06  
# byte puerto_a=05

char contador=0,estado=1;

 void main() {

set_tris_b(0x00);    
set_tris_a(0x1F);  

puerto_b=0;
 
 if (input(pin_a0)) estado=0;     //Si se pulsa.
 if (estado==0 && (input(pin_a0))) { //Si se pulsa y se libera.
 contador++;
   if (contador>4) contador=1;
   estado=1;
  }
  switch (contador){
    case 1:output_high(PIN_B0);
           delay_us(1499);     //1.500us (neutral)
           output_low(PIN_B0);
           delay_us(8500);
           break;
    case 2:output_high(PIN_B0);
           delay_us (1949);   //1.950us (-45 grados)
           output_low(PIN_B0);
           delay_us(18050);
           break;
    case 3:output_high(PIN_B0);
           Delay_us(1049);   //1.050us (+45 grados)
           output_low(PIN_B0);
           Delay_us(18950);
           break;
    case 4:output_high(PIN_B0);
           Delay_us(599);    //600us (+90 grados)
           output_low(PIN_B0);
           Delay_us(19400);
           break;
         
  }
 }
```


----------



## JuanGa94 (Ene 27, 2013)

Hola, las variables contador y estado me parece que tendrían que ser int, y pone el conjunto de instrucciones de los case: entre {}.Proba asi.
Saludos


----------



## kurt cobain (Ene 27, 2013)

Hola JuanGa94, hice lo que me sugeriste pero sigue igual  de inicio el servomotor se va a -90º  pero no responde al presionar en ra0...


----------



## JuanGa94 (Ene 28, 2013)

Hola, como estas? Me quede enganchado con el tema y estuve investigando y programando lamentablemente sin llegar a nada.
*Existe la posibilidad de cambiar de PIC?, debido a que este no tiene modulo CPP, lo que haría la tarea mucho mas fácil, o es parte de algún proyecto a nivel escuela/universidad.
*¿Que servo estas usando, marca? Modelo? para saber los valores de los pulsos según los grados.
*Tuve que modificar el esquema en Proteus porque estaba mal, faltaba el cristal y los capacitores, las resistencia en los pulsadores y... la duda de el servo, así que lo cambie.
*En cuanto al el funcionamiento, en que posición arranca el servo?

Se que son muchas preguntas, pero de verdad me quede pensando en esto...

Saludos


----------



## kurt cobain (Ene 28, 2013)

Hola. Bien gracias. Desgraciadamente no se puede cambiar de pic por a ese esta enfocado el curso. El servo es un sg90 sus pulsos son de 1ms, 1.5ms y 2ms para 0º, 90º y 180º. El esquema en proteus no es el que deveria de ser, lo adjunte mal, una disculpa...
De inicio de debe ir a 0º o a 90º pero con 2 pulsadores debo mover su posiciones (mas grados o menos grados) no sè si me doy a entender... 

No te preocupes por las preguntas, responderé las que sean necesarias 

Saludos y muchas gracias.


----------



## kurt cobain (Feb 2, 2013)

JuanGa94 dijo:


> Hola, como estas? Me quede enganchado con el tema y estuve investigando y programando lamentablemente sin llegar a nada.
> *Existe la posibilidad de cambiar de PIC?, debido a que este no tiene modulo CPP, lo que haría la tarea mucho mas fácil, o es parte de algún proyecto a nivel escuela/universidad.
> *¿Que servo estas usando, marca? Modelo? para saber los valores de los pulsos según los grados.
> *Tuve que modificar el esquema en Proteus porque estaba mal, faltaba el cristal y los capacitores, las resistencia en los pulsadores y... la duda de el servo, así que lo cambie.
> ...



Ya he logrado el correcto funcionamiento del servomotor.

Saludos


----------



## kurt cobain (Feb 28, 2013)

JuanGa94 dijo:
			
		

> Excelentes noticias, podrías subir el código?
> 
> Saludos



Disculpa la tardanza me tome unas pequeñas vacaciones  aquí tienes el código (espero aun te sirva)


----------



## JuanGa94 (Mar 16, 2013)

Hola de nuevo, estoy de vuelta con los servos y tengo un problema...Ahora quiero controlar un servo con un PIC16F628A usando el modulo CCP en modo PWM, necesito generar un pulso de 50Hz y variar su Duty Cycle.
Ahora cuando tengo que poner los argumentos de setup_timer_2(modo,periodo,postcaler); me encuentro con que no puedo generar 50Hz, el mínimo es 244Hz mas o menos (con un cristal de 4MHz). Aun llevando el modo a T2_DIV_BY_16 el periodo sale del rango de 0 a 255 (1249). Hay alguna forma de hacer esto así?¿Puedo usar los servos con otra frecuencia?

Saludos


----------



## JuanGa94 (Mar 22, 2013)

Hola, bueno aparentemente la única forma de generar 50Hz usando el CCP1, es cambiando el cristal por uno menor. Ahora estoy tratando de hacerlo con interrupciones, el timer2 y como salida RB3:
Codigo:

```
#include <16f628a.h>             //PIC16F628A
#use delay (clock=4000000)       //Cristal 4MHz
#define TRISB=0b11110111         //Puerto B
#define TRISA=0b11111            //Puerto A Entradas
#FUSES XT,NOWDT,NOPUT,NOPROTECT,BROWNOUT,MCLR,LVP,NOCPD
#use fast_io(A)
#use fast_io(B)
#byte portA = 5                  //Direcciones de memoria         
#byte portB = 6                  //Direcciones de memoria

const int TOTAL=2000;            //PASOS TOTALES 2000*0,01ms=20ms -->50Hz
int HIGH=100;                    //PASOS EN ALTO (MAXIMO 400 SINO NO ANDA??)
//HIGH=100 -->1ms -->-90º
//HIGH=150 -->1.5ms -->0º
//HIGH=200 -->2ms -->+90º
int PASOS=0;                     //PASOS DADOS

//     _____            ___
//    |     |          |
//    |     |          |
//____|     |__________|
//    /     /
//     HIGH
//    /      TOTAL     /

#int_TIMER2                      //INTERRUPCION TIMER2
void int_tmr1(void)
{
   PASOS++;
   IF(PASOS==TOTAL)              //CUANDO LLEGA A 2000 PASOS VUELVE A 0
      {PASOS=0;   
      setup_timer_2(T2_DIV_BY_1,100,1);
      }
   ELSE if(PASOS<=HIGH)          //ENTRE 0 Y HIGH,NIVEL ALTO
      {output_HIGH(pin_b3);
      setup_timer_2(T2_DIV_BY_1,100,1);
      }
   ELSE IF(PASOS>HIGH)           //ENTRE HIGH Y 2000,NIVEL BAJO
      {output_LOW(pin_b3);
      setup_timer_2(T2_DIV_BY_1,100,1);
      }
}

void main(void)
{
   set_tris_b (0b11110111);            //DEFINE TRIS B 
   set_tris_a (0b11111);               //DEFINE TRIS A COMO ENTRADAS 1
   PORTA=(0x00);                       //INICIALIZA TODO EN 0
   PORTB=(0x00);
   
   setup_timer_0(RTCC_INTERNAL|RTCC_DIV_1);
   setup_timer_1(T1_DISABLED);
   enable_interrupts(GLOBAL);          //Habilito interrupciones globales
   enable_interrupts(INT_TIMER2);      //Habilito interrupción particular del TIMER1
   setup_timer_2(T2_DIV_BY_1,100,1);   //DESBORDA CADA 0,01MS/0,0001S
   
   while(TRUE)
   {
   }

   return;
}
```
El programa no funciona... El problema esta en la interrupción,pero no puedo arreglarlo a pesar de que es un código simple ¿Pueden ayudarme?

Saludos


----------



## Nuyel (Mar 26, 2013)

momento, ¿HIGH no entra en conflicto con otras declaraciones del programa?, solo es una duda que me surgió asi que lo cambio a PWM_HIGH por si acaso, luego no uses  tantos if, solo compara una ves y usas un codigo más simple.

```
#int_TIMER2                      //INTERRUPCION TIMER2
void int_tmr1(void) //No se si esto importa pero ¿por que dice 1 y el timer usado es 2?
{
   PASOS++;
   IF(PASOS==TOTAL)              //CUANDO LLEGA A 2000 PASOS VUELVE A 0
      {
      PASOS=0;
      output_HIGH(pin_b3);                      //Mandas el pin a HIGH al comenzar a contar
      setup_timer_2(T2_DIV_BY_1,100,1);  //¿esto es necesario?
      }
   IF(PASOS= PWM_HIGH)           //Cuando los PASOS alcancen el ciclo mandas LOW, solo necesitas 
      {output_LOW(pin_b3);         //compararlo una ves y no agregas intrucciones inecesarias
      setup_timer_2(T2_DIV_BY_1,100,1); //repito, ¿esta linea es necesaria?
      }
}
```


----------



## JuanGa94 (Mar 28, 2013)

Hola, gracias Nuyel por tu ayuda, ahora funciona. Con el PR2=99 tengo 10=-89.3º ,15=+0.72º ,20=+90º y todos los valores intermedios. 
Quiero lograr mayor precisión usando variables float o double, e incrementando el contador en 0.1, para así en vez de ingresar por ejemplo 10, ingreso 10.x y tengo mayor precisión...pero no funciona, el simple hecho de cambiar el tipo de variable de int a float o double hace que siempre este en 0.¿Sabes por que sera?
Codigo:

```
#include <16f628a.h>             //PIC16F628A
#use delay (clock=4000000)       //Cristal 4MHz
#define TRISB=0b11110111         //Puerto B
#define TRISA=0b11111            //Puerto A Entradas
#FUSES XT,NOWDT,NOPUT,NOPROTECT,BROWNOUT,MCLR,LVP,NOCPD
#use fast_io(A)
#use fast_io(B)
#byte portA = 5                  //Direcciones de memoria         
#byte portB = 6                  //Direcciones de memoria

const int TOTAL=200;            //PASOS TOTALES 200*0,1ms=20ms -->50Hz
int pwm_HIGH=15;                //PASOS EN ALTO
//HIGH=10 -->1ms -->-90º
//HIGH=15 -->1.5ms -->0º
//HIGH=20 -->2ms -->+90º
int PASOS=0;
//     _____            ___
//    |     |          |
//    |     |          |
//____|     |__________|
//    /     /
//     HIGH
//    /      TOTAL     /

#int_TIMER2                      //INTERRUPCION TIMER2
void int_tmr2(void)
{
   PASOS++;
   IF(PASOS==TOTAL)              //CUANDO LLEGA A 200 PASOS VUELVE A 0
      {
      PASOS=0;
      output_HIGH(pin_b3);        //Mandas el pin a HIGH al comenzar a contar
      }
   IF(PASOS== PWM_HIGH)           //Cuando los PASOS alcancen el ciclo mandas LOW, solo necesitas 
      {output_LOW(pin_b3);         //compararlo una ves y no agregas intrucciones inecesarias
      }
}

void main(void)
{
   set_tris_b (0b11110111);            //DEFINE TRIS B 
   set_tris_a (0b11111);               //DEFINE TRIS A COMO ENTRADAS 1
   PORTA=(0x00);                       //INICIALIZA TODO EN 0
   PORTB=(0x00);
   
   setup_timer_0(RTCC_INTERNAL|RTCC_DIV_1);
   setup_timer_1(T1_DISABLED);
   enable_interrupts(GLOBAL);          //Habilito interrupciones globales
   enable_interrupts(INT_TIMER2);      //Habilito interrupción particular del TIMER1
   //setup_timer_2(t2_div_by_PRESCALER,PR2,POSTCALER);
   setup_timer_2(T2_DIV_BY_1,99,1);   //DESBORDA CADA 0,1MS/0,0001S, 99 aprox. 100
   
   while(TRUE)
   {
   }

   return;
}
```


----------



## Nuyel (Mar 28, 2013)

Es que para tener mayor resolución no lo lograras con cambiar el tipo de variable, la resolución dependerá de la variable TOTAL, pero el aumentar valor de esta incrementa el tiempo requerido para completar el ciclo, en ese caso tambien necesitas aumentar la frecuencia con en que se ejecuta la interrupción.
Por ejemplo, si ejecutas la interrupción cada 0,05ms puedes usar TOTAL = 400 y duplicas la resolución conservando los mismos tiempos.


----------



## ansaro360 (Feb 27, 2015)

el programa de cervo.rar alguien sabra como va el diagrama por lo que entiendo va las inputs van con push botton al a0 y a1 y la de la salida b0 va directo a la entrada del dato del servo motor ?
pero no funciona asi lo único que hace es ir a 90° y ya no funciona el programa


----------



## D@rkbytes (Feb 28, 2015)

Si va conectado así como mencionas pero ese programa tiene un error en una instrucción.
 if(y<18020);    // <--- Esta instrucción no tiene efecto.
y=18020;
Se tendría que quitar el punto y coma ";" pero aún eliminándolo, en simulación sigue sin funcionar.

Prueba este programa que adjunto. No lo he probado físicamente porque no tengo servomotores.


----------

