Uso de interrupciones PIC C Compiler

¿Qué son las interrupciones?

Imagina que estás cocinando, ya colocaste agua a hervir, ya licuaste todo lo necesario y de repente tocan el timbre de tu casa, te están interrumpiendo, entonces tu función en ese momento será ir a abrir la puerta, atender a la persona y cerrar la puerta, cuando terminas, regresas a cocinar, pero no vuelves a empezar, sino que sigues cocinando desde donde te quedaste.

Cuando se activa una interrupción, el microcontrolador dejará de hacer lo que está haciendo para realizar la función de interrupción que se haya declarado anteriormente, para después regresar a donde se había quedado.

Una interrupción puede verse como un aviso que puede ser activado tanto por algún proceso específico del microcontrolador (final de conversión del ADC, recepción de datos del módulo EUSART, desborde de timer, etc) o por un cambio externo al mismo (cambio en algún puerto específico, cambio de un pin, etc.).

Interrupciones en PIC C Compiler

En la pestaña View del PIC C Compiler podremos encontrar una casilla que dice “Interrupts”, en ella tendremos la información de las interrupciones con las que cuenta nuestro microcontrolador. Por ejemplo, para el caso de Miuva (PIC 18F4550) encontraremos la siguiente lista:

  • AD: Conversión completa del ADC
  • BUSCOL: Colisión del bus
  • CCP1: Captura o comparación en la unidad 1
  • CCP2: Captura o comparación en la unidad 2
  • COMP: Evento de comparación
  • EEPROM: Escritura completa
  • EXT: Interrupción externa (RB0)
  • EXT1: Interrupción externa (RB1)
  • EXT2: Interrupción externa (RB2)
  • LOWVOLT: Bajo voltaje detectado
  • OSCF: Fallo en el sistema del oscilador
  • RB: Cambios en el puerto B (RB4:RB7)
  • RDA: Datos recibidos disponibles RS232
  • RTCC: Desborde del timer0 (RTCC)
  • SPP: Puerto de retransmisión paralelo Escritura/Lectura
  • SSP: Actividad SPI / I2C
  • TBE: Buffer de transmisión vacío RS232
  • TIMER0: Desborde de timer0
  • TIMER1: Desborde de timer1
  • TIMER2: Desborde de timer2
  • TIMER3: Desborde de timer3
  • USB: Actividad USB

La forma de activar las interrupciones es muy similar en todos los casos, tenemos que “avisarle” al compilador que la función por describir será de interrupción y posteriormente agregar la función como si fuera cualquier otra. De la siguiente forma:

#int_XXXXX
void XXXXX_isr(){
        //Declaramos la función de interrupción
   }
}

Las “XXXX” deberán ser sustituidas por el nombre la interrupción a utilizar (lo podemos encontrar en la sección de View -> Interrupts), por ejemplo, para activar la función de interrupción externa (RB0), tendremos que sustituir por “EXT”. Quedando de la siguiente forma:

#int_EXT
void ext_isr(){
      //Función de interrupción externa (RB0)
   }
}

Dentro de nuestra función main, tenemos que activar las interrupciones con las siguientes líneas:

enable_interrupts(GLOBAL); //Habilita interrupciones
enable_interrupts(int_XXXX);  //Habilita interrupción deseada

Siguiendo el caso de la interrupción externa:

enable_interrupts(GLOBAL); //Habilita interrupciones
enable_interrupts(int_EXT);  //Habilita interrupción externa (RB0)

Por ejemplo, el siguiente código, en su función principal estará aumentando un contador de 16 bits y mostrando su valor en la LCD, pero al momento de que se active la interrupción externa (RB0), invertirá el valor de un led ubicado en el PIN E0. Despues de invertir el valor del LED regresará al programa principal y seguirá contando en el número en el que estaba.

#include <18f4550.h>          // la librería del PIC
#Fuses HSPLL, NOWDT, NOPROTECT, NOLVP, NODEBUG, USBDIV, PLL2, CPUDIV1, VREGEN
#use delay (clock=48M)        //Seleccionamos la frecuencia de reloj de 48MHz
#use standard_io(b)
#use standard_io(e)
#include "MLCD.c"

int16 contador = 0;
   
#int_EXT
void ext_isr(){
    output_toggle(PIN_E0);
}

