Está en la página 1de 12

INTERRUPCIONES EN CCS 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.).
Directivas para el manejo de interrupciones en CCS Compiler
#INT_DEFAULT Función de interrupción por defecto
Indica al Compilador que la función que sigue a la directiva será llamada si el PIC activa una
interrupción y ninguno de los flags de interrupción está activo.
Ejemplo
#int_default
control_interrupcion(){
Instrucciones;
}

#INT_GLOBAL Función Global

Indica al compilador que la función que sigue a esta directiva reemplaza al distribuidor de
interrupciones del compilador; dicha función toma el control de las interrupciones y el
compilador no salva ningún registro. Normalmente no es necesario usar esto y debe tratarse con
gran prudencia.

#INT_XXXX Función de interrupción


En el compilador C, la directiva habitual en el manejo de las interrupciones es #INT_XXXX.
Especifica que la función que le sigue es una función de interrupción; además, las funciones de
interrupción no pueden tener ningún parámetro.
Ejemplo 1
#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)
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 dl PIC 16F877A encontraremos la siguiente lista:

Ejemplo 2
Para activar la función de interrupción externa (RB0), tendremos que sustituir “XXXX” por
“EXT”. Quedando de la siguiente forma:
#int_EXT
void ext_isr(){
//Función de interrupción externa (RB0)
}
}
Nota
Al utilizar directivas de interrupción, el compilador genera el código necesario para ejecutar la
función que sigue a la directiva. Además del código necesario para guardar al principio y restituir
al final el contexto; también borra el flag activo de la interrupción.
Funciones para el manejo de las directivas de interrupción
enable_interrupts ()
Habilita la interrupción especificada por Nivel, afectando a los registros INTCON, PIE1 y PIE2.
Por ejemplo, en el PIC16F877, los niveles son:

Nota
GLOBAL equivale a GIE = PEIE = 1 y debe activarse de forma independiente. El resto activaran
la máscara correspondiente.

disable_interrupts (Nivel)
Deshabilita la interrupción especificada por Nivel.

ext_int_edge ({H_TO_L o L_TO_H})


Esta función determina el flanco de activación de la interrupción externa (INT_EXT). El flanco
puede ser de subida (L_TO_H) o de bajada (H_TO_L).Activa el Flag INTF.

Ejemplo Interrupción externa por RB0


Es una interrupción básica que permite generar una interrupción tras el cambio de nivel de alto a
bajo o de bajo a alto en la entrada RB0.
La directiva utilizada es #INT_EXT y se debe acompañar de las siguientes funciones (afectan al
bit 6 del registro OPTION_REG).
Programa 1
Encender y apagar, consecutivamente, un LED en la patilla RB7 cuando se produzca un cambio
de nivel en la patilla RB0.

#include <16F877A.h>
#fuses XT,NOWDT,PUT,NOWRT
#use delay (clock= 4000000)

#use fast_io(B)
Int1 cambio=0; //Variable de cambio

#INT_EXT //Atención a interrupción por cambio en RB0


Ext_isr() { //Función de interrupción
Output_toggle(pin_B7);
}
Void main() {

set_tris_B(0x01); //B0 como entrada, B7 como salida


output_low(PIN_B7); //Apaga el LED
port_b_pullups(TRUE); //Anclaje para RB0

enable_interrupts(int_ext); //Habilita int. RB0


ext_int_edge(L_TO_H); //por flanco de subida
enable_interrupts(GLOBAL); //Habilita la int. General

while(1) { //bucle infinito en espera


}
}
TIMER0/WATCHDOG en CCS Compiler
El bloque funcional TIMER0/WATCHDOG es un contador (registro) de 8 bits programable,
incrementado por hardware. La cuenta máxima es de 255 (el incremento es constante e
independiente).
Modos de funcionamiento
 Contador: cuenta eventos externos (a través del pin RA4/TOCK1)

 Temporizador: cuenta los pulsos internos de reloj.

 El bloque del TMR0 puede funcionar para generar el WATCHDOG, que permite durante
el funcionamiento normal del microcontrolador, que un desbordamiento (o timeout) del
watchdog provoque un reset (watchdog Timer Reset). Para evitar el desbordamiento,
cada cierto tiempo, antes de llegar al límite, se debe ejecutar una instrucción CLRWDT
que repone el Watchdog, haciendo que comience nuevamente desde cero. Está basado en
un oscilador RC interno, independiente del oscilador del microcontrolador y que no
requiere ningún componente externo.

Registros afectados por la configuración del TIMER0/WDT


 OPTION_REG: Configura el hardware del TIMER0/WDT.

 INTCON: Permite trabajar con la interrupción del TIMER0/WDT.


 TRISA: habilita la patilla RA4.
 Registro OPTION_REG (dirección RAM: 81h/181h)
