0% encontró este documento útil (0 votos)
292 vistas18 páginas

Interrupciones Con Arduino

El documento describe las interrupciones en los microcontroladores. Explica que las interrupciones permiten asociar funciones a eventos específicos para responder más rápidamente que mediante bucles de consulta repetitiva. Detalla los tipos de interrupciones, cómo se procesan y las características de las interrupciones en Arduino, incluyendo los pines que las admiten.

Cargado por

Daniel Torres
Derechos de autor
© © All Rights Reserved
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como DOCX, PDF, TXT o lee en línea desde Scribd
0% encontró este documento útil (0 votos)
292 vistas18 páginas

Interrupciones Con Arduino

El documento describe las interrupciones en los microcontroladores. Explica que las interrupciones permiten asociar funciones a eventos específicos para responder más rápidamente que mediante bucles de consulta repetitiva. Detalla los tipos de interrupciones, cómo se procesan y las características de las interrupciones en Arduino, incluyendo los pines que las admiten.

Cargado por

Daniel Torres
Derechos de autor
© © All Rights Reserved
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como DOCX, PDF, TXT o lee en línea desde Scribd
Está en la página 1/ 18

Interrupciones

12 respuestas

Si queremos detectar un cambio de estado en esta entrada, el método que hemos usado
hasta ahora es emplear las entradas digitales para consultar repetidamente el valor de la
entrada, con un intervalo de tiempo (delay) entre consultas.

Este mecanismo se denomina “poll”, y tiene 3 claras desventajas.

 Suponer un continuo consumo de procesador y de energía, al tener que preguntar


continuamente por el estado de la entrada.
 Si la acción necesita ser atendida inmediatamente, por ejemplo en una alerta de
colisión, esperar hasta el punto de programa donde se realiza la consulta puede ser
inaceptable.
 Si el pulso es muy corto, o si el procesador está ocupado haciendo otra tarea mientras
se produce, es posible que nos saltemos el disparo y nunca lleguemos a verlo.

Para resolver este tipo de problemas, los microprocesadores incorporan el concepto


de interrupción, que es un mecanismo que permite asociar una función a la ocurrencia de un
determinado evento. Esta función de callback asociada se denomina ISR (Interruption Service
Rutine).

En programación, una interrupción es una señal recibida por el procesador o MCU, para
indicarle que debe «interrumpir» el curso de ejecución actual y pasar a ejecutar código
específico para tratar esta situación.

Una interrupción es una suspensión temporal de la ejecución de un proceso, para pasar a


ejecutar una subrutina de servicio de interrupción, la cual, por lo general, no forma parte del
programa. Una vez finalizada dicha subrutina, se reanuda la ejecución del programa. Las
interrupciones HW son generadas por los dispositivos periféricos habilitando una señal del
CPU (llamada IRQ del inglés “interrupt request”) para solicitar atención del mismo.

Todos los dispositivos que deseen comunicarse con el procesador por medio de
interrupciones deben tener asignada una línea única capaz de avisar al CPU cuando le
requiere para realizar una operación. Esta línea se denomina IRQ.

Las IRQ son líneas que llegan al controlador de interrupciones, un componente de


hardware dedicado a la gestión de las interrupciones, y que está integrado en la MCU.

El controlador de interrupciones debe ser capaz de habilitar o inhibir las líneas de


interrupción y establecer prioridades entre las mismas. Cuando varias líneas de petición de
interrupción se activan a la vez, el controlador de interrupciones utilizará estas prioridades
para escoger la interrupción sobre la que informará al procesador principal. También puede
darse el caso de que una rutina de tratamiento de interrupción sea interrumpida para realizar
otra rutina de tratamiento de una interrupción de mayor prioridad a la que se estaba
ejecutando.

Procesamiento de una Interrupción:

1. Terminar la ejecución de la instrucción máquina en curso.


2. Salvar el estado del procesador (valores de registros y flags) y el valor del contador de
programa en la pila, de manera que en la CPU, al terminar el proceso de interrupción,
pueda seguir ejecutando el programa a partir de la última instrucción.
3. La CPU salta a la dirección donde está almacenada la rutina de servicio de
interrupción (Interrupt Service Routine, o abreviado ISR) y ejecuta esa rutina que tiene
como objetivo atender al dispositivo que generó la interrupción.
4. Una vez que la rutina de la interrupción termina, el procesador restaura el estado que
había guardado en la pila en el paso 2 y retorna al programa que se estaba usando
anteriormente.

