ESP32 GPIO programming

In this blog post we will learn about esp32 GPIO programming we will use the following example as reference

https://github.com/espressif/esp-idf/tree/master/examples/peripherals/gpio

GPIO basic Info

  • ESP32 has 34 GPIO pins

  • Configuration Options are

    • internal pull-up
    • internal pull-down
    • high impedance
    • The input can be set to edge trigger / level trigger to generate CPU interrupts

Now we will go through the example program provided by esp-idf to understand how we should configure & use GPIO of the ESP32 microcontroller

Our Goal

  • configure 2 GPIO pins as output &
  • 2 GPIO pins as input ,
  • Enable the interrupts of the input pins .
  • Toggle the output pins in an infinite while loop & create interrupt events for the input pins
  • The code should handle the events and print it via the debug interface

We will need to manually connect the input, output pin pairs using jumper wires.

Pinout

The pinout of the esp32 module is

The Pins used in this project are

Pin Table

Pin Mode
GPIO18 output
GPIO19 output
GPIO4 Input, pulled up, interrupt from rising and falling edge
GPIO5 Input, pulled up, interrupt from rising edge

The above picture shows how i shorted the pin pairs

Note

In AVR programming we normally see that GPIO pins are grouped , for example PORTA, PORTB etc. but in ESP32 all the pins are in One Group and as the pin numbers are more than 32 so we have to use 64 bit masking to select the pins

#define GPIO_OUTPUT_IO_0    18
#define GPIO_OUTPUT_IO_1    19
#define GPIO_OUTPUT_PIN_SEL  ((1ULL<<GPIO_OUTPUT_IO_0) | (1ULL<<GPIO_OUTPUT_IO_1))

Here in the example code , you can see that the 64bit value is used for bit masking (ULL = Unsigned Long Long = 64bit )

Another reference is the gpio_config_t structure type ingpio.h file provided by the esp-idf sdk, here uint64_t size is used for bit masking

/**
 * @brief Configuration parameters of GPIO pad for gpio_config function
 */
typedef struct {
    uint64_t pin_bit_mask;          /*!< GPIO pin: set with bit mask, each bit maps to a GPIO */
    gpio_mode_t mode;               /*!< GPIO mode: set input/output mode                     */
    gpio_pullup_t pull_up_en;       /*!< GPIO pull-up                                         */
    gpio_pulldown_t pull_down_en;   /*!< GPIO pull-down                                       */
    gpio_int_type_t intr_type;      /*!< GPIO interrupt type                                  */
} gpio_config_t;

In the main() function first the output pins are configured using the io_conf variable of type gpio_config_t.

Throughout your esp32 programming journey you will have to use these kind of structures provided by the esp-idf framework.

So you can see that using the structure above you can set multiple pins according to your needs. Only the Bit masking should be right

Next the code has ro set both GPIO4 & GPIO5 as Input pins with rising edge detection enabled, using the same structure.

But then as GPIO4 needs to be both rising & falling detection enabled so the pin’s interrupt mode is changed using the following function

//change gpio interrupt type for one pin   
gpio_set_intr_type(GPIO_INPUT_IO_0, GPIO_INTR_ANYEDGE);

Now let’s look at the following diagram where the overall working mechanism of the code is depicted

Analysis

Here you can see that an infinite loop in main is toggling GPIO18 & GPIO19. As GPIO18 & GPIO19 are shortened to GPIO4 & GPIO5 so the toggling in turn creates interrupt events which are captured by the gpio_isr_handler() function .

The handler fills the gpio_evt_queue. In the Code there is a freertos task named gpio_task_example() which is blocked waiting on the gpio_evt_queue, So whenever an item is available at the queue the task starts running . The item passed to the task is actually the pin number from where the interrupt is generated . the task prints the Pin number and its current state via the debug port

Code snippet for creating FreeRtos Queue

static xQueueHandle gpio_evt_queue = NULL;
//create a queue to handle the gpio event from isr   
gpio_evt_queue = xQueueCreate(10,sizeof(uint32_t));

Code snippet for Creating FreeRtos Task

//start gpio task   
xTaskCreate(gpio_task_example,"gpio_task_example",2048,NULL,10,NULL);

FreeRtos task creation Signature is

BaseType_t xTaskCreate( TaskFunction_t pvTaskCode,
                        const char * const pcName,
                        configSTACK_DEPTH_TYPE usStackDepth,
                        void *pvParameters,
                        UBaseType_t uxPriority,
                        TaskHandle_t *pxCreatedTask
                       );

Let’s look into what are the parameter types

pvTaskCode Pointer Entry to the function gpio_task_example
pcName A descriptive name for the task “gpio_task_example”
usStackDepth The Task Stack Size(in words) 2048
pvParameters A value that will be passed into the created task as task’s parameter NULL
uxPriority The priority at which the task will run 10
pxCreatedTask Used to pass a handle to the created task for later manipulation NULL

In the example the interrupt handler has the following signature

static void IRAM_ATTR gpio_isr_handler(void\* arg)

The IRAM_ATTR forces the code to reside in IRAM instead of flash

you have to add the interrupt handler function to the gpio driver

//install gpio isr service   
gpio_install_isr_service(ESP_INTR_FLAG_DEFAULT);   //hook isr handler for specific gpio pin   gpio_isr_handler_add(GPIO_INPUT_IO_0, gpio_isr_handler, (void\*) GPIO_INPUT_IO_0);   //hook isr handler for specific gpio pin   gpio_isr_handler_add(GPIO_INPUT_IO_1, gpio_isr_handler, (void\*) GPIO_INPUT_IO_1);

let’s build and flash

~/esp/esp-idf/examples/peripherals/gpio$ . ~/esp/esp-idf/export.sh
~/esp/esp-idf/examples/peripherals/gpio$ idf.py -p /dev/ttyUSB0 flash monitor

you will see following kind of output

I (0) cpu_start: Starting scheduler on APP CPU.
I (306) gpio: GPIO[18]| InputEn: 0| OutputEn: 1| OpenDrain: 0| Pullup: 0| Pulldown: 0| Intr:0 
I (316) gpio: GPIO[19]| InputEn: 0| OutputEn: 1| OpenDrain: 0| Pullup: 0| Pulldown: 0| Intr:0 
I (326) gpio: GPIO[4]| InputEn: 1| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:1 
I (336) gpio: GPIO[5]| InputEn: 1| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:1 
cnt: 0
cnt: 1
GPIO[4] intr, val: 1
GPIO[5] intr, val: 1
cnt: 2
GPIO[4] intr, val: 0
cnt: 3
GPIO[4] intr, val: 1
GPIO[5] intr, val: 1
cnt: 4
GPIO[4] intr, val: 0
cnt: 5
GPIO[4] intr, val: 1
GPIO[5] intr, val: 1

Here you can see that both rising and falling edge of GPIO4 is got & rising edge of GPIO5 is got

Written on November 3, 2019