El ESP32 es un microcontrolador potente y versátil que permite configurar y gestionar interrupciones GPIO para mejorar el rendimiento de tus proyectos. En este artículo aprenderás cómo realizar esta configuración utilizando Arduino IDE, permitiéndote aprovechar al máximo las capacidades del ESP32. ¡Sigue leyendo para descubrir todo lo que necesitas saber sobre la configuración y manejo de interrupciones GPIO en este poderoso dispositivo!
En un proyecto, a menudo desea que el ESP32 ejecute su programa normal mientras monitorea continuamente un evento. Una solución común a esto es utilizar interrumpe.
Interrupciones en el ESP32
ESP32 ofrece hasta 32 ranuras de interrupción para cada núcleo. Cada interrupción tiene un cierto nivel de prioridad y se puede dividir en dos tipos.
Interrupciones de hardware – Se producen en respuesta a un evento externo. Por ejemplo, una interrupción GPIO (cuando se presiona un botón) o una interrupción táctil (cuando se detecta un toque).
Interrupción del software – Estos ocurren en respuesta a una instrucción del software. Por ejemplo, una simple interrupción del temporizador o una interrupción del temporizador de vigilancia (cuando el temporizador expira).
Interrupción ESP32 GPIO
En ESP32 podemos definir una función de rutina de servicio de interrupción que se llamará cuando el pin GPIO cambie su nivel lógico.
Todos los pines GPIO en una placa ESP32 se pueden configurar para actuar como entradas de solicitud de interrupción.
Adjuntar una interrupción a un pin GPIO
En el IDE de Arduino utilizamos una función llamada attachInterrupt()
para establecer una interrupción pin por pin. La sintaxis se ve así.
attachInterrupt(GPIOPin, ISR, Mode);
Esta función acepta tres argumentos:
GPIOPin – Establece el pin GPIO como un pin de interrupción, lo que le indica a ESP32 qué pin monitorear.
ISR – es el nombre de la función que se llamará cada vez que ocurra la interrupción.
modo – define cuándo se debe activar la interrupción. Cinco constantes están predefinidas como valores válidos:
BAJO | Activa la interrupción cuando el pin está BAJO |
ALTO | Activa la interrupción cuando el pin está ALTO |
CAMBIAR | Activa la interrupción cuando el pin cambia de valor, de ALTO a BAJO o de BAJO a ALTO |
CAER | Activa la interrupción cuando el pin cambia de ALTO a BAJO |
CRECIENTE | Activa la interrupción cuando el pin cambia de BAJO a ALTO |
Desconectar una interrupción de un pin GPIO
Si desea que ESP32 deje de monitorear el pin, puede llamarlo detachInterrupt()
Función. La sintaxis se ve así.
detachInterrupt(GPIOPin);
Interrumpir la rutina del servicio.
La Rutina de Servicio de Interrupción (ISR) es una función que se llama cada vez que ocurre una interrupción en el pin GPIO.
La sintaxis se ve así.
void IRAM_ATTR ISR() {
Statements;
}
Los ISR en ESP32 son tipos especiales de funciones que tienen algunas reglas únicas que la mayoría de las otras funciones no tienen.
- Un ISR no puede tener ningún parámetro y no debería devolver nada.
- Los ISR deben ser lo más cortos y rápidos posible porque bloquean la ejecución normal del programa.
- Deberías tener esto
IRAM_ATTR
Atributo, correspondiente al documentación ESP32.
¿Qué es IRAM_ATTR?
Cuando marcamos una sección de código con el IRAM_ATTR
Atributo, el código compilado se almacena en la RAM interna (IRAM) del ESP32. De lo contrario, el código se mantiene en Flash. Y el flash en 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 ejecutarlo lo más rápido posible. Si tuviéramos que “esperar” a que el ISR se cargue desde Flash, las cosas podrían salir terriblemente mal.
Conexión de hardware
¡Basta de teoría! Veamos un ejemplo práctico.
Conectemos un botón al GPIO#18 (D18) en el ESP32. No es necesario tirar hacia arriba para este pasador, ya que estamos tirando del pasador internamente.
Código de ejemplo: interrupción simple
El siguiente esquema muestra el uso de interrupciones y la forma correcta de escribir una rutina de servicio de interrupción.
Este programa monitorea GPIO#18 (D18) para detectar el flanco DE CAÍDA. En otras palabras: busca un cambio de voltaje que va de ALTO lógico a BAJO lógico cuando se presiona el botón. Cuando esto sucede, la función isr
se llama. El código en esta función cuenta cuántas veces se presionó el botón.
struct Button {
const uint8_t PIN;
uint32_t numberKeyPresses;
bool pressed;
};
Button button1 = {18, 0, false};
void IRAM_ATTR isr() {
button1.numberKeyPresses++;
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 has been pressed %u timesn", button1.numberKeyPresses);
button1.pressed = false;
}
}
Después de cargar el boceto, presione el botón EN en el ESP32 y abra el monitor en serie con la velocidad en baudios 115200. Cuando presione el botón obtendrá el siguiente resultado.
Explicación del código
Al comienzo del boceto creamos una estructura llamada Button
. Esta estructura consta de tres elementos: el número de pin, el número de pulsaciones de teclas y el estado de pulsación. Para su información: una estructura es una colección de variables de diferentes tipos (pero lógicamente relacionadas entre sí) bajo un solo nombre.
struct Button {
const uint8_t PIN;
uint32_t numberKeyPresses;
bool pressed;
};
Luego creamos una instancia de la estructura del botón e inicializamos el número de pin con 18
el número de pulsaciones de teclas hasta 0
y el estado presionado predeterminado false
.
Button button1 = {18, 0, false};
El siguiente código es una rutina de servicio de interrupción. Como se mencionó anteriormente, el ISR en ESP32 debe tener esto IRAM_ATTR
Atributo.
En el ISR simplemente incrementamos el contador de KeyPresses en 1 y configuramos el estado de la tecla presionada en Verdadero.
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 serial con la PC y luego habilitamos el pullup interno para el pin GPIO D18.
A continuación, le indicamos al ESP32 que monitoree el pin D18 y llame a la rutina de servicio de interrupción. isr
cuando el pasador va de ALTO a BAJO, es decir, borde DE CAÍDA.
Serial.begin(115200);
pinMode(button1.PIN, INPUT_PULLUP);
attachInterrupt(button1.PIN, isr, FALLING);
En la parte del bucle del código, simplemente verificamos si se presionó la tecla, luego imprimimos cuántas veces se ha presionado la tecla hasta el momento y configuramos el estado de la tecla presionada en falso para que podamos continuar recibiendo interrupciones.
if (button1.pressed) {
Serial.printf("Button 1 has been pressed %u timesn", button1.numberKeyPresses);
button1.pressed = false;
}
Administrar el rebote del interruptor
Un problema común con las interrupciones es que a menudo se activan varias veces para el mismo evento. Si observa la salida en serie del ejemplo anterior, notará que incluso si presiona el botón solo una vez, el contador aumentará varias veces.
Para saber por qué sucede esto, hay que fijarse en la señal. Si monitorea el voltaje del pin en el analizador de señal mientras presiona el botón, obtendrá una señal como esta:
Es posible que sienta que el contacto se realiza de inmediato, pero en realidad las piezas mecánicas dentro del botón entran en contacto varias veces antes de estabilizarse en un estado determinado. Esto desencadena múltiples interrupciones.
Es un fenómeno puramente mecánico conocido como “interruptor de salto', como dejar caer una pelota: rebota hacia arriba y hacia abajo varias veces antes de aterrizar finalmente en el suelo.
El tiempo que tarda la señal en estabilizarse es muy rápido y nos parece casi instantáneo, pero para un ESP32 es una cantidad de tiempo enorme. Puede ejecutar múltiples instrucciones durante este período.
El proceso de eliminar el rebote del interruptor se llama «rebote'. Hay dos formas de lograrlo.
- A través de hardware: agregando un filtro RC apropiado para suavizar la transición.
- A través de software: ignorando temporalmente más interrupciones durante un corto período de tiempo después de que se activa la primera interrupción.
Código de ejemplo: Rebotar una interrupción
Aquí se reescribe el boceto anterior para demostrar cómo evitar una interrupción mediante programación. En este boceto, permitimos que el ISR se ejecute solo una vez por cada pulsación de tecla, en lugar de ejecutarlo varias veces.
Los cambios en el boceto se resaltan en Verde.
struct Button {
const uint8_t PIN;
uint32_t numberKeyPresses;
bool pressed;
};
Button button1 = {18, 0, false};
//variables to keep track of the timing of recent interrupts
unsigned long button_time = 0;
unsigned long last_button_time = 0;
void IRAM_ATTR isr() {
button_time = millis();
if (button_time - last_button_time > 250)
{
button1.numberKeyPresses++;
button1.pressed = true;
last_button_time = button_time;
}
}
void setup() {
Serial.begin(115200);
pinMode(button1.PIN, INPUT_PULLUP);
attachInterrupt(button1.PIN, isr, FALLING);
}
void loop() {
if (button1.pressed) {
Serial.printf("Button has been pressed %u timesn", button1.numberKeyPresses);
button1.pressed = false;
}
}
Echemos otro vistazo a la salida en serie mientras presionamos la tecla. Tenga en cuenta que el ISR solo se invoca una vez por cada pulsación de tecla.
Explicación del código:
Esta solución funciona porque cada vez que se ejecuta el ISR, se compara la hora actual devuelta por millis()
La función vuelve a la hora de la última llamada al ISR.
Si está dentro de los 250 ms, ESP32 ignora la interrupción e inmediatamente vuelve a lo que estaba haciendo. De lo contrario, se ejecutará el código que contiene. if
Declaración que incrementa y actualiza el contador. last_button_time
Variable para que la función tenga un nuevo valor con el que comparar cuando se active en el futuro.
Preguntas Frecuentes sobre Configuración y manejo de interrupciones GPIO ESP32 en Arduino IDE
¿Qué son las interrupciones en ESP32?
Las interrupciones son eventos que ocurren en el ESP32 en respuesta a un evento externo o a una instrucción de software, permitiendo al ESP32 monitorear continuamente ciertos eventos mientras realiza su programa normal.
¿Cuántas ranuras de interrupción provee ESP32 para cada núcleo?
ESP32 proporciona hasta 32 ranuras de interrupción para cada núcleo.
¿Qué es un ISR en ESP32?
ISR, por sus siglas en inglés, es el Routine Servicio de Interrupción, una función que se invoca cada vez que ocurre una interrupción en el pin GPIO.
¿Cómo puedo adjuntar una interrupción a un pin GPIO en ESP32?
En la Arduino IDE, se utiliza la función attachInterrupt() para establecer una interrupción en un pin GPIO. La sintaxis es la siguiente:
- GPIOPin: define el pin GPIO como el pin de interrupción que ESP32 debe monitorear.
- ISR: es el nombre de la función que se llamará cada vez que ocurra la interrupción.
- Modo: define cuándo se debe activar la interrupción.
¿Cómo gestiono el rebote de un interruptor en ESP32?
El rebote del interruptor es un problema común con las interrupciones, y se puede gestionar mediante un filtro RC apropiado en hardware o ignorando temporalmente las interrupciones adicionales en software.
Para más información detallada sobre cómo configurar y manejar interrupciones GPIO en ESP32 en Arduino IDE, consulta este enlace.
Me encantó este tutorial, me está facilitando mucho el trabajo con mis proyectos en ESP32. ¡Gracias por explicar de forma tan clara y detallada!
¡Este tutorial es justo lo que necesitaba para aprender a configurar las interrupciones GPIO en mi ESP32! Gracias por la explicación clara y concisa. ¡Excelente aporte! 🙌🏼
Excelente tutorial, muy sencillo de entender y seguir. ¡Gracias por compartir!
Muy útil la información, me ha ayudado a resolver un problema con mis proyectos en ESP32. ¡Gracias por el aporte!