Tipos de Interrupciones:

 Interrupciones HW o externas: Estas son asíncronas a la ejecución del procesador, es


decir, se pueden producir en cualquier momento independientemente de lo que esté
haciendo el CPU en ese momento. Las causas que las producen son externas al
procesador y a menudo suelen estar ligadas con los distintos dispositivos de entrada o
salida.
 Interrupciones SW: Las interrupciones por software son aquellas generadas por un
programa en ejecución. Para generarlas, existen distintas instrucciones en el código
máquina que permiten al programador producir una interrupción. Arduino no soporta
las interrupciones por software
 Un evento programado o Timer. Son las interrupciones asociadas a los timers y
gracias a ellas funciona millis().
 Excepciones: Son aquellas que se producen de forma síncrona a la ejecución del
procesador típicamente causada por una condición de error en un programa.
Normalmente son causadas al realizarse operaciones no permitidas tales como la
división entre 0, el desbordamiento, el acceso a una posición de memoria no permitida,
etc.

Definiciones genéricas de Interrupciones:

 https://es.wikipedia.org/wiki/Interrupci%C3%B3n
 http://programarfacil.com/blog/arduino-blog/interrupciones-con-arduino-ejemplo-
practico/
 https://en.wikipedia.org/wiki/Interrupt
Interrupciones en Arduino

Internamente, Arduino (mejor dicho el microcontrolador AtMega) tiene ciertas interrupciones


configuradas que lanza según la situación. Para la transmisión de datos a través del puerto
serie, para resetear la placa antes de cargar un programa, comunicación I2C, etc…

El uso de interrupciones es casi obligatorio en un programa avanzado de un microcontrolador.


Básicamente cuando un evento ocurre se levanta una bandera y la ejecución se traslada a
una rama de código diferente. De esta forma no es necesario esperar un loop a comprobar
que un evento ha ocurrido para ejecutar una acción.

Las interrupciones pueden ocurrir por un cambio en un puerto (solo en aquellos que soporten
interrupciones HW), overflow en un timer, comunicación serie (USART), etc…

Normalmente no se ve, pero las interrupciones ocurren constantemente durante la operación


normal de Arduino. Por ejemplo las interrupciones ayudan a las funciones delay() y millis() así
como a la función Serial.read().

El procesador dentro de cualquier Arduino tiene dos tipos de interrupciones: “externas” y


“cambio de pin”.

Más información:

 http://www.prometec.net/interrupciones/
 http://programarfacil.com/blog/arduino-blog/interrupciones-con-arduino-ejemplo-
practico/
 http://www.sites.upiicsa.ipn.mx/polilibros/portal/Polilibros/P_terminados/PolilibroFC/Uni
dad_V/Unidad%20V_2.htm
 http://playground.arduino.cc/Code/Interrupts

Las interrupciones son muy útiles para hacer que las cosas ocurran automáticamente en los
programas del microcontrolador y pueden resolver problemas de temporización.
Las tareas más usuales en las que usar interrupciones son en la monitorización de entradas
de usuario o entradas externas críticas en el tiempo, así como en lectura de periféricos con
requisitos de temporización muy específicos donde queramos capturar un evento que tiene
una duración muy corta inferior al tiempo de loop de nuestro programa.

Para definir una interrupción necesitamos tres cosas:

 Un pin de Arduino que recibirá la señal de disparo


 Una condición de disparo
 Una función que se ejecutará, cuando se dispara la interrupción (Llamada call back
function).

En cuanto a la condición de disparo puede ser:

 LOW, La interrupción se dispara cuando el pin es LOW.


 CHANGE, Se dispara cuando pase de HIGH a LOW o viceversa.
 RISING, Dispara en el flanco de subida (Cuando pasa de LOW a HIGH).
 FALLING, Dispara en el flanco de bajada (Cuando pasa de HIGH a LOW).
 Y una solo para el DUE: HIGH se dispara cuando el pin esta HIGH.

Para saber todo sobre las interrupciones en el ATmega328p, debemos consultar su


información en la página 32 y 82 de http://www.atmel.com/Images/Atmel-42735-8-bit-AVR-
Microcontroller-ATmega328-328P_datasheet.pdf

