Interrupción por Pin Externo

Al estudiar las interrupciones, el ejemplo más común con el que nos encontramos es la interrupción por Pin Externo, y lo es debido a su simplicidad de configuración  y explicación, por eso si eres principiante lo mejor es entender desde lo más básico y luego pasar al tipo de interrupción que te interesa.

En el artículo pasado explicamos los pasos que sigue el microcontrolador al momento de presentarse una interrupción, ahora vamos a explicar cómo configurarlo y a atender la interrupción al momento que aparezca.

Así como el conversor análogo digital tiene su registro correspondiente para configurarlo, las interrupciones también tienen registros asociados para su configuración, el principal es el registro INTCON, en él podemos habilitar o deshabilitar las interrupciones de modo global, seleccionar las que vamos a usar y también tenemos las banderas que nos indican cuál de todas ellas ocurrió.

Registro INTCON

GIE PEIE TMR0IE INTE IOCIE TMR0IF INTF IOCIF
Bit 7 Bit 6 Bit 5 Bit 4 Bit 3 Bit 2 Bit 1 Bit 0

 

GIE: Habilitación global de las interrupciones.

1 = Habilita todas las interrupciones que estén configuradas.

0 = Deshabilita todas las interrupciones.

PEIE: Interrupciones por periféricos.

1 = Habilita todas las interrupciones por periféricos que estén configuradas.

0 = Deshabilita todas las interrupciones por periféricos.

TMR0IE: Interrupción por desborde de Timer0.

1 = Habilita la interrupción por Timer0.

0 = Deshabilita la interrupción por Timer0.

INTE: Interrupción por Pin Externo.

1 = Habilita la interrupción por Pin Externo.

0 = Deshabilita la interrupción por Pin Externo.

IOCIE: Interrupción por cambio de estado en puerto.

1 = Habilita la interrupción por cambio de estado en puerto.

0 = Deshabilita la interrupción por cambio de estado en puerto.

TMR0IF: Bandera de interrupción por desborde de Timer0.

1 = El registro TMR0 se ha desbordado.

0 = El registro TMR0 no se ha desbordado.

INTF: Bandera de interrupción por Pin Externo.

1 = Ha ocurrido la interrupción por Pin Externo.

0 = No ha ocurrido la interrupción por Pin Externo.

IOCIF: Bandera de interrupción por cambio de estado en puerto.

1 = Uno de los pines ha cambiado de estado.

0 = Ninguno de los pines ha cambiado de estado.

Para este ejemplo explicaremos los bits del registro INTCON que vamos a usar, los demás tendrán su explicación a su tiempo, esto con el fin de no alargarnos mucho y evitar confusiones.

GIE (Global Interrup Enable / Habilitación Global de interrupciones), permite habilitar o deshabilitar todas la interrupciones en un momento dado, es decir que aunque tengamos configurada alguna interrupción, si dejamos este bit en 0, la interrupción nunca se llevará a cabo. También es muy útil si llegamos a un punto del programa donde realizamos una operación delicada que no puede ser interrumpida. Debemos tener en cuenta de volver a habilitar GIE luego de la operación.

INTE, poniendo en 1 este bit, tendremos habilitada la interrupción por Pin Externo, este pin está asociado en la mayoría de los microcontroladores al bit RB0 (Bit 0 del puerto B).

INTF, cada interrupción tiene asociado un bit denominado bandera, este se pone en 1 automáticamente cuando la interrupción ocurre. Al momento de suceder una interrupción el microcontrolador salta a una función “interrupt”, en ella es nuestro deber averiguar cuál de todas las banderas posibles están en 1. El bit INTF se pone automáticamente en 1 cuando se dispara la interrupción por Pin Externo.

El modo en que se dispara una interrupción  por Pin Externo, es al detectar un cambio de estado, de 1 a 0 (Flanco de bajada) o de 0 a 1 (Flanco de subida). Por defecto, nuestro microcontrolador está configurado para detectar la interrupción por flanco de bajada pero si queremos que esto suceda por flanco de subida, debemos modificar el bit INTEDG en el registro OPTION_REG, poniéndolo en 1.

INTEDG: Selección de flaco para interrupción por Pin Externo.

1 = Interrupción por flanco de subida.

0 = Interrupción por flanco de bajada.

