Interrupciones en Arduino con AttachInterrupt

Las interrupciones son un mecanismo muy importante de Arduino que permite a los dispositivos externos interactuar con el controlador cuando ocurren diferentes eventos.

Instalando el controlador de interrupciones de hardware en el sketch, podremos responder a la activaci贸n o desactivaci贸n de un bot贸n, al pulsar el teclado, el rat贸n, los tics del temporizador RTC, obtener nuevos datos en UART, I2C o SPI.

En este art铆culo aprenderemos c贸mo funcionan las interrupciones en las placas Arduino Uno, Mega o Nano y daremos un ejemplo del uso de la funci贸n attachInterrupt() de Arduino.

Interrupciones en Arduino

Una interrupci贸n es una se帽al que indica al procesador cu谩ndo ocurre un evento que requiere atenci贸n inmediata. El procesador debe responder a esta se帽al interrumpiendo las instrucciones actuales y pasando el control al controlador de interrupciones (ISR, Interrupt Service Routine). El controlador es una funci贸n com煤n que escribimos nosotros mismos y colocamos all铆 el c贸digo que debe responder al evento.

Flujo del programa con interrupci贸n ISR en Arduino

Despu茅s de la interrupci贸n del servicio ISR, la funci贸n termina y el procesador contin煤a con las instrucciones interrumpidas – contin煤a ejecutando el c贸digo desde el punto donde fue detenido. Todo esto sucede autom谩ticamente, por lo que nuestra tarea es s贸lo escribir el controlador de interrupciones, sin complicarnos demasiado.

Tendr谩s que entender el esquema, el funcionamiento de los dispositivos conectados, y la frecuencia con la que se pueden desencadenar las interrupciones y las peculiaridades de las mismas. Todo esto es la principal dificultad del uso de las interrupciones.

Interrupciones de hardware y software

Las interrupciones en Arduino pueden ser divididas en varios tipos:

  • Interrupciones de hardware: Interrupci贸n a nivel de arquitectura del microprocesador. El evento en s铆 puede ser un momento productivo desde un dispositivo externo, por ejemplo, al pulsar un bot贸n del teclado, al mover el rat贸n de un ordenador, etc.
  • Interrupciones de software: Se ejecutan dentro del programa utilizando instrucciones especiales. Se utilizan para llamar al controlador de interrupciones (ISR).
  • Interrupciones internas (s铆ncronas) . Una interrupci贸n interna ocurre como resultado de un cambio, violaci贸n o error en la ejecuci贸n del programa (por ejemplo, cuando se accede a una direcci贸n no v谩lida, c贸digo de operaci贸n no v谩lido, etc.).

Por qu茅 necesitamos interrupciones de hardware

Las interrupciones son 煤tiles en los programas de Arduino porque ayudan a resolver problemas de sincronizaci贸n. Por ejemplo, cuando se trabaja con interrupciones UART, no se permite hacer un seguimiento de la llegada de cada car谩cter. Un dispositivo de hardware externo env铆a una se帽al de interrupci贸n, el procesador llama inmediatamente al manejador de interrupciones, que captura el s铆mbolo en el tiempo. Esto ahorra tiempo de CPU, que se gastar铆a en comprobar el estado de UART sin interrupciones, en su lugar todas las acciones necesarias son realizadas por el manejador de interrupciones, sin afectar al programa principal. No se requieren caracter铆sticas especiales del dispositivo de hardware.

Las principales razones por las que se deben desencadenar las interrupciones son las siguientes

  • Determinaci贸n del cambio de estado de salida;
  • Interrupci贸n del temporizador;
  • Interrupciones a SPI, I2C, USART;
  • Conversi贸n anal贸gico-digital;
  • EEPROM lista para usar, memoria flash.

C贸mo se implementan las interrupciones en Arduino

Cuando se recibe una se帽al de interrupci贸n, la operaci贸n en el bucle loop() se detiene. Se inicia la ejecuci贸n de la funci贸n, que se declara ejecutada durante la interrupci贸n. La funci贸n declarada no puede aceptar valores de entrada y valores de retorno al final de la operaci贸n. El c贸digo en s铆 mismo en el bucle principal del programa no se ve afectado por la interrupci贸n. La funci贸n est谩ndar attachInterrupt() se utiliza para trabajar con interrupciones en Arduino.