Los vectores de interrupción es una tabla en memoria que contiene la dirección de memoria
de la primera instrucción de interrupción. ATmega328p Interrupt vectors, además esta tabla
establece la prioridad de las interrupciones:
Y así funciona el ISR vector table:

Las librería de avr-libc que se usa para manejar las interrupciones es <avr/interrupt.h>:

 http://www.nongnu.org/avr-libc/user-manual/group__avr__interrupts.html
 http://www.atmel.com/webdoc/AVRLibcReferenceManual/group__avr__interrupts.html

Más información:

 http://courses.cs.washington.edu/courses/csep567/10wi/lectures/Lecture7.pdf
 http://ee-classes.usc.edu/ee459/library/documents/avr_intr_vectors/
 http://www.luisllamas.es/2016/04/que-son-y-como-usar-interrupciones-en-arduino/
 http://avrmicrotutor.blogspot.com.es/2011/08/basic-understanding-of-
microcontroller.html
 https://felixmaocho.wordpress.com/2013/08/05/arduino-manejo-de-interrupciones/

Interrupciones Externas en Arduino

Estas interrupciones hardware, se diseñaron por la necesidad de reaccionar a suficiente


velocidad en tiempos inimaginablemente cortos a los que la electrónica trabaja habitualmente
y a los que ni siquiera el software era capaz de reaccionar.

Para las interrupciones externas o hardware, solo hay dos pines que las soportan en los
ATmega 328 (p.e. Arduino UNO), son las INT0 y INT1 que están mapeadas a los pines 2 y 3.
Estas interrupciones se pueden configurar con disparadores en RISING o FALLING para
flancos o en nivel LOW. Los disparadores son interpretados por hardware y la interrupción es
muy rápida.

El Arduino mega tiene más pines disponibles para interrupciones externas. Pines de External
Interrupts para Mega: 2 (interrupt 0), 3 (interrupt 1), 18 (interrupt 5), 19 (interrupt 4), 20
(interrupt 3), and 21 (interrupt 2). Estos pines pueden ser configurados para disparar una
interrupción al detectar un nivel bajo, un flanco ascendente, un flanco descendente o un
cambio de nivel.

En el pin de reset también hay otra interrupción que sólo se dispara cuando detecta voltaje
LOW y provoca el reset del microcontrolador.

Para configurar una interrupción en Arduino se usa la función attachInterrupt(). El primer


parámetro a configurar es el número de interrupción, normalmente se usa el nº de pin para
traducir al nº de interrupción.

Tabla de pines que soportan interrupción HW en Arduino:

Board Digital Pins Usable For Interrupts

Uno, Nano, Mini, other 328-based 2, 3

Mega, Mega2560, MegaADK 2, 3, 18, 19, 20, 21

Micro, Leonardo, other 32u4-based 0, 1, 2, 3, 7

Zero all digital pins, except 4


MKR1000 Rev.1 0, 1, 4, 5, 6, 7, 8, 9, A1, A2

Due all digital pins

Esto quiere decir que el Arduino UNO puede definir dos interrupciones hardware llamadas 0 y
1, conectadas a los pines 2 y 3

Para saber qué número de interrupción estás asociada a un pin, debemos usar la
función digitalPinToInterrupt(pin). El número de interrupción su mapeo en los pines
dependerá del MCI. El uso de número de interrupción puede provocar problemas de
compatibilidad cuando el sketch funciona en diferentes placas.

Tabla de interrupciones y número de pin asociado:

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

Arduino Due tiene grandes capacidades a nivel de interrupciones que permiten asociar una
interrupción a cada uno de los pines disponibles. Arduino Zero permite asociar una
interrupción a todos los pines excepto para el pin 4.

Aspectos importantes a tener en cuenta con el uso de interrupciones:

 Dentro de la función llamada desde la interrupción, la función delay() no funciona y el


valor devuelto por millis() no aumenta. La razón es que estas funciones hacen uso de
interrupciones que no se disparan mientras está disparada una interrupción externa.
 Los datos recibidos por el puerto serie se pueden perder mientras se está en la función
llamada por la interrupción.
 Se deben declarar como “volatile” cualquier variable que sea modificada dentro de la
función llamada por una interrupción: https://www.arduino.cc/en/Reference/Volatile

Tutorial de Interrupciones externas en MCUs AVR de 8 bits: http://www.avr-