Orden de configuración, ejecución y atención de una interrupción por Pin Externo:

  • Configurar el pin RB0 como entrada digital.
  • En el registro INTCON poner en 1 el bit INTE.
  • Establecer el flanco por el cual se disparará la interrupción en el bit INTEDG del registro OPTION_REG, por defecto viene como flanco de bajada.
  • Habilitar las interrupciones globalmente escribiendo un 1 en el bit GIE del registro INTCON.
  • Al presentarse la interrupción por Pin Externo la bandera INTF se pondrá en 1 automáticamente.
  • El programa del microcontrolador “salta” a la función asociada al vector de interrupciones, comúnmente llamada isr.
  • Dentro de la función se debe de asegurar si la bandera INTF está en 1, preguntando con un if.
  • Ejecutar el código correspondiente que atienda la interrupción, se recomienda que sea muy breve.
  • Poner en 0 manualmente la bandera INTF.
  • Luego de terminar la rutina del vector de interrupciones, el programa regresa al punto donde se quedó al momento de presentarse la interrupción.

Teniendo esto en cuenta, veamos un ejemplo muy sencillo, donde tendremos un microcontrolador PIC 16F1827 conectado a un display de 7 segmentos a través de un decodificador BCD 7448, realizando un conteo constante de 0 a 9, en el pin RB0 estará conectado a positivo por medio de una resistencia de pull-up y un botón pulsador conectado a tierra que al ser pulsado cambiará el estado de RB0 de 1 a 0.

Al pulsarse el botón se dispara la interrupción, deteniendo el programa principal del contador y ejecutando el código dentro del vector de interrupciones que lo que hará es encender y apagar cinco veces el diodo Led que se encuentra conectado al pin RB7.

Este es el circuito que usaremos:

Este es el código:

#include <xc.h>

#pragma config FOSC = XT        // Oscilador con cristal de cuarzo de 4MHz conectado en los pines 15 y 16
#pragma config WDTE = OFF       // Perro guardián (WDT deshabilitado)
#pragma config MCLRE = ON       // Master clear habilitado (pin reset)

#define _XTAL_FREQ 4000000      // Oscilador a 4MHz

// Definimos macros
#define LED PORTBbits.RB7       // Led indicador conectado a RB7
#define DISPLAY PORTA           // Display de 7 segmentos conectado a los bits
                                // menos significativos del puerto A

void interrupt isr(void);       // Se declara la función que ejecuta al momento
                                // de presentarse cualquier interrupción

void main(){
    // Configuración de puertos
    TRISA = 0XF0;               // RA0, RA1, RA2 Y RA3 SALIDAS
                                // RA4, RA5, RA6 Y RA7 ENTRADAS
    ANSELA = 0X00;              // Puerto A digital
    TRISB = 0X01;               // Puerto B todo salida, excepto RB0
    ANSELB = 0X00;              // Puerto B digital
    PORTA = 0;                  // Limpiamos puerto A
    PORTB = 0;                  // Limpiamos puerto B
    
    // Configuración de la interrupción
    INTCONbits.INTE = 1;        // Habilitamos la interrupción por pin externo
    OPTION_REGbits.INTEDG = 0;  // la interrupción se da por flanco de bajada.
    // El pin estará siempre en 1 debido  a la resistencia de pull up, cuando se pulse el botón
    // pasa a 0 y se presenta la interrupción.
    INTCONbits.GIE = 1;         // Habilitamos las interrupciones
    
    // Declaración de Variables
    char cont = 0;              // Variable para realizar conteo de 0 a 9
    
    // Este ciclo mostrara el valor de cont en el Display y luego lo irá incrementando
    // de a 1 hasta llegar a 9, luego volvera a 0 y repetirá.
    while(1){                   // Programa que se ejecuta continuamente
        DISPLAY = cont;         // Enviamos al diplay el valor en cont
        __delay_ms(1000);        // Esperamos 500 milisegundos
        cont++;                 // Incrementamos en 1 la variable contador
        if(cont > 9){           // Si cont es mayor a 9    
            cont = 0;           // Lo volvemos a 0
        }
    }
}

// Función que se llama cada que aparezca una interrupción
void interrupt isr(void){
    // Preguntamos si la bandera de interrupción por pin externo fue la que se disparó
    if(INTCONbits.INTF){
        // En este ciclo encendemos y apagamos el LED 5 Veces
        for(char i = 0; i < 10; i++){
            LED = ~LED;                 // Con ~ cambiamos el estado de 0 a 1 y de 1 a 0.
            __delay_ms(1000);            // Esperamos 100 milisegundos entre cada cambio de estado
        }
        INTCONbits.INTF = 0;            // Deshabilitamos la bandera antes de salir de la interrupción
    }
}