Hay ocasiones en la que nos interesa que el flujo principal del programa este haciendo unas tareas y cuando se genera un evento pre-programado dejarlo todo para atender este evento, una vez atendido se continúa con la ejecución normal del programa, esto se consigue con el manejo de interrupciones en Arduino. Para estos casos existen las interrupciones, una característica de todos los microcontroladores incluido arduino.
Un ejemplo muy simple podría ser un simple motor, imaginemos que tenemos que controlar el giro de un motor y nos interesa saber cuándo el eje llega a una posición determinada en la tenemos instalado algún tipo de sensor, óptico, inductivo, capacitivo… este sensor nos dará un pulso justo cuando el eje este en la posición y es ese preciso momento que el software necesita conocer que el eje está en la posición, para este caso utilizaremos una interrupción por hardware.
También existen las llamadas interrupciones de software, este tipo de interrupciones están bastante limitadas en Arduino, la mayoría de microcontroladores te permiten activar por software algún Flag de interrupción para poder generar una interrupción cunado programamos el Software para reconocer alguna situación, lamentablemente en Arduino este tipo de interrupciones están limitadas a los timers, el timer0 ya dispone de una función específica para poder generar una interrupción cada x tiempo.
Interrupciones por Hardware
Según qué placa de desarrollo Arduino tengamos, dispondremos de más o menos pines con capacidad de interrupción, aquí pongo una tabla por modelo de placa de desarrollo y los pines de interrupción asociados:
Modelo | INT0 | INT1 | INT2 | INT3 | INT4 | INT5 |
---|---|---|---|---|---|---|
Mini | 2 | 3 | ||||
Nano | 2 | 3 | ||||
Uno | 2 | 3 | ||||
Leonardo | 3 | 2 | 0 | 1 | 7 | |
Mega | 2 | 3 | 21 | 20 | 19 | 18 |
Due | Todos los pines |
A la hora de programar nuestra ISR (Interruption Service Routine) debemos tener en cuenta varias cosas:
- El código ha de ser muy simple, activar alguna bandera, actualizar variable…. Durante el tiempo que se está ejecutando la ISR, el programa está parado, pudiendo llegar nuevas interrupciones las cuales no serían atendidas.
- Las variables que se manipulen en la ISR se han de declarar como variables “volatile”
- Las funciones de los timers como micros(), milis(), etc… dejan de dar servicio, esto es debido a que estas funciones también utilizan interrupciones y al estar tratando una interrupción, se desatiende el resto de interrupciones.
ISR es la rutina que realiza el manejo de interrupciones en Arduino, es una abreviacion generica dentro del mundo de la programacion.
¿Cómo se detecta una interrupción por Hardware?
Cuando hablamos de interrupciones por hardware, decimos que el micro ha de ser capaz de reconocer que en el pin asociado a la interrupción ha ocurrido algún evento que queremos controlar.El manejo de interrupciones en Arduino puede ser por Hardware o por Software (interrupcion que veremos mas tarde). Estos eventos pueden ser de 5 tipos:
- FALLING, arduino detecta un flanco de bajada en el pin de interrupción
- RISING, Arduino detecta un flanco de subida en el pin de la interrupción
- CHANGE, Arduino detecta un cambio de estado en el pin de la interrupción
- LOW, Arduino detecta el estado LOW en el pin de la interrupción
- HIGH, Arduino detecta el estado HIGH en el pin de la interrupción
Para definir la interrupción, son necesarios 2 pasos, el primer paso es parametrizar la interrupción, es decir indicar el pin a controlar, el evento a controlar y la función que se ejecutará al reconocer la interrupción, el segundo paso es generar la propia ISR para tratar la interrupción.
attachInterrupt ( digitalPinToInterrupt (pin), ISR, mode );
- pin –> pin donde recibimos el evento ( pin 2 o 3 para arduino nano)
- ISR –> Nombre de la ISR donde se tratará la interrupción
- mode –> FALLING, RISING, CHANGE, LOW o HIGH
Ejemplo de manejo de una Interrupción por Hardware
En un Arduino nano, conectaremos la salida de algún sensor al pin D2 de arduino, debemos conocer qué tipo de salida nos ofrece el sensor que conectamos, para este ejemplo imaginamos que el sensor nos da cero mientras está detectando y conectamos un led al Arduino en el pin D10. El circuito básico es el siguiente:
Cada vez que el sensor detecte, activaremos el led y cuando no detecte, lo apagaremos.
int ledPin = 10;
int intSensorPin = 2;
volatile bool sensorStatus = false;
void setup()
{
pinMode (ledPin, OUTPUT);
pinMode(intSensorPin, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(intSensorPin), ISR_SENSOR, CHANGE);
}
void loop()
{
if (sensorStatus){
digitalWrite(ledPin, LOW);
}else{
digitalWrite(ledPin, HIGH);
}
}
void ISR_SENSOR (){
if (digitalRead(2) == LOW)
{
sensorStatus = true;
} else {
sensorStatus = false;
}
}
Tienes el software disponible aquí.
Interrupción por software Timer1
En el Tutorial para generar señales PWM con Arduino, vimos que los timers tienen varias funcionalidades, pueden actuar como temporizadores, contadores o generadores de señales PWM, en este apartado vermos una libreria muy simple para configurar nuestro TimerOne de Arduino y lincar la interrrupcion a su ISR correspondiente
Para este ejemplo vamos a conservar el mismo montaje y vamos a variar un poco el código, de manera que tendremos la interrupción por Hardware que activará el led y añadiremos una interrupción del Timer1 para que nos genere una interrupción cada 500ms ya haga parpadear el led cuando esté activado. Recordar que este Timer se utiliza para controlar servos, por lo que si programamos alguna interrupción de timer1, no será posible utilizar servos.
Para aprender a programar interrupciones de los 1 y 2 mira esta entrada donde te explico cómo hacerlo modificando los timers registros del Arduino.
- Primero, debemos descargar e incluir la librería TimerOne.h
- Segundo, cargamos el timer1 con el valor de los 500ms
- Tercero asignamos el nombre de la ISR para esta interrupción
#include <TimerOne.h>
int ledPin = 10;
int intSensorPin = 2;
volatile bool sensorStatus = false;
volatile bool ledStatus = false;
void setup()
{
pinMode (ledPin, OUTPUT);
pinMode(intSensorPin, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(intSensorPin), ISR_SENSOR, CHANGE);
Timer1.initialize (500000);
Timer1.attachInterrupt (ISR_BlinkLed);
}
void loop()
{
if (sensorStatus && !ledStatus){
digitalWrite(ledPin, LOW);
}else{
digitalWrite(ledPin, HIGH);
}
}
void ISR_BlinkLed (){
if (sensorStatus){
if (ledStatus){
ledStatus = false;
} else {
ledStatus = true;
}
}
}
void ISR_SENSOR (){
if (digitalRead(2) == LOW)
{
sensorStatus = true;
} else {
sensorStatus = false;
}
}
Tienes disponible el codigo en mi repositorio