Autor Tema: Proyecto Encoder Incremental de 2 canales por USB  (Leído 18159 veces)

0 Usuarios y 1 Visitante están viendo este tema.

Desconectado JJcorps

  • PIC12
  • **
  • Mensajes: 51
    • Proyectos de JJcorps
Proyecto Encoder Incremental de 2 canales por USB
« en: 13 de Abril de 2008, 17:05:06 »
Hola!

Este es un antiguo proyecto que he retomado ahora. La razón de no continuar con el antiguo hilo es pq hace 6 meses de ese hilo por eso he decidido crear uno nuevo.

La definición del proyecto es simple: sacar toda la informacion posible de un encoder de 2 canales. Dicha información es: posición actual del encoder (pasos), sentido, velocidad actual, velocidad media y velocidad de transferencia de los paquetes USB.

El funcionamiento de este encoder esta explicado en el antiguo hilo pero voy a volver a exponerlo para tenerlo todo centralizado en este hilo, además de miedo a que se borre debido al tiempo q ha pasado.

ENCODER INCREMENTAL

Un encoder incremental es un dispositivo que transforma un movimiento rotatorio en pulsos digitales. Estos pulsos pueden ser usados para controlar desplazamientos de tipo angular o de tipo lineal si se asocia a una cremallera o un husillo



El encoder se compone de 3 partes:
  • Un disco ranurado formado con lineas opacas y transparentes
  • Un diodo LED
  • Uno o varios Fototransistores

El funcionamiento es el siguiente: cuando el sistema comienza a funcionar, el led crea un haz de luz que incide en el foto transistor; a medida que el eje vaya girando, se producirán una serie de pulsos de luz en el receptor, correspondientes a la luz que atarviesa los huecos entre las marcas. Llevando una cuenta de esos pulsos es posible conocer la posición del eje.

METODOS DE CONTROL

El encoder que voy a usar es un encoder incremental de 2 canales desfasados 90º hay algunos pic que incorporan el modulo QEI que es especifico para el control de estos encoders, por desgracia, ningun picUSB los incorpora.

Este tipo de encoder se puede usar de 3 maneras (sin modulo QEI):

  • Modo 1X: Usando solo un canal para el conteo y el otro obtener el sentido
  • Modo 2X: Atendiendo a los cambios de nivel para el conteo y para el sentido
  • Modo 4X: Atendiendo a los flancos para el conteo y para el sentido


Modo 1X: Usando solo un canal para el conteo y el otro obtener el sentido

Este modo es el mas sencillo de hacer solo se tiene que conectar el Canal A a la interrupción externa del pic, configuramos la interrupción por flanco de subida (aunque se puede hacer por flanco de bajada). El otro canal lo podemos conectar a cualquier patilla de configurada como entrada en el pic, esta patilla nos dara el sentido.

El funcionamiento de este método es el siguiente: Al girar el encoder de izquierda-derecha los flancos de subida son los flancos izquierdos del Canal A, fijandose en la imagen anterior, el nivel del Canal B en todos los flancos de subida del Canal A es nivel alto. Si el encoder gira derecha-izquierda los flancos de subida son los flancos derechos del Canal A, que coinciden con el nivel bajo del Canal B.

Código: [Seleccionar]
int16 pasos=0;
int sentido=1;
#int_EXT
void  EXT_isr(void)
{
   
   if(input(PIN_B1))    //Miro el estado de el otro canal q me indica el sentido
   {
      sentido=1;
      pasos=pasos+1;
   }
   else
   {
      sentido=0;
      pasos=pasos-1;
   }
}

Modo 2X: Atendiendo a los cambios de nivel para el conteo y para el sentido

Por Hacer....

Modo 4X: Atendiendo a los flancos para el conteo y para el sentido

Por Hacer....

PROGRAMACIÓN DEL FIRMWARE

El pic que uso es el 18F2550. El firmware lo tengo totalmente verde es un clon del proyecto de J1M pero en vez de hacer la suma manda el valor del encoder. Me da verguenza de ponerlo.  :P  En cuanto tenga algo mas personal actualizaré este apartado.

PROGRAMACION DE APLICACIÓN


El lenguaje que uso para la aplicacón es el Visual C# 2005 express. La aplicación hace un puleo al USB pidiendo el estado del encoder al pulsar el boton de Muestreo, mostrando el numero de pasos del encoder en el Label. Para ello he hecho 2 métodos nuevos en la clase PicUSBAPI:

Código: [Seleccionar]
       
