PIC Encoder con un 18F4550.

En los apartados anteriores hemos utilizado los PICs 16F628A y 16F876A. Ahora haremos un encoder simple con el PIC 18F4550 y es válido para todos los PIC 18Fxx5x.

A y B son las entradas para un encoder tipo foto barrera (encoder óptico), usando por ejemplo dos TCST 1103.

No recomiendo usar encoder mecánicos (tipo potenciómetro) cuando se usa interrupción externa como en este caso. Sí puedes hacerlo con sensores Hall digitales, entonces puedes quitar los dos 74LS14 que hacen de acondicionador de la señales del encoder e inyectarlas directamente al PIC. Si tienes cualquier tipo de duda sobre la construcción o uso de un encoder, haz clic aquí.

Funciona de la siguiente manera: Podemos hacer que la interrupción se active con un flanco de subida o de bajada, esto es programable modificando el registro INTEDG. Cuando ocurre una interrupción por flanco de subida hemos de programar la siguiente interrupción para que se active por flanco de bajada e incrementamos el contador si RB1 está a 1. Y viceversa, si ocurre una interrupción por flanco de bajada hemos de programar la siguiente interrupción por flanco de subida y decrementamos el contador si RB1 está a 1.

Los PICs 18F2455 y 18F2550 no tienen puerto D, tendrías que hacer la salida a los LED a través del puerto A (sin olvidar poner una resistencia de RA4 a positivo porque esa salida es de colector abierto). En este caso sólo tendríamos 6 bits en vez de 8 pero para ver el funcionamiento de conteo es suficiente.

En principio el programa está configurado para un cristal de 4MHz pero como en este ejercicio no usamos el USB puedes usar el cristal que desees mientras esté en el rango de 4 a 20MHz porque no utiliza temporizaciones ni referencias temporales.

Existen algunos cambios según sea CCS o Proton IDE:

  • En Proton IDE, la dirección del registro OPTION_REG.6 (16Fxxxx) pasa a llamarse INTCON2.6 (18Fxx5x), es el registro en el que configuramos si queremos la interrupción cuando se produzca un flanco de subida o de bajada. Haz clic aquí para más información. Es lo único que cambia en Proton IDE.
  • En CCS la dirección de los puertos A B C D E cambia de lugar.

Expongo dos versiones del programa PIC Encoder para la familia 18Fxx5x: en CCS y en Proton IDE.

(Teniendo en cuenta que los PIC 18F2455/2550 no tienen puerto D y habría que cambiar este detalle por el puerto A.)

Código PIC Encoder en CCS:

The translation could modify the code. Use the code without translating.
#Include <18F4550.h>           // Usamos el PIC 18F4550, puedes declarar cualquiera de los 18Fxx5x.
#Fuses XT, NOWDT, NOPROTECT, NOLVP, BROWNOUT
#use Delay( Clock = 4000000 )

#Byte PortA = 0xF80            // Dirección del puerto A para la familia 18Fxx5x.
#Byte PortB = 0xF81            // Dirección del puerto B para la familia 18Fxx5x.
#Byte PortC = 0xF82            // Dirección del puerto C para la familia 18Fxx5x.
#Byte PortD = 0xF83            // Dirección del puerto D para la familia 18Fxx5x (Sólo 40/44 pines).
#Byte PortE = 0xF84            // Dirección del puerto E para la familia 18Fxx5x.

// ------ 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 eso declaramos esta variable antes la interrupción
                                // y de "void main".

// --------- Interrupción ---------
#INT_EXT                        // Interrupción Externa por RB0: Decodificación de Encoder.
void IntRB0()                   // Si te da error en esta línea, sustituye "void IntRB0()" por "void int_ext_isr(void)"
{
   // CCS se encarga de desactiva automáticamente cualquier interrupción.
   // No hace falta guardar contextos de registros.
   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 18F4550.
   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 a través de RB0. 
   Ext_Int_Edge(L_TO_H);        // Inicialmente detectar interrupción por flanco de subida. 
   Enable_Interrupts(GLOBAL);   // Interrupciones Generales Activadas. 
   Set_Tris_A(0b11111111);      // 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(0b11111111);      // Puerto C todo entradas, en este caso no usamos el Puerto C.
   Set_Tris_D(0b00000000);      // Puerto D todo salidas  (8 bits que irán a los LEDs).
   Set_Tris_E(0b11111111);      // Puerto E todo entradas, en este caso no usamos el Puerto E.
   
   // ---------- Programa Principial ----------
   While (True)
   {
          PortD = x;            // El valor de X sale por el Puerto D a los 8 LED. 
   }      
}

Código PIC Encoder en Proton IDE:

The translation could modify the code. Use the code without translating.
Device = 18F4550  ' Usamos el PIC 18F4550, pero puedes declarar cualquiera de los 18Fxx5x.
REMINDERS = 1     ' Comunicar todos los avisos de compilación.
XTAL = 4          ' Pon el cristal que quieras; en principio usamos un cristal de 4MHz.
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 = INTCON2.6        ' Flag = 0 int. por flanco bajada. Flag = 1 int. por flanco subida.
On_INTERRUPT GoTo Interrupcion   ' Interrupción por externa (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
                                 ' por flanco de subida.
ALL_DIGITAL = TRUE               ' Todas las entradas y salidas son digitales.
                                                                              
TRISA  = %11111111               ' Puerto A todo entradas, en este caso no usamos el Puerto A.
TRISB  = %11111111               ' Puerto B todo entradas, sólo usamos las entradas RB0 y RB1.
TRISC  = %11111111               ' Puerto C todo entradas, en este caso no usamos el Puerto C.
TRISD  = %00000000               ' Puerto D todo salidas  (8 bits que irán a los LEDs).
TRISE  = %11111111               ' Puerto E todo entradas, en este caso no usamos el Puerto E.          
Dim x As  Byte                   ' Variable X ---> contador de posición actual con resolución
                                 ' 0..255.
x=0                              
 
While 1=1                        ' |------ Programa Principal ------|
     
     PORTD = x                   ' El contenido de X se visualiza en el Puerto D a través de los
                                 ' LEDs.
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 por flanco de
                              ' bajada.
       If PORTB.1 = 1 Then    ' Si RB1 está a 1,
          Inc x               ' entonces incrementar el contador X.
       EndIf
    Else                      ' Si RB0 se ha puesto a 0 (flanco de bajada),  
       INTEDG  = 1            ' entonces activar la siguiente interrupción por 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.