esp32-interrupciones-hardware

How to use interrupts on an ESP32

  • 4 min

Hardware interrupts, or GPIO interrupts, allow the microcontroller to detect and respond to specific changes on input pins, even while the main program is running.

When an event occurs that triggers an interrupt, the main program is momentarily paused and the Callback function is executed. Once the Callback function is completed, the main program resumes its execution.

This is especially useful in situations where we need to handle events very quickly, which we might otherwise miss. Or, to avoid constantly checking the state of a sensor, for example.

However, we should not overuse them. Keep in mind that while the interrupt is being serviced, the processor is ignoring everything else. So use them wisely, and always ensure the ISR is as short as possible.

Using GPIO Interrupts on the ESP32

Using GPIO interrupts on the ESP32 with the Arduino IDE is very similar to using them on a “conventional” Arduino, but it has some peculiarities.

Define the callback function (ISR)

First, we define the callback function, which will execute when the interrupt occurs on the GPIO pin. This function must have a void return type and should not take any arguments.

void IRAM_ATTR myInterruptFunction() {
  // Here you can add the code that will be executed when the interrupt occurs
}
Copied!

Here comes one of the important differences: we have labeled the ISR with the IRAM_ATTR attribute. This tells the compiler to place the function’s code in the ESP32’s internal RAM.

Without this attribute, the function would go to Flash, which is much slower. As we said, we want the ISR to be as short and fast as possible. Therefore, we must add it.

Configure the interrupt

Once we have defined the callback function, we configure the interrupt on the desired GPIO pin using the attachInterrupt() function.

This function accepts three arguments: the GPIO pin number, the callback function, and the interrupt mode.

The interrupt mode determines when the interrupt will be triggered. The possible modes are:

  • RISING: The interrupt is triggered when the pin changes from low to high (rising edge).
  • FALLING: The interrupt is triggered when the pin changes from high to low (falling edge).
  • CHANGE: The interrupt is triggered on both rising and falling edges.

For example, to configure an interrupt on pin 12 triggered on a rising edge:

const int interruptPin = 12; // GPIO pin where the interrupt will be configured

void setup() {
  pinMode(interruptPin, INPUT_PULLUP); // Configure the pin as an input with an internal pull-up resistor
  attachInterrupt(digitalPinToInterrupt(interruptPin), myInterruptFunction, RISING); // Configure the interrupt
}
Copied!

Deactivate the interrupt (optional)

If at any point you wish to deactivate the interrupt, you can use the detachInterrupt() function. This function takes as an argument the GPIO pin number where the interrupt was configured.

detachInterrupt(digitalPinToInterrupt(interruptPin)); // Disable the interrupt
Copied!

Code Example

Below is a complete example of how to use an interrupt with a button connected to pin 12 of the ESP32:

const int buttonPin = 12; // GPIO pin where the button is connected

volatile bool has_interrupted = false;
void IRAM_ATTR myInterruptFunction() {
  has_interrupted = true;
}

void setup() 
{
  Serial.begin(115200);
  pinMode(buttonPin, INPUT_PULLUP); // Configure the pin as an input with an internal pull-up resistor
  attachInterrupt(digitalPinToInterrupt(buttonPin), myInterruptFunction, RISING); // Configure the interrupt
}

void loop() 
{
  if(has_interrupted)
  {
    Serial.println("Button pressed!");
    has_interrupted = false;
  }
}
Copied!

In this example,

  • When the button connected to pin 12 is pressed, the interrupt will be triggered
  • The callback function myInterruptFunction() will execute
  • It will print the message Button pressed! to the serial monitor.