Interrupciones
37
4 INTERRUPCIONES Un microcontrolador puede poseer una gran variedad de módulos dentro del mismo chip, por ejemplo: puertos de comunicaciones, convertidores ADC y DAC, comparadores, moduladores de ancho de pulso, etc. Estos módulos comúnmente trabajan independientemente del CPU del microcontrolador para reducir su carga de trabajo. Sin embargo llega el momento en que los módulos necesitan comunicarse con el CPU, ya sea para transferencia de datos a memoria, procesamiento matemático o transferencia de información de un módulo a otro. La manera en que los módulos se comunican con el CPU es interrumpiéndolo. De aquí el nombre de interrupciones. Cuando ocurre una interrupción, el CPU deja pendiente lo que estaba haciendo para atender al módulo que lo interrumpió; una vez atendido, el CPU regresa al punto donde fue interrumpido para continuar lo que estaba haciendo. En los PIC18 USB, la mayoría de los módulos y periféricos pueden generar interrupciones. Una interrupción siempre va acompañada de una rutina de servicio de interrupción, llamada, la cual llamaremos RSI (o ISR por sus siglas en ingles). Esta rutina, es la que indica lo que debe de hacer el microcontrolador al ser interrumpido, por lo tanto en la RSI escribimos el código que queremos que el microcontrolador ejecute al ocurrir la interrupción.
4.1 Funcionamiento de una Interrupción sin prioridades Cuando ocurre una interrupción pasan los siguientes eventos: 1. Se activa una bandera de interrupción, la cual es un bit que se pone en “1” indicando que ocurrió una interrupción. 2. El procesador deja pendiente lo que estaba haciendo y salta a la localidad 08 de la memoria de programa (respalda el PC en la pila y escribe un 08 en el PC). Esta localidad es llamada Vector de Interrupción.
Ing. Juan Ramon Terven Salinas
Interrupciones
38
Es a partir de esta localidad donde nosotros debemos escribir el código que queremos que se ejecute cuando ocurre una interrupción. Comúnmente lo que se hace es poner una llamada a una función dentro del Vector de Interrupción y dicha función está definida en otra parte del programa.
Para generar el Vector de Interrupción en la localidad 08 en C18 se hace de la siguiente manera: #pragma code Interrupcion = 0x8 void VectorInterrupcion (void) { _asm GOTO FuncionInterrupcion _endasm }
En el código se muestra con azul el código que nunca cambia, las palabras de color negro pueden variar al gusto del programador. Observe que dentro de la función VectorInterrupcion está un salto a la función denominada FuncionInterrupcion.
Una vez creado el Vector de Interrupcion ahora creamos la función a la que salta (FuncionInterrupcion) de la siguiente manera: #pragma code #pragma interrupt FuncionInterrupcion void FuncionInterrupcion(void) { // Aqui ponemos nuestro código // que queremos que se ejecute al ocurrir la interrupción
// Antes de salir de la rutina de interrupción debemos // limpiar la bandera de interrupción que se activó }
La directiva #pragma interrupt indica al compilador que la función es una rutina de interrupción.
A continuación veremos la interrupción externa INT.
Ing. Juan Ramon Terven Salinas
Interrupciones
39
4.2 Interrupciones INTx Las interrupciones tipo INTx también llamadas “Interrupciones externas”, son interrupciones provocadas por eventos externos; estos eventos pueden ser flancos ascendentes o flancos descendentes. Los PIC18 USB posee 3 fuentes de interrupción de este tipo: INT0 INT1 INT2 La cuales se encuentran en los pines RB0, RB1 y RB2 respectivamente.
Hay que tener en cuenta que al Reset todas las fuentes de interrupción se encuentran deshabilitadas.
Para habilitar alguna de estas interrupciones usando el lenguaje C18, se usan las siguientes funciones de la librería portb.h: OpenRB0INT OpenRB1INT OpenRB2INT Las cuales se muestran en la Figura 4-1.
Ing. Juan Ramon Terven Salinas
Interrupciones
40
Figura 4-1. Funciones para activar interrupciones INTx [7]
Ing. Juan Ramon Terven Salinas
Interrupciones
41
Ejemplo 4-1. Interrupción Externa INT1 Armar el circuito de la Figura 4-2. Escribir, compilar y grabar el Programa 4-1 en su tabla de pruebas.
/* Interrupción INT1 * * Contador en PORTA * Cada vez que recibe un flanco descendente en INT1 reinicia contador */ #include <p18cxxx.h> #include <portb.h> #include <delays.h> // Prototipo de funcion void FuncionInterrupcion(void); // Sección de variables globales no inicializadas #pragma udata unsigned char contador; //Código para bootloader extern void _startup (void); #pragma code REMAPPED_RESET_VECTOR = 0x1000 void _reset (void) { _asm goto _startup _endasm } // Sección de código #pragma code // Aquí inicia el programa void main() { TRISA = 0; // PORTA como salida TRISB = 0xFF;
// Configura todo PORTB como entrada
ADCON1 |= 0x0f; // Configura pines como digitales // Configura interrupción externa del pin RB1 OpenRB1INT(PORTB_CHANGE_INT_ON & FALLING_EDGE_INT & PORTB_PULLUPS_ON); // Habilitación global de interrupciones INTCONbits.GIE = 1; // Inicia contador de pulsos contador = 0;
Ing. Juan Ramon Terven Salinas
Interrupciones
42
while(1) { LATA = contador; contador ++; Delay10KTCYx(10); //retardo de 10,000 TCYs } } // Vector de Interrupci贸n (direcci贸n 0x1008 si usamos bootloader) #pragma code Interrupcion = 0x1008 // Si no se usa bootloader es 0x8 void VectorInterrupcion(void) { _asm GOTO FuncionInterrupcion _endasm } /****** Rutina de Interrupci贸n INT1 *****/ #pragma interrupt FuncionInterrupcion void FuncionInterrupcion(void) { /* Reinicia el contador */ contador = 0; // Limpia la bandera de interrupci贸n INTCON3bits.INT1IF = 0; } Programa 4-1. INT1 modo compatibilidad
Figura 4-2. Circuito uso de INT1
Ing. Juan Ramon Terven Salinas
Interrupciones
43
Ejemplo 4-2. INT1 e INT2 en modo compatibilidad En el siguiente ejemplo se usan 2 interrupciones externas, INT1 e INT2 con la intención de mostrar como se procesan 2 interrupciones en el mismo vector de interrupción en modo compatibilidad. En este ejemplo, al recibir un flanco descendente por INT1 el contador se reinicia y al recibir un flanco descendente por INT2 el contador se detiene o se reanuda. Al circuito de la Figura 4-2 agregue otro pulsador de RB2 a tierra. Escribir, compilar y grabar el Programa 4-2 en su tabla de pruebas. /* * * * * */
Interrupciones INT1 e INT2 Contador en PORTA Cada vez que recibe un flanco descendente en INT1 reinicia contador cuando recibe flanco descendente en INT2 detiene o reanuda contador
#include <p18cxxx.h> #include <portb.h> #include <delays.h> // Prototipo de funcion void FuncionInterrupcion(void); // Sección de variables globales no inicializadas #pragma udata char contador; char cuenta; //Código para bootloader extern void _startup (void); #pragma code REMAPPED_RESET_VECTOR = 0x1000 void _reset (void) { _asm goto _startup _endasm } // Sección de código #pragma code // Aquí inicia el programa void main() { TRISA = 0; // PORTA como salida TRISB = 0xFF;
// Configura todo PORTB como entrada
ADCON1 |= 0x0f; // Configura pines como digitales // Configura interrupción externa del pin RB1
Ing. Juan Ramon Terven Salinas
Interrupciones OpenRB1INT(PORTB_CHANGE_INT_ON & FALLING_EDGE_INT & PORTB_PULLUPS_ON); // Configura interrupción externa del pin RB2 OpenRB2INT(PORTB_CHANGE_INT_ON & FALLING_EDGE_INT & PORTB_PULLUPS_ON); // Habilitación global de interrupciones INTCONbits.GIE = 1; // Inicia contador de pulsos contador = 0; cuenta = 1; while(1) { LATA = contador; if(cuenta) contador ++; Delay10KTCYx(30); } } // Vector de Interrupción (dirección 0x1008 si usamos bootloader) #pragma code Interrupcion = 0x1008 // Si no se usa bootloader es 0x8 void VectorInterrupcion(void) { _asm GOTO FuncionInterrupcion _endasm } // Sección de código #pragma code #pragma interrupt FuncionInterrupcion void FuncionInterrupcion(void) { // Si se disparó INT1 if(INTCON3bits.INT1IF) { contador = 0; // reinicia contador INTCON3bits.INT1IF = 0; } // Si se disparó INT2 else if(INTCON3bits.INT2IF) { cuenta = 1-cuenta; // pausa o reanuda contador INTCON3bits.INT2IF = 0; } } Programa 4-2. INT1 e INT2 modo compatibilidad
Ing. Juan Ramon Terven Salinas
44