tutorials.com/interrupts/The-AVR-8-Bits-Microcontrollers-External-Interrupts

Programación de interrupciones externas en C: http://www.avr-tutorials.com/interrupts/avr-


external-interrupt-c-programming

Las interrupciones de hardware, también conocidas como INT0 e INT1, llaman a una rutina de
servicio de interrupción cuando algo sucede con uno de los pines asociados. La ventaja es
que Arduino tiene una simple rutina de configuración para conectar la rutina de servicio de
interrupción al evento: attachInterrupt (). La desventaja es que sólo funciona con dos pines
específicos: pin digital 2 y 3 en la placa Arduino.

El Atmega328p solo tiene dos interrupciones de hardware INT0 e INT1, sin embargo los
microcontroladores AVR pueden tener una interrupción ante un cambio en cualquier pin, es lo
que denomina pin change interrupt. http://playground.arduino.cc/Code/Interrupts. Estas
interrupciones no son soportadas directamente por Arduino y necesitan ser accedidas a través
de una librería adicional.

Las interrupciones de cambio de pin pueden habilitarse en más pines. Para los ATmega
328, pueden habilitarse en cualquiera de los pines de señal disponibles. Estas son disparadas
igual en flancos RISING o FALLING, pero depende del código de la interrupción configurar el
pin que recibe la interrupción y determinar qué ha pasado. Las interrupciones de cambio de
pin están agrupadas en 3 puertos de la MCU, por lo tanto hay 3 vectores de interrupciones
para todo el conjunto de pines. Esto hace el trabajo de resolver la acción en una interrupción
más complicada.

Si necesita más pines para interrupciones, hay un mecanismo para generar una interrupción
cuando se cambia cualquier pin en uno de los puertos de 8 bits. No sabes pin, sino sólo en
qué puerto. Si se genera una interrupción cuando se cambia uno de los pines ADC0 a ADC5
(utilizado como entrada digital). En ese caso, se llama a la rutina de servicio de interrupción
ISR (PCINT1_vect). En la rutina se puede averiguar cuál de los pines específicos dentro de
ese puerto ha sido el que ha cambiado.

En primer lugar, los indicadores de habilitación de interrupción de cambio de pin deben


ajustarse en el registro PCICR. Estos son los bits PCIE0, PCIE1 y PCIE2 para los grupos de
pines PCINT7..0, PCINT14..8 y PCINT23..16 respectivamente. Los pines individuales se
pueden activar o deshabilitar en los registros PCMSK0, PCMSK1 y PCMSK2. En el circuito
Arduino, en combinación con la hoja de datos Atmel Atmega328, se puede ver que el pin
PCINT0 corresponde al pin 0 en el puerto B (llamado PB0). Este es el pin 8 en el Arduino Uno.
Otro ejemplo es el pin A0 de la tarjeta Arduino, que puede utilizarse como una entrada digital:

 Pin Change Interrupt Request 0 (pins D8 to D13) (PCINT0_vect)


 Pin Change Interrupt Request 1 (pins A0 to A5) (PCINT1_vect)
 Pin Change Interrupt Request 2 (pins D0 to D7) (PCINT2_vect)

“The Pin Change Interrupt Request 2 (PCI2) will trigger if any enabled PCINT[23:16] pin
toggles. The Pin Change Interrupt Request 1 (PCI1) will trigger if any enabled PCINT[14:8] pin
toggles. The Pin Change Interrupt Request 0 (PCI0) will trigger if any enabled PCINT[7:0] pin
toggles.”

PCINT son unos pines determinados que se puede ver en la página 19 a que corresponden
de http://www.atmel.com/Images/Atmel-42735-8-bit-AVR-Microcontroller-ATmega328-
328P_datasheet.pdf
Los pines de interrupción deben estar configurados como INPUT, las resistencias pullup
pueden habilitarse para poder detectar interruptores simples.

Es cierto que solo se puede configurar una rutina de interrupción para cada grupo de pines en
un puerto, pero hay muchos casos donde es suficiente.

Más información:

 https://thewanderingengineer.com/2014/08/11/arduino-pin-change-interrupts/
 http://www.geertlangereis.nl/Electronics/Pin_Change_Interrupts/PinChange_en.html
 http://playground.arduino.cc/Main/PinChangeInterrupt
 http://www.ermicro.com/blog/?p=2292
 http://gammon.com.au/interrupts
 http://playground.arduino.cc//Main/PinChangeIntExample
 http://www.avr-tutorials.com/interrupts/about-avr-8-bit-microcontrollers-interrupts

También disponemos de la PinChangeInt Library. PinChangeInt implementa interrupciones de


cambio de pin para el entorno Arduino. Esta biblioteca fue diseñada para el Arduino Uno /
Duemilanove, y funciona bien en el Nano. El soporte MEGA está incluido pero no de una
forma completa.

 http://playground.arduino.cc/Main/PinChangeInt
 https://github.com/GreyGnome/PinChangeInty
 nueva versión EnableInterrupt https://github.com/GreyGnome/EnableInterrupt
 wiki https://github.com/GreyGnome/EnableInterrupt/wiki

Registros para las interrupciones externas y pin change son:


 External Interrupt Control Register A: The External Interrupt Control Register A
contains control bits for interrupt sense control.
Name: EICRA
 External Interrupt Mask Register: When addressing I/O Registers as data space using
LD and ST instructions, the provided offset must be used. When using the I/O specific
commands IN and OUT, the offset is reduced by 0x20, resulting in an I/O address
offset within 0x00 – 0x3F.
Name: EIMSK
 External Interrupt Flag Register: When addressing I/O Registers as data space using
LD and ST instructions, the provided offset must be used. When using the I/O specific
commands IN and OUT, the offset is reduced by 0x20, resulting in an I/O address
offset within 0x00 – 0x3F.
Name: EIFR
 Pin Change Interrupt Control Register
Name: PCICR
 Pin Change Interrupt Flag Register: When addressing I/O Registers as data space
using LD and ST instructions, the provided offset must be used. When using the I/O
specific commands IN and OUT, the offset is reduced by 0x20, resulting in an I/O
address offset within 0x00 – 0x3F.
Name: PCIFR
 Pin Change Mask Register 2
Name: PCMSK2
 Pin Change Mask Register 1
Name: PCMSK1
 Pin Change Mask Register 0
Name: PCMSK0

Cada interrupción está asociada con dos bits, un bit de indicador de interrupción (flag) y un bit
habilitado de interrupción (enabled). Estos bits se encuentran en los registros de E/S
asociados con la interrupción específica:

 El bit de indicador de interrupción se establece siempre que se produce el evento de


interrupción, independientemente de que la interrupción esté o no habilitada.
Registro EIFR
 El bit activado por interrupción se utiliza para activar o desactivar una interrupción
específica. Básicamente se le dice al microcontrolador si debe o no responder a la
interrupción si se dispara. Registro EIMSK

En resumen, básicamente, tanto la Interrupt Flag como la Interrupt Enabled son necesarias
para que se genere una solicitud de interrupción como se muestra en la figura siguiente.

Aparte de los bits habilitados para las interrupciones específicas, el bit de enable de
interrupción global DEBE ser habilitado para que las interrupciones se activen en el
microcontrolador.

Para el microcontrolador AVR de 8 bits, este bit se encuentra en el registro de estado de E/S
(SREG). La interrupción global habilitada es bit 7, en el SREG.
Interrupciones Internas en Arduino

Las interrupciones internas en Arduino son aquellas interrupciones relacionadas con los timers
y que también son denominadas interrupciones de eventos programados.

Arduino tiene tres timers. Son el timer cero, timer uno, timer dos.Timer cero y dos son de 8 bits
y el temporizador uno es de 16 bits.

Crearemos una interrupción interna utilizando la interrupción de un temporizador. Para ello


necesitaremos una librería adecuada para el temporizador que usemos. Podemos crear tres
tipos de interrupción interna, son interrupción de desbordamiento de temporizador (timer
overflow), interrupción de comparación de salida (Output Compare Match Interrupt),
interrupción de captura de entrada (Timer Input Capture Interrupt).

Ya vimos que las interrupciones de los timers se usan para PWM y también con la librería
MSTimer2: http://www.pjrc.com/teensy/td_libs_MsTimer2.html

En el apartado de timers trataremos más a fondo los temporizadores de Arduino y sus


interrupciones asociadas.

Problemas y soluciones con las interrupciones en


Arduino: http://rcarduino.blogspot.com.es/2013/04/the-problem-and-solutions-with-arduino.html

ISR (Interrupt Service Routines)