Cálculo del tiempo de desbordamiento de TMR0
El tiempo de desbordamiento de TMR0 se calcula según la siguiente ecuación:
Tiempo=TCM∗Preescaler (256−Carga TMR 0)
Tiempo∗FOSC
Carga TMR 0=256−
4∗Preescaler
Donde TCM es el ciclo de maquina calculado como TCM = 4/FOSC
Funciones para el manejo del timer0
setup_timer_0(modo);
Configura el timer0, donde modo ya está definido en el fichero de cabecera (Afecta a los bits 5:0
del registro OPTION_REG):

Nota
Los distintos modos pueden ser agrupados mediante el empleo del símbolo |. Por ejemplo:
setup_timer_0 (RTCC_DIV_2 | RTCC_EXT_L_TO_H);
setup_wdt (modo);
Configura el WDT, donde modo ya está definido en el fichero de cabecera (Afecta a los bits 3:0
del registro OPTION_REG):

Nota
Para activar el Watchdog se deben utilizar los bits de configuración mediante la directiva
#FUSES de la siguiente manera:
#fuses WDT //Activado.
#fuses NOWDT //Desactivado

Set_timer0 (valor);

Permite escribir un valor en el registro (TIMER0/WATCHDOG). Donde valor debe ser un


entero de 8 bits.

valor = get_timer0();
Permite leer el valor del registro (TIMER0/WATCHDOG).

restart_wdt();
Permite realizar la puesta a cero del watchdog (como CLRWDT).

Ejemplos

Programa 2
Generar una señal cuadrada de 1 KHz utilizando la interrupción del TIMER0.

Para generar una señal de 1 KHz se necesita un semiperiodo de 500μs. Según la ecuación de
desbordamiento de K, utilizando un cristal de 4 MHz y un pre escalador de 2:

T=(TCM)(Prescaler)(256-carga TMR0)
500μs= (4/4000000) (2)(256-x)
Donde x = 6, es decir, que se debe cargar el TIMER0 con el valor 6.

Pero esta relación solo se cumple si se trabaja en assembler. Al trabajar en C, el compilador


genera líneas de código que aumentan el tiempo de ejecución del programa, por lo tanto, es
necesario ajustar el valor final. En este caso utilizamos el valor de carga 27 (0x1B).

#INCLUDE <16F877A.h>
#use delay(clock=4000000)
#fuses XT,NOWDT
#use standard_io(B)

#int_TIMER0
void TIMER0isr(void) {
output_toggle(PIN_B0);
set_timer0 (0x1B); } //Se recarga el TIMER0

void main() {
setup_timer_0(RTCC_INTERNAL|RTCC_DIV_2); //Configuración TIMER0
set_timer0 (0x1B); //Carga de TIMER0
enable_interrupts(INT_TIMER0); //Habilita interrupción del TIMER0
enable_interrupts(global); //Habilita interrupción general
while (1); //Bucle infinito
}

Nota
El compilador se encarga, al entrar en la interrupción, de inhabilitar las interrupciones y al salir
de borrar los flags, por lo que ya no es necesario hacerlo por programa.

TIMER1 y TIMER2 en CCS Compiler

Funciones para la configuración del Timer1


setup_timer_1(modo);

Donde modo está definido en el fichero de cabecera (afecta a los bits 5:0 del registro T1CON):

Nota
Los distintos modos pueden ser agrupados mediante el empleo del símbolo |.

Set_timer1 (valor);

Permite escribir un valor en el registro (TIMER1). Donde valor debe ser un entero de 16 bits.

valor = get_timer1();
Permite leer el valor del registro (TIMER1).

Funciones para la configuración del Timer2

Setup_timer_2 (modo, periodo, postscaler);

Donde:
 Periodo es un valor entero de 8 bits (0 a 255) para el registro PR2.
 Postscaler es el valor del postscaler (1 a 16. Afecta a los bits 6:3 del registro T2CON)

 Modo afecta a los bits 2:0 del registro T2CON.

Set_timer2 (valor);

Permite escribir un valor en el registro (TIMER2). Donde valor debe ser un entero de 8 bits.

valor = get_timer2();
Permite leer el valor del registro (TIMER2).

Ejemplos

Programa 3

Generar una función que permita realizar retardos de 1 segundo empleando el TIMER1.

Se calcula un periodo parcial de 0.5 segundos y se repite dos veces:


0,5=(4/FOSC) (65536-x) (Preescaler)

Con FOSC=4MHz y Preescaler 1:8, el TMR1=x=3036

Con interrupción

#include <16f877a.h>
#fuses XT,NOWDT
#use delay(clock=4000000)
#use standard_io(b)
Int1 cont=0;

#int_TIMER1 //interrupción TIMER1


Void temp1s(void) //función
{
If (cont==1) output_toggle(PIN_B1); //cada dos interrupciones de 0,5 s
set_timer1 (3036);
cont++;
}

