Generic GPIO Example
Note
This document is automatically translated using AI. Please excuse any detailed errors. The official English version is still in progress.
Example Description
This example demonstrates how to initialize and configure GPIO, as well as how to use GPIO interrupts to detect input signal changes and handle them. Before studying this example, it is recommended to master the related content of FreeRTOS queue. The basic explanation and API usage examples can be referred to in the Queue Management document.
Running Method
The complete code of the example can be found in Generic GPIO Example. The configuration instructions, build and burn process before running can be found in the README.md file in the example directory.
For custom configuration items and viewing default values, refer to the Macro Definition Description section.
Header File Description
The header files used in this example cover basic modules such as FreeRTOS task management, common system tools, and GPIO drivers. They provide support for task creation, GPIO initialization, and interrupt handling.
The header files are categorized by function as follows:
FreeRTOS Task Scheduling: Provides task management and task and communication functions.
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
System Tools: Provides input and output, string processing, memory management, and fixed-length integer type support.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <inttypes.h>
GPIO Driver: Provides GPIO configuration, read and write levels, and interrupt handling functions.
#include "driver/gpio.h"
Macro Definition Description
The macro definitions involved in this example are mainly used to define GPIO input and output pins and interrupt flag bits.
The macro definitions are categorized by function as follows:
GPIO Input/Output Pin Definition
Use configuration macro definitions for GPIO input and output pins. The specific pin numbers can be viewed in the
sdkconfig
file. In this example, the input pins correspond to GPIO 4 and GPIO 5; the output pins correspond to GPIO 8 and GPIO 9.#define GPIO_INPUT_IO_0 CONFIG_GPIO_INPUT_0 #define GPIO_INPUT_IO_1 CONFIG_GPIO_INPUT_1 #define GPIO_OUTPUT_IO_0 CONFIG_GPIO_OUTPUT_0 #define GPIO_OUTPUT_IO_1 CONFIG_GPIO_OUTPUT_1
Generate input and output pin selection masks through bit shift operations, used to configure multiple input or output pins at once.
#define GPIO_INPUT_PIN_SEL ((1ULL<<GPIO_INPUT_IO_0) | (1ULL<<GPIO_INPUT_IO_1)) #define GPIO_OUTPUT_PIN_SEL ((1ULL<<GPIO_OUTPUT_IO_0) | (1ULL<<GPIO_OUTPUT_IO_1))
Note
Configuration macros start with
CONFIG_
, which are automatically generated by the ESP-IDF build system based on themenuconfig
settings and saved in thesdkconfig
file.When defining GPIO pins, use configuration macros instead of directly writing pin numbers. This allows for flexible modification of pins at compile time without modifying the source code, facilitating project expansion and management.
If you want to modify the pins used, just modify
menuconfig
and rebuild/compile the example, and it will automatically update synchronously, without the need to manually change the code.
Interrupt configuration flag: Used to specify the configuration flags of the interrupt service. In this example, when it is defined as 0, it means using the default configuration, that is, not enabling any special interrupt options (such as edge triggering, shared interrupts, etc.), suitable for most regular GPIO interrupt scenarios.
#define ESP_INTR_FLAG_DEFAULT 0
Task function description
This example includes two core functions to implement GPIO interrupt functionality: the Interrupt Service Routine (ISR) and the FreeRTOS task function for handling GPIO events.
Interrupt Service Routine (ISR)
gpio_isr_handler()
is the ISR, which is called when a GPIO pin triggers an interrupt. It is responsible for quickly responding to GPIO interrupts and passing information to the task. The function uses the IRAM_ATTR
modifier to ensure that the function is placed in internal RAM, improving interrupt response speed.
In this example, the execution logic of this function is:
Save the incoming parameter
arg
as the GPIO pin numbergpio_num
, and perform type conversion to use this value correctly.Send
gpio_num
to the FreeRTOS message queue. For API description, refer to Send data.
It should be noted that only lightweight operations should be performed in the ISR to ensure interrupt response speed and system stability.
FreeRTOS task function
gpio_task_example()
is a FreeRTOS task function, responsible for handling GPIO events in the task context. It works with the ISR to implement the mechanism of interrupt triggering, queue passing, and task processing, avoiding time-consuming operations in the ISR.
Its execution logic is:
Loop to wait for the GPIO pin number in the message queue (sent by the ISR). For API description, refer to Receive data.
After receiving the number, get the current level of the pin.
Print the pin number and level status.
In this task, the waiting time for the message queue is set to portMAX_DELAY
, that is, wait indefinitely. During the waiting process, the task will be blocked, thereby saving CPU resources.
Note
PRIu32
is a formatting macro defined in the C standard library, used in printf()
and other functions to correctly print fixed-width unsigned 32-bit integers, avoiding manual selection of %u
, %lu
and other format symbols, ensuring consistent cross-platform printing.
Main function description
This example demonstrates the initialization of GPIO, input and output configuration, interrupt response and processing, task and queue cooperation, as well as simple output control and system memory monitoring, reflecting the complete process of GPIO and FreeRTOS cooperation on ESP32.
Define a structure for configuring GPIO.
Apply the above GPIO configuration.
Use multiple pin masks to configure GPIO18 and GPIO19 as output modes at the same time, disable pull-up and pull-down and interrupts.
Use a multi-pin mask to configure GPIO4 and GPIO5 as input modes simultaneously, enable pull-up, and set to trigger on the rising edge.
Modify the interrupt trigger mode of
GPIO_INPUT_IO_0
to trigger on any edge.
This input pin is configured to trigger on the rising edge during initialization, but the two input pins in the example need to be set to trigger on the rising edge and any edge respectively, in order to flexibly respond to different external events while ensuring the reliability of the signal.
Therefore, it is necessary to call
gpio_set_intr_type()
to modify the interrupt trigger mode of one of the pins separately, and the pin number and the new interrupt type need to be passed in as parameters. For further introduction and parameter description, please refer to General GPIO API.
Create a FreeRTOS queue to pass events between the GPIO interrupt service routine and tasks. For a detailed introduction and API usage examples of FreeRTOS queues, please refer to the Queue Management document and the Dynamic Queue Creation API.
Create the
gpio_task_example
task, which is responsible for receiving and processing GPIO interrupt events from the queue. For a detailed introduction and API usage examples of FreeRTOS task creation, please refer to the Task Creation and Deletion document and the Dynamic Task Creation API.
Install the interrupt service, passing in the defined interrupt configuration flags.
Bind the interrupt service function
gpio_isr_handler()
to the two input pins respectively, the data passed to the ISR is the corresponding GPIO pin number, which is used to identify which pin triggered the interrupt within the ISR.Remove and rebind the interrupt service of
GPIO_INPUT_IO_0
, demonstrating how to dynamically manage ISR, which can be used to temporarily remove the interrupt response of a certain pin or replace the ISR.
Print the minimum available heap memory during system operation:
Used to monitor the memory usage of FreeRTOS tasks and applications, evaluate the peak memory usage of the program, and help avoid task crashes or exceptions due to insufficient heap memory.
Call
esp_get_minimum_free_heap_size()
to get the minimum remaining heap memory since system startup (in bytes). For further explanation, refer to Miscellaneous System API.
Enter an infinite loop, performing the following operations each time:
Print the current count value
cnt
and increment it.Delay for 1 second to execute the task at a fixed interval, while releasing the CPU to other tasks.
Call
gpio_set_level()
to control the level status of two output pinsGPIO_OUTPUT_IO_0
andGPIO_OUTPUT_IO_1
, making them alternate high and low levels according to the parity of the count value. For further explanation, refer to General GPIO API.
Program Phenomenon
The code execution result is as follows:
Minimum free heap size: 388708 bytes
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
Execution Flow:
Complete the initialization of each module, including GPIO configuration, ISR installation, and task creation.
After the
gpio_task_example
task starts, it blocks and waits for event signals in the message queue.Change the output GPIO level in the main loop to trigger the interrupt of the input GPIO pin.
When the corresponding input pin triggers an interrupt, the ISR is called and sends the triggered GPIO number to the message queue.
The blocked
gpio_task_example
task receives the queue signal, reads the pin level, and prints the event information.
In this example, the output pin changes the level through software control, and its signal is transmitted to the input pin through circuit connection. After the input pin detects the level change, it triggers an interrupt, and the ISR sends the event to the task for processing and printing. Since the two input GPIO pins are configured with different interrupt trigger modes (one is triggered by the rising edge, and the other is triggered by any edge), although the level status changes synchronously, the conditions for triggering interrupts are different, so the order of events received by the task and the printing results are also different. This phenomenon intuitively reflects the impact of different interrupt modes on program behavior and event response order.