Como se maneja un encoder con el PIC?

No entiendo como se debe de manejar un encoder con un microcontrolador, por ejemplo, tecnicamente no puedes usarlo como un switch por que cambia su estado en cada vuelta, por decirlo de una forma: esta en normalmente abierto al girarlo se cierra y queda en este estado, al girarlo un poco mas se abre y otra vez se queda en este estado. Como hay que manejar esto? alguien a visto algun ejemplo? de preferencia en asm. Si, es la primera vez que intento usar un encoder y estoy totalmente verde.


EC11J_D_21.GIF


EC11J_C_11.GIF


EC11J_C_12.GIF
 
No puedo creer que tengas tanta suerte... justo acabo de realizar un programa para 16f84a y un compañero para el 18f4550. te dejo los dos para ayudarte. Claro uno en asm y el otro en C

ambos sirven.... el que yo hice es mas barato, solo eso...

PD: Lo hice para la facultad.
 

Adjuntos

  • enconder1_108.7z
    35.8 KB · Visitas: 1,105
  • encoder_utn_177.asm
    3.6 KB · Visitas: 791
en realidad estaba con poco tiempo, pero debo explicarte que hacen los programas.

emiten una señal para el driver de un motor paso a paso, siendo medido por el encoder.... pero que a su ves, la señal es recibida del pc. Osea... con la PC pido que de 1/16 vuelta, y el motro lo hace hasta que el encoder mide que se realizo eso.

los encoder trabajan con una señal defasada (90ª) en la señal A y B en relacion de si va hacia adelante o hacia atras, no estoy seguro que es lo que se te complica, porque no me lo aclaras muy bien. Pero te puedo asegurar que vos deberias trabajar con las interrupciones, para poder ver las señales (sincronizarlas con el pic).

siempre pensa en el hecho tambien de las frecuencias que se manejan, mi programa en ASM esta ideado para 20 khz de señales de entrada/salida maxima, aprox. (debido a la "DEMORA" que cree. y la cantidad de pasos que tiene). Pensando en que mi PIC trabaja a 4mhz. (en realidad la salida es la que sale lenta.)
 

Adjuntos

  • imagen000_306.jpg
    imagen000_306.jpg
    208.8 KB · Visitas: 809
Lo que quiero hacer con este es lo siguiente: Tengo un reloj y para cambiar la hora(por ejemplo) utilizo dos switch uno para aumentar y otro para disminuirla, simplemente quiero quitar los interruptores y usar este encoder si gira para la izq disminuye y para la derecha aumenta.
Viendo tu codigo se me ocurrio una forma, voy a intentar armar algo y lo pongo.
Como puedes ver no voy a ser algo muy sofisticado.
 
mmmm... solo de curioso no mas.... crei que los encoder siempre eran cosas de alta precision, donde la mecanica no entra,( voy a tener que averiguar de eso... ) y tampoco son para lugares donde hay 2 posciones, incluso son para "MEDIR".

Bueno me alegro que te halla servido, y sobre todo me alegro que te haya sido util mi programa.
 
Bueno aqui hay una confusion mia, porque yo sabia que los encoder (en un topico mecanico) eran los que son discos perforados
MT029_Figure1.gif