La función de callback asociada a una interrupción se denomina ISR (Interruption Service


Rutine). ISR es una función especial que tiene algunas limitaciones, una ISR no puede tener
ningún parámetro en la llamada y no pueden devolver ninguna función.
Dos ISR no pueden ejecutarse de forma simultánea. En caso de dispararse otra interrupción
mientras se ejecuta una ISR, la función ISR se ejecuta una a continuación de otra.

Una ISR debe ser tan corta y rápida como sea posible, puesto que durante su ejecución se
paraliza el curso normal del programa y las interrupciones se deshabilitan.

Se se usan varias ISR en el sketch, solo una se puede ejecutar y otras interrupciones serán
ejecutadas después de que la ISR actual finalice, en un orden que depende las prioridad de
las interrupciones y que depender de interrupt handler.

La función millis() no funciona dentro del ISR puesto que usa interrupciones para su uso. La
función micros() funciona dentro de ISR pero después de 1-2 ms se empieza a comportar de
forma extraña. delayMicroseconds() no usa ningún contador y funcionará correctamente
dentro del ISR. Por lo tanto si la ISR dura mucho tiempo provocará retrasos en el reloj interno,
puesto que millis() no avanza mientras se ejecuta el ISR.

Para pasar datos entre el programa principal y el ISR se usan las variables globales, pero para
que estas variables se actualicen correctamente deben declararse con el modificador
“volatile”: https://www.arduino.cc/en/Reference/Volatile

Volatile es una palabra reservada que se pone delante de la definición de una variable para
modificar la forma en que el compilador y el programa trata esa variable.

Declarar una variable volátil es una directiva para el compilador. Específicamente, dice al
compilador que cargue la variable desde la RAM y no desde un registro de almacenamiento,
que es una ubicación de memoria temporal donde se almacenan y manipulan las variables del
programa. Bajo ciertas condiciones, el valor de una variable almacenada en registros puede
ser inexacto.

Una variable debe ser declarada volátil siempre que su valor pueda ser cambiado por algo
más allá del control de la sección de código en la que aparece, como un subproceso que se
ejecuta simultáneamente. En Arduino, el único lugar en el que es probable que ocurra es en
secciones de código asociadas con interrupciones, las llamadas rutinas de servicio de
interrupción (ISR).

Para poder modificar una variable externa a la ISR dentro de la misma debemos declararla
como “volatile”. El indicador “volatile” indica al compilador que la variable tiene que ser
consultada siempre antes de ser usada, dado que puede haber sido modificada de forma
ajena al flujo normal del programa (lo que, precisamente, hace una interrupción). Al indicar
una variable como Volatile el compilador desactiva ciertas optimizaciones, lo que supone una
pérdida de eficiencia. Por tanto, sólo debemos marcar como volatile las variables que
realmente lo requieran, es decir, las que se usan tanto en el bucle principal como dentro de la
ISR.

Esta es la secuencia cuando se dispara una interrupción:

1. El microcontrolador completa la instrucción que está siendo ejecutada.


2. El programa de Arduino que se está ejecutando, transfiere el control a la Interrupt
Service Routine (ISR). Cada interrupción tiene asociada una ISR que es una función
que le dice al microcontrolador que hacer cuando ocurre una interrupción.
3. Se ejecuta la ISR mediante la carga de la dirección de comienzo de la ISR en el
contador del programa.
4. La ejecución del ISR continua hasta que se encuentra el RETI (return from the
interrupt instruction). Más
información: http://www.atmel.com/webdoc/avrassembler/avrassembler.wb_RETI.html
5. Cuando ha finalizado ISR, el microcontrolador continua la ejecución del programa
donde lo dejó antes de que ocurriera la interrupción.

Más información en: http://gammon.com.au/interrupts

Hay muchos I/O registros en el microcontrolador asociados a las interrupciones externas.


Estos registros almacenan los flag de las interrupciones, los bits de enable, así como
información de control de cada interrupción externa.

Más información de
interrupciones: http://cs4hs.cs.pub.ro/wiki/roboticsisfun/chapter2/ch2_10_interrupts

Funciones de Interrupciones en Arduino

El core de Arduino ofrece una serie de instrucciones para programar las interrupciones
externas, pero no las de pin change ni las de temporizadores:

 interrupts() – https://www.arduino.cc/en/Reference/Interrupts
