Un ejemplo de encoder incremental usando la interrupción externa RB0/INT con Proton IDE o en CCS
y usando un PIC 16F876A, pero puede ser cualquiera de los 16F, sólo has de cambiar el nombre del PIC. Es muy importante acondicionar las señales de los encoders ópticos porque si no, no funcionaría. Se usa para acondicionar la señal del encoder un 7414 ó 74LS14 (siempre de tecnología TTL), nunca uses CMOS (por ejemplo el 74HC14) porque te fallaría, comprobado.
Esquema, código y web con completa información sobre este tema:
(http://sites.google.com/site/proyectosroboticos/encoder/encoder-por-software/Encoder16F876FotoBarrera.PNG?attredirects=0)
Código Proton IDE:
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.
Código en CCS:
#Include <16F876A.h> // Usamos el PIC 16F876A.
#Fuses NOWDT, XT, NOPROTECT, NODEBUG, NOBROWNOUT, NOLVP, NOCPD, NOWRT
#Use Delay( Clock = 4000000 ) // Usamos un cristal de 4MHz, por ejemplo.
#Byte PortA = 0x05 // Dirección del puerto A
#Byte PortB = 0x06 // Dirección del puerto B
#Byte PortC = 0x07 // Dirección del puerto C
// ------ Variable Global ------
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. Por tanto declaramos esta variable antes
// de la interrupción y de "Void Main".
// --------- Interrupción ---------
#Int_Ext // Interrupción Externa por RB0: Decodificación de Encoder.
Void IntRB0()
{
// CCS se encarga de desactiva automáticamente cualquier interrupción.
// No hace falta guardar contextos de registros por que CCS se encarga de hacerlo por ti.
If (Bit_Test(PortB, 0)) // Si RB0 se ha puesto a 1 (flanco de subida),
{
Ext_Int_Edge(H_TO_L); // entonces activar la siguiente interrupción por flanco de bajada.
If (Bit_Test(PortB, 1)) // Si RB1 está a 1,
{
x++; // entonces incrementar una unidad el valor de X.
}
}
Else // Si RB0 se ha puesto a 0 (flanco de bajada),
{
Ext_Int_Edge(L_TO_H); // entonces activar la siguiente interrupción por flanco de subida.
If (Bit_Test(PortB, 1)) // Si RB1 está 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.
}
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);
Ext_Int_Edge(L_TO_H); // Inicialmente detectar interrupción por flanco de subida.
Enable_Interrupts(Int_Ext); // Activar Interrupción Externa.
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.
}
}
Más info aquí: http://sites.google.com/site/proyectosroboticos/encoder/encoder-por-software