Diferencia en la implementaci贸n de interrupciones en diferentes placas Arduino

pines de interrupcion por modelo de arduino

Dependiendo de la implementaci贸n de hardware de un modelo particular de Arduino, hay varias interrupciones posibles. La placa Arduino Uno tiene 2 interrupciones en el segundo y tercer pin, pero si se necesitan m谩s de dos salidas, la placa soporta un modo especial de “cambio de pin”. Este modo funciona cambiando la entrada de todos los pines.

La diferencia del modo de interrupci贸n al cambiar la entrada es que las interrupciones se pueden generar en cualquiera de los ocho pines. El procesamiento en este caso ser谩 m谩s complicado y m谩s largo, ya que tendr谩 que controlar el 煤ltimo estado de cada uno de los contactos.

Otras placas tienen un mayor n煤mero de interrupciones. Por ejemplo, la placa Arduino Mega 2560 tiene 6 pines que pueden manejar interrupciones externas y la Arduino Due puede hacerlo en todos sus pines. Para todas las tarjetas Arduino, cuando se trabaja con la funci贸n attachInterrupt (interrupci贸n, funci贸n, modo), el argumento Inerrupt 0 est谩 asociado al pin 2 digital.

Ejemplos de interrupciones en Arduino

Ahora pasemos a la pr谩ctica y hablemos de c贸mo utilizar las interrupciones en nuestros proyectos.

Sintaxis attachInterrupt()

La funci贸n attachInterrupt se utiliza para manejar las interrupciones. Se usa para conectar una interrupci贸n externa al controlador.

Sintaxis de la llamada: attachInterrupt(interrupt, function, mode)

Argumentos de la funci贸n:

  • interrupt – n煤mero de la interrupci贸n llamada (est谩ndar 0 – para el 2潞 pin, para la placa Arduino Uno 1 – para el 3潞 pin),
  • function – el nombre de la funci贸n que se llamar谩 en la interrupci贸n (es importante que la funci贸n no acepte ni devuelva ning煤n valor),
  • mode – es una condici贸n para activar una interrupci贸n.

Se pueden ajustar las siguientes condiciones de funcionamiento:

  • LOW – se realiza en un nivel de se帽al bajo cuando el contacto es cero. La interrupci贸n puede repetirse c铆clicamente, por ejemplo, cuando se pulsa el bot贸n.
  • CHANGE – la interrupci贸n ocurre cuando la se帽al cambia de alta a baja o viceversa. Se realiza una vez en cualquier cambio de se帽al.
  • RISING – interrumpe una vez al cambiar la se帽al de LOW a HIGH.
  • FALLING – interrumpe una vez al cambiar la se帽al de HIGH a LOW.

Observaciones importantes

Al trabajar con interrupciones se deben tener en cuenta las siguientes limitaciones importantes:

  • La funci贸n del controlador no debe ser ejecutada por mucho tiempo. El punto es que Arduino no puede procesar varias interrupciones simult谩neamente. Mientras se ejecuta la funci贸n de su controlador, todas las dem谩s interrupciones ser谩n ignoradas y es posible que se pierdan eventos importantes. Si necesitas hacer algo grande, simplemente pasa el procesamiento de eventos en el bucle principal(). En el controlador de bucle s贸lo se puede fijar un indicador de evento, y en el bucle se puede verificar el indicador y procesarlo.
  • Hay que tener mucho cuidado con las variables. El compilador inteligente de C++ puede “reoptimizar” su programa eliminando las variables que considere innecesarias. El compilador simplemente no ver谩 que has establecido variables en una parte y las usas en otra. Para eliminar esta probabilidad en el caso de tipos de datos b谩sicos, puedes utilizar la palabra reservada volatile, por ejemplo: volatile boolean state = 0. Pero este m茅todo no funcionar谩 con estructuras de datos complejas.
  • No se recomienda utilizar un gran n煤mero de interrupciones (intenta no utilizar m谩s de 6-8). Un gran n煤mero de varios eventos requieren una complicaci贸n compleja del c贸digo y, por lo tanto, conduce a errores. Adem谩s, debes entender que no hay precisi贸n de ejecuci贸n en sistemas con gran cantidad de interrupciones – nunca entender谩s exactamente cu谩l es el intervalo entre llamadas de comandos importantes.
  • No se puede usar delay() en los controladores. El mecanismo para determinar el intervalo de retardo utiliza temporizadores, y tambi茅n trabajan en las interrupciones que su controlador bloquear谩. Como resultado, todos estar谩n esperando a alguien y el programa se colgar谩. Por la misma raz贸n, no puedes utilizar protocolos de comunicaci贸n basados en interrupciones (por ejemplo, i2c).

Ejemplos de c贸mo usar attachInterrupt

Pong谩monos a practicar y consideremos un ejemplo sencillo de c贸mo usar las interrupciones. En el ejemplo definimos una funci贸n de controlador que cambiar谩 el estado del pin 13 al que tradicionalmente conectamos el LED cuando se cambia la se帽al en el pin 2 del Arduino Uno.

Veamos algunos ejemplos de interrupciones m谩s complejas y sus controladores: para el temporizador y los botones.

Interrupciones al presionar un bot贸n: Efecto Rebote

En las interrupciones generadas al presionar un bot贸n (push button) se suelen presentar problema de rebotes, ya que los contactos oscilar谩n, causando una serie de disparadores, antes de que entren en contacto entre s铆 al pulsar el bot贸n. Hay dos maneras de combatir estos “ruidos” o “efecto rebote”: por hardware, es decir, soldando un capacitor al bot贸n, o por software.

Puedes deshacerte de los ruidos usando la funci贸n milis, que permite registrar el tiempo transcurrido desde la primera operaci贸n del bot贸n.

Este c贸digo nos permite eliminar el rebote y no bloquea la ejecuci贸n del programa, como en el caso de la funci贸n delay, que es inaceptable en las interrupciones.

Interrupciones con Timer

Un temporizador es un contador que cuenta a alguna frecuencia derivada de los procesadores de 16 MHz. Es posible configurar el divisor de frecuencia para obtener el modo de conteo deseado. Tambi茅n es posible configurar el contador para generar interrupciones cuando se alcanza el punto de ajuste.

Las interrupciones mediante Timer permiten realizar interrupciones una vez por milisegundo. El Arduino tiene 3 temporizadores – Timer0, Timer1 y Timer2. El Timer0 se utiliza para generar interrupciones una vez por milisegundo, y el contador se actualiza y pasa a la funci贸n de milis (). Este temporizador es de ocho bits y cuenta de 0 a 255. Se genera una interrupci贸n cuando se alcanza el valor de 255. Por defecto, el divisor del reloj es 65 para obtener una frecuencia cercana a 1 kHz.

Los registros de comparaci贸n se utilizan para comparar el estado del temporizador y los datos almacenados. En este ejemplo, el c贸digo generar谩 una interrupci贸n cuando el contador llegue a 0xAF.

OCR0A = 0xAF;

TIMSK0 |= _BV(OCIE0A);

Es necesario determinar el manejador de la interrupci贸n para el vector de la interrupci贸n por medio de un temporizador. El vector de interrupci贸n es un puntero a la ubicaci贸n del comando a ejecutar cuando se invoca la interrupci贸n. Varios vectores de interrupci贸n se combinan en una tabla de vectores de interrupci贸n. El temporizador en este caso se llamar谩 TIMER0_COMPA_vect. En este controlador se realizar谩n las mismas acciones que en el loop().

En resumen

Las interrupci贸nes de Arduino son un tema bastante complicado porque hay que pensar integramente en toda la arquitectura del proyecto, imaginar c贸mo se ejecutar谩 el c贸digo, cu谩les son los posibles eventos, qu茅 suceder谩 cuando se interrumpa el c贸digo principal, etc.

No pretendemos en este art铆culo revelar todas las peculiaridades de trabajar con esta construcci贸n del lenguaje; el objetivo principal era introducirnos en los principales casos de uso. En otros art铆culos desarrollaremos los conceptos sobre las interrupciones con m谩s detalle.