No pude encontrar una imagen divertida para esta lección, solo encontré algún tipo de conferencia sobre programación, y el comienzo de esta conferencia nos explica perfectamente qué es una interrupción. Una interrupción en Arduino se puede describir exactamente de la misma manera: el microcontrolador «deja todo», cambia a la ejecución del bloque de funciones en el controlador de interrupciones, las ejecuta y luego regresa exactamente al punto en el código principal donde fue detenido.
Una llamada desde el exterior.
Las interrupciones son diferentes, es decir, no las interrupciones en sí mismas, sino sus razones: una interrupción puede ser activada por un convertidor de analógico a digital, un temporizador-contador o, literalmente, un pin de microcontrolador. Estas interrupciones se denominan interrupciones externas de hardware y hoy hablaremos de ellas.
La interrupción de hardware externa es una interrupción causada por un cambio en el voltaje en un pin del microcontrolador. El punto principal es que el microcontrolador (núcleo de computación) no sondea el pin y no pierde tiempo en él, otra pieza de hardware está conectada en el pin. Tan pronto como cambia el voltaje en el pin (lo que significa que se aplica una señal digital, se aplica +5 / se elimina + 5), el microcontrolador recibe una señal, deja todo, procesa la interrupción y vuelve a funcionar. ¿Por qué es necesario? La mayoría de las veces, las interrupciones se utilizan para detectar eventos cortos: pulsos o incluso para contar su número sin cargar el código principal. Una interrupción de hardware puede detectar una pulsación breve de un botón o un disparador de sensor durante cálculos largos y complejos o retrasos en el código, es decir, en términos generales, el pin se consulta en paralelo con el código principal. Además, las interrupciones pueden despertar al microcontrolador de los modos de ahorro de energía, cuando en general casi todos los periféricos están apagados. Veamos cómo trabajar con interrupciones de hardware en el IDE de Arduino.
Uso de las Interrupciones en Arduino.
Comencemos con el hecho de que no todos los pines pueden «interrumpir». Sí, existe algo llamado pinChangeInterrupts, pero hablaremos de ello en los tutoriales avanzados. Ahora debemos entender que las interrupciones de hardware solo las pueden generar ciertos pines:
Mc / número de interrupción | INT 0 | INT 1 | INT 2 | INT 3 | INT 4 | INT 5 |
ATmega 328/168 (Nano, UNO, Mini) | D2 | D3 | – | – | – | – |
ATmega 32U4 (Leonardo, Micro) | D3 | D2 | D0 | D1 | D7 | – |
ATmega 2560 (Mega) | D2 | D3 | D21 | D20 | D19 | D18 |
Como entendió en la tabla, las interrupciones tienen su propio número, que difiere del número de pin. Por cierto, hay una función relacionada, digitalPinToInterrupt ( pin ) que toma un número pin y devuelve el número de interrupción. Habiendo alimentado esta función con el número 3 en el Arduino nano, obtenemos 1. Todo según la tabla anterior, una función para los perezosos.
Una interrupción se conecta mediante la función attachInterrupt(pin, handler, mode):
- pin – número de interrupción
- handler– el nombre de la función del controlador de interrupciones (debe crearla usted mismo)
- mode– «modo» de operación de interrupción:
- LOW (bajo): se activa cuando una señal está BAJA en el pin
- RISING (crecimiento): se activa cuando la señal en el pin cambia de BAJA a ALTA
- FALLING (caída): se activa cuando la señal en el pin cambia de ALTA a BAJA
- CHANGE (cambio): se activa cuando la señal cambia (de BAJA a ALTA y viceversa)
La interrupción también se puede desactivar mediante la función detachInterrupt ( pin ), donde pin es nuevamente el número de interrupción de arduino.
También puede deshabilitar globalmente las interrupciones con la función noInterrupts () y resolverlos de nuevo con interrupts (). ¡Cuidado con ellos! noInterrupts () también detendrá las interrupciones de los temporizadores, y todas las funciones de tiempo y la generación de PWM se «detendrán».
Veamos un ejemplo en el que las pulsaciones de botones se cuentan en la interrupción y, en el bucle principal, se emiten con un retraso de 1 segundo. Trabajando con el botón en el modo habitual, es imposible combinar una salida tan aproximada con un delay:
volatile int counter = 0; //contador variable void setup() { Serial.begin(9600);// abre un puerto para la comunicación // conectó el botón a D2 y GND pinMode(2, INPUT_PULLUP); \ // D2 es interrupción 0 // controlador - función buttonTick // CAYENDO - cuando se presiona el botón, habrá una señal de 0, y la capturamos attachInterrupt(0, buttonTick, FALLING); } void buttonTick() { counter++; // + clic } void loop() { Serial.println(counter); // salida delay(1000); // espera }
¡Así que nuestro código cuenta los clics incluso durante un delay; Excelente. Pero que es volatile? Hemos declarado una variable global counter, que almacenará el número de veces que se presionó el botón del arduino. Si el valor de la variable cambia en la interrupción, debe informar al microcontrolador sobre esto utilizando el especificador volatile, que se escribe antes de especificar el tipo de datos de la variable, de lo contrario el trabajo será incorrecto. Solo debe recordarse esto: si una variable cambia en una interrupción, hágala volatile.
Algunos puntos más importantes:
- Las variables modificadas en la interrupción deben declararse como volátile
- La interrupción no tiene retrasos como delay()
- No cambian su valor durante las interrupción las funciones millis () y micros ()
- En la interrupción, la salida al puerto serie no funciona correctamente (Serial.print ()), tampoco debe usarlo allí: carga el kernel.
- Debe intentar hacer el menor número de cálculos posible durante una interrupción y, en general, acciones “largas”. ¡Ralentizará el funcionamiento del MC con frecuentes interrupciones! ¿Qué hacer? Lee abajo.
Atrapar el evento.
Si una interrupción detecta un evento arduino que no necesita ser procesado inmediatamente, entonces es mejor usar el siguiente algoritmo de interrupción:
- En el controlador de interrupciones, simplemente levante la bandera.
- En el bucle principal del programa, comprobamos la bandera, si se levanta, la reseteamos y realizamos las acciones necesarias.
volatile boolean intFlag = false; // bandera void setup() { Serial.begin(9600); // conectó el botón a D2 y GND pinMode(2, INPUT_PULLUP); // D2 es interrupción 0 // controlador - función buttonTick // falling - cuando se presiona el botón, habrá una señal de 0, y la capturamos attachInterrupt(0, buttonTick, FALLING); } void buttonTick() { intFlag = true; // levantó la bandera de interrupción } void loop() { if (intFlag) { intFlag = false; // reinicia // alguna accion Serial.println("Interrupt!"); } }
El siguiente escenario posible: necesitamos captar la señal del «sensor» y responder inmediatamente una vez hasta que aparezca la siguiente señal. Si el sensor es un botón, nos espera el rebote de los contactos. Es mejor lidiar con el rebote en el hardware, pero puede resolver el problema en el software: recuerde el momento de presionar e ignore las operaciones posteriores. Considere un ejemplo donde la interrupción está configurada para cambiar (CHANGE).
void setup() { // interrumpt en D2 (UNO / NANO) attachInterrupt(0, isr, CHANGE); } volatile uint32_t debounce; void isr() { // deja un tiempo de espera de 100 ms para el rebote // CHANGE no proporciona el estado del pin, // tengo que averiguarlo usando digitalRead if (millis() - debounce >= 100 && digitalRead(2)) { debounce = millis(); // su código para interrumpcion en señal alta aqui } } void loop() { }
Podría decir: ¡pero millis () no cambia de valor en la interrupción! Sí, no es así, ¡pero cambia entre interrupciones!
Esto es básicamente todo lo que necesitas saber sobre interrupciones, analizaremos casos más específicos en el curso avanzado.