void main(){
   lcd_init();
   set_tris_b(0xFF);    //Puerto b como entrada
   set_tris_e(0x00);    //Puerto e como salida
   output_e(0x07);      //Apaga led rgb
   enable_interrupts(GLOBAL); //Habilita interrupciones
   enable_interrupts(int_EXT);  //Habilita interrupcion externa
   while(true){
      contador++;
      printf(lcd_putc, "Cont = %5lu", contador);
   }
}

Para obtener más información de en que momento se activa cada una de las diferentes interrupciones es importante leer la hoja de datos.

Niveles de interrupción

En muchas ocasiones nuestros programas necesitan realizar varias tareas y algunas pueden ser más importantes que otras, es por eso que cuando trabajamos con interrupciones podemos declarar diferentes prioridades para ellas, entre más alta sea su prioridad más importante es la tarea a realizar, entonces si se activa una interrupción de alta prioridad, no importa lo que esté realizando nuestro programa (incluso aunque esté realizando otra función de interrupción de baja prioridad) saltará a la función correspondiente y después regresará a donde estaba.

Para activar los niveles de prioridad en PIC C Compiler es necesario agregar la siguiente línea al inicio de nuestro código:

#device HIGH_INTS=TRUE //Activamos niveles de prioridad

Posteriormente será necesario especificar cual de las interrupciones querremos configurar como alta prioridad, para esto tendremos que agregar “HIGH” después de que declaremos que se estará usando una función de interrupción, de la siguiente forma:

#int_EXT1 HIGH
void ext_isr(){
       //Función de interrupción de alta prioridad
   }
}

En el siguiente código de ejemplo, vamos a definir dos interrupciones, ambas externas, una habilitada por el pin RB1 y la otra por el pin RB2, ya que según la hoja de datos del microcontrolador, ambas pueden ser configuradas tanto de alta como de baja prioridad. Nuestro código va a tener tres contadores independientes, uno se aumentará en la función main [contador], otro más cuando se active una interrupción de baja prioridad [contador2] y el último al activarse la interrupción de alta prioridad [contador3]. Esto servirá para que el usuario corrobore el funcionamiento de los niveles, ya que  en la LCD se mostrará el valor de los 3 contadores.

Cuando no se activen interrupciones estará aumentando el valor de [contador], si se activa la interrupción de baja prioridad (RB2) dejará de aumentar el valor del contador del main y aumentará el valor del contador de la función de baja prioridad [contador2] hasta llegar a 10000 o hasta que se active la interrupción de alta prioridad la cual iniciará su propio contador [contador3] hasta que este llegue a 10000.

Se puede notar que no hay nada que pueda interrumpir a la función de alta prioridad, por lo tanto, solo será posible salir de esa función hasta que el contador llegue al valor definido (10000). A diferencia de la función de baja prioridad que puede ser pausada por la de alta. Y a diferencia de la función principal que podrá ser pausada por la de baja o la de alta prioridad.

#include <18f4550.h>          // la librería del PIC
#device HIGH_INTS=TRUE 
#Fuses HSPLL, NOWDT, NOPROTECT, NOLVP, NODEBUG, USBDIV, PLL2, CPUDIV1, VREGEN
#use delay (clock=48M)        //Seleccionamos la frecuencia de reloj de 48MHz
#use standard_io(b)
#use standard_io(e)
#include "MLCD.c"

int16 contador = 0;
int16 contador2 = 0;
int16 contador3 = 0;
   
#int_EXT1 HIGH
void ext1_isr(){
   for (contador3 = 0; contador3 <= 10000; contador3++){
      lcd_gotoxy(1,1);
      printf(lcd_putc, "%5lu", contador3);
   }
}

#int_EXT2
void EXT2_isr(){
   contador2++;
   for (contador2 = 0; contador2 <= 10000; contador2++){   
     lcd_gotoxy(1,2);
     printf(lcd_putc, "%5lu", contador2); 
   }
}

void main(){
   lcd_init();
   set_tris_b(0xFF);    //Puerto b como entrada
   set_tris_e(0x00);    //Puerto e como salida
   output_e(0x07);      //Apaga led rgb
   enable_interrupts(GLOBAL); //Habilita interrupciones
   enable_interrupts(int_EXT2);
   enable_interrupts(int_EXT1);
   while(true){
      if (contador >= 10000){
         contador = 0;
      }
      else{
         contador++;
      }
      lcd_gotoxy(10,1);
      printf(lcd_putc, "%5lu", contador);
   }
}

 

Menú