ATmega16 – Interrupciones

Existe dos formas de procesar datos ingresados hacia el uC, uno es por consulta, que como se vio en el post de GPIO el uC siempre está realizando la lectura de los pines y actualiza la acción de cada pin.

Esto tiene una seria desventaja, por ejemplo, supongamos que el uC está realizando la actualización de pines ubicados en el puerto C y en ese mismo momento ocurre un cambio en un pin del puerto A, es obvio que pasará un tiempo hasta que el uC llegue a leer nuevamente los pines del puerto A, por lo tanto se habrá realizado un cambio pero no será detectado.

El procesamiento de datos por interrupciones consiste en que cada vez que se produzca un cambio en alguno de ciertos pines este avise al uC para que deje lo que estaba realizando y proceda al procesamiento de la interrupción, luego de terminar regresa al programa principal y continua lo que estaba ejecutando el antes de producirse la interrupción.

Este tipo de procesamiento tiene la ventaja de dejar al uC realizando otras actividades hasta que se produzca una interrupción.

Entonces podemos definir a una interrupción como un procedimiento que se lleva a cabo en determinado momento.

Las fuentes de interrupción son:

  • Inicialización o reset.
  • Tres interrupciones externas.
  • Ocho interrupciones de los timers (comparación, captura, desbordamiento).
  • Al completar una transmisión serial por el SPI.
  • Tres por el UART (transmisión, recepción, buffer vacío).
  • Término de conversión ADC.
  • Finalización de escritura EEPROM.
  • Comparador Analógico.
  • Interfaz serial TWI.
  • Escritura en memoria de programa.

Al inicializar el uC las interrupciones están deshabilitadas por defecto.

Cada ISR (Rutina de Servicio de Interrupciones) tiene un habilitador individual, además existe un habilitador global.

Cuando comienza a ejecutarse un ISR se desactiva el habilitador global para evitar aceptar más ISR.

Toda ISR termina con una instrucción de retorno, esta instrucción recupera la dirección de retorno al programa principal y activa el habilitador global.

Las interrupciones ocupan 2 words (16 bits) en la memoria flash, debido a que el ISR es bastante extensa y no entrarían en dos words, se utilizan instrucciones de salto.

Si dos o más interrupciones se activan al mismo tiempo y debido a que solo puede ejecutarse sólo una de ellas, las interrupciones se realizarán tomando la siguiente prioridad:

El RESET es el de mayor prioridad.

Las interrupciones se ejecutan de la siguiente manera:

  • Ocurre el evento.
  • Setea el flag de interrupción.
  • Verifica las interrupciones.
  • Termina la instrucción que se estaba ejecutando.
  • Se guarda la dirección de retorno.
  • Carga el PC de la ISR.
  • Desactiva el habilitador global de interrupciones (I=0)
  • Se desarrolla la interrupción.
  • La instrucción de retorno se encarga de activar el habilitador global y obtener la dirección de retorno.

Interrupciones Externas:

Las interrupciones externas se activan cada vez que existe un cambio de nivel en el pin correspondiente a la interrupción, tiene aplicaciones como un contador de flancos o un contador de eventos externos.

El uC ATmega16 cuenta con tres interrupciones externas INT0, INT1, INT2 que se ubican en los pines 16, 17 y 3 respectivamente.

Los registros que configuran estas interrupciones son los siguientes:

MCUCR: Se encarga de configurar las INT0 e INT1 ya sea por nivel bajo, cambio lógico, flanco de bajada o flanco de subida.

ISC11 Y ISC10 controlan como va ha ser activada la INT1.

ISC01 Y ISC00 controlan como va ha ser activada la INT0.

MCUCSR: Se encarga de configurar INT2 que solo puede ser por flanco de bajada o flanco de subida.

ISC2 controla cómo va ha ser activada la INT2, si es cero se activa con flanco de bajada y si es uno se activa con flanco de subida.

GICR: Se encarga de habilitar las interrupciones, con uno habilitamos las interrupciones y con cero las deshabilitamos.