Habilita las interrupciones (antes han debido ser inhabilitadas con noInterrupts()). Las
interrupciones permiten a ciertas tareas importantes que se ejecuten en segundo plano
y defecto las interrupciones están habilitadas. Algunas funciones no funcionarán si se
deshabilitan las interrupciones y las comunicaciones entrantes serán ignoradas,
también podrán modificar ligeramente la temporización del código. Las interrupciones
se pueden deshabilitar para casos particulares de secciones críticas del código.
 noInterrupts() – https://www.arduino.cc/en/Reference/NoInterrupts
Deshabilita las interrupciones. Las interrupciones pueden ser habilitadas de nuevo con
interrupts().
 attachInterrupt() – https://www.arduino.cc/en/Reference/AttachInterrupt
Me permite configurar una interrupción externa, pero no otro tipo de interrupciones. El
primer parámetro es el número de interrupción que va asociado a un pin, luego la
función ISR y finalmente el modo.
 detachInterrupt() – https://www.arduino.cc/en/Reference/DetachInterrupt
Deshabilita la interrupción. El parámetro que se le pasa es el número de la
interrupción.
 digitalPinToInterrupt(pin) traduce el pin al número de interrupción específica.
 usingInterrupt() – https://www.arduino.cc/en/Reference/SPIusingInterrupt
Deshabilita la interrupción externa pasada como parámetro en la llamada
a SPI.beginTransaction() y se habilita de nuevo en endTransaction() para prevenir
conflictos en las transacciones del bus SPI

En attachInterrupt() los modos disponibles que definen cuando una interrupción externa es
disparada, se hace mediante 4 constantes:

 LOW, La interrupción se dispara cuando el pin es LOW.


 CHANGE, Se dispara cuando pase de HIGH a LOW o viceversa, es decir un cambio
en el estado del pin.
 RISING, Dispara en el flanco de subida (Cuando pasa de LOW a HIGH).
 FALLING, Dispara en el flanco de bajada (Cuando pasa de HIGH a LOW).
 Y solo para el DUE: HIGH se dispara cuando el pin esta HIGH.

Normalmente, las variables globales se utilizan para pasar datos entre un ISR y el programa
principal. Para asegurarse de que las variables compartidas entre un ISR y el programa
principal se actualizan correctamente, declararlas como volátiles.

La función delay() no deshabilita las interrupciones, por lo tanto los datos recibidos por el serial
Rx son guardados, los valores PWM funcionan y las interrupciones externas funcionan. Sin
embargo la función delayMicroseconds() deshabilita las interrupciones mientras está
ejecutándose.

Más información:

 https://www.arduino.cc/en/Reference/AttachInterrupt
 http://www.engblaze.com/we-interrupt-this-program-to-bring-you-a-tutorial-on-arduino-
interrupts/

Para manejar las interrupciones por cambio de pin disponemos de varias librerías:
Ver http://playground.arduino.cc/Main/LibraryList#Interrupts

Multitarea

Utilizar interrupciones nos permitirá olvidarnos de controlar ciertos pines. Esto muy importante
ya que dentro de una aplicación o programa, no vamos a hacer una única cosa. Por ejemplo,
queremos que un LED se encienda o se apague cuando pulsamos un botón. Esto es
relativamente sencillo pero cuando además queremos que otro LED parpadee, la cosa se
complica.

La probabilidad de capturar el evento cuando se pulsa el botón disminuye con el aumento de


tiempo de parpadeo. En estos casos, y en muchos otros, nos interesa liberar el procesador de
Arduino para que solo cuando se pulse el botón, haga una acción determinada. Así no
tendremos que estar constantemente comprobando el pin X si ha pulsado el botón.
Precisamente este es el sentido de las interrupciones, poder hacer otras cosas mientras no
suceda el evento, pero cuando ese evento externo esté presente, que ejecute rápidamente el
código asociado.

Los controladores de interrupciones: administran la ejecución de tareas por interrupciones, lo


cual permite la multitarea.

Todas las tareas que hemos realizado hasta ahora han sido síncronas. Es decir, solicitamos
unos datos (puerto serie, ethernet, etc…), esperamos la respuesta y los mostramos en
pantalla. La contraposición es un proceso asíncrono, nosotros lanzamos la petición y en
cuanto se pueda se realizará y se mostrará el resultado sin esperar.

Si nos encontramos con respuestas lentas, no es buena técnica esperar de forma “síncrona”
porque seguramente obtengamos un error de que se ha excedido el tiempo de espera
“timeout”. Si estamos descargando datos de gran volumen, tampoco es buena técnica que
dejemos la aplicación “congelada” mientras se descargan los datos. Lo ideal es lanzar la
descarga de fondo, es decir, de forma “asíncrona”.

Un proceso síncrono es aquel que se ejecuta y hasta que no finaliza solo se ejecuta ese
proceso (todo en el mismo loop), mientras que uno asíncrono comienza la ejecución y
continúan ejecutándose otras tareas por lo que el proceso total se completa en varios loops.
Son dos formas de atacar un problema. Este nuevo concepto es muy interesante y abre
muchas posibilidades a nuestros programas que requieren conexiones remotas.

Multitask en Arduino, muy recomendable:

 https://learn.adafruit.com/multi-tasking-the-arduino-part-1
 https://learn.adafruit.com/multi-tasking-the-arduino-part-1/using-millis-for-timing
 http://harteware.blogspot.com.es/2010/11/protothread-powerfull-library.html

Ejercicios Interrupciones Arduino

Hacer un ejercicio simple donde asociemos al pin 2 una interrupción que encienda y apague el
pin 13.

Código:

1 const byte ledPin = 13;


2 const byte interruptPin = 2;
volatile byte state = LOW;
3
4 void setup() {
5 pinMode(ledPin, OUTPUT);
6 pinMode(interruptPin, INPUT_PULLUP);
7 attachInterrupt(digitalPinToInterrupt(interruptPin), blink, CHANGE);
8 }
9
void loop() {
10 digitalWrite(ledPin, state);
11 }
12
13 void blink() {
14 state = !state;
15 }
16
17

En el siguiente código definimos el pin digital 10 como salida, para emular una onda cuadrada
de periodo 300ms (150ms ON y 150ms OFF). Para visualizar el funcionamiento de la
interrupción, en cada flanco activo del pulso simulado, encendemos/apagamos el LED
integrado en la placa, por lo que el LED parpadea a intervalos de 600ms (300ms ON y 300ms
OFF). No estamos encendiendo el LED con una salida digital, si no que es la interrupción que
salta la que enciende y apaga el LED (el pin digital solo emula una señal externa).

1
2
3 const int emuPin = 10;
4
const int LEDPin = 13;
5 const int intPin = 2;
6 volatile int state = LOW;
7
8 void setup() {
9 pinMode(emuPin, OUTPUT);
10 pinMode(LEDPin, OUTPUT);
pinMode(intPin, INPUT_PULLUP);
11 attachInterrupt(digitalPinToInterrupt(intPin), blink, RISING);
12 }
13
14 void loop() {
15 //esta parte es para emular la salida
digitalWrite(emuPin, HIGH);
16 delay(150);
17 digitalWrite(emuPin, LOW);
18 delay(150);
19 }
20
21 void blink() {
state = !state;
22 digitalWrite(LEDPin, state);
23 }
24
25

El siguiente código empleamos el mismo pin digital para emular una onda cuadrada, esta vez
de intervalo 2s (1s ON y 1s OFF). En cada interrupción actualizamos el valor de un contador.
Posteriormente, en el bucle principal, comprobamos el valor del contador, y si ha sido
modificado mostramos el nuevo valor. Al ejecutar el código, veremos que en el monitor serie
se imprimen números consecutivos a intervalos de dos segundos.

1 const int emuPin = 10;


2
const int intPin = 2;
3 volatile int ISRCounter = 0;
4 int counter = 0;
5
6
7 void setup()
8 {
pinMode(emuPin, OUTPUT);
9
10 pinMode(intPin, INPUT_PULLUP);
11 Serial.begin(9600);
12 attachInterrupt(digitalPinToInterrupt(intPin), interruptCount, LOW);
13 }
14
15 void loop()
{
16 //esta parte es para emular la salida
17 digitalWrite(emuPin, HIGH);
18 delay(1000);
19 digitalWrite(emuPin, LOW);
delay(1000);
20
21
22
if (counter != ISRCounter)
23 {
24 counter = ISRCounter;
25 Serial.println(counter);
26 }
}
27
28
void interruptCount()
29 {
30 ISRCounter++;
31 timeCounter = millis();
32 }
33
34
35
36
37

También podría gustarte