Codigo C Control de servomotor (Grados) con PIC16F84A

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.

Código:
#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
 
Última edición por un moderador:
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.
 
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 :facepalm: ) bueno a base de eso intente hacer parte del programa que necesito, pero no funciona, no soy muy bueno en esto :confused: espero alguien me pueda decir en que estoy mal ...

Código:
# 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;
         
  }
 }
 
Última edición por un moderador:
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...:unsure:

Saludos
 
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.
 
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...:unsure:

Saludos

Ya he logrado el correcto funcionamiento del servomotor.

Saludos
 
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
 
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:
Código:
#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
 
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.
Código:
#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?
      }
}
 
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:
Código:
#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;
}
 
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.
 
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
 
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.
 

Adjuntos

  • Esquema PWM Servo.jpg
    Esquema PWM Servo.jpg
    62.5 KB · Visitas: 127
  • 16F84A PWM Servo.rar
    20.7 KB · Visitas: 188
Atrás
Arriba