ESP32 Timer Interrupt using ESP-IDF

In this tutorial, we will learn how to configure and generate timer interrupts of ESP32 using ESP-IDF. ESP32 have two 64-bit general purpose timer groups and each have 16-bit pre-scalers and 64-bit up/down counters. We will see how to trigger a specific ISR routine based on timer interrupt.

The timer interrupts will be configured in ESP-IDF using esp_timer APIs. ESP-IDF provides a driver esp_timer High Resolution Timer that contains APIs to create both periodic and one-shot timers with a time resolution of microsecond and 64 bit range for the hardware timers.

Before we move ahead, make sure you have the latest version of VS Code installed on your system with the ESP-IDF extension configured.

ESP-IDF Timer Interrupt Example 

We will build and create a project in VS Code with ESP-IDF extension whereby a timer interrupt will be used to toggle the onboard LED of ESP32. The LED will be set up as a digital output and will be toggled after a set number of intervals. Lets show you how to achieve this by using esp_timer.h functions.

Create Example Project

Open your VS Code and head over to View > Command Palette. Type ESP-IDF: New Project in the search bar and press enter.

Specify the project name and directory. We have named our project ‘ESP32_TIMER_INTERRUPT.’ For the ESP-IDF board, we have chosen the custom board option. For ESP-IDF target, we have chosen ESP32 module. Click ‘Choose Template’ button to proceed forward.

ESP32 Timer Interrupt using ESP-IDF

In the Extension, select ESP-IDF option:

ESP-IDF in VS Code New Project 2

We will click the ‘sample_project’ under the get-started tab. Now click ‘Create project using template sample_project.’

ESP-IDF in VS Code New Project 3

You will get a notification that the project has been created. To open the project in a new window, click ‘Yes.’

This opens our ESP32_TIMER_INTERRUPT project that we created inside the EXPLORER tab. There are several folders inside our project folder. This is the same for every project which you will create through ESP-IDF Explorer. Lets head over to the main.c file. The main folder contains the source code meaning the main.c file will be found here.

Now go to main > main.c and open it. Copy the code given below in that file and save it.

ESP32 Timer Interrupt Code

#include <stdio.h>
#include "esp_timer.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "driver/gpio.h"

void timer_callback(void *param)
{
  static bool ON;
  ON = !ON;
  
  gpio_set_level(GPIO_NUM_2, ON);
}

void app_main(void)
{
  gpio_pad_select_gpio(GPIO_NUM_2);
  gpio_set_direction(GPIO_NUM_2, GPIO_MODE_OUTPUT);

  const esp_timer_create_args_t my_timer_args = {
      .callback = &timer_callback,
      .name = "My Timer"};
  esp_timer_handle_t timer_handler;
  ESP_ERROR_CHECK(esp_timer_create(&my_timer_args, &timer_handler));
  ESP_ERROR_CHECK(esp_timer_start_periodic(timer_handler, 50));

  while (true)
  {
    esp_timer_dump(stdout);
    vTaskDelay(pdMS_TO_TICKS(1000));
  }
}

How the Code Works?

The High Resolution Timer APIs are defined in components/esp_timer/include/esp_timer.h. Therefore, we will include this driver at the start along with other libraries which are needed such as FreeRTOS libraries to generate delay, esp_log.h for debugging and driver/gpio.h to configure the output GPIO of ESP32.

#include <stdio.h>
#include "esp_timer.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "driver/gpio.h"

The timer has a callback function which is called every time the timer progresses. The timer_callback function will be used to toggle the onboard LED. The onboard LED of ESP32 is connected with GPIO2. Hence, we call gpio_set_level() function to set the level of GPIO2 to the value held in the variable ‘ON.’ The ‘ON’ variable is of type static bool which will reverse its logical value as it holds !ON value.

void timer_callback(void *param)
{
  static bool ON;
  ON = !ON;
  
  gpio_set_level(GPIO_NUM_2, ON);
}

Inside the app_main() function, we will first configure the output GPIO. To do that, we will first use the gpio_pad_select_gpio() function to specify GPIO_NUM_2 as a GPIO pin. Then, we will set the direction of the pin as an output using the gpio_set_direction() function. This function takes in two arguments. The first argument is the GPIO pin and the second argument is the mode (input or output) we want to set the pin in. In our case, we want to set the GPIO_NUM_2 as an output pin.

gpio_pad_select_gpio(GPIO_NUM_2);
gpio_set_direction(GPIO_NUM_2, GPIO_MODE_OUTPUT);

Create Timer

Next, we will define the timer configuration that will be passed to esp_timer_create() function. The timer configuration structure esp_timer_create_args_t holds the timer callback function which will be called when the timer expires and the name of the timer which will be used in the esp_timer_dump() function.

  const esp_timer_create_args_t my_timer_args = {
      .callback = &timer_callback,
      .name = "My Timer"};

Then we create a esp_timer_handle_t variable which is the timer handler for a single esp_timer. It is ‘timer_handler’ in our case.

esp_timer_handle_t timer_handler;

After defining the timer handler, we will create the instance for esp_timer using esp_timer_create(). This function takes in two parameters. The first parameter is the pointer to the esp_tier_create_args_t stucture that we previously defined. It is ‘&my_timer_args’ in our case. The second parameter is the pointer to the esp_timer_handle_t variable holds the timer handle that we previously created. It is ‘&timer_handler’ in our case.

  ESP_ERROR_CHECK(esp_timer_create(&my_timer_args, &timer_handler));

Start Timer

After creating the timer, then it is started. To start a periodic timer, esp_timer_start_periodic() function is used. This function call will start the periodic timer that will trigger after every time period in microseconds that is specified. It takes in two parameters. The first parameter is the timer handle and the second parameter is the time period in microseconds with which the callback function is called. The time period is set as 1000000 microseconds which is equivalent to 1 second. Hence, after every second the timer interrupt will be triggered.

  ESP_ERROR_CHECK(esp_timer_start_periodic(timer_handler, 1000000));

Inside the infinite while loop, we will dump the list of timers to the stream after every second using the function esp_timer_dump(). It takes in a single parameter which is the stream where the data will be dumped. This function will print the list of active timers according to the format: timer name, period of timer and time of the next alarm since boot in microseconds.

  while (true)
  {
    esp_timer_dump(stdout);
    vTaskDelay(pdMS_TO_TICKS(1000));
  }

Compiling the Sketch

To flash your chip, type the following command in the serial terminal. Remember to replace the COM port with the one through which your board is connected.

idf.py -p COMX flash monitor

After the code flashes successfully, the onboard LED will start blinking with a delay of 1 second. After every second, the timer interrupt will be triggered which will then call the timer_callback() function that will toggle the LED. Moreover, the ESP-IDF terminal will also print the timer along with the timer period and time since boot for the next alarm.

ESP32 Timer Interrupt using ESP-IDF Terminal

You may also like to read:

1 thought on “ESP32 Timer Interrupt using ESP-IDF”

Leave a Comment