Interrupciones en Arduino



Las interrupciones son un componente imprescindible en todo componente microcontrolado tal como el Arduino. Dichas interrupciones permiten que el Arduino deje de realizar una tarea especifica dentro del código y salte directamente a una etapa URGENTE del código la cual debe ser atendida y posteriormente continúe con la tarea que estaba ejecutando.
Un ejemplo claro del uso de interrupciones es cuando queremos detectar algún cambio en las entradas digitales de Arduino.
Por ejemplo, cuando queremos leer el estado de una entrada digital (pulsador, una señal binaria, una señal de emergencia, una alarma) siempre teníamos que estar leyendo el estado de la entrada digital lo cual:

.Consume más energía, pues constantemente estoy leyendo una entrada que puede ni variar en mucho tiempo;
.Si la señal fue instantánea, puede que ni me de cuenta pues hay que esperar que el código llegue a la parte donde esta la lectura de la entrada digital;
.Voy a demorarme en darme cuenta del evento, por estar en otra parte del código que quizás no sea tan importante, antes de atender esa URGENCIA.
Esa es la importancia del mecanismo de interrupción, la cual dentro de los microprocesadores es una rutina de callback que se conoce como ISR (Interruption Service Rutine).

Tipos de Interrupciones en Arduino

Como es típico en todo microcontrolador, las interrupciones disponibles son cambios provenientes del Hardware (botones, switches, etc) y desbordamiento de tiempo en los temporizadores (Timer con Arduino o delay en interrupciones arduino), en esta entrada solo hablaremos de los primeros.
Los pines que pueden ser utilizados como interrupciones dependen del tipo de placa que se esté utilizando:

BOARD
DIGITAL PINS USABLE FOR INTERRUPTS
Uno, Nano, Mini, other 328-based
2, 3
Uno WiFi Rev.2
Todos los pines
Mega, Mega2560, MegaADK
2, 3, 18, 19, 20, 21
Micro, Leonardo, other 32u4-based
0, 1, 2, 3, 7
Zero
Todos los pines, exepto el 4
MKR Family boards
0, 1, 4, 5, 6, 7, 8, 9, A1, A2
Due
Todos los pines
101
Todos los pines (solo pines 2, 5, 7, 8, 10, 11, 12, 13 work con CAMBIO)

Función ISR Arduino

Dentro de los microcontroladores la función ISR Arduino (Interruption Service Rutine) es una función que no posee ni parámetros de entrada ni de salida
Si dos interrupciones ocurren al mismo tiempo, el ISR tiene la característica de activarse una después de la otra en secuencia.
Una buena práctica es hacer que las funciones de interrupción por ISR sean ejecutadas en el menor tiempo posible, por eso trate de ser enfático en lo que se desea hacer en esta función, dado que las demás partes del programa se encuentran deshabilitadas.
Por lo tanto, debe evitarse realizar cálculos complejos dentro de una interrupción.
Las variables que sean compartidas por el programa principal y la función de Interrupción, deben ser declaradas como VOLATILE, dado que una variable volátil tiene la característica de que debe ser consultada antes de ser usada, porque que pudo modificarse por algún flujo diferente en la programación del Arduino, que es exactamente lo que ocurre cuando se modifica dentro de un ISR.

Cuidado con los Retardos y medidas de tiempo – millis()

Hasta este punto del curso, solo hemos visto la instrucción
delay(milisegundos);
Dicha instrucción crea un retardo parando totalmente el código hasta que el tiempo de milisegundos se cumpla.
Sin embargo en este punto es importante entender que existen otros dos comando para la medición del tiempo llamada millis() y micros() que son funciones que retornan el tiempo en milisegundos y microsegundos respectivamente desde que el programa del Arduino inicio.
Se debe tener un especial cuidado con las mediciones del tiempo dentro de las interrupciones dado que el Arduino no actualiza el valor de la función millis y micros lo que provoca un desfasaje de tiempo.

Crear Interrupciones en Arduino

La sintaxis para usar las interrupciones en Arduino es:
attachInterrupt (digitalPinToInterrupt (pin), ISR, modo); //(recomendado)
attachInterrupt (Interrupt, ISR, modo); //(no recomendado)
attachInterrupt (pin, ISR, modo); //Arduino SAMD Boards, One WiFi Rev2, Due y 101.

Parámetros



.interrupt: el número de la interrupción. Tipos de datos permitidos: int
.pin: el número de pin de Arduino.
.ISR: el ISR para llamar cuando se produce la interrupción; Esta función no debe tener parámetros y no devuelve nada. Esta función a veces se conoce como una rutina de servicio de interrupción.
.Modo: definir cuándo debe activarse la interrupción. Cuatro constantes están predefinidas como valores válidos:
.LOW para activar la interrupción cuando el pin está bajo;
.CHANGE para activar la interrupción cada vez que el pin cambia de valor;
.RISING para disparar cuando el pin va de bajo a alto;
.FALLING para cuando el pin pasa de alto a bajo.


Las placas Due, Zero y MKR1000 también permiten:

.HIGH para activar la interrupción siempre que el pin esté alto.
los números de interrupción se refieren al número que se debe pasar a attachInterrupt().

BOARD
INT.0
INT.1
INT.2
INT.3
INT.4
INT.5
Uno, Ethernet
2
3
    
Mega2560
2
3
21
20
19
18
32u4 based (e.g Leonardo, Micro)
3
2
0
1
7


Antirebote (Bounce) con Sistemas Mecánicos y Interrupciones

Cuando conectemos un sistema mecánico (pulsador, switche, etc) a una interrupción del Arduino, recordemos que deberemos aplicar un método de anti-rebote.
En una entrada anterior, vimos como usar el antirebote o debounce con Arduino colocando un delay(150) o delay(200), sin embargo, como ya lo dijimos anteriormente, el delay ya NO funciona dentro de la rutina de interrupciones.
Por lo tanto, una opción es colocar un capacitor en serie con el sistema electromecanico, para adicionarle un retardo por hardware y eliminar el rebote.

debounce Arduino en Interrupciones

Otra forma, es hacerlo por código pero usando el comando millis(), de la siguiente forma:
if (millis() - startTime > timeThreshold)
{
//Colocar aqui la rutina de interrupción (lo más corta posible)
startTime = millis();
}

Se define un tiempo inicial y una banda muerta, así solo se ejecuta lo que hay dentro del if, si se cumple que la interrupción fue activada por fuera de la banda muerta.

attachinterrupt arduino – Ejemplo ISR

Encender y apagar un LED con un retardo de 3 segundos dentro del Loop, con el objetivo de poner a realizar una tarea de consumo de tiempo dentro del Arduino. Adicionalmente colocar un pulsador en el PIN2 de Arduino y configurarlo para ser activado por interrupción, teniendo en consideración el rebote que puede generar este sistema mecánico. La idea es que el Arduino aumente un contador y lo muestre por el puerto serial, cada vez que el botón es presionado.
Compara el resultado, cuando simplemente lees el pulsador como si fuera una entrada digital y analiza el resultado con respecto al funcionamiento con interrupción.
Interrupciones Arduino

Código de implementación

A continuación tienes el código de implementación del ejemplo, el cual fue detallado en el video del canal de YouTube, para descargarlo basta con compartir este post con cualquiera de los siguientes botones, para que ayudes a que este sitio WEB continue aportando más contenido gratuito y de calidad.
//Declara los nombres de los pines del arduino
const byte push = 2;  //Pulsador
byte led = 12;        //Led
volatile int ISR_Cont = 0; //Variable volatil para la interrupción
int Cont = 0;
//Variables para contar el tiempo con millis() dentro de la interrupción
const int Banda = 150;
long Time = 0;
void setup()
{
//Configura las entradas y salidas
pinMode(led, OUTPUT);   
pinMode(push, INPUT_PULLUP);
//Inicializa la comunicación serial a 9600 baudios
Serial.begin(9600);
//Configura la interrupción en el PIN del pulsador en modo FALLING
attachInterrupt(digitalPinToInterrupt(push), InterContador, FALLING);
}
void loop()
{
//Enciende y apaga un LED con mucho tiempo de retardo
//simplemente para consumir tiempo dentro del Arduino
digitalWrite(led, HIGH);
delay(3000);
digitalWrite(led, LOW);
delay(3000);
//Esta etapa  incrementa el contador cuando ISR_Cont
//es modificado dentro de la interrupción
if (Cont != ISR_Cont)
{
Cont = ISR_Cont;
Serial.print("Contador: ");
Serial.println(Cont);
}
}
//Función que se ejecuta cuando se ACTIVA la INTERRUPCIÓN
void InterContador()
{
//Incrementa el contador
ISR_Cont++;
// Debounce o Antirebote por código
/*if (millis() - Time > Banda)
{
ISR_Cont++;
Time = millis();
}*/
}





No hay comentarios:

Publicar un comentario