Main ()
{
setup_timer_1 (T1_INTERNAL | T1_DIV_BY_8);
set_timer1 (3036); //recarga del TMR1
enable_interrupts(INT_TIMER1); //habilita interrupción timer1
enable_interrupts(global); //habilita interrupción general
while(1);
}

Sin interrupción

#include <16F877A.h>
#use XT,NOWDT
#use delay(clock=4000000)
#use standard_io(b)

Temp1s()
{
int cont=0;
output_toggle(PIN_B1);
while (cont<2) //para contar 2 veces 0,5seg
{
set_timer1 (3086); //inicializa el TMR1
while (get_timer1()>=3036); //Espera a que desborde (0,5 seg)
cont++;
}
}

Main()
{
Setup_timer_1 (T1_INTERNAL | T1_DIV_BY_8);
While(1){
Temp1s (); //llamada a la función de temporización
}
}

Programa 4

Medir el ancho de un pulso mediante el TIMER1 y la interrupción externa por RB0.

Para medir el ancho de un pulso se necesita detectar su flanco de subida y después su flanco de
bajada. Esto se puede realizar utilizando la interrupción RB0 ya que nos permite configurar el
flanco de disparo.

Al producirse una interrupción, por ejemplo, en el flanco de subida, se puede inicializar el valor
del temporizador (TIMER1), en ese momento se cambia la activación de la interrupción RB0 a
flanco de bajada y cuando se vuelva a producir la siguiente interrupción por dicho flanco se
guarda el valor del temporizador.
El ancho del pulso será la diferencia entre el valor del TIMER1 en el flanco de subida y el flanco
de bajada. El ancho de pulso máximo para una frecuencia de 4 MHz (ciclo de máquina de 1μs) es
de 65536 ms (un ciclo de TIMER1). El minino dependerá del tiempo que tarda un programa en
gestionar la interrupción y los cálculos. En el ejemplo se puede conseguir medir anchos de pulso
de entre 64934 ms y 69 μs.

#include <16f877A.h>
#fuses XT, NOWDT
#use delay(clock=4000000)

#include <lcd.c>

Int16 TBF; //Tiempo flanco de bajada


Float AP; //Valor final del ancho de pulso
Int1 nuevopulso=0; //Entra otro pulso
Int1 cambio=0; //Cambio de flanco de disparo

#int_ext
void funcion_ext_int(){ //Funcion interrupcion

if(cambio==0){ //Flanco de subida


set_timer1(0); //Inicializa TMR1
ext_int_edge(0,H_TO_L); //Configura para flanco de bajada
cambio=1; //control de cambio de flanco
}else{
TBF=get_timer1(); //Valor del TIMER1 para flanco de bajada
ext_int_edge(0,L_TO_H); //Configura para flanco de subida
cambio=0; //Control de cambio de flanco
If(nuevopulso==0){ //fin de pulso
nuevopulso=1; //Pulso a calcular
}
}
}

Void main() {

Lcd_init();
setup_timer_1(T1_INTERNAL | T1_DIV_BY_1); //config. TIMER1
ext_int_edge(0,L_TO_H); //configura para flanco de subida
cambio = 0; //Control de cambio de flanco
enable_interrupts(int_ext); //Habilitacion int RB0
enable_interrupts(global); //Habilitacion general
do {
if(nuevopulso==1){ //Pulso nuevo?
AP = TBF*1.0; //Ancho de pulso en ƒÊs de TIMER1..a 4MHz
Printf(lcd_putc,"\nPulso = %6.1fuS",AP); //Visualiza medida en LCD
}
nuevopulso=0; //Pulso medido
}
} while (TRUE); // Bucle infinito
}

Programa 5

Generar una señal cuadrada de 1KHz utilizando la interrupción TIMER2.

Se requiere un semiperiodo de 500μs, según la ecuación de desbordamiento del TIMER2,


utilizando un cristal de 4MHz, un preescaler de 4 y un postescaler de 1:

T=TCM(Preescaler)(carga TMR2+1)(Postescaler)

500 μs=(4/4000000)(4)(X+1)(1)

Donde X=124; es decir, se debe cargar el TIMER2 con el valor 124.

Pero esta relación solo cumple si se trabajase en ensamblador. Al trabajar en C el compilador


genera líneas de código que aumentan el tiempo de ejecución del programa y por lo tanto, es
necesario ajustar el valor final. En este caso se ha utilizado un valor de carga de 11.

#include <16F877A.h>
#use delay(clock=4000000)
#fuses XT,NOWDT
#use standard_io(B)

#int_TIMER2
void timer2_isr(void) {
output_TOGGLE(PIN_B0); //para semiperiodo alto
set_timer2(11); //se recarga el TIMER2

void main() {
setup_timer_2(T2_DIV_BY_4,124,1); //configuración TIMER2
enable_interrupts(INT_TIMER2); //habilita interrupción TIMER2
enable_interrupts(global); //habilita interrupción general
while(1); //bucle infinito
}