Programa Proton IDE de un PIC Encoder con un 16F876A, 16 bits de resolución, por Interrupción.

Primera Versión:

Device=16F876A
 
REMINDERS = FALSE
     Config XT_OSC, PWRTE_ON, CPD_OFF, WDT_OFF, BODEN_OFF, LVP_OFF
REMINDERS = TRUE
ALL_DIGITAL = TRUE 
On_INTERRUPT GoTo Interrupcion
Symbol INTE   = INTCON.4         ' RB0 Habilitar interrupción externa.
Symbol INTF   = INTCON.1         ' RB0 Flag de interrupción externa.
Symbol GIE    = INTCON.7         ' Global Interrupt Enable.
Symbol INTEDG = OPTION_REG.6     ' Flag = 0 Flanco bajada. Flag = 1 Flanco subida.
GIE    = 1                       ' Activa interrupciones generales.
INTE   = 1                       ' Activa la interrupción externa RB0/INT.
INTEDG = 1                       ' Disparar la Interrupción con flanco de subida.
Dim X As  DWord                  ' Variable de Posición actual (32bits).
Dim P As  DWord                  ' Variable de Posición final  (32bits).
                                                                              
TRISA  = %100011
TRISB  = %11111111
TRISC  = %11111111
PORTA.2 = 0                      ' Motor parado.
PORTA.3 = 0
PORTA.4 = 0                      ' LED apagado.
X=0                              ' Variables a cero.           
P=0
'---------- Programa Principal ------------
Bucle:
    If X = P Then  
       PORTA.4 = 0          ' Apaga el LED.
       PORTA.2 = 0          ' Motor Parado.
       PORTA.3 = 0
       P.BYTE0 = PORTB      ' Carga el byte bajo de los 16 bits.
       P.BYTE1 = PORTC      ' Carga el byte alto de los 16 bits.
       
       P.0     = PORTA.0    ' RA0 y RA1 pasan a ocupar los bits P0 y P1,
       P.1     = PORTA.1    ' sobre-escribiéndolos.
       GoTo Bucle
    EndIf
    
    PORTA.4 = 1        ' Enciende el LED.
    
    If X > 70000 Then  ' Si x<0, Motor hacia delante.
       PORTA.2 = 1
       PORTA.3 = 0
       GoTo Bucle
    EndIf 
 
    If X > P Then      ' Si x>p, motor hacia atrás.
       PORTA.2 = 0
       PORTA.3 = 1
       GoTo Bucle
    EndIf
     
    If X < P Then      ' Si x<p, motor hacia delante.
       PORTA.2 = 1
       PORTA.3 = 0
       GoTo Bucle
    EndIf
GoTo Bucle
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.
    Context Restore           ' Restablece el contexto de los registros tal como estaban antes de la
                              ' interrupción.

Cuando programaba en Proton IDE no se podía utilizar números negativos, al menos con el compilador que tenía. Por esta razón hago un truco. Hoy en día ya no lo uso, y el programa tiene muchos años, es recopilado de aquella época cuando controlaba motores con este lenguaje de programación.

Jerarquía en las Comparaciones.

El orden de las comparaciones para controlar el motor es jerárquico. Si alteras ese orden podría no funcionar correctamente, especialmente cuando le pides al PIC que el motor vaya a la posición cero. La primera comparación ha de ser siempre la de -igual-: "If X = P Then...". La comparación -menor de cero-: "If X > 70000 Then..." ha de estar antes de -mayor que- "If X > P Then..." y de -menor que- "If X < P Then...". Esto es importante.

Una vez que el motor se pone en marcha adquiere inercia:

  • Imagina que queremos que el motor vaya a la posición 65.535. El PIC se pondrá a contar pulsos, comparará la posición actual con la posición final y hará que se dirija en un sentido. Cuando llega a la posición 65.535, el PIC le dice al motor que pare, pero el motor tiene una inercia y no lo hace de golpe, entonces sucede una cosa, y es que por esa inercia el motor da unas cuantas vueltas de más, por ejemplo, llega hasta la posición 65538. Cuando mires el programa verás que las variables X y P (posición actual y posición final respectivamente) se las declaran con 32 bits de resolución en vez de 16 y es por esta razón, para que el PIC pueda seguir contando más allá del límite 65.535.
  • Lo mismo sucede cuando le pedimos al PIC que el motor vaya hasta la posición cero. El PIC llevará al motor a esa posición, pero una vez que llega a cero, se pasará varias vueltas por la inercia. ¿Qué hay detrás de la posición cero? Podría comenzar con número negativos, pero existe otra forma de resolverlo y es la que aplico, siempre con números positivos. De la posición cero, si sigues restando, la siguiente es 4.294.967.295 (recuerda que declaramos las variables de posición actual y final con 32 bits de resolución) e imagina que por inercia llega hasta la posición 4.294.967.293. Necesitamos que el motor de la vuelta para ir hasta la posición cero. Entonces hacemos lo siguiente: Si Posición Actual es mayor de 70000 entonces hacer que gire el motor en sentido contrario. He puesto 70.000, pero puede ser cualquier número superior a 65.535 más las vueltas de inercia que pueda dar el motor. En la mayoría de los casos con 65.550 (en vez de 70.000) sería más que suficiente, pero como hay mucho margen he preferido 70.000 y me curo en salud. Si quieres modificar esta cifra, nunca uses números demasiado cercanos a 65.535 ni a 4.294.967.295.