Configuración y manejo de las interrupciones del ESP32 GPIO en el IDE de Arduino Deja un comentario

Interrupciones en el ESP32

A menudo en un proyecto necesitas el ESP32 para ejecutar tu programa normal, mientras que continuamente necesitas que se monitorize para algún tipo de evento. Una solución ampliamente adoptada es el uso de una interrupción.

El ESP32 ofrece hasta 32 ranuras de interrupción para cada núcleo. Cada interrupción tiene un cierto nivel de prioridad y se puede clasificar en dos tipos.

  • Interrupciones de hardware – Estas ocurren en respuesta a un evento externo. Por ejemplo, una interrupción de GPIO (cuando se pulsa una tecla) o una interrupción de toque o pulsación (cuando se detecta el pulsar)
  • Interrupciones de software – Estas ocurren en respuesta a una instrucción de software. Por ejemplo, una simple interrupción de temporizador o una interrupción de temporizador de vigilancia (cuando el temporizador se agota)

Interrupción en el ESP32 GPIO

En el ESP32, podemos definir una función de rutina de servicio de interrupción que se llamará cuando un pin GPIO cambie el valor de su señal.

Con una placa ESP32, todos los pines GPIO pueden ser configurados para funcionar como entradas de solicitud de interrupción.

Adjuntar la interrupción a un PIN GPIO

En el IDE de Arduino, usamos una función llamada attachInterrupt() para establecer una interrupción en base a un pin por pin. La sintaxis recomendada es la siguiente.

attachInterrupt(GPIOPin, ISR, Mode);

Esta función toma tres parámetros:

  • GPIOPin: Establece la clavija GPIO como una clavija de interrupción, que le dice al ESP32 qué clavija debe monitorear.
  • ISR: Es el nombre de la función que se llamará cada vez que se dispare la interrupción.
  • Mode: Define cuándo se debe disparar la interrupción. Cinco constantes están predefinidas como valores válidos:
LOW Los disparadores interrumpen cuando el pin está LOW
HIGH Los disparadores interrumpen cuando el pin es HIGH
CHANGE Los disparadores interrumpen cuando el pin cambia de valor, de HIGH a LOW o LOW a HIGH
FALLING Los disparadores interrumpen cuando el pin va de HIGH a LOW
RISING Los disparadores interrumpen cuando el pin va de LOW a HIGH

Desconectar la interrupción de un pin GPIO

Opcionalmente, puedes llamar a la función de separación de interrupciones cuando ya no quieras que el ESP32 monitorice un pin. La sintaxis a utilizar es la siguiente.

detachInterrupt(GPIOPin);

Rutina de interrupción del servicio

La Rutina de Servicio de Interrupción se invoca cuando se produce una interrupción en cualquier pin GPIO. Su sintaxis es como la siguiente.

void IRAM_ATTR ISR() {
    Statements;
}

Las ISR en ESP32 son tipos especiales de funciones que tienen algunas reglas únicas que la mayoría de las otras funciones no tienen.

  • La rutina de servicio de interrupción debe tener un tiempo de ejecución lo más corto posible, porque bloquea la ejecución normal del programa.
  • Las rutinas de servicio de interrupción deben tener el atributo IRAM_ATTR, según la documentación de ESP32

¿Qué es IRAM_ATTR?

Al marcar un trozo de código con el atributo IRAM_ATTR estamos declarando que el código compilado se colocará en la RAM interna (IRAM) del ESP32.

De lo contrario, el código se coloca en la memoria Flash. Y el flash en el ESP32 es mucho más lento que la RAM interna.

Si el código que queremos ejecutar es una rutina de servicio de interrupción (ISR), generalmente queremos ejecutarla lo más rápido posible. Si tuviéramos que “esperar” a que una ISR se cargue desde el flash, las cosas irían terriblemente mal.

Conexión de hardware

Veamos un ejemplo práctico.

Enganchemos un pulsador al pin GPIO 18 (D18) del ESP32. No es necesario tirar de esta clavija porque tiraremos de la clavija internamente.

struct Button { const uint8_t PIN; uint32_t numberKeyPresses; bool pressed; }; Button button1 = {18, 0, false}; void IRAM_ATTR isr() { button1.numberKeyPresses += 1; button1.pressed = true; } void setup() { Serial.begin(115200); pinMode(button1.PIN, INPUT_PULLUP); attachInterrupt(button1.PIN, isr, FALLING); } void loop() { if (button1.pressed) { Serial.printf("Button 1 has been pressed %u timesn", button1.numberKeyPresses); button1.pressed = false; } //Detach Interrupt after 1 Minute static uint32_t lastMillis = 0; if (millis() - lastMillis > 60000) { lastMillis = millis(); detachInterrupt(button1.PIN); Serial.println("Interrupt Detached!"); } }

Una vez que subas el código, presiona el botón EN en el ESP32 y abre el monitor serial a una velocidad de 115200 baudios. Ahora obtendremos la salida como se muestra a continuación, cuando presiones el botón.

Explicación del código

Al principio del código creamos una estructura llamada Botón. Tiene tres miembros, a saber, número de pin, número de pulsaciones de teclas y estado de pulsación. Si no lo sabes, la estructura es el conjunto de variables de diferentes tipos (pero lógicamente relacionadas entre sí) bajo un único nombre.

struct Button {
  const uint8_t PIN;
  uint32_t numberKeyPresses;
  bool pressed;
};

A continuación creamos una instancia de la estructura de los botones e inicializamos el número de pin a 18, el número de pulsaciones de teclas a 0 y el estado de pulsación por defecto a false.

Button button1 = {18, 0, false};

La siguiente pieza de código es una Rutina de Servicio de Interrupción. Como se mencionó anteriormente, ISR en ESP32 debe tener el atributo IRAM_ATTR.

En ISR simplemente incrementamos el contador de KeyPresses en 1 y ponemos el estado de botón pulsado en True.

void IRAM_ATTR isr() {
  button1.numberKeyPresses += 1;
  button1.pressed = true;
}

En la sección de configuración del código, primero inicializamos la comunicación en serie con el PC. Luego configuramos la entrada para subir la clavija D18.

Luego le decimos al ESP32 que monitoree el pin D18 y llamamos a la rutina de servicio de interrupción isr cuando el pin va de HIGH a LOW, es decir, estado FALLING.

Serial.begin(115200);
pinMode(button1.PIN, INPUT_PULLUP);
attachInterrupt(button1.PIN, isr, FALLING);

En la sección de bucle del código, simplemente comprobamos si el estado del botón pulsado vuelve a ser verdadero (true). Cuando lo hace, simplemente imprimimos el número de tecla pulsada hasta ahora y ponemos el estado de botón pulsado LOW para que podamos seguir recibiendo las siguientes interrupciones.

if (button1.pressed) {
      Serial.printf("Button 1 has been pressed %u timesn", button1.numberKeyPresses);
      button1.pressed = false;
}

En la sección de bucle también comprobamos el número de milisegundos que han pasado desde que el programa empezó a usar la función millis(). Cuando este tiempo es superior a 60.000 milisegundos o 1 minuto, simplemente le decimos a ESP32 que no monitorice la clavija D18 usando la función de detachInterrupt().

//Detach Interrupt after 1 Minute
static uint32_t lastMillis = 0;
if (millis() - lastMillis > 60000) {
  lastMillis = millis();
  detachInterrupt(button1.PIN);
  Serial.println("Interrupt Detached!");
}

 

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.

Enviar Whatsapp
Hola 👋
¿En qué podemos ayudarte?