GIFR: Nos muestra las banderas de estado de las Interrupciones Externas, cada vez que ocurre el evento toma valor uno, además sirve como solicitud de interrupción, es decir, organiza las interrupciones.

Ejemplo:

// * Author: Victor Dueñas Guardia
// * Info: www.netzek.com
// @Ejemplo:
// El programa cambia el estado del LED correspondiente cada vez que se active el pulsador.
// Pseudocódigo:
// – Configuramos Interrupciones
// – Al producirse una interrupción el LED cambia de estado.
.INCLUDE "M16DEF.INC //Incluye definición archivos ATmega16
.ORG $00
JMP PROGRAMA //Interrupción Reset
.ORG $02
JMP INTER0 //Interrupción 0
.ORG $04
JMP INTER1 //Interrupción 1
PROGRAMA:
LDI R16,HIGH(RAMEND) //Coloco la Pila al final de la RAM
OUT SPH,R16
LDI R16,LOW(RAMEND)
OUT SPL,R16
LDI R16,0B00000011
OUT DDRA,R16 //Puerto A Salida
LDI R16,0B00001010
OUT MCUCR,R16 //INT0 e INT1 Flanco de Bajada
LDI R16,0B11000000
OUT GICR,R16 //INT0 e INT1 Habilitada
SEI
BUCLE: RJMP BUCLE
//……INTERRUPCIONES
INTER0:
CALL RETARDO
LDI R17,0B00000001
CALL CAMBIAR
RETI
INTER1:
CALL RETARDO
LDI R17,0B00000010
CALL CAMBIAR
RETI
//……PROCEDIMIENTOS
CAMBIAR:
IN R16,PINA
EOR R16,R17 //Cambio el estado del LED
OUT PORTA,R16
RET
RETARDO:
LDI R25,31 //Configurado Evitar Rebote
Bucle_Externo:
LDI R24,255
Bucle_Interno:
NOP
NOP
NOP
NOP
NOP
NOP
NOP
DEC R24
BRNE Bucle_Interno
DEC R25
BRNE Bucle_Externo
RET
view raw IntExt.asm hosted with ❤ by GitHub
/*
* IntExt.c
*
* Author: Victor Dueñas Guardia
* Info: www.netzek.com
@Ejemplo:
El programa cambia el estado del LED correspondiente cada vez que se active el pulsador.
Pseudocódigo:
– Configuramos Interrupciones
– Al producirse una interrupción el LED cambia de estado.
*/
#include <avr/io.h>
//Incluyo las definiciones del ATmega16
#include <avr/interrupt.h>
//Incluyo las definiciones de las Interrupciones
#include "mdelay.h"
//Incluyo las definiciones de retardo
//__INTERRUPCIONES
ISR(INT0_vect)
{
_delay_ms(300);
//Delay para evitar efecto rebote
PORTA=PINA^0x01;
}
ISR(INT1_vect)
{
_delay_ms(300);
PORTA=PINA^0x02;
}
//__PROGRAMA PRINCIPAL
int main(void)
{
DDRA |= (1<<PA0) | (1<<PA1);
//Puerto A como salida
MCUCR |= (1<<ISC11) | (1<<ISC01);
//INT0 e INT1 Flanco de Bajada
GICR |= (1<<INT1) | (1<<INT0);
//INT0 e INT1 Habilitada
sei();
//Interrupciones habilitadas
while (1)
{
}
//Bucle Infinito
}
view raw IntExt.c hosted with ❤ by GitHub

Recomendaciones importantes que se deben tomar en cuenta al usar interrupciones:

  • Mantener la rutina de interrupción lo más corta posible.
  • Evitar en lo posible de usar rutinas de delay dentro de las rutinas de interrupción.
  • El software de la rutina de interrupción no debe habilitar o deshabilitar las interrupciones.
  • Se debe procurar usar variables “flag” y/o “mailbox” para manejar las interrupciones.