public void Pulea(Label pasos)
        {
            poolling = true;
            while (poolling)
            {
                Application.DoEvents();
                SumaPIC(10, 10);
                pasos.Text = (ResultadoPIC()).ToString();
            }
        }
        public void PararPuleo()
        {
            poolling = false;
        }

Descargate el código

AYUDA

Despues de este rollo queria pediros vuestra ayuda en como implementar los modos 2X y 4X de los metodos de control, de la manera mas rápida posible ya que cuanta mas precision tiene el metodo mas veces va interrumpir al pic.

Gracias
« Última modificación: 14 de Abril de 2008, 13:15:41 por JJcorps »

Desconectado gu1llermo

  • Colaborador
  • PIC16
  • *****
  • Mensajes: 217
Re: Proyecto Encoder Incremental de 2 canales por USB
« Respuesta #1 en: 14 de Abril de 2008, 00:34:55 »
Hola! JJcorps, muy organizado y útil lo que estás haciendo, yo también tuve que diseñar un programa para interpretar las señales de los encoders, pero en mi caso eran dos encoders con un solo pic, más dos conversiones AD, lo puedes leer aquí ya lo implementé y funciona, pero no fuí tan ordenado como tú  :oops:

Los modos que haces mención yo los conozco como 1X, 2X y 4X, me es más fácil e intuitivo llamarlos así, porque digo, 1X: Leo 1 flanco en todo el período, 2X: leo 2 flancos y 4X: 4 flancos  8)

2X: Lo haría por interrupción de los pines RB7:RB4, vamos a elegir el pin RB7 como entrada para la señal A y otro pin distinto a RB6:RB4 para la señal B, por ejemplo RB1  :mrgreen: el código sería algo así como esto:

Código: [Seleccionar]
#define A PIN_B7
#define B PIN_B1
#define derecha 1
#define izquierda 0
int16 pasos=0;
int1 sentido=1;
int1 A_old;// Valor anterior de A
int1 A_new;// Valor actual de A
int8 aux;// Variable auxiliar para leer el puerto B

#int_RB
void  rb_isr(void){

   aux=input_b();// Leemos el puerto B
   
   A_new=bit_test(aux, 7);// Leemos el valor actual de A
   
   if ((A_old==1)&&(A_new==0)){// 'A' sufrió una transición descendente??
      if (input(B)) {
         sentido=derecha;
         pasos++;// Incrementamos pasos
      }
      else{
         sentido=izquierda;
         pasos--;// Decrementamos pasos
      }
   }
   else{// Entonces sufrió una transición ascendente, porque solo el cambio
   // de estado en el pin RB7 genera la interrupción
      if (input(B)) {
         sentido=izquierda;
         pasos--;// Decrementamos pasos
      }
      else{
         sentido=derecha;
         pasos++;// Incrementamos pasos
      } 
   }
   A_old=A_new;// Actualizamos el valor anterior, ojo!, muy importante!
}

void main(){
   ...
   aux=input_b();
   // Leemos la señal A, una sola vez nada más, es para tener un valor
   // referencial
   A_old=bit_test(aux, 7);
   ...
}

PD1: JJcorps, gracias a tu post me he dado cuenta que hubiese podido optimizar el programa que hice aquella vez (que no hace mucho tiempo tampoco) y con éste pic16f fácilmente podríamos leer las señales de 5 encoders al mismo tiempo en modo 1X, 4 conectados en los pines RB7:RB4 y 1 conectado a RB0, me refiero a su señal A por supuesto, y la señal B conectarla en cualquier otro pin.  :wink: 8)

PD2: Sería bueno que en vez de mostrar partes de códigos, se realizara el código completo y simulado en proteus de una vez  :idea: :c)

Saludos.
« Última modificación: 14 de Abril de 2008, 08:19:21 por gu1llermo »

Desconectado JJcorps

  • PIC12
  • **
  • Mensajes: 51
    • Proyectos de JJcorps
Re: Proyecto Encoder Incremental de 2 canales por USB
« Respuesta #2 en: 14 de Abril de 2008, 13:21:59 »
Gracias gu1llermo por la contestacion.

Sobe los codigos, ire actualizando el post inicial con ellos en cuanto lo tenga mas presentable, por ahora me estoy dedicando mas a  la aplicacion en Visual C.

He cambiado el nombre de los modos a 1X 2X y 4X como decia gu1llermo, con esos nombres los conocia yo tambien pero no me salieron a la hora de escribir el post :P

Desconectado Lealadrian

  • PIC10
  • *
  • Mensajes: 5
Re: Proyecto Encoder Incremental de 2 canales por USB
« Respuesta #3 en: 29 de Junio de 2010, 20:01:33 »
Hola! JJcorps, muy organizado y útil lo que estás haciendo, yo también tuve que diseñar un programa para interpretar las señales de los encoders, pero en mi caso eran dos encoders con un solo pic, más dos conversiones AD, lo puedes leer aquí ya lo implementé y funciona, pero no fuí tan ordenado como tú  :oops:

Los modos que haces mención yo los conozco como 1X, 2X y 4X, me es más fácil e intuitivo llamarlos así, porque digo, 1X: Leo 1 flanco en todo el período, 2X: leo 2 flancos y 4X: 4 flancos  8)

2X: Lo haría por interrupción de los pines RB7:RB4, vamos a elegir el pin RB7 como entrada para la señal A y otro pin distinto a RB6:RB4 para la señal B, por ejemplo RB1  :mrgreen: el código sería algo así como esto:

Código: [Seleccionar]
#define A PIN_B7
#define B PIN_B1
#define derecha 1
#define izquierda 0
int16 pasos=0;
int1 sentido=1;
int1 A_old;// Valor anterior de A
int1 A_new;// Valor actual de A
int8 aux;// Variable auxiliar para leer el puerto B

#int_RB
void  rb_isr(void){

   aux=input_b();// Leemos el puerto B
   
   A_new=bit_test(aux, 7);// Leemos el valor actual de A
   
   if ((A_old==1)&&(A_new==0)){// 'A' sufrió una transición descendente??
      if (input(B)) {
         sentido=derecha;
         pasos++;// Incrementamos pasos
      }
      else{
         sentido=izquierda;
         pasos--;// Decrementamos pasos
      }
   }
   else{// Entonces sufrió una transición ascendente, porque solo el cambio
   // de estado en el pin RB7 genera la interrupción
      if (input(B)) {
         sentido=izquierda;
         pasos--;// Decrementamos pasos
      }
      else{
         sentido=derecha;
         pasos++;// Incrementamos pasos
      } 
   }
   A_old=A_new;// Actualizamos el valor anterior, ojo!, muy importante!
}

void main(){
   ...
   aux=input_b();
   // Leemos la señal A, una sola vez nada más, es para tener un valor
   // referencial
   A_old=bit_test(aux, 7);
   ...
}

PD1: JJcorps, gracias a tu post me he dado cuenta que hubiese podido optimizar el programa que hice aquella vez (que no hace mucho tiempo tampoco) y con éste pic16f fácilmente podríamos leer las señales de 5 encoders al mismo tiempo en modo 1X, 4 conectados en los pines RB7:RB4 y 1 conectado a RB0, me refiero a su señal A por supuesto, y la señal B conectarla en cualquier otro pin.  :wink: 8)

PD2: Sería bueno que en vez de mostrar partes de códigos, se realizara el código completo y simulado en proteus de una vez  :idea: :c)

Saludos.


Hola
queria saber si podes pasar este codigo a ASEMBLER ya que necesito leer un encoder de iguales caracteristicas
y todavia no manejo el  C  :oops: no me interesa la coneccion USB. MI Intencion es que cada paso o pulso del
encoder corresponda a  1 mm lineal, lo que lograre con una rueda de un determinado diametro, acoplada al
eje del encoder, para poder medir una barra, la long. total de la barra sera de 6000 mm, y se efectuaran medidas
intermedias de distintos valores pejem. 2530 mm, 3255 mm, 500 mm, etc.  Lo q tendre que hacer es utilizar un sistema de 16 Bits.
les agradesere en lo que puedan ayudarme

gracias

Desconectado Oceano

  • PIC10
  • *
  • Mensajes: 22
    • Proyectos Roboticos.
Re: Proyecto Encoder Incremental de 2 canales por USB
« Respuesta #4 en: 04 de Julio de 2010, 19:03:15 »
Propongo un encoder con el PIC 16F876A, pero en principio puede ser cualquier PIC que tenga Int/RB0.

Pongo dos códigos, uno para CCS y otro para Proton IDE, antes de nada el esquema:


Funciona por la interrupción RB0/INT y cada vez que detecta un flanco (sea de subida o de bajada) hace cambiar el registro INTEDG. Así se consigue no perder pulsos, no cuenta de más ni de menos y es muy rápido. Uso un disco de 5 aspas (o dientes) en el eje de un motorcillo de juguete, lo alimento a 12 (es de 3v.) y pese a la velocidad que coge no pierde pulsos. Probar este código sólo te llevará un momento. Es esencial poner como acondicionador de señal del encoder un disparador Schmitt tipo 7414 ó 74LS14 (ver circuito de arriba); son puertas NOT con tecnología trigger Schmitt y TTL, funcionan la mar de bien. Evita usar como acondicionador de señal tecnología CMOS, como por ejemplo el 74HC14, porque al menos a mi me ha dado problemas intermitentes con esta tecnología. Para más info haz clic en el enlace final de este post.


Código para CCS

Código: [Seleccionar]
#include <16F876A.h>         //Usamos el PIC 16F876A.

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

#use delay(clock=4000000)  // Usamos un cristal de 4MHz.

#ZERO_RAM                        // Todo a cero.

#byte porta = 0x05              // Asignamos PortA (No lo usamos).
#byte portb = 0x06              // Asignamos PortB (Usamos RB0 y RB1).
#byte portc = 0x07              // Asignamos PortC (8 salidas a LED).

// ------ Variables Globales ------
int8   x=0;                          //Declaramos el valor de X como byte, es decir, 8 bits.
// ---------- Interrupción ----------

#INT_EXT
void IntRB0()
{
     //disable_interrupts(int_EXT);
     if (bit_test(portb,0))
     {  
         ext_int_edge(H_TO_L);
         if ((bit_test(portb,1)) == 1)
         {
            x++;
         }
     }
     else
     {  
         ext_int_edge(L_TO_H);
         if ((bit_test(portb,1)) == 1)
         {
            x--;
         }
     }
     //enable_interrupts(int_EXT);
}

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

void main()
{
   port_b_pullups(FALSE);
   setup_adc_ports(NO_ANALOGS);
   setup_adc(ADC_CLOCK_DIV_2);
   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);
  
   //---- Fin de la configuración del 16F876A ----
  
   enable_interrupts(int_ext);    //Activar Interrupcion Externa.
   ext_int_edge(L_TO_H);          //Inicialmente Interrupción por Flaco de Subida.
   enable_interrupts(GLOBAL);     //Interrupciones Generales Activadas.

   set_tris_a(0b111111);          //Puerto A como entrada (No usado).
   set_tris_b(0b11111111);        //Puerto B como entrada (sólo usamos RB0 y RB1).
   set_tris_c(0b00000000);        //Puerto C todo como salida (Salida a LED, 8 bits).

   While (true)
   {
          portc = x;               //El valor de X sale por el Puerto C, los 8 LED de salida.
   }      
  
}



Código para Proton IDE

Código: [Seleccionar]

Device=16F876A

REMINDERS = FALSE
     Config XT_OSC, PWRTE_ON, CPD_OFF, WDT_OFF, BODEN_OFF, LVP_OFF
REMINDERS = TRUE

Symbol INTF   = INTCON.1         ' RB0 External Interrupt Flag
Symbol INTE   = INTCON.4         ' RB0 External Interrupt Enable
Symbol GIE    = INTCON.7         ' Global Interrupt Enable
Symbol INTEDG = OPTION_REG.6     ' Flag = 0 Flanco bajada. Flag = 1 Flanco subida.

On_INTERRUPT GoTo Interrupcion   ' Interrupción por Hardware (es la más rápida).

GIE    = 1                       ' Activa interrupciones generales.
INTE   = 1                       ' Activa la interrupción externa RB0/INT.
INTEDG = 1                       ' Hace que inicialmente la interrupción se habilite
                                 ' para flanco de subida.

ALL_DIGITAL = TRUE               ' Todas las entradas y salidas son digitales.
                                                                              
TRISA  = %111111
TRISB  = %11111111               ' Puerto A y B todo entradas.
TRISC  = %00000000               ' Puerto C como salida para visualizar a través de los LED.
          
Dim x As  Byte                   ' Variable X ---> contador de posición actual.


x=0                              
 
While 1=1                        ' |------ Programa Principal ------|
    
     PORTC = x                   ' El contenido de X se visualiza en el Puerto C a través de los LED.

Wend                             ' |--------------------------------|
            
End                              

    
Interrupcion:                 '-------- Decodificador de Encoder --------------
      
    Context SAVE              ' Salva en contexto de los registros antes de operar con la interrupción.
    
    If PORTB.0 = 1    Then    ' Si RB0 se ha puesto a 1 (flanco de subida),
       INTEDG  = 0            ' entonces activar la siguiente interrupción para flanco de bajada.
       If PORTB.1 = 1 Then    ' Si RB1 está a 1
          Inc x               ' entonces incrementar el contador X.
       EndIf
    EndIf
    
    If PORTB.0 = 0    Then    ' Si RB0 se ha puesto a 0 (flanco de bajada),
       INTEDG  = 1            ' entonces activar la siguiente interrupción para flanco de subida.
       If PORTB.1 = 1 Then    ' Si RB1 está 1
          Dec x               ' entonces decrementar el contador X.
       EndIf
    EndIf
    
    INTF = 0                  ' Borra el "flag" de la interrupción RB0/INT para poder permitir la  
                              ' siguiente interrupción, cuando ocurra.
    Context Restore           ' Restablece el contexto de los registros tal como estaban antes de la
                              ' interrupción.

Información de este proyecto aquí:                     http://sites.google.com/site/proyectosroboticos/encoder/encoder-por-software

Información sobre encoder con sensor Hall aquí:  http://sites.google.com/site/proyectosroboticos/encoder/encoder-con-hall

Información sobre encoder electrónico puro aquí: http://sites.google.com/site/proyectosroboticos/encoder/encoder-por-hardware

Pic Encoder USB:                                               http://sites.google.com/site/proyectosroboticos/encoder/pic-encoder-usb
« Última modificación: 05 de Septiembre de 2010, 07:18:23 por Oceano »

Desconectado voc

  • PIC10
  • *
  • Mensajes: 2
Re: Proyecto Encoder Incremental de 2 canales por USB
« Respuesta #5 en: 12 de Agosto de 2010, 11:46:05 »
Que tal soy nuevo en el foro.. y eh esado leyendo muchos de los interesantes temas que se encuentran.

pues me tope que con este que en lo particular es de mi interes.. y pues anos ala obra encuanto lo vi..
implemente el primero de los emtodos que se publica ..
per0 he tenido algunos detalles.. con perdida de pulsos.
estoy utilizando el pic18f252  y el eocnder que estoy utilizando me genera 2048 pulsos por vuelta.... pero como necesito estar monitoreando cuantos pulsos se han contado. tengo habilitada tmb la interrupcion del RTCC..
para que me attive un flag  y de ahi mandar a imprimir. por el puerto serie en el cuerpo de codigo..
pero al momento que se ejecuta esta interrupcion.. los pulsos que llegaron en ese momento no me los cuenta...

Entonces he tenido la idea de quiza contar con el timer1 que me cuente los pulsos externos. bueno con dos timers..
porque solo contarian entonces con uno podria contar cuando girea un lado y con otro cuando gire hacia le otro y luego restarlos! xD
jjejej no se le he estado pensado..

saludos.. si alguien ha tenido algun problema de este tipo.. o si me dan una idea.. de antemano gracias!



Desconectado Oceano

  • PIC10
  • *
  • Mensajes: 22
    • Proyectos Roboticos.
Re: Proyecto Encoder Incremental de 2 canales por USB
« Respuesta #6 en: 21 de Agosto de 2010, 23:42:36 »
Hablo sin conocimiento: pienso que un pic (los que yo conozco: serie 16F y 18F) no puede contar tantos pulsos en una sola revolución y de forma bidireccional (en ambos sentidos y sin perdidas de pulsos). Si estuviera en lo cierto, se necesitaría una electrónica que contara esos pulsos, y el PIC leyera esa electrónica. Hacerlo directamente para esa resolución de encoder por vuelta no lo conozco.

Creo que algunos DSPIC sí lo pueden hacer. Te pongo un enlace:
http://techtrain.microchip.com/webseminars/documents/dsPICQEI_033005.pdf

Por otra parte, también me paso que si hacía comunicaciones por el puerto serie (COM1 por ejemplo), no es que perdiera pulsos (uso interrupciones por RB0/Int), sino que la comunicación fallaba y se enviaba caracteres erróneos.

Ahora he probado hacerlo por USB (en el anterior post está el enlace de cómo lo hice) y no me ha dado problemas ni en el contaje ni en la información que recibo (los números salen correctos en el monitor del PC). Desconozco la resolución que tiene mis encoders con PIC. Sólo sé que poniendo un disco de 5 aspas o dientes a un motor de 3V, lo alimento con 12 voltios y pese a la velocidad que coge sigue contando correctamente, sin perdidas de pulsos.
« Última modificación: 22 de Agosto de 2010, 00:10:56 por Oceano »


 

anything