el detalle es que la mayoria de los aparatos nuevos de audio, que trabajan digitalmente utilizan una especie de potenciometro "digital" y al abrirlos puedes verlos, investigue un poco y al buscar al fabricante (ALPS http://www.alps.com/ ) este los llama encoders ò switch/encoder y trabajan internamente de forma mecanica, bueno este es el tipo de encoder que quiero utilizar, espero no haber expresado mal mi primer post y haberte confundido.

swigalpsec11bc15.jpg
 
Hice un programilla pero es sin utilizar interrupciones para el tipo de uso que le pienzo dar creo que estara bien, aqui dejo el codigo y un video funcionando.


Código:
	list	p = 12F675

	#include	P12F675.inc

	ERRORLEVEL -302

	__CONFIG	( _PWRTE_OFF & _BODEN_OFF & _MCLRE_OFF & _CP_OFF & _WDT_OFF & _INTRC_OSC_NOCLKOUT  )
	

PDEL1    	EQU     0x20
PDEL2    	EQU     0x21	
PDEL3		EQU		0x22 
DEL1		EQU		0x23
DEL2		EQU		0x24

LFE			EQU		0x25
RFE			EQU		0x26
LFEC		EQU		0x27
RFEC		EQU		0x28

RETARDO		EQU		0x29


;-------------------------------------------------------------------

start
	ORG	0x00

	bcf 	STATUS,RP0 	;Bank 0
	bcf 	INTCON,GIE 	;Desabilita INTs 
	movlw 	07h 		; GP<2:0>  
	movwf 	CMCON 		;digital IO
	bsf 	STATUS,RP0 	;Bank 1
	clrf 	ANSEL 		;Digital I/O
	movlw 	B'00111000'	;GP<0,1,2> salidas GP<3,4,5> entradas
	movwf 	TRISIO 		;se carga la configuracion

	bcf 	STATUS,RP0 	;Bank 0

	clrf	GPIO		;limpia el puerto

	clrf	LFE
	clrf	RFE






INICIO
	call	ENCODER		;llama a checar el encoder
	call	REGRESA		;en regresa vuelve al estado normal
	goto	INICIO		;bucle


REGRESA
	bcf		GPIO,0		;apaga el led 
	bcf		GPIO,2		;apaga el led
	bsf		GPIO,1		;enciende el led de enmedio
		RETURN			;regresa


DER	bcf		GPIO,1		;apaga el led
	bcf		GPIO,0		;apaga el led
	bsf		GPIO,2		;enciende el led del pin 5
	call	DEMORA		;llama a un retardo
		RETURN			;regresa


IZQ	bcf		GPIO,1		;apaga el led
	bcf		GPIO,2		;apaga el led
	bsf		GPIO,0		;enciende el led del pin 7
	call	DEMORA		;llama un retardo
		RETURN			;regresa



ENCODER
	movfw	LFE			;el valor previo se carga a w
	movwf	LFEC		;y se guarda para compararlo
	movfw	RFE			;el valor previo se carga a w		
	movwf	RFEC		;y se guarda para compararlo
	

	bcf		RFE,0		;se pone en 0 el bit
	btfsc	GPIO,4		;si se detecta lo contrario se cambia
	bsf		RFE,0		;a uno


	bcf		LFE,0		;se pone en 0 el bit
	btfsc	GPIO,3		;si se detecta lo contrario se cambia
	bsf		LFE,0		;a uno
		
	movfw	RFE			;se carga a w el registro nuevo detectado
	xorwf	LFE,0		;se hace una operacion or exlusica buscando un cambio
	btfsc	STATUS,Z	;si esto no ocurre se regresa al llamado
		RETURN
	
	movfw	LFE			;si ocurre se carga a w el registro recien ingresado
	xorwf	LFEC,0		;y se compara con el anterior
	btfss	STATUS,Z	;si son diferentes llama a una funcion
	goto	IZQ			;en este caso IZQ
		
	movfw	RFE			;si ocurre se carga a w el registro recien ingresado
	xorwf	RFEC,0		;y se compara con el anterior
	btfss	STATUS,Z	;si son diferentes llama a una funcion
	goto	DER			;en este caso DER
		RETURN			
		



		


;-------------------------------------------------------------
;RETARDOS
;-------------------------------------------------------------

DEMORA  
 	movlw   .3				;Carga un numero para la DEMORA
 	movwf   PDEL3			;en este Registro
	movlw	.150 	     	;Obtiene el valor del retardo  
 	movwf   PDEL2			;en este Registro
 	movlw   .255			;Carga un numero para la DEMORA
 	movwf   PDEL1			;en este Registro
 	decfsz  PDEL1,1			;Decremente el segistro
 	goto    $-1				;hasta que llega a 0 y salta esta instruccion
	decfsz  PDEL2,1			;Decremente el registro
 	goto    $-5				;hasta que llega a 0 y salta esta instruccion
	decfsz  PDEL3,1			;Decremente el registro
 	goto    $-9				;hasta que llega a 0 y salta esta instruccion	
		RETURN	              

	



	END



YouTube - Encoder con PIC
 
Te pongo un PIC Encoder. Funciona perfectamente y está probado. Tienes el esquema, y dos versiones para programarlo, en CCS y en Proton IDE.

Funciona por la interrupción RB0/INT. Es la más rápida y realmente puede llegar a contar muy rápido. Lo probé poniéndolo en el eje de un motor de 3V y no pierde pulsos. Probar uno de los dos códigos sólo te llevará un momento.

Encoder%2016F876%20FotoBarrera.PNG


Código en CCS:

Código:
#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, por ejemplo. 

#ZERO_RAM                       // Variables a cero.

#byte porta = 0x05              // Asignamos PortA
#byte portb = 0x06              // Asignamos PortB
#byte portc = 0x07              // Asignamos PortC

// ------ Variables Globales ------
int8   x = 0;                   // Declaramos el valor de X como Byte, es decir, 8 bits.
                                // Esta variable ha de ser global porque su valor lo usaremos
                                // en la interrupción y en el programa principal.

// --------- Interrupción ---------
#INT_EXT                        // Interrupción: Decodificación de Encoder.
void IntRB0() 
{
   // CCS se encarga de desactiva automáticamente cualquier interrupción.
   // No hace falta guardar contextos de registros, al menos con el PIC 16F876A.

   if (bit_test(portb, 0))      // Si PortB.0 = 1,
   {  
       ext_int_edge(H_TO_L);    // entonces activar la siguiente interrupción para flanco de bajada. 
       if (bit_test(portb, 1))  // Si PortB.1 = 1,
       {
           x++;                 // entonces incrementar una unidad el valor de X.
       }
   }
   else                         // Si PortB.0 = 0, 
   {  
       ext_int_edge(L_TO_H);    // entonces activar la siguiente interrupción para flanco de subida.
       if (bit_test(portb, 1))  // Si PortB.1 = 1,
       {
           x--;                 // entonces decrementar una unidad el valor de X.
       }
   }
   // Al finalizar la interrupción CCS se encarga de volver a poner automáticamente
   // la badera INTF = 0 ---> borra la interrupción para poder permitir la siguiente;
   // no hemos de hacer nada por nuestra parte, al menos con el PIC 16F876A.
}

void main()                     // Inicio y configuración.
{
   port_b_pullups(FALSE);       // Configuración para el PIC 16F876A.
   setup_adc_ports(NO_ANALOGS); // Sin comparadores ni ADCs, todo digital, etc...
   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);
   
   enable_interrupts(int_ext);  // Activar Interrupción Externa. 
   ext_int_edge(L_TO_H);        // Inicialmente detectar interrupción por flanco de subida. 
   enable_interrupts(GLOBAL);   // Interrupciones Generales Activadas. 

   set_tris_a(0b111111);        // Puerto A todo entradas (en este caso no usamos el Puerto A).
   set_tris_b(0b11111111);      // Puerto B todo entradas (sólo usamos las entradas RB0 y RB1).
   set_tris_c(0b00000000);      // Puerto C todo salidas  (8 bits que irán a los LEDs).

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

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

Código en Proton IDE:

Código:
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 int. por flanco bajada. Flag = 1 int. por 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 con resolución 0..255


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.

Más información aquí:
http://sites.google.com/site/proyectosroboticos/encoder/encoder-por-software
 
Última edición:
aqui te dejo dos uno para encoder rotativo de giro loco y el otro de auto posicionado
use un pic 16f84 en los pines ra0 y ra1 puse el encoder ( pin A y B el comun a 5V)
y en el puerto b 8 led. con esto podras ver el funcionamiento


PD: parti de este pdf
 

Adjuntos

  • encoder1.txt
    974 bytes · Visitas: 577
  • encoder2.txt
    1.2 KB · Visitas: 274
  • RotaryEncoder.pdf
    31.5 KB · Visitas: 568
Última edición:
hola. estuve mirando sus ejemplos. Lo que yo necesito es un contador que incremente o decremente segun el giro de un encoder rotativo tipo potenciometro, este contador se visualiza en un display lcd de 16x2. Lo estoy trabajando con el pic 16f84 pero me parece que la forma de hacerlo es por interrupciones por cambio de estado de los pines RB4 a RB7. Estoy armando el código pero no se si debería hacerlo de esa manera o por interrupciones por RB0
 
nuevamente escribo por que no recibí respuesta alguna y la verdad sigo sin entender como plantear el código en assembler para incrementar o decrementar un contador leyendo un encoder rotativo. Me gustaría poner algún avance pero la verdad es que no se ni como empezar.Vi muchos ejemplos pero no los entiendo
 
De pics ni idea, hace mil años que no uso uno.
Hace poco hice el código con Arduino de lo mas sencillo.
Una de las señales a una interrupción, si al recibirla la otra es 0 es que gira en un sentido y si es 1 es que es el contrario. Dos líneas de código.
 
hola. estoy utilizando el pic16f84 y los pines de interrupciones por rb0 o de cambio de estado(rb4 a rb7) ya los tengo ocupados. Necesito un ejemplo de rutina que sea capaz de distinguir el sentido de giro

los ejemplos que vi no los llego a comprender bien por eso mi duda
 
Entonces está complicado, hay que detectar un flanco en uno de los pines y ver en ese momento como está el otro pin, eso consume mucha cpu
 
si se puede hacer por software
algo que puedes hacer cuando se te acaban los registros especiales o el hardware especial es que puedes emular todo lo que se te ocurra.
eso si gastas memoria y ciclos de reloj

aca te dejo un codigo que use , esta escrito en lenguaje C del PICC COMPILLER de CCS


PHP:
#include...
#Byte portb = 0xF81


   char   enc;  // Se almacenará el valor actual de RA0 y RA1, hasta la siguiente comparación.
   char   aux;  // Se almacenará el valor anterior de RA0 y RA1, hasta la siguiente comparación.
  
unsigned char ILIM;//incrementa o decrementa mi corriente maxima 

while(1)
{

//--------AQUI SE LEE EL ENCODER -----------------------------------//
            aux=enc;               // Igualamos 'AUX' y 'ENC' para luego comparar cuando cambie 'ENC'.
            enc=portb & 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,
            {  
               ILIM++;               // entonces incrementar una unidad el valor de X.
       
            }
          
            if ((aux==3)&&(enc==2))// Si en la comparación hay flanco de bajada,
            {  
               ILIM--;               // entonces decrementar una unidad el valor de X.
               if(ILIM<=1)
               {
                  ILIM=1;
               }
            }
        //--------------------SE DEJO DE LEER EL ENCODER---------//

/*
TU CODIGO
*/


}
 
Última edición por un moderador:
buenas. estuve tratando de interpretar el código en ccs pero no me queda bien en claro. Si no es molestia me vendría bien una mano para la interpretación y adaptación del código en lenguaje ensamblador. Muchas gracias.
 
Atrás
Arriba