Programación de Odómetro con PIC

Buenos dias compañeros del foro, pues ahora estoy realizando un proyecto que consiste en que por medio de un encoder rotacional(COM-10596)pueda medir distancias, el problema con el que me enfrento actualmente es que al momento que obtengo mis salidas de señal del encoder( que son dos, A y B) no me se me ocurre como programar en mi pic(16f877a) para que se de cuenta que voy avanzando o retrociendo el encoder, vi en la pagina del fabricante un programa para arduino mas sin embargo no entiendo muchas cosas, ya que nunca he manejado arduino.
Use un flip flop tipo D, pero no tuve mucho exito. Espero puedan ayudarme a resolver este problema. Ademas anexo mi codigo para que lo vean.
 

Adjuntos

  • Odometro.txt
    957 bytes · Visitas: 30
Buenos dias compañeros del foro, pues ahora estoy realizando un proyecto que consiste en que por medio de un encoder rotacional(COM-10596)pueda medir distancias, el problema con el que me enfrento actualmente es que al momento que obtengo mis salidas de señal del encoder( que son dos, A y B) no me se me ocurre como programar en mi pic(16f877a) para que se de cuenta que voy avanzando o retrociendo el encoder, vi en la pagina del fabricante un programa para arduino mas sin embargo no entiendo muchas cosas, ya que nunca he manejado arduino.
Use un flip flop tipo D, pero no tuve mucho exito. Espero puedan ayudarme a resolver este problema. Ademas anexo mi codigo para que lo vean.

Justamente, las 2 salidas por su diferencia de "Fase" permiten interpretar el sentido de avance o retroceso.
 

Adjuntos

  • 04359[1].png
    04359[1].png
    17.1 KB · Visitas: 164
Si exacto, entiendo eso. Pero a lo que voy es esto, van desfasadas, pero una se activa mientras la otra aún tiene un pulso alto, mientras A esta en 5v a la mitad de su ciclo, se activa B. Entonces ahí mi duda es, debido a que no se activa una primero y después la otra si no a la mitad, entonces como podría decirle yo a mi pic, si A se activa primero y B después incrementa, de lo contrario disminuye. Por eso comentaba que pensé en un usar un flip flop tipo D pero no me funciono para así predecir el próximo estado. Y por so preguntaba acerca de esa duda.ImageUploadedByTapatalk1395929640.645396.jpg
A manera ilustrativa coloco una imagen a ver si pueden entender el problema que tengo.
 
Mira con atención el dibujo y verás que cuando va en sentido horario los flancos de B siempre preceden a los de A y en sentido antihorario es al revés. Entonces sólo es cuestión de revisar cuál cambió de estado primero y eso te dice en qué sentido está girando.

Supongamos que está en reposo y B está en alto y A en bajo, si en la siguiente lectura A está en alto entonces está girando en sentido horario porque B precedió a A.
 
Última edición:
Sería más fácil leer el estado lógico y comparar la secuencia binaria del encoder. Por ejemplo si asignamos el bit 0 a la salida A y el bit 1 a la salida B la secuencia sería 2, 3, 1, 0 en sentido horario y al revés en sentido antihorario.
 
Hola ThaConectted

Mirando la imagen en tu mensaje #3 se puede determinar que si se genera primero B por el PIN 3 del encoder es que éste ha sido girado en el sentido de las manecillas del reloj (CW).
Por el contrario, si se genera primero A por el PIN 2 del encoder es que éste ha sido girado en el sentido contrario a las manecillas del reloj (CCW).

Entonces: la terminal 1 del encoder se conectaría al Vcc.
La terminal 2 se conectaría a un puerto del PIC y la terminal 3 a otro puerto del PIC.

Y:
Si 3 = 1
contar + 1

si 2 = 1
contar –1

Pero si no llega ninguno es que no se ha movido.

Cada que 3 = 1 se debe contar +1.
Cada que 2 = 1 se debe contar –1.

saludos
a sus ordenes
 

Adjuntos

  • Encoder.jpg
    Encoder.jpg
    53.5 KB · Visitas: 16
  • Flow Diagram.jpg
    Flow Diagram.jpg
    41.5 KB · Visitas: 19
En el esquema que publique puedes ver como se detecta el sentido de giro empleando un Flip-Flop


La salida del Flip-Flop la mandas al PIC, una da los pulsos de conteo y la otra el sentido de giro CW o CCW

El odometro de los automóviles siempre cuanta ascendente, NO descuenta, aunque vaya hacia atrás ;)
 
Muchas gracias, ya hize el contador, mas sin embargo me hace los cambios muy lentamente. A lo que voy es que cuando regreso hasta la segunda o tercera vuelta me regresa el contador,como podria hacer para que ese contador detecte el cambio mas rapido? y como podria traducir eso a una distancia?
 

Adjuntos

  • OdometroA.txt
    880 bytes · Visitas: 28
Hola ThaConectted

Analiza la imagen que adjuntaste en tu mensaje #3. notarás que ese encoder hace un cambio cada 90° de giro.

Eso quiere decir que no tiene mucha resolución. Sería mejor un encoder que te diera un cambio de estado en sus salidas cada 5° de giro.

saludos
a sus ordenes.
 
si tienes mucha razon,creo que es un problema, tratare de buscar uno mejor. Una pregunta mas, quiero seguir tratando de acomodar este, y por eso dentro de la programacion estoy colocando el contador pero este sigue avanzando siempre! hay alguna forma de que avanze y se quede esperando, y ya despues sume o reste dependiendo del estado?
 
Hola ThaConectted

Lo que ocurre con tu programa:

set_adc_channel(0);
delay_us(20);
A=read_adc(); NO debes leerlo como ANALOGO sino como Digital 1 o 0.
set_adc_channel(1);
B=read_adc(); NO debes leerlo como ANALOGO sino como Digital 1 o 0.
delay_us(20);
setup_adc(adc_off);

if(A>=220) Aquí debería ser: if(A=1)
{
contador=contador+1;
}

if(B>=220) Aquí debería ser: if(B=1)
{
contador=contador-1;
}

printf(lcd_putc,"%02.1f",contador);
delay_ms(1000);

además, antes de entrar a este bucle, debes declarar esos puertos como Digitales, o sea NO análogos.
O utilizar otros puertos que solo sean digitales.
Es probable que con eso se solucione el problema.

Otra cosa, me parece que estás tomando A y B en el otro sentido.
Ya que si A = 1 debe contar –1 y si B = 1 debe contar +1.

Pero te advierto yo casi no se de programación.

No se si se pudiera hacer por medio de interrupciones: si interrumpe A cuenta –1, si interrumpe B cuenta +1.
Dale una visitada a este enlace, tal vez tenga algo que te sirve:
https://www.forosdeelectronica.com/f24/maneja-encoder-pic-19542/

saludos
a sus ordenes
 
Hola, viendo el programa, puede que no funcione cómo uno lo espera.
Si analizamos las sig. líneas vemos que:

if(A>=220) Aquí debería ser: if(A=1)
{
contador=contador+1;
}

if(B>=220) Aquí debería ser: if(B=1)
{
contador=contador-1;
}

Cuando A es 1 incrementa contador, luego cuando B es 1 decrementa, verdad?
Pues si vemos la gráfica, del encoder podemos ver, cómo ocurren las señales, y es así:
Por cada avance ocurren dos flancos ascendentes, uno por parte de A y el otro en B.
Entonces el contador se incrementa y decrementa sin poder moverse más allá de ese valor
entonces debemos condicionar la sentencia if, algo así:

if(A==1 && B==0 ) contador=contador+1;

if(A==1 && B==1) contador=contador-1;

Porque esto?, pues en un sentido cada vez que ocurre un flanco ascendente en A, B valdrá 1 por ejem. y siempre será así. Pero en el sentido opuesto, cuando ocurre el flanco ascendente en A, B vale 0. De ésta manera se determina el sentido de giro y la acumulación de la variable "contador".
Debe tenerse en cuenta los posibles desbordes de dicha variable, es decir, si contador=0 no podemos continuar decrementando y lo mismo ocurre si alcanza su max. valor, no podemos continuar incrementando.
En éste caso como leemos valores digitales, no tiene sentido utilizar el ADC para determinar un nivel y considerarlo cómo L o H. Sólo lee el valor del puerto.
 
Última edición:
Ok, creo que tienes razón déjame probarlo y te informo que obtuve



Acabo de probarlo si inicia el contador más sin embargo no logró detener el contador, ya que se sigue avanzando,ya sea hacia adelante o atrás.
 
Última edición:
Pues una opción es la que ponía fogonazo arriba conectarlo al flip flop, pero lo conecto y sigue detectando nivel. Lo estoy tratando de hacer programado todo por medio de software pero no se bien como implementarlo. He leído que por interrupciones pero no tengo un ejemplo claro de como usarlas, podrían apoyarme con algún buen ejemplo, para entender?

Listo, como no pude acomodar por medio de hardware mi encoder decidi empezar a hacerlo por medio de software, junto con los links que estuve viendo, aunque ahora me surge una duda en el codigo que anexo estoy obteniendo una salida de 8 bits de que forma podria manipularla para colocarla a un LCD y que me muestre el avanze y retroceso?

Código:
#include <16F628A.h>


#FUSES NOWDT, XT, PUT, NOPROTECT, NOBROWNOUT, NOLVP, NOCPD

#use delay(clock=4000000)


#byte porta = 0x05        // Asignamos PortA.
#byte portb = 0x06        // Asignamos PortB.

// ---------- Programa Principial ----------

void main()
{
   port_b_pullups(FALSE);                   // Sin resistencias pullups a las salidas del puerto B.
   setup_timer_0(RTCC_INTERNAL|RTCC_DIV_1); // Al no usar el TIMER configuramos lo más básico. 
   setup_comparator(NC_NC_NC_NC);           // Sin comparadores.
   setup_vref(FALSE);                       // Al no usar comparadores no necesitamos Vref.  
   
   //---- Fin de la configuración del 16F628A ----

   int8   x;    // Declaramos el valor de X como byte, se corresponderá con los 8 LEDs de salida.
   int8   enc;  // Se almacenará el valor actual de RA0 y RA1, hasta la siguiente comparación.
   int8   aux;  // Se almacenará el valor anterior de RA0 y RA1, hasta la siguiente comparación.

   set_tris_a(0b11111);     // Puerto A como entradas. Sólo usamos RA0 y RA1.
   set_tris_b(0b00000000);  // Puerto B como salidas, para los 8 LEDs.
   
   portb=0;   // Inicialmente ponemos a cero el puerto B.
   x=0;       // Inicialmente ponemos a cero la variable que se usa para contar.
   enc=0;     // Inicialmente ponemos a cero la variable que tomará los valores de RA0 y RA1.
   
     
   
   While (true)
   {
      
          aux=enc;               // Igualamos 'AUX' y 'ENC' para luego comparar cuando cambie 'ENC'.
          enc=porta & 3;         // Aislamos RA0 y RA1 como un número de 2 bits y se carga en la variable 'ENC'.
          
          If ((aux==2)&&(enc==3))// Si en la comparación hay flanco de subida,
          {  
              x++;               // entonces incrementar una unidad el valor de X.
          }
          
          If ((aux==3)&&(enc==2))// Si en la comparación hay flanco de bajada,
          {  
              x--;               // entonces decrementar una unidad el valor de X.
          }
          
          portb = x;             // El valor de X sale por el puerto B, los 8 LED de salida.  
   }
              
}
 
Última edición por un moderador:
Vas muy bien con tu programa, pero le faltan más comparaciones:

Código:
#include <16F628A.h>


 #FUSES NOWDT, XT, PUT, NOPROTECT, NOBROWNOUT, NOLVP, NOCPD

 #use delay(clock=4000000)


 #byte porta = 0x05 // Asignamos PortA.
 #byte portb = 0x06 // Asignamos PortB.

 // ---------- Programa Principial ----------

 void main()
 {
 port_b_pullups(FALSE); // Sin resistencias pullups a las salidas del puerto B.
 setup_timer_0(RTCC_INTERNAL|RTCC_DIV_1); // Al no usar el TIMER configuramos lo más básico. 
 setup_comparator(NC_NC_NC_NC); // Sin comparadores.
 setup_vref(FALSE); // Al no usar comparadores no necesitamos Vref. 

 //---- Fin de la configuración del 16F628A ----

 int8 x; // Declaramos el valor de X como byte, se corresponderá con los 8 LEDs de salida.
 int8 enc; // Se almacenará el valor actual de RA0 y RA1, hasta la siguiente comparación.
 int8 aux; // Se almacenará el valor anterior de RA0 y RA1, hasta la siguiente comparación.

 set_tris_a(0b11111); // Puerto A como entradas. Sólo usamos RA0 y RA1.
 set_tris_b(0b00000000); // Puerto B como salidas, para los 8 LEDs.

 portb=0; // Inicialmente ponemos a cero el puerto B.
 x=0; // Inicialmente ponemos a cero la variable que se usa para contar.
 enc=0; // Inicialmente ponemos a cero la variable que tomará los valores de RA0 y RA1.



 While (true)
 {

 aux=enc; // Igualamos 'AUX' y 'ENC' para luego comparar cuando cambie 'ENC'.
 enc=porta & 3; // Aislamos RA0 y RA1 como un número de 2 bits y se carga en la variable 'ENC'.

[COLOR="Red"]//acuérdate que la secuencia de valores es 0, 2, 3, 1 cw y 0, 1, 3, 2 ccw[/COLOR]

     if(aux==0){
               if (enc==2){x++;}
               else if (enc==1){x--;}
     }
     else if(aux==1){
               if (enc==0){x++;}
               else if (enc==3){x--;}
     }
     else if(aux==2){
               if (enc==3){x++;}
               else if (enc==0){x--;}
     }
     else if(aux==3){
               if (enc==1){x++;}
               else if (enc==0){x--;}
     }

portb = x; // El valor de X sale por el puerto B, los 8 LED de salida.
}
}

O se podría hacer de la siguiente manera usando el operador xor ^ lo que se te haga más conveniente. Nota que si el xor da como resultado 0 significa que el encoder no ha cambiado de estado.

Código:
if ((aux==0)||(aux==3)){
     aux=aux^enc;
     if(aux==2){
          x++;
     }
     else if(aux==1){
          x--;
     }
}
else{
     aux=aux^enc;
     if(aux==2){
          x--;
     }
     else if(aux==1){
          x++;
     }
}
 
Última edición:
Muchas gracias eso que me dices no lo sabia, oye pero una pregunta sabes como puedo hacer para que esos bits en vez de que los muestre como leds, pueda procesarlos para que aparescan en el LCD?
 
Atrás
Arriba