Created
March 3, 2026 18:38
-
-
Save jaguilar/221871095ebf0aefb37b04e919ce074e to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /* | |
| * SPDX-FileCopyrightText: 2020-2025 Espressif Systems (Shanghai) CO LTD | |
| * | |
| * SPDX-License-Identifier: Apache-2.0 | |
| */ | |
| #include <inttypes.h> | |
| #include <stdio.h> | |
| #include <stdlib.h> | |
| #include <string.h> | |
| #include <algorithm> | |
| #include <array> | |
| #include <vector> | |
| #include "driver/gpio.h" | |
| #include "driver/ledc.h" | |
| #include "esp_intr_alloc.h" | |
| #include "esp_private/esp_clk.h" | |
| #include "esp_timer.h" | |
| #include "freertos/FreeRTOS.h" | |
| #include "freertos/queue.h" | |
| #include "freertos/task.h" | |
| /** | |
| * Brief: | |
| * This test code shows how to configure gpio and how to use gpio interrupt. | |
| * | |
| * GPIO status: | |
| * GPIO_OUTPUT_IO_0: output | |
| * GPIO_OUTPUT_IO_1: output | |
| * GPIO_INPUT_IO_0: input, pulled up, interrupt from rising edge and falling | |
| * edge GPIO_INPUT_IO_1: input, pulled up, interrupt from rising edge. | |
| * | |
| * Note. You can check the default GPIO pins to be used in menuconfig, and the | |
| * IOs can be changed. | |
| * | |
| * Test: | |
| * Connect GPIO_OUTPUT_IO_0 with GPIO_INPUT_IO_0 | |
| * Connect GPIO_OUTPUT_IO_1 with GPIO_INPUT_IO_1 | |
| * Generate pulses on GPIO_OUTPUT_IO_0/1, that triggers interrupt on | |
| * GPIO_INPUT_IO_0/1 | |
| * | |
| */ | |
| #define GPIO_OUTPUT_IO_0 GPIO_NUM_5 | |
| #define GPIO_OUTPUT_IO_1 GPIO_NUM_7 | |
| #define GPIO_OUTPUT_PIN_SEL \ | |
| ((1ULL << GPIO_OUTPUT_IO_0) | (1ULL << GPIO_OUTPUT_IO_1)) | |
| /* | |
| * Let's say, GPIO_OUTPUT_IO_0=18, GPIO_OUTPUT_IO_1=19 | |
| * In binary representation, | |
| * 1ULL<<GPIO_OUTPUT_IO_0 is equal to 0000000000000000000001000000000000000000 | |
| * and 1ULL<<GPIO_OUTPUT_IO_1 is equal to | |
| * 0000000000000000000010000000000000000000 GPIO_OUTPUT_PIN_SEL | |
| * 0000000000000000000011000000000000000000 | |
| * */ | |
| #define GPIO_INPUT_IO_0 GPIO_NUM_6 | |
| #define GPIO_INPUT_IO_1 GPIO_NUM_1 | |
| #define GPIO_INPUT_PIN_SEL \ | |
| ((1ULL << GPIO_INPUT_IO_0) | (1ULL << GPIO_INPUT_IO_1)) | |
| /* | |
| * Let's say, GPIO_INPUT_IO_0=4, GPIO_INPUT_IO_1=5 | |
| * In binary representation, | |
| * 1ULL<<GPIO_INPUT_IO_0 is equal to 0000000000000000000000000000000000010000 | |
| * and 1ULL<<GPIO_INPUT_IO_1 is equal to | |
| * 0000000000000000000000000000000000100000 GPIO_INPUT_PIN_SEL | |
| * 0000000000000000000000000000000000110000 | |
| * */ | |
| #define ESP_INTR_FLAG_DEFAULT 0 | |
| static QueueHandle_t gpio_evt_queue = NULL; | |
| volatile int count_intr = 0; | |
| volatile int intr_cycles_idx = 0; | |
| std::array<volatile esp_cpu_cycle_count_t, 1000> intr_cycles; | |
| std::array<volatile int64_t, 1000> times; | |
| static void IRAM_ATTR gpio_isr_handler(void* arg) { | |
| uint32_t gpio_num = (uint32_t)arg; | |
| if (gpio_num != GPIO_INPUT_IO_0) { | |
| // xQueueSendFromISR(gpio_evt_queue, &gpio_num, NULL); | |
| } else { | |
| ++count_intr; | |
| if (intr_cycles_idx < intr_cycles.size()) { | |
| times[intr_cycles_idx] = esp_timer_get_time(); | |
| intr_cycles[intr_cycles_idx] = esp_cpu_get_cycle_count(); | |
| intr_cycles_idx = (intr_cycles_idx + 1); | |
| } | |
| } | |
| } | |
| static void IRAM_ATTR gpio_task_example(void* arg) { | |
| for (;;) { | |
| vTaskDelay(100 / portTICK_PERIOD_MS); | |
| } | |
| } | |
| extern "C" void app_main(void) { | |
| // zero-initialize the config structure. | |
| gpio_config_t io_conf = {}; | |
| // disable interrupt | |
| io_conf.intr_type = GPIO_INTR_DISABLE; | |
| // set as output mode | |
| io_conf.mode = GPIO_MODE_OUTPUT; | |
| // bit mask of the pins that you want to set,e.g.GPIO18/19 | |
| io_conf.pin_bit_mask = GPIO_OUTPUT_PIN_SEL; | |
| // disable pull-down mode | |
| io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE; | |
| // disable pull-up mode | |
| io_conf.pull_up_en = GPIO_PULLUP_DISABLE; | |
| // configure GPIO with the given settings | |
| gpio_config(&io_conf); | |
| // interrupt of rising edge | |
| io_conf.intr_type = GPIO_INTR_POSEDGE; | |
| // bit mask of the pins, use GPIO4/5 here | |
| io_conf.pin_bit_mask = GPIO_INPUT_PIN_SEL; | |
| // set as input mode | |
| io_conf.mode = GPIO_MODE_INPUT; | |
| // enable pull-up mode | |
| io_conf.pull_up_en = GPIO_PULLUP_ENABLE; | |
| gpio_config(&io_conf); | |
| // change gpio interrupt type for one pin | |
| gpio_set_intr_type(GPIO_INPUT_IO_0, GPIO_INTR_NEGEDGE); | |
| // create a queue to handle gpio event from isr | |
| gpio_evt_queue = xQueueCreate(10, sizeof(uint32_t)); | |
| // start gpio task | |
| xTaskCreate(gpio_task_example, "gpio_task_example", 2048, NULL, 10, NULL); | |
| // install gpio isr service | |
| gpio_install_isr_service(ESP_INTR_FLAG_IRAM); | |
| // 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); | |
| // remove isr handler for gpio number. | |
| gpio_isr_handler_remove(GPIO_INPUT_IO_0); | |
| // hook isr handler for specific gpio pin again | |
| gpio_isr_handler_add(GPIO_INPUT_IO_0, gpio_isr_handler, | |
| (void*)GPIO_INPUT_IO_0); | |
| printf("Minimum free heap size: %" PRIu32 " bytes\n", | |
| esp_get_minimum_free_heap_size()); | |
| constexpr uint32_t timer_freq_hz = 4096; | |
| constexpr uint8_t duty_resolution = 8; | |
| ledc_timer_config_t timer_config = { | |
| .speed_mode = LEDC_LOW_SPEED_MODE, | |
| .duty_resolution = static_cast<ledc_timer_bit_t>(duty_resolution), | |
| .timer_num = LEDC_TIMER_0, | |
| .freq_hz = timer_freq_hz, | |
| .clk_cfg = LEDC_USE_RC_FAST_CLK, | |
| }; | |
| ESP_ERROR_CHECK(ledc_timer_config(&timer_config)); | |
| // Configure the output GPIO to a PWM with a period of PI seconds, so that | |
| // we get a falling edge approximate. | |
| ledc_channel_config_t channel_config = { | |
| .gpio_num = GPIO_OUTPUT_IO_0, | |
| .speed_mode = LEDC_LOW_SPEED_MODE, | |
| .channel = LEDC_CHANNEL_0, | |
| .timer_sel = LEDC_TIMER_0, | |
| .duty = static_cast<uint32_t>(0.5 * (1 << duty_resolution)), | |
| }; | |
| ESP_ERROR_CHECK(ledc_channel_config(&channel_config)); | |
| int cnt = 0; | |
| while (1) { | |
| printf("cnt: %d\n", cnt++); | |
| vTaskDelay(1000 / portTICK_PERIOD_MS); | |
| gpio_set_level(GPIO_OUTPUT_IO_1, cnt % 2); | |
| volatile int count_intr = 0; | |
| std::swap(count_intr, ::count_intr); | |
| printf("count_intr: %d\n", count_intr); | |
| if (intr_cycles_idx == intr_cycles.size()) { | |
| std::vector<int64_t> inter_interrupt_cycles; | |
| inter_interrupt_cycles.reserve(intr_cycles_idx); | |
| for (int i = 1; i < intr_cycles_idx; ++i) { | |
| inter_interrupt_cycles.push_back(times[i] - times[i - 1]); | |
| } | |
| if (inter_interrupt_cycles.empty()) { | |
| printf("No interrupts recorded, skipping latency calculation\n"); | |
| } else { | |
| std::sort(inter_interrupt_cycles.begin(), inter_interrupt_cycles.end()); | |
| auto gpio_time_med = | |
| inter_interrupt_cycles[inter_interrupt_cycles.size() / 2]; | |
| auto gpio_time_p99 = | |
| inter_interrupt_cycles[inter_interrupt_cycles.size() * 99 / 100]; | |
| auto gpio_time_p999 = | |
| inter_interrupt_cycles[inter_interrupt_cycles.size() * 999 / 1000]; | |
| printf("%d\n", esp_clk_cpu_freq()); | |
| /* | |
| auto to_us = [](uint32_t cycles) { | |
| return static_cast<float>(cycles) / (esp_clk_cpu_freq() / 1'000'000.); | |
| }; | |
| */ | |
| printf( | |
| "GPIO interrupt latency (us): median %lld, p99 %lld, p999 %lld\n", | |
| gpio_time_med, gpio_time_p99, gpio_time_p999); | |
| } | |
| intr_cycles_idx = 0; | |
| } | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| idf_component_register(SRCS "blink_example_main.cc" | |
| INCLUDE_DIRS "." | |
| REQUIRES esp_driver_ledc esp_timer) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment