diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index f24c7a5777..6c2ed996fb 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -176,6 +176,8 @@ before_script: scapy google-api-python-client - cd $IDF_PATH + - export EXTRA_CFLAGS=${PEDANTIC_CFLAGS} + - export EXTRA_CXXFLAGS=${PEDANTIC_CXXFLAGS} default: retry: diff --git a/components/driver/Kconfig b/components/driver/Kconfig index aa93ed6ad0..1b7cf24aeb 100644 --- a/components/driver/Kconfig +++ b/components/driver/Kconfig @@ -186,6 +186,13 @@ menu "Driver configurations" pullup/pulldown mode in sleep. If this option is selected, chip will automatically emulate the behaviour of switching, and about 450B of source codes would be placed into IRAM. + + config GPIO_CTRL_FUNC_IN_IRAM + bool "Place GPIO control functions into IRAM" + default n + help + Place GPIO control functions (like intr_disable/set_level) into IRAM, + so that these functions can be IRAM-safe and able to be called in the other IRAM interrupt context. endmenu # GPIO Configuration menu "GDMA Configuration" diff --git a/components/driver/include/driver/gpio.h b/components/driver/include/driver/gpio.h index fe01ee74d5..f35d3b4c1e 100644 --- a/components/driver/include/driver/gpio.h +++ b/components/driver/include/driver/gpio.h @@ -90,6 +90,8 @@ esp_err_t gpio_intr_enable(gpio_num_t gpio_num); /** * @brief Disable GPIO module interrupt signal * + * @note This function is allowed to be executed when Cache is disabled within ISR context, by enabling `CONFIG_GPIO_CTRL_FUNC_IN_IRAM` + * * @param gpio_num GPIO number. If you want to disable the interrupt of e.g. GPIO16, gpio_num should be GPIO_NUM_16 (16); * * @return @@ -102,6 +104,8 @@ esp_err_t gpio_intr_disable(gpio_num_t gpio_num); /** * @brief GPIO set output level * + * @note This function is allowed to be executed when Cache is disabled within ISR context, by enabling `CONFIG_GPIO_CTRL_FUNC_IN_IRAM` + * * @param gpio_num GPIO number. If you want to set the output level of e.g. GPIO16, gpio_num should be GPIO_NUM_16 (16); * @param level Output level. 0: low ; 1: high * diff --git a/components/driver/linker.lf b/components/driver/linker.lf index 9e118f876e..c026bdc807 100644 --- a/components/driver/linker.lf +++ b/components/driver/linker.lf @@ -17,3 +17,6 @@ entries: pulse_cnt: pcnt_unit_stop (noflash) pulse_cnt: pcnt_unit_clear_count (noflash) pulse_cnt: pcnt_unit_get_count (noflash) + if GPIO_CTRL_FUNC_IN_IRAM = y: + gpio: gpio_set_level (noflash) + gpio: gpio_intr_disable (noflash) diff --git a/components/driver/test/test_adc.c b/components/driver/test/test_adc.c index 6d674ba6b5..dbbb934cf3 100644 --- a/components/driver/test/test_adc.c +++ b/components/driver/test/test_adc.c @@ -11,6 +11,7 @@ #include "test_utils.h" #include "esp_adc_cal.h" #include "driver/adc_common.h" +#include "esp_cpu.h" __attribute__((unused)) static const char *TAG = "ADC"; diff --git a/components/driver/test/test_gpio.c b/components/driver/test/test_gpio.c deleted file mode 100644 index 5f73b2ebb8..0000000000 --- a/components/driver/test/test_gpio.c +++ /dev/null @@ -1,897 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD - * - * SPDX-License-Identifier: Apache-2.0 - */ -/** - * About test environment UT_T1_GPIO: - * Please connect TEST_GPIO_EXT_OUT_IO and TEST_GPIO_EXT_IN_IO - */ -#include -#include - -#include "esp_system.h" -#include "esp_sleep.h" -#include "unity.h" -#include "driver/gpio.h" -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#include "freertos/queue.h" -#include "sdkconfig.h" -#include "esp_rom_uart.h" -#include "esp_rom_sys.h" -#include "test_utils.h" - - -#define WAKE_UP_IGNORE 1 // gpio_wakeup function development is not completed yet, set it deprecated. - -#if CONFIG_IDF_TARGET_ESP32 -#define TEST_GPIO_EXT_OUT_IO 18 // default output GPIO -#define TEST_GPIO_EXT_IN_IO 19 // default input GPIO -#define TEST_GPIO_OUTPUT_PIN 23 -#define TEST_GPIO_INPUT_ONLY_PIN 34 -#define TEST_GPIO_OUTPUT_MAX GPIO_NUM_34 -#define TEST_GPIO_INPUT_LEVEL_HIGH_PIN 2 -#define TEST_GPIO_INPUT_LEVEL_LOW_PIN 4 -#elif CONFIG_IDF_TARGET_ESP32S2 -// ESP32_S2 DEVKIC uses IO19 and IO20 as USB functions, so it is necessary to avoid using IO19, otherwise GPIO io pull up/down function cannot pass -// Also the first version of ESP32-S2-Saola has pullup issue on GPIO18, which is tied to 3V3 on the -// runner. Also avoid using GPIO18. -#define TEST_GPIO_EXT_OUT_IO 17 // default output GPIO -#define TEST_GPIO_EXT_IN_IO 21 // default input GPIO -#define TEST_GPIO_OUTPUT_PIN 12 -#define TEST_GPIO_INPUT_ONLY_PIN 46 -#define TEST_GPIO_OUTPUT_MAX GPIO_NUM_46 -#define TEST_GPIO_INPUT_LEVEL_HIGH_PIN 17 -#define TEST_GPIO_INPUT_LEVEL_LOW_PIN 1 -#elif CONFIG_IDF_TARGET_ESP32S3 -// IO19 and IO20 are connected as USB functions. -#define TEST_GPIO_EXT_OUT_IO 17 // default output GPIO -#define TEST_GPIO_EXT_IN_IO 21 // default input GPIO -#define TEST_GPIO_OUTPUT_PIN 12 -#define TEST_GPIO_OUTPUT_MAX GPIO_NUM_MAX -#define TEST_GPIO_USB_DM_IO 19 // USB D- GPIO -#define TEST_GPIO_USB_DP_IO 20 // USB D+ GPIO -#define TEST_GPIO_INPUT_LEVEL_HIGH_PIN 17 -#define TEST_GPIO_INPUT_LEVEL_LOW_PIN 1 -#elif CONFIG_IDF_TARGET_ESP32C3 -#define TEST_GPIO_EXT_OUT_IO 2 // default output GPIO -#define TEST_GPIO_EXT_IN_IO 3 // default input GPIO -#define TEST_GPIO_OUTPUT_PIN 1 -#define TEST_GPIO_OUTPUT_MAX GPIO_NUM_MAX -#define TEST_GPIO_USB_DM_IO 18 // USB D- GPIO -#define TEST_GPIO_USB_DP_IO 19 // USB D+ GPIO -#define TEST_GPIO_INPUT_LEVEL_HIGH_PIN 10 -#define TEST_GPIO_INPUT_LEVEL_LOW_PIN 1 -#elif CONFIG_IDF_TARGET_ESP32C2 -#define TEST_GPIO_EXT_OUT_IO 2 // default output GPIO -#define TEST_GPIO_EXT_IN_IO 3 // default input GPIO -#define TEST_GPIO_OUTPUT_PIN 1 -#define TEST_GPIO_OUTPUT_MAX GPIO_NUM_MAX -#define TEST_GPIO_INPUT_LEVEL_HIGH_PIN 10 -#define TEST_GPIO_INPUT_LEVEL_LOW_PIN 1 -#elif CONFIG_IDF_TARGET_ESP32H2_BETA_VERSION_2 -#define TEST_GPIO_EXT_OUT_IO 6 // default output GPIO -#define TEST_GPIO_EXT_IN_IO 7 // default input GPIO -#define TEST_GPIO_OUTPUT_PIN 1 -#define TEST_GPIO_OUTPUT_MAX GPIO_NUM_MAX -#define TEST_GPIO_USB_DM_IO 24 // USB D- GPIO -#define TEST_GPIO_USB_DP_IO 25 // USB D+ GPIO -#define TEST_GPIO_INPUT_LEVEL_HIGH_PIN 9 -#define TEST_GPIO_INPUT_LEVEL_LOW_PIN 1 -#endif - -// If there is any input-only pin, enable input-only pin part of some tests. -#define SOC_HAS_INPUT_ONLY_PIN (CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2) - -// define public test io on all boards -#define TEST_IO_9 GPIO_NUM_9 -#define TEST_IO_10 GPIO_NUM_10 - -static volatile int disable_intr_times = 0; // use this to calculate how many times it go into interrupt -static volatile int level_intr_times = 0; // use this to get how many times the level interrupt happened -static volatile int edge_intr_times = 0; // use this to get how many times the edge interrupt happened -#if !WAKE_UP_IGNORE -static bool wake_up_result = false; // use this to judge the wake up event happen or not -#endif - - -/** - * do some initialization operation in this function - * @param num: it is the destination GPIO wanted to be initialized - * - */ -static gpio_config_t init_io(gpio_num_t num) -{ - TEST_ASSERT(num < TEST_GPIO_OUTPUT_MAX); - gpio_config_t io_conf = { - .intr_type = GPIO_INTR_DISABLE, - .mode = GPIO_MODE_OUTPUT, - .pin_bit_mask = (1ULL << num), - .pull_down_en = 0, - .pull_up_en = 0, - }; - return io_conf; -} - -// edge interrupt event -__attribute__((unused)) static void gpio_isr_edge_handler(void *arg) -{ - uint32_t gpio_num = (uint32_t) arg; - esp_rom_printf("GPIO[%d] intr on core %d, val: %d\n", gpio_num, cpu_hal_get_core_id(), gpio_get_level(gpio_num)); - edge_intr_times++; -} - -#if !TEMPORARY_DISABLED_FOR_TARGETS(ESP32S2, ESP32S3, ESP32C3, ESP32C2, ESP32H2) -//No runners -// level interrupt event with "gpio_intr_disable" -static void gpio_isr_level_handler(void *arg) -{ - uint32_t gpio_num = (uint32_t) arg; - disable_intr_times++; - esp_rom_printf("GPIO[%d] intr, val: %d, disable_intr_times = %d\n", gpio_num, gpio_get_level(gpio_num), disable_intr_times); - gpio_intr_disable(gpio_num); -} - -// level interrupt event -static void gpio_isr_level_handler2(void *arg) -{ - uint32_t gpio_num = (uint32_t) arg; - level_intr_times++; - esp_rom_printf("GPIO[%d] intr, val: %d\n", gpio_num, gpio_get_level(gpio_num)); - if (gpio_get_level(gpio_num)) { - gpio_set_level(TEST_GPIO_EXT_OUT_IO, 0); - } else { - gpio_set_level(TEST_GPIO_EXT_OUT_IO, 1); - } - esp_rom_printf("GPIO[%d] intr, val: %d, level_intr_times = %d\n", TEST_GPIO_EXT_OUT_IO, gpio_get_level(TEST_GPIO_EXT_OUT_IO), level_intr_times); - esp_rom_printf("GPIO[%d] intr, val: %d, level_intr_times = %d\n", gpio_num, gpio_get_level(gpio_num), level_intr_times); -} -#endif //!TEMPORARY_DISABLED_FOR_TARGETS(ESP32S2, ESP32S3, ESP32C3, ESP32C2, ESP32H2) - -#if !WAKE_UP_IGNORE -// get result of waking up or not -static void sleep_wake_up(void *arg) -{ - gpio_config_t io_config = init_io(TEST_GPIO_EXT_IN_IO); - io_config.mode = GPIO_MODE_INPUT; - gpio_config(&io_config); - TEST_ESP_OK(gpio_wakeup_enable(TEST_GPIO_EXT_IN_IO, GPIO_INTR_HIGH_LEVEL)); - esp_light_sleep_start(); - wake_up_result = true; -} - -// wake up light sleep event -static void trigger_wake_up(void *arg) -{ - gpio_config_t io_config = init_io(TEST_GPIO_EXT_OUT_IO); - gpio_config(&io_config); - gpio_set_level(TEST_GPIO_EXT_OUT_IO, 0); - gpio_install_isr_service(0); - gpio_isr_handler_add(TEST_GPIO_EXT_OUT_IO, gpio_isr_level_handler, (void *) TEST_GPIO_EXT_IN_IO); - gpio_set_level(TEST_GPIO_EXT_OUT_IO, 1); - vTaskDelay(100 / portTICK_PERIOD_MS); -} -#endif //!WAKE_UP_IGNORE - -static void prompt_to_continue(const char *str) -{ - printf("%s , please press \"Enter\" to go on!\n", str); - char sign[5] = {0}; - while (strlen(sign) == 0) { - /* Flush anything already in the RX buffer */ - while (esp_rom_uart_rx_one_char((uint8_t *) sign) == 0) { - } - /* Read line */ - esp_rom_uart_rx_string((uint8_t *) sign, sizeof(sign) - 1); - } -} - -static void drive_capability_set_get(gpio_num_t num, gpio_drive_cap_t capability) -{ - gpio_config_t pad_io = init_io(num); - TEST_ESP_OK(gpio_config(&pad_io)); - TEST_ASSERT(gpio_set_drive_capability(num, GPIO_DRIVE_CAP_MAX) == ESP_ERR_INVALID_ARG); - - gpio_drive_cap_t cap; - TEST_ESP_OK(gpio_set_drive_capability(num, capability)); - TEST_ESP_OK(gpio_get_drive_capability(num, &cap)); - TEST_ASSERT_EQUAL_INT(cap, capability); -} - - -// test the basic configuration function with right parameters and error parameters -TEST_CASE("GPIO config parameters test", "[gpio]") -{ - //error param test - //ESP32 test 41 bit, ESP32-S2 test 48 bit, ESP32-S3 test 50 bit - gpio_config_t io_config = { 0 }; - io_config.intr_type = GPIO_INTR_DISABLE; - io_config.pin_bit_mask = ((uint64_t)1 << (GPIO_NUM_MAX + 1)); - TEST_ASSERT(gpio_config(&io_config) == ESP_ERR_INVALID_ARG); - - // test 0 - io_config.pin_bit_mask = 0; - TEST_ASSERT(gpio_config(&io_config) == ESP_ERR_INVALID_ARG); - - //ESP32 test 40 bit, ESP32-S2 test 47 bit, ESP32-S3 test 49 bit - io_config.pin_bit_mask = ((uint64_t)1 << GPIO_NUM_MAX); - TEST_ASSERT(gpio_config(&io_config) == ESP_ERR_INVALID_ARG); - - io_config.pin_bit_mask = ((uint64_t)1 << TEST_GPIO_OUTPUT_PIN); - TEST_ESP_OK(gpio_config(&io_config)); - - //This IO is just used for input, C3 and S3 doesn't have input only pin. -#if SOC_HAS_INPUT_ONLY_PIN - io_config.pin_bit_mask = ((uint64_t)1 << TEST_GPIO_INPUT_ONLY_PIN); - io_config.mode = GPIO_MODE_INPUT; - TEST_ESP_OK(gpio_config(&io_config)); - io_config.mode = GPIO_MODE_OUTPUT; - // The pin is input only, once set as output should log something - TEST_ASSERT(gpio_config(&io_config) == ESP_ERR_INVALID_ARG); -#endif // SOC_HAS_INPUT_ONLY_PIN -} - -#if !TEMPORARY_DISABLED_FOR_TARGETS(ESP32S2, ESP32S3, ESP32C3, ESP32C2, ESP32H2) -//No runners -TEST_CASE("GPIO rising edge interrupt test", "[gpio][test_env=UT_T1_GPIO]") -{ - edge_intr_times = 0; // set it as 0 prepare to test - //init input and output gpio - gpio_config_t output_io = init_io(TEST_GPIO_EXT_OUT_IO); - gpio_config_t input_io = init_io(TEST_GPIO_EXT_IN_IO); - input_io.intr_type = GPIO_INTR_POSEDGE; - input_io.mode = GPIO_MODE_INPUT; - input_io.pull_up_en = 1; - TEST_ESP_OK(gpio_config(&output_io)); - TEST_ESP_OK(gpio_config(&input_io)); - TEST_ESP_OK(gpio_set_level(TEST_GPIO_EXT_OUT_IO, 0)); - - //rising edge intr - TEST_ESP_OK(gpio_set_intr_type(TEST_GPIO_EXT_IN_IO, GPIO_INTR_POSEDGE)); - TEST_ESP_OK(gpio_install_isr_service(0)); - gpio_isr_handler_add(TEST_GPIO_EXT_IN_IO, gpio_isr_edge_handler, (void *)TEST_GPIO_EXT_IN_IO); - TEST_ESP_OK(gpio_set_level(TEST_GPIO_EXT_OUT_IO, 1)); - TEST_ASSERT_EQUAL_INT(edge_intr_times, 1); - vTaskDelay(100 / portTICK_PERIOD_MS); - gpio_isr_handler_remove(TEST_GPIO_EXT_IN_IO); - gpio_uninstall_isr_service(); -} - -TEST_CASE("GPIO falling edge interrupt test", "[gpio][test_env=UT_T1_GPIO]") -{ - edge_intr_times = 0; - gpio_config_t output_io = init_io(TEST_GPIO_EXT_OUT_IO); - gpio_config_t input_io = init_io(TEST_GPIO_EXT_IN_IO); - input_io.intr_type = GPIO_INTR_POSEDGE; - input_io.mode = GPIO_MODE_INPUT; - input_io.pull_up_en = 1; - TEST_ESP_OK(gpio_config(&output_io)); - TEST_ESP_OK(gpio_config(&input_io)); - TEST_ESP_OK(gpio_set_level(TEST_GPIO_EXT_OUT_IO, 1)); - - gpio_set_intr_type(TEST_GPIO_EXT_IN_IO, GPIO_INTR_NEGEDGE); - gpio_install_isr_service(0); - gpio_isr_handler_add(TEST_GPIO_EXT_IN_IO, gpio_isr_edge_handler, (void *) TEST_GPIO_EXT_IN_IO); - gpio_set_level(TEST_GPIO_EXT_OUT_IO, 0); - vTaskDelay(100 / portTICK_PERIOD_MS); - TEST_ASSERT_EQUAL_INT(edge_intr_times, 1); - vTaskDelay(100 / portTICK_PERIOD_MS); - gpio_isr_handler_remove(TEST_GPIO_EXT_IN_IO); - gpio_uninstall_isr_service(); -} - -TEST_CASE("GPIO both rising and falling edge interrupt test", "[gpio][test_env=UT_T1_GPIO]") -{ - edge_intr_times = 0; - gpio_config_t output_io = init_io(TEST_GPIO_EXT_OUT_IO); - gpio_config_t input_io = init_io(TEST_GPIO_EXT_IN_IO); - input_io.intr_type = GPIO_INTR_POSEDGE; - input_io.mode = GPIO_MODE_INPUT; - input_io.pull_up_en = 1; - TEST_ESP_OK(gpio_config(&output_io)); - TEST_ESP_OK(gpio_config(&input_io)); - TEST_ESP_OK(gpio_set_level(TEST_GPIO_EXT_OUT_IO, 0)); - int level = 0; - - gpio_set_intr_type(TEST_GPIO_EXT_IN_IO, GPIO_INTR_ANYEDGE); - gpio_install_isr_service(0); - gpio_isr_handler_add(TEST_GPIO_EXT_IN_IO, gpio_isr_edge_handler, (void *) TEST_GPIO_EXT_IN_IO); - // for rising edge in GPIO_INTR_ANYEDGE - while (1) { - level = level + 1; - gpio_set_level(TEST_GPIO_EXT_OUT_IO, level * 0.2); - if (level > 10) { - break; - } - vTaskDelay(100 / portTICK_PERIOD_MS); - } - vTaskDelay(100 / portTICK_PERIOD_MS); - // for falling rdge in GPIO_INTR_ANYEDGE - while (1) { - level = level - 1; - gpio_set_level(TEST_GPIO_EXT_OUT_IO, level / 5); - if (level < 0) { - break; - } - vTaskDelay(100 / portTICK_PERIOD_MS); - } - vTaskDelay(100 / portTICK_PERIOD_MS); - TEST_ASSERT_EQUAL_INT(edge_intr_times, 2); - vTaskDelay(100 / portTICK_PERIOD_MS); - gpio_isr_handler_remove(TEST_GPIO_EXT_IN_IO); - gpio_uninstall_isr_service(); -} - -TEST_CASE("GPIO input high level trigger, cut the interrupt source exit interrupt test", "[gpio][test_env=UT_T1_GPIO]") -{ - level_intr_times = 0; - gpio_config_t output_io = init_io(TEST_GPIO_EXT_OUT_IO); - gpio_config_t input_io = init_io(TEST_GPIO_EXT_IN_IO); - input_io.intr_type = GPIO_INTR_POSEDGE; - input_io.mode = GPIO_MODE_INPUT; - input_io.pull_up_en = 1; - TEST_ESP_OK(gpio_config(&output_io)); - TEST_ESP_OK(gpio_config(&input_io)); - TEST_ESP_OK(gpio_set_level(TEST_GPIO_EXT_OUT_IO, 0)); - - gpio_set_intr_type(TEST_GPIO_EXT_IN_IO, GPIO_INTR_HIGH_LEVEL); - gpio_install_isr_service(0); - gpio_isr_handler_add(TEST_GPIO_EXT_IN_IO, gpio_isr_level_handler2, (void *) TEST_GPIO_EXT_IN_IO); - gpio_set_level(TEST_GPIO_EXT_OUT_IO, 1); - vTaskDelay(100 / portTICK_PERIOD_MS); - TEST_ASSERT_EQUAL_INT_MESSAGE(level_intr_times, 1, "go into high-level interrupt more than once with cur interrupt source way"); - gpio_isr_handler_remove(TEST_GPIO_EXT_IN_IO); - gpio_uninstall_isr_service(); - -} - -TEST_CASE("GPIO low level interrupt test", "[gpio][test_env=UT_T1_GPIO]") -{ - disable_intr_times = 0; - gpio_config_t output_io = init_io(TEST_GPIO_EXT_OUT_IO); - gpio_config_t input_io = init_io(TEST_GPIO_EXT_IN_IO); - input_io.intr_type = GPIO_INTR_POSEDGE; - input_io.mode = GPIO_MODE_INPUT; - input_io.pull_up_en = 1; - TEST_ESP_OK(gpio_config(&output_io)); - TEST_ESP_OK(gpio_config(&input_io)); - TEST_ESP_OK(gpio_set_level(TEST_GPIO_EXT_OUT_IO, 1)); - - gpio_set_intr_type(TEST_GPIO_EXT_IN_IO, GPIO_INTR_LOW_LEVEL); - gpio_install_isr_service(0); - gpio_isr_handler_add(TEST_GPIO_EXT_IN_IO, gpio_isr_level_handler, (void *) TEST_GPIO_EXT_IN_IO); - gpio_set_level(TEST_GPIO_EXT_OUT_IO, 0); - printf("get level:%d\n", gpio_get_level(TEST_GPIO_EXT_IN_IO)); - vTaskDelay(100 / portTICK_PERIOD_MS); - TEST_ASSERT_EQUAL_INT_MESSAGE(disable_intr_times, 1, "go into low-level interrupt more than once with disable way"); - gpio_isr_handler_remove(TEST_GPIO_EXT_IN_IO); - gpio_uninstall_isr_service(); -} - -TEST_CASE("GPIO multi-level interrupt test, to cut the interrupt source exit interrupt ", "[gpio][test_env=UT_T1_GPIO]") -{ - level_intr_times = 0; - gpio_config_t output_io = init_io(TEST_GPIO_EXT_OUT_IO); - gpio_config_t input_io = init_io(TEST_GPIO_EXT_IN_IO); - input_io.intr_type = GPIO_INTR_POSEDGE; - input_io.mode = GPIO_MODE_INPUT; - input_io.pull_up_en = 1; - TEST_ESP_OK(gpio_config(&output_io)); - TEST_ESP_OK(gpio_config(&input_io)); - TEST_ESP_OK(gpio_set_level(TEST_GPIO_EXT_OUT_IO, 0)); - - gpio_set_intr_type(TEST_GPIO_EXT_IN_IO, GPIO_INTR_HIGH_LEVEL); - gpio_install_isr_service(0); - gpio_isr_handler_add(TEST_GPIO_EXT_IN_IO, gpio_isr_level_handler2, (void *) TEST_GPIO_EXT_IN_IO); - gpio_set_level(TEST_GPIO_EXT_OUT_IO, 1); - vTaskDelay(100 / portTICK_PERIOD_MS); - TEST_ASSERT_EQUAL_INT_MESSAGE(level_intr_times, 1, "go into high-level interrupt more than once with cur interrupt source way"); - gpio_set_level(TEST_GPIO_EXT_OUT_IO, 1); - vTaskDelay(200 / portTICK_PERIOD_MS); - TEST_ASSERT_EQUAL_INT_MESSAGE(level_intr_times, 2, "go into high-level interrupt more than once with cur interrupt source way"); - gpio_isr_handler_remove(TEST_GPIO_EXT_IN_IO); - gpio_uninstall_isr_service(); -} - -TEST_CASE("GPIO enable and disable interrupt test", "[gpio][test_env=UT_T1_GPIO]") -{ - disable_intr_times = 0; - gpio_config_t output_io = init_io(TEST_GPIO_EXT_OUT_IO); - gpio_config_t input_io = init_io(TEST_GPIO_EXT_IN_IO); - input_io.intr_type = GPIO_INTR_POSEDGE; - input_io.mode = GPIO_MODE_INPUT; - input_io.pull_up_en = 1; - TEST_ESP_OK(gpio_config(&output_io)); - TEST_ESP_OK(gpio_config(&input_io)); - - TEST_ESP_OK(gpio_set_level(TEST_GPIO_EXT_OUT_IO, 0)); // Because of GPIO_INTR_HIGH_LEVEL interrupt, 0 must be set first - TEST_ESP_OK(gpio_set_intr_type(TEST_GPIO_EXT_IN_IO, GPIO_INTR_HIGH_LEVEL)); - TEST_ESP_OK(gpio_install_isr_service(0)); - TEST_ESP_OK(gpio_isr_handler_add(TEST_GPIO_EXT_IN_IO, gpio_isr_level_handler, (void *) TEST_GPIO_EXT_IN_IO)); - TEST_ESP_OK(gpio_set_level(TEST_GPIO_EXT_OUT_IO, 1)); - TEST_ESP_OK(gpio_isr_handler_remove(TEST_GPIO_EXT_IN_IO)); - TEST_ESP_OK(gpio_set_level(TEST_GPIO_EXT_OUT_IO, 0)); - TEST_ASSERT_EQUAL_INT_MESSAGE(disable_intr_times, 1, "go into high-level interrupt more than once with disable way"); - - // not install service now - vTaskDelay(100 / portTICK_PERIOD_MS); - TEST_ESP_OK(gpio_intr_disable(TEST_GPIO_EXT_IN_IO)); - TEST_ESP_OK(gpio_set_level(TEST_GPIO_EXT_OUT_IO, 1)); - TEST_ASSERT_EQUAL_INT_MESSAGE(disable_intr_times, 1, "disable interrupt does not work, still go into interrupt!"); - - gpio_uninstall_isr_service(); //uninstall the service - TEST_ASSERT(gpio_isr_handler_add(TEST_GPIO_EXT_IN_IO, gpio_isr_level_handler, (void *) TEST_GPIO_EXT_IN_IO) == ESP_ERR_INVALID_STATE); - TEST_ASSERT(gpio_isr_handler_remove(TEST_GPIO_EXT_IN_IO) == ESP_ERR_INVALID_STATE); -} -#endif //DISABLED_FOR_TARGETS(ESP32S2, ESP32S3, ESP32C3, ESP32C2, ESP32H2) - -#if !CONFIG_FREERTOS_UNICORE -static void install_isr_service_task(void *arg) -{ - uint32_t gpio_num = (uint32_t) arg; - //rising edge intr - TEST_ESP_OK(gpio_set_intr_type(gpio_num, GPIO_INTR_POSEDGE)); - TEST_ESP_OK(gpio_install_isr_service(0)); - gpio_isr_handler_add(gpio_num, gpio_isr_edge_handler, (void *) gpio_num); - vTaskSuspend(NULL); -} - -TEST_CASE("GPIO interrupt on other CPUs test", "[gpio]") -{ - TaskHandle_t gpio_task_handle; - gpio_config_t input_output_io = init_io(TEST_GPIO_EXT_OUT_IO); - input_output_io.mode = GPIO_MODE_INPUT_OUTPUT; - input_output_io.pull_up_en = 1; - TEST_ESP_OK(gpio_config(&input_output_io)); - - for (int cpu_num = 1; cpu_num < portNUM_PROCESSORS; ++cpu_num) { - // We assume unit-test task is running on core 0, so we install gpio interrupt on other cores - edge_intr_times = 0; - TEST_ESP_OK(gpio_set_level(TEST_GPIO_EXT_OUT_IO, 0)); - xTaskCreatePinnedToCore(install_isr_service_task, "install_isr_service_task", 2048, (void *) TEST_GPIO_EXT_OUT_IO, 1, &gpio_task_handle, cpu_num); - - vTaskDelay(200 / portTICK_PERIOD_MS); - TEST_ESP_OK(gpio_set_level(TEST_GPIO_EXT_OUT_IO, 1)); - vTaskDelay(100 / portTICK_PERIOD_MS); - TEST_ASSERT_EQUAL_INT(edge_intr_times, 1); - gpio_isr_handler_remove(TEST_GPIO_EXT_OUT_IO); - gpio_uninstall_isr_service(); - test_utils_task_delete(gpio_task_handle); - } -} -#endif //!CONFIG_FREERTOS_UNICORE - -// ESP32 Connect GPIO18 with GPIO19, ESP32-S2 Connect GPIO17 with GPIO21, -// ESP32-S3 Connect GPIO17 with GPIO21, ESP32C3 Connect GPIO2 with GPIO3 -// use multimeter to test the voltage, so it is ignored in CI -TEST_CASE("GPIO set gpio output level test", "[gpio][ignore][UT_T1_GPIO]") -{ - gpio_config_t io_conf; - io_conf.intr_type = GPIO_INTR_DISABLE; - io_conf.mode = GPIO_MODE_OUTPUT; - io_conf.pin_bit_mask = ((uint64_t)1 << TEST_GPIO_EXT_OUT_IO); - io_conf.pull_down_en = 0; - io_conf.pull_up_en = 0; - gpio_config(&io_conf); - - io_conf.pin_bit_mask = ((uint64_t)1 << TEST_GPIO_EXT_IN_IO); - io_conf.mode = GPIO_MODE_INPUT; - gpio_config(&io_conf); - - gpio_set_level(TEST_GPIO_EXT_OUT_IO, 0); - // tested voltage is around 0v - TEST_ASSERT_EQUAL_INT_MESSAGE(gpio_get_level(TEST_GPIO_EXT_IN_IO), 0, "get level error! the level should be low!"); - vTaskDelay(1000 / portTICK_PERIOD_MS); - gpio_set_level(TEST_GPIO_EXT_OUT_IO, 1); - // tested voltage is around 3.3v - TEST_ASSERT_EQUAL_INT_MESSAGE(gpio_get_level(TEST_GPIO_EXT_IN_IO), 1, "get level error! the level should be high!"); - - //This IO is just used for input, C3 and S3 doesn't have input only pin. -#if SOC_HAS_INPUT_ONLY_PIN - io_conf.pin_bit_mask = ((uint64_t)1 << TEST_GPIO_INPUT_ONLY_PIN); - io_conf.mode = GPIO_MODE_OUTPUT; - gpio_config(&io_conf); - TEST_ASSERT(gpio_config(&io_conf) == ESP_ERR_INVALID_ARG); -#endif // SOC_HAS_INPUT_ONLY_PIN -} - -// TEST_GPIO_INPUT_LEVEL_HIGH_PIN connects to 3.3v pin, TEST_GPIO_INPUT_LEVEL_LOW_PIN connects to the GND pin -// use multimeter to test the voltage, so it is ignored in CI -TEST_CASE("GPIO get input level test", "[gpio][ignore]") -{ - gpio_num_t num1 = TEST_GPIO_INPUT_LEVEL_HIGH_PIN; - int level1 = gpio_get_level(num1); - printf("TEST_GPIO_INPUT_LEVEL_HIGH_PIN's level is: %d\n", level1); - TEST_ASSERT_EQUAL_INT_MESSAGE(level1, 1, "get level error! the level should be high!"); - - gpio_num_t num2 = TEST_GPIO_INPUT_LEVEL_LOW_PIN; - int level2 = gpio_get_level(num2); - printf("TEST_GPIO_INPUT_LEVEL_LOW_PIN's level is: %d\n", level2); - TEST_ASSERT_EQUAL_INT_MESSAGE(level2, 0, "get level error! the level should be low!"); - printf("the memory get: %d\n", esp_get_free_heap_size()); - //when case finish, get the result from multimeter, the TEST_GPIO_INPUT_LEVEL_HIGH_PIN is 3.3v, the TEST_GPIO_INPUT_LEVEL_LOW_PIN is 0.00v -} - -TEST_CASE("GPIO io pull up/down function", "[gpio]") -{ - // First, ensure that the output IO will not affect the level - gpio_config_t io_conf = init_io(TEST_GPIO_EXT_OUT_IO); - gpio_config(&io_conf); - gpio_set_direction(TEST_GPIO_EXT_OUT_IO, GPIO_MODE_INPUT); - io_conf = init_io(TEST_GPIO_EXT_IN_IO); - gpio_config(&io_conf); - gpio_set_direction(TEST_GPIO_EXT_IN_IO, GPIO_MODE_INPUT); - TEST_ESP_OK(gpio_pullup_en(TEST_GPIO_EXT_IN_IO)); // pull up first - vTaskDelay(100 / portTICK_PERIOD_MS); - TEST_ASSERT_EQUAL_INT_MESSAGE(gpio_get_level(TEST_GPIO_EXT_IN_IO), 1, "gpio_pullup_en error, it can't pull up"); - TEST_ESP_OK(gpio_pulldown_dis(TEST_GPIO_EXT_IN_IO)); //can't be pull down - vTaskDelay(100 / portTICK_PERIOD_MS); - TEST_ASSERT_EQUAL_INT_MESSAGE(gpio_get_level(TEST_GPIO_EXT_IN_IO), 1, "gpio_pulldown_dis error, it can pull down"); - TEST_ESP_OK(gpio_pulldown_en(TEST_GPIO_EXT_IN_IO)); // can be pull down now - vTaskDelay(100 / portTICK_PERIOD_MS); - TEST_ASSERT_EQUAL_INT_MESSAGE(gpio_get_level(TEST_GPIO_EXT_IN_IO), 0, "gpio_pulldown_en error, it can't pull down"); - TEST_ESP_OK(gpio_pullup_dis(TEST_GPIO_EXT_IN_IO)); // can't be pull up - vTaskDelay(100 / portTICK_PERIOD_MS); - TEST_ASSERT_EQUAL_INT_MESSAGE(gpio_get_level(TEST_GPIO_EXT_IN_IO), 0, "gpio_pullup_dis error, it can pull up"); -} - -#if !TEMPORARY_DISABLED_FOR_TARGETS(ESP32S2, ESP32S3, ESP32C3, ESP32C2, ESP32H2) -//No runners -TEST_CASE("GPIO output and input mode test", "[gpio][test_env=UT_T1_GPIO]") -{ - //ESP32 connect io18 and io19, ESP32-S2 connect io17 and io21, ESP32-S3 connect io17 and io21, ESP32C3 Connect GPIO2 with GPIO3 - gpio_config_t output_io = init_io(TEST_GPIO_EXT_OUT_IO); - gpio_config_t input_io = init_io(TEST_GPIO_EXT_IN_IO); - gpio_config(&output_io); - gpio_config(&input_io); - int level = gpio_get_level(TEST_GPIO_EXT_IN_IO); - - //disable mode - gpio_set_direction(TEST_GPIO_EXT_OUT_IO, GPIO_MODE_DISABLE); - gpio_set_direction(TEST_GPIO_EXT_IN_IO, GPIO_MODE_OUTPUT); - gpio_set_level(TEST_GPIO_EXT_OUT_IO, !level); - TEST_ASSERT_EQUAL_INT_MESSAGE(gpio_get_level(TEST_GPIO_EXT_IN_IO), level, "direction GPIO_MODE_DISABLE set error, it can output"); - - //input mode and output mode - gpio_set_direction(TEST_GPIO_EXT_OUT_IO, GPIO_MODE_OUTPUT); - gpio_set_direction(TEST_GPIO_EXT_IN_IO, GPIO_MODE_INPUT); - gpio_set_level(TEST_GPIO_EXT_OUT_IO, 1); - TEST_ASSERT_EQUAL_INT_MESSAGE(gpio_get_level(TEST_GPIO_EXT_IN_IO), 1, "direction GPIO_MODE_OUTPUT set error, it can't output"); - gpio_set_level(TEST_GPIO_EXT_OUT_IO, 0); - TEST_ASSERT_EQUAL_INT_MESSAGE(gpio_get_level(TEST_GPIO_EXT_IN_IO), 0, "direction GPIO_MODE_OUTPUT set error, it can't output"); - - // open drain mode(output), can just output low level - gpio_set_direction(TEST_GPIO_EXT_OUT_IO, GPIO_MODE_OUTPUT_OD); - gpio_set_direction(TEST_GPIO_EXT_IN_IO, GPIO_MODE_INPUT); - gpio_set_level(TEST_GPIO_EXT_OUT_IO, 1); - TEST_ASSERT_EQUAL_INT_MESSAGE(gpio_get_level(TEST_GPIO_EXT_IN_IO), 0, "direction GPIO_MODE_OUTPUT set error, it can't output"); - gpio_set_level(TEST_GPIO_EXT_OUT_IO, 0); - TEST_ASSERT_EQUAL_INT_MESSAGE(gpio_get_level(TEST_GPIO_EXT_IN_IO), 0, "direction GPIO_MODE_OUTPUT set error, it can't output"); - - // open drain mode(output and input), can just output low level - // output test - gpio_set_direction(TEST_GPIO_EXT_OUT_IO, GPIO_MODE_INPUT_OUTPUT_OD); - gpio_set_direction(TEST_GPIO_EXT_IN_IO, GPIO_MODE_INPUT); - gpio_set_level(TEST_GPIO_EXT_OUT_IO, 1); - TEST_ASSERT_EQUAL_INT_MESSAGE(gpio_get_level(TEST_GPIO_EXT_IN_IO), 0, "direction GPIO_MODE_OUTPUT set error, it can't output"); - gpio_set_level(TEST_GPIO_EXT_OUT_IO, 0); - TEST_ASSERT_EQUAL_INT_MESSAGE(gpio_get_level(TEST_GPIO_EXT_IN_IO), 0, "direction GPIO_MODE_OUTPUT set error, it can't output"); - - // GPIO_MODE_INPUT_OUTPUT mode - // output test - level = gpio_get_level(TEST_GPIO_EXT_IN_IO); - gpio_set_direction(TEST_GPIO_EXT_OUT_IO, GPIO_MODE_INPUT_OUTPUT); - gpio_set_direction(TEST_GPIO_EXT_IN_IO, GPIO_MODE_INPUT); - gpio_set_level(TEST_GPIO_EXT_OUT_IO, !level); - TEST_ASSERT_EQUAL_INT_MESSAGE(gpio_get_level(TEST_GPIO_EXT_IN_IO), !level, "direction set error, it can't output"); -} - -TEST_CASE("GPIO repeate call service and isr has no memory leak test", "[gpio][test_env=UT_T1_GPIO][timeout=90]") -{ - gpio_config_t output_io = init_io(TEST_GPIO_EXT_OUT_IO); - gpio_config_t input_io = init_io(TEST_GPIO_EXT_IN_IO); - input_io.intr_type = GPIO_INTR_POSEDGE; - input_io.mode = GPIO_MODE_INPUT; - input_io.pull_up_en = 1; - TEST_ESP_OK(gpio_config(&output_io)); - TEST_ESP_OK(gpio_config(&input_io)); - TEST_ESP_OK(gpio_set_level(TEST_GPIO_EXT_OUT_IO, 0)); - //rising edge - uint32_t size = esp_get_free_heap_size(); - for (int i = 0; i < 1000; i++) { - TEST_ESP_OK(gpio_set_intr_type(TEST_GPIO_EXT_IN_IO, GPIO_INTR_POSEDGE)); - TEST_ESP_OK(gpio_install_isr_service(0)); - TEST_ESP_OK(gpio_isr_handler_add(TEST_GPIO_EXT_IN_IO, gpio_isr_edge_handler, (void *)TEST_GPIO_EXT_IN_IO)); - gpio_set_level(TEST_GPIO_EXT_OUT_IO, 1); - TEST_ESP_OK(gpio_isr_handler_remove(TEST_GPIO_EXT_IN_IO)); - gpio_set_level(TEST_GPIO_EXT_OUT_IO, 0); - gpio_uninstall_isr_service(); - } - TEST_ASSERT_INT32_WITHIN(size, esp_get_free_heap_size(), 100); -} -#endif //DISABLED_FOR_TARGETS(ESP32S2, ESP32S3, ESP32C3, ESP32C2, ESP32H2) - -#if !WAKE_UP_IGNORE -//this function development is not completed yet, set it ignored -TEST_CASE("GPIO wake up enable and disenable test", "[gpio][ignore]") -{ - xTaskCreate(sleep_wake_up, "sleep_wake_up", 4096, NULL, 5, NULL); - xTaskCreate(trigger_wake_up, "trigger_wake_up", 4096, NULL, 5, NULL); - vTaskDelay(100 / portTICK_PERIOD_MS); - TEST_ASSERT_TRUE(wake_up_result); - - wake_up_result = false; - TEST_ESP_OK(gpio_wakeup_disable(TEST_GPIO_EXT_IN_IO)); - gpio_set_level(TEST_GPIO_EXT_OUT_IO, 1); - vTaskDelay(100 / portTICK_PERIOD_MS); - TEST_ASSERT_FALSE(wake_up_result); -} -#endif // !WAKE_UP_IGNORE - -// this case need the resistance to pull up the voltage or pull down the voltage -// ignored because the voltage needs to be tested with multimeter -TEST_CASE("GPIO verify only the gpio with input ability can be set pull/down", "[gpio][ignore]") -{ - gpio_config_t output_io = init_io(TEST_GPIO_EXT_OUT_IO); - gpio_config_t input_io = init_io(TEST_GPIO_EXT_IN_IO); - gpio_config(&output_io); - input_io.mode = GPIO_MODE_INPUT; - gpio_config(&input_io); - - printf("pull up test!\n"); - // pull up test - gpio_set_direction(TEST_GPIO_EXT_OUT_IO, GPIO_MODE_OUTPUT); - TEST_ESP_OK(gpio_set_pull_mode(TEST_GPIO_EXT_OUT_IO, GPIO_PULLUP_ONLY)); - prompt_to_continue("mode: GPIO_MODE_OUTPUT"); - - gpio_set_direction(TEST_GPIO_EXT_OUT_IO, GPIO_MODE_OUTPUT_OD); - TEST_ESP_OK(gpio_set_pull_mode(TEST_GPIO_EXT_OUT_IO, GPIO_PULLUP_ONLY)); - - // open drain just can output low level - gpio_set_direction(TEST_GPIO_EXT_OUT_IO, GPIO_MODE_INPUT_OUTPUT_OD); - TEST_ESP_OK(gpio_set_pull_mode(TEST_GPIO_EXT_OUT_IO, GPIO_PULLUP_ONLY)); - prompt_to_continue("mode: GPIO_MODE_OUTPUT_OD"); - - gpio_set_direction(TEST_GPIO_EXT_OUT_IO, GPIO_MODE_INPUT_OUTPUT); - TEST_ESP_OK(gpio_set_pull_mode(TEST_GPIO_EXT_OUT_IO, GPIO_PULLUP_ONLY)); - prompt_to_continue("mode: GPIO_MODE_INPUT_OUTPUT"); - - gpio_set_direction(TEST_GPIO_EXT_OUT_IO, GPIO_MODE_INPUT); - TEST_ESP_OK(gpio_set_pull_mode(TEST_GPIO_EXT_OUT_IO, GPIO_PULLUP_ONLY)); - prompt_to_continue("mode: GPIO_MODE_INPUT"); - - // after pull up the level is high now - // pull down test - printf("pull down test!\n"); - - gpio_set_direction(TEST_GPIO_EXT_OUT_IO, GPIO_MODE_OUTPUT); - TEST_ESP_OK(gpio_set_pull_mode(TEST_GPIO_EXT_OUT_IO, GPIO_PULLDOWN_ONLY)); - prompt_to_continue("mode: GPIO_MODE_OUTPUT"); - - gpio_set_direction(TEST_GPIO_EXT_OUT_IO, GPIO_MODE_OUTPUT_OD); - TEST_ESP_OK(gpio_set_pull_mode(TEST_GPIO_EXT_OUT_IO, GPIO_PULLDOWN_ONLY)); - prompt_to_continue("mode: GPIO_MODE_OUTPUT_OD"); - - gpio_set_direction(TEST_GPIO_EXT_OUT_IO, GPIO_MODE_INPUT_OUTPUT_OD); - TEST_ESP_OK(gpio_set_pull_mode(TEST_GPIO_EXT_OUT_IO, GPIO_PULLDOWN_ONLY)); - prompt_to_continue("mode: GPIO_MODE_INPUT_OUTPUT_OD"); - - gpio_set_direction(TEST_GPIO_EXT_OUT_IO, GPIO_MODE_INPUT_OUTPUT); - TEST_ESP_OK(gpio_set_pull_mode(TEST_GPIO_EXT_OUT_IO, GPIO_PULLDOWN_ONLY)); - prompt_to_continue("mode: GPIO_MODE_INPUT_OUTPUT"); - - gpio_set_direction(TEST_GPIO_EXT_OUT_IO, GPIO_MODE_INPUT); - TEST_ESP_OK(gpio_set_pull_mode(TEST_GPIO_EXT_OUT_IO, GPIO_PULLDOWN_ONLY)); - prompt_to_continue("mode: GPIO_MODE_INPUT"); -} - -/** - * There are 5 situation for the GPIO drive capability: - * 1. GPIO drive weak capability test - * 2. GPIO drive stronger capability test - * 3. GPIO drive default capability test - * 4. GPIO drive default capability test2 - * 5. GPIO drive strongest capability test - * - * How to test: - * when testing, use the sliding resistor and a multimeter - * adjust the resistor from low to high, 0-10k - * watch the current change - * the current test result: - * weak capability: (0.32-10.1)mA - * stronger capability: (0.32-20.0)mA - * default capability: (0.33-39.8)mA - * default capability2: (0.33-39.9)mA - * strongest capability: (0.33-64.2)mA - * - * the data shows: - * weak capabilitygpio_num, gpio_get_level(param->gpio_num)); - param->isr_cnt++; -} - -/** The previous GPIO interrupt service routine polls the interrupt raw status register to find the GPIO that triggered the interrupt. - * But this will incorrectly handle the interrupt disabled GPIOs, because the raw interrupt status register can still be set when - * the trigger signal arrives, even if the interrupt is disabled. - * First on the core 0: - * 1. Configure the GPIO9 and GPIO10(ESP32, ESP32C3)/GPIO21(ESP32-S2) input_output mode. - * 2. Enable GPIO9 dual edge triggered interrupt, enable GPIO10(ESP32, ESP32C3)/GPIO21(ESP32-S2) falling edge triggered interrupt. - * 3. Trigger GPIO9 interrupt, than disable the GPIO18 interrupt, and than trigger GPIO18 again(This time will not respond to the interrupt). - * 4. Trigger GPIO10(ESP32, ESP32C3)/GPIO21(ESP32-S2) interrupt. - * If the bug is not fixed, you will see, in the step 4, the interrupt of GPIO9 will also respond. - */ -TEST_CASE("GPIO ISR service test", "[gpio][ignore]") -{ - gpio_isr_param_t io9_param = { - .gpio_num = TEST_IO_9, - .isr_cnt = 0, - }; - gpio_isr_param_t io10_param = { - .gpio_num = TEST_IO_10, - .isr_cnt = 0, - }; - gpio_config_t io_conf; - io_conf.intr_type = GPIO_INTR_DISABLE; - io_conf.mode = GPIO_MODE_INPUT_OUTPUT; - io_conf.pin_bit_mask = (1ULL << TEST_IO_9) | (1ULL << TEST_IO_10); - io_conf.pull_down_en = 0; - io_conf.pull_up_en = 1; - TEST_ESP_OK(gpio_config(&io_conf)); - TEST_ESP_OK(gpio_set_level(TEST_IO_9, 0)); - TEST_ESP_OK(gpio_set_level(TEST_IO_10, 0)); - TEST_ESP_OK(gpio_install_isr_service(0)); - TEST_ESP_OK(gpio_set_intr_type(TEST_IO_9, GPIO_INTR_ANYEDGE)); - TEST_ESP_OK(gpio_set_intr_type(TEST_IO_10, GPIO_INTR_NEGEDGE)); - TEST_ESP_OK(gpio_isr_handler_add(TEST_IO_9, gpio_isr_handler, (void *)&io9_param)); - TEST_ESP_OK(gpio_isr_handler_add(TEST_IO_10, gpio_isr_handler, (void *)&io10_param)); - printf("Triggering the interrupt of GPIO9\n"); - vTaskDelay(1000 / portTICK_PERIOD_MS); - //Rising edge - TEST_ESP_OK(gpio_set_level(TEST_IO_9, 1)); - printf("Disable the interrupt of GPIO9\n"); - vTaskDelay(100 / portTICK_PERIOD_MS); - //Disable GPIO9 interrupt, GPIO18 will not respond to the next falling edge interrupt. - TEST_ESP_OK(gpio_intr_disable(TEST_IO_9)); - vTaskDelay(100 / portTICK_PERIOD_MS); - //Falling edge - TEST_ESP_OK(gpio_set_level(TEST_IO_9, 0)); - - printf("Triggering the interrupt of GPIO10\n"); - vTaskDelay(100 / portTICK_PERIOD_MS); - TEST_ESP_OK(gpio_set_level(TEST_IO_10, 1)); - vTaskDelay(100 / portTICK_PERIOD_MS); - //Falling edge - TEST_ESP_OK(gpio_set_level(TEST_IO_10, 0)); - vTaskDelay(100 / portTICK_PERIOD_MS); - TEST_ESP_OK(gpio_isr_handler_remove(TEST_IO_9)); - TEST_ESP_OK(gpio_isr_handler_remove(TEST_IO_10)); - gpio_uninstall_isr_service(); - TEST_ASSERT((io9_param.isr_cnt == 1) && (io10_param.isr_cnt == 1)); -} - -#if SOC_USB_SERIAL_JTAG_SUPPORTED -TEST_CASE("GPIO input and output of USB pins test", "[gpio]") -{ - const int test_pins[] = {TEST_GPIO_USB_DP_IO, TEST_GPIO_USB_DM_IO}; - gpio_config_t io_conf = { - .intr_type = GPIO_INTR_DISABLE, - .mode = GPIO_MODE_INPUT_OUTPUT, - .pin_bit_mask = (BIT64(test_pins[0]) | BIT64(test_pins[1])), - .pull_down_en = 0, - .pull_up_en = 0, - }; - gpio_config(&io_conf); - - for (int i = 0; i < sizeof(test_pins) / sizeof(int); i++) { - int pin = test_pins[i]; - // test pin - gpio_set_level(pin, 0); - // tested voltage is around 0v - esp_rom_delay_us(10); - TEST_ASSERT_EQUAL_INT_MESSAGE(gpio_get_level(pin), 0, "get level error! the level should be low!"); - vTaskDelay(1000 / portTICK_PERIOD_MS); - gpio_set_level(pin, 1); - esp_rom_delay_us(10); - // tested voltage is around 3.3v - TEST_ASSERT_EQUAL_INT_MESSAGE(gpio_get_level(pin), 1, "get level error! the level should be high!"); - vTaskDelay(1000 / portTICK_PERIOD_MS); - gpio_set_level(pin, 0); - esp_rom_delay_us(10); - // tested voltage is around 0v - TEST_ASSERT_EQUAL_INT_MESSAGE(gpio_get_level(pin), 0, "get level error! the level should be low!"); - vTaskDelay(1000 / portTICK_PERIOD_MS); - gpio_set_level(pin, 1); - esp_rom_delay_us(10); - // tested voltage is around 3.3v - TEST_ASSERT_EQUAL_INT_MESSAGE(gpio_get_level(pin), 1, "get level error! the level should be high!"); - } -} -#endif //SOC_USB_SERIAL_JTAG_SUPPORTED diff --git a/components/driver/test_apps/gpio/CMakeLists.txt b/components/driver/test_apps/gpio/CMakeLists.txt new file mode 100644 index 0000000000..353f5f8732 --- /dev/null +++ b/components/driver/test_apps/gpio/CMakeLists.txt @@ -0,0 +1,18 @@ +# This is the project CMakeLists.txt file for the test subproject +cmake_minimum_required(VERSION 3.5) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(gpio_test) + +if(CONFIG_COMPILER_DUMP_RTL_FILES) + add_custom_target(check_test_app_sections ALL + COMMAND ${PYTHON} $ENV{IDF_PATH}/tools/ci/check_callgraph.py + --rtl-dir ${CMAKE_BINARY_DIR}/esp-idf/driver/ + --elf-file ${CMAKE_BINARY_DIR}/gpio_test.elf + find-refs + --from-sections=.iram0.text + --to-sections=.flash.text,.flash.rodata + --exit-code + DEPENDS ${elf} + ) +endif() diff --git a/components/driver/test_apps/gpio/main/CMakeLists.txt b/components/driver/test_apps/gpio/main/CMakeLists.txt new file mode 100644 index 0000000000..532eca65f6 --- /dev/null +++ b/components/driver/test_apps/gpio/main/CMakeLists.txt @@ -0,0 +1,18 @@ +set(srcs "test_app_main.c" + "test_gpio.c") + +set(include_tests "-u test_app_include_gpio") + +if(CONFIG_SOC_DEDICATED_GPIO_SUPPORTED) + list(APPEND srcs "test_dedicated_gpio.c") + list(APPEND include_tests "-u test_app_include_dedicated_gpio") +endif() + +if(CONFIG_SOC_SIGMADELTA_SUPPORTED) + list(APPEND srcs "test_sigmadelta.c") + list(APPEND include_tests "-u test_app_include_sigmadelta") +endif() + +idf_component_register(SRCS ${srcs}) + +target_link_libraries(${COMPONENT_LIB} INTERFACE ${include_tests}) diff --git a/components/driver/test_apps/gpio/main/test_app_main.c b/components/driver/test_apps/gpio/main/test_app_main.c new file mode 100644 index 0000000000..ad06ff0d02 --- /dev/null +++ b/components/driver/test_apps/gpio/main/test_app_main.c @@ -0,0 +1,41 @@ +/* + * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "unity.h" +#include "unity_test_runner.h" +#include "esp_heap_caps.h" + +// Some resources are lazy allocated in gpio/dedicated_gpio/delta_sigma driver, the threshold is left for that case +#define TEST_MEMORY_LEAK_THRESHOLD (-400) + +static size_t before_free_8bit; +static size_t before_free_32bit; + +static void check_leak(size_t before_free, size_t after_free, const char *type) +{ + ssize_t delta = after_free - before_free; + printf("MALLOC_CAP_%s: Before %u bytes free, After %u bytes free (delta %d)\n", type, before_free, after_free, delta); + TEST_ASSERT_MESSAGE(delta >= TEST_MEMORY_LEAK_THRESHOLD, "memory leak"); +} + +void setUp(void) +{ + before_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT); + before_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT); +} + +void tearDown(void) +{ + size_t after_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT); + size_t after_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT); + check_leak(before_free_8bit, after_free_8bit, "8BIT"); + check_leak(before_free_32bit, after_free_32bit, "32BIT"); +} + +void app_main(void) +{ + unity_run_menu(); +} diff --git a/components/driver/test/test_dedicated_gpio.c b/components/driver/test_apps/gpio/main/test_dedicated_gpio.c similarity index 92% rename from components/driver/test/test_dedicated_gpio.c rename to components/driver/test_apps/gpio/main/test_dedicated_gpio.c index 5992fc84a4..5f33da1923 100644 --- a/components/driver/test/test_dedicated_gpio.c +++ b/components/driver/test_apps/gpio/main/test_dedicated_gpio.c @@ -1,20 +1,25 @@ /* - * SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ + #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "freertos/semphr.h" #include "unity.h" +#include "unity_test_utils.h" #include "esp_rom_sys.h" #include "soc/soc_caps.h" #include "hal/cpu_ll.h" #include "driver/gpio.h" -#if SOC_DEDICATED_GPIO_SUPPORTED #include "driver/dedic_gpio.h" -TEST_CASE("Dedicated GPIO bundle install/uninstall", "[dedic_gpio]") +void test_app_include_dedicated_gpio(void) +{ +} + +TEST_CASE("Dedicated_GPIO_bundle_install/uninstall", "[dedic_gpio]") { const int test_gpios[SOC_DEDIC_GPIO_OUT_CHANNELS_NUM / 2] = {0}; const int test2_gpios[SOC_DEDIC_GPIO_OUT_CHANNELS_NUM / 2 + 1] = {0}; @@ -136,12 +141,13 @@ static void test_dedic_gpio_on_specific_core(void *args) TEST_ESP_OK(dedic_gpio_del_bundle(bundleB)); xSemaphoreGive(ctx->sem); - vTaskDelete(NULL); + vTaskSuspend(NULL); } -TEST_CASE("Dedicated GPIO run on multiple CPU core", "[dedic_gpio]") +TEST_CASE("Dedicated_GPIO_run_on_multiple_CPU_cores", "[dedic_gpio]") { SemaphoreHandle_t sem = xSemaphoreCreateCounting(SOC_CPU_CORES_NUM, 0); + TaskHandle_t task_handle[SOC_CPU_CORES_NUM]; for (int i = 0; i < SOC_CPU_CORES_NUM; i++) { int start_gpio = i * TEST_GPIO_GROUP_SIZE; @@ -149,13 +155,18 @@ TEST_CASE("Dedicated GPIO run on multiple CPU core", "[dedic_gpio]") .sem = sem, .gpios = {start_gpio, start_gpio + 1, start_gpio + 2, start_gpio + 3} }; - xTaskCreatePinnedToCore(test_dedic_gpio_on_specific_core, "dedic_gpio_test_tsk", 4096, &isr_ctx, 1, NULL, i); + xTaskCreatePinnedToCore(test_dedic_gpio_on_specific_core, "dedic_gpio_test_tsk", 4096, &isr_ctx, 1, + &task_handle[i], i); } for (int i = 0; i < SOC_CPU_CORES_NUM; i++) { xSemaphoreTake(sem, pdMS_TO_TICKS(1000)); } + vSemaphoreDelete(sem); + for (int i = 0; i < SOC_CPU_CORES_NUM; i++) { + unity_utils_task_delete(task_handle[i]); + } } IRAM_ATTR static void test_dedic_gpio_isr_callback(void *args) @@ -169,7 +180,7 @@ IRAM_ATTR static void test_dedic_gpio_isr_callback(void *args) } } -TEST_CASE("Dedicated GPIO interrupt and callback", "[dedic_gpio]") +TEST_CASE("Dedicated_GPIO_interrupt_and_callback", "[dedic_gpio]") { SemaphoreHandle_t sem = xSemaphoreCreateBinary(); @@ -214,5 +225,3 @@ TEST_CASE("Dedicated GPIO interrupt and callback", "[dedic_gpio]") TEST_ESP_OK(dedic_gpio_del_bundle(bundle)); vSemaphoreDelete(sem); } - -#endif // #if SOC_DEDICATED_GPIO_SUPPORTED diff --git a/components/driver/test_apps/gpio/main/test_gpio.c b/components/driver/test_apps/gpio/main/test_gpio.c new file mode 100644 index 0000000000..48e4a90403 --- /dev/null +++ b/components/driver/test_apps/gpio/main/test_gpio.c @@ -0,0 +1,843 @@ +/* + * SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * No specific runner required to run GPIO unit test. + * TEST_GPIO_EXT_OUT_IO and TEST_GPIO_EXT_IN_IO are connected internally through gpio matrix. + * + * If wants to externally connect TEST_GPIO_EXT_OUT_IO to TEST_GPIO_EXT_IN_IO (UT_T1_GPIO), please set + * TEST_GPIO_INTERNAL_ROUTING to 0 + */ + +#include +#include +#include "test_gpio.h" +#include "esp_system.h" +#include "esp_sleep.h" +#include "unity.h" +#include "unity_test_utils.h" +#include "driver/gpio.h" +#include "hal/gpio_ll.h" +#include "soc/gpio_periph.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/queue.h" +#include "freertos/semphr.h" +#include "sdkconfig.h" +#include "esp_rom_uart.h" +#include "esp_rom_sys.h" +#include "esp_spi_flash.h" +#include "esp_attr.h" + +void test_app_include_gpio(void) +{ +} + +// Enable internal routing for the output and input gpio pins +#define TEST_GPIO_INTERNAL_ROUTING 1 + +// If there is any input-only pin, enable input-only pin part of some tests. +#define SOC_HAS_INPUT_ONLY_PIN (CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2) + +static volatile int disable_intr_times = 0; // use this to calculate how many times it go into interrupt +static volatile int level_intr_times = 0; // use this to get how many times the level interrupt happened +static volatile int edge_intr_times = 0; // use this to get how many times the edge interrupt happened + +/** + * Do some initialization operation in this function + * @param num it is the destination GPIO wanted to be initialized + */ +static gpio_config_t test_init_io(gpio_num_t num) +{ + TEST_ASSERT(GPIO_IS_VALID_OUTPUT_GPIO(num)); + gpio_config_t io_conf = { + .intr_type = GPIO_INTR_DISABLE, + .mode = GPIO_MODE_OUTPUT, + .pin_bit_mask = (1ULL << num), + .pull_down_en = 0, + .pull_up_en = 0, + }; + return io_conf; +} + +/** + * Configure gpio pin as GPIO_MODE_INPUT_OUTPUT for all the interrupt related tests to avoid runner requirements + */ +static void test_gpio_config_mode_input_output(gpio_num_t num) +{ + gpio_config_t input_output_io = test_init_io(num); + input_output_io.mode = GPIO_MODE_INPUT_OUTPUT; + input_output_io.pull_up_en = 1; + TEST_ESP_OK(gpio_config(&input_output_io)); +} + +// test the basic configuration function with right parameters and error parameters +TEST_CASE("GPIO_config_parameters_test", "[gpio]") +{ + gpio_config_t io_config = { 0 }; + io_config.intr_type = GPIO_INTR_DISABLE; + + // test 0 + io_config.pin_bit_mask = 0; + TEST_ASSERT(gpio_config(&io_config) == ESP_ERR_INVALID_ARG); + + // test a non-exist pin + io_config.pin_bit_mask = ((uint64_t)1 << GPIO_NUM_MAX); + TEST_ASSERT(gpio_config(&io_config) == ESP_ERR_INVALID_ARG); + + // test an available pin + io_config.pin_bit_mask = ((uint64_t)1 << TEST_GPIO_EXT_OUT_IO); + TEST_ESP_OK(gpio_config(&io_config)); + + //This IO is just used for input, C3 and S3 doesn't have input only pin. +#if SOC_HAS_INPUT_ONLY_PIN + io_config.pin_bit_mask = ((uint64_t)1 << TEST_GPIO_INPUT_ONLY_PIN); + io_config.mode = GPIO_MODE_INPUT; + TEST_ESP_OK(gpio_config(&io_config)); + io_config.mode = GPIO_MODE_OUTPUT; + // The pin is input only, once set as output should log something + TEST_ASSERT(gpio_config(&io_config) == ESP_ERR_INVALID_ARG); +#endif // SOC_HAS_INPUT_ONLY_PIN +} + +// edge interrupt event +static void gpio_isr_edge_handler(void *arg) +{ + uint32_t gpio_num = (uint32_t) arg; + esp_rom_printf("GPIO[%d] intr on core %d, val: %d\n", gpio_num, cpu_hal_get_core_id(), gpio_get_level(gpio_num)); + edge_intr_times++; +} + +// level interrupt event with "gpio_intr_disable" +static void gpio_isr_level_handler(void *arg) +{ + uint32_t gpio_num = (uint32_t) arg; + disable_intr_times++; + esp_rom_printf("GPIO[%d] intr, val: %d, disable_intr_times = %d\n", gpio_num, gpio_get_level(gpio_num), disable_intr_times); + gpio_intr_disable(gpio_num); +} + +// level interrupt event with "gpio_set_level(!gpio_get_level)" +static void gpio_isr_level_handler2(void *arg) +{ + uint32_t gpio_num = (uint32_t) arg; + level_intr_times++; + esp_rom_printf("GPIO[%d] intr, val: %d, level_intr_times = %d\n", gpio_num, gpio_get_level(gpio_num), level_intr_times); + if (gpio_get_level(gpio_num)) { + gpio_set_level(gpio_num, 0); + } else { + gpio_set_level(gpio_num, 1); + } +} + +TEST_CASE("GPIO_rising_edge_interrupt_test", "[gpio]") +{ + edge_intr_times = 0; // set it as 0 prepare to test + test_gpio_config_mode_input_output(TEST_GPIO_INPUT_OUTPUT_IO1); + TEST_ESP_OK(gpio_set_level(TEST_GPIO_INPUT_OUTPUT_IO1, 0)); + + // Rising edge intr + TEST_ESP_OK(gpio_set_intr_type(TEST_GPIO_INPUT_OUTPUT_IO1, GPIO_INTR_POSEDGE)); + TEST_ESP_OK(gpio_install_isr_service(0)); + gpio_isr_handler_add(TEST_GPIO_INPUT_OUTPUT_IO1, gpio_isr_edge_handler, (void *) TEST_GPIO_INPUT_OUTPUT_IO1); + TEST_ESP_OK(gpio_set_level(TEST_GPIO_INPUT_OUTPUT_IO1, 1)); + vTaskDelay(100 / portTICK_PERIOD_MS); + TEST_ASSERT_EQUAL_INT(1, edge_intr_times); + gpio_isr_handler_remove(TEST_GPIO_INPUT_OUTPUT_IO1); + gpio_uninstall_isr_service(); +} + +TEST_CASE("GPIO_falling_edge_interrupt_test", "[gpio]") +{ + edge_intr_times = 0; + test_gpio_config_mode_input_output(TEST_GPIO_INPUT_OUTPUT_IO1); + TEST_ESP_OK(gpio_set_level(TEST_GPIO_INPUT_OUTPUT_IO1, 1)); + + gpio_set_intr_type(TEST_GPIO_INPUT_OUTPUT_IO1, GPIO_INTR_NEGEDGE); + gpio_install_isr_service(0); + gpio_isr_handler_add(TEST_GPIO_INPUT_OUTPUT_IO1, gpio_isr_edge_handler, (void *) TEST_GPIO_INPUT_OUTPUT_IO1); + gpio_set_level(TEST_GPIO_INPUT_OUTPUT_IO1, 0); + vTaskDelay(100 / portTICK_PERIOD_MS); + TEST_ASSERT_EQUAL_INT(1, edge_intr_times); + gpio_isr_handler_remove(TEST_GPIO_INPUT_OUTPUT_IO1); + gpio_uninstall_isr_service(); +} + +TEST_CASE("GPIO_both_rising_and_falling_edge_interrupt_test", "[gpio]") +{ + edge_intr_times = 0; + test_gpio_config_mode_input_output(TEST_GPIO_INPUT_OUTPUT_IO1); + TEST_ESP_OK(gpio_set_level(TEST_GPIO_INPUT_OUTPUT_IO1, 0)); + int level = 0; + + gpio_set_intr_type(TEST_GPIO_INPUT_OUTPUT_IO1, GPIO_INTR_ANYEDGE); + gpio_install_isr_service(0); + gpio_isr_handler_add(TEST_GPIO_INPUT_OUTPUT_IO1, gpio_isr_edge_handler, (void *) TEST_GPIO_INPUT_OUTPUT_IO1); + // For rising edge in GPIO_INTR_ANYEDGE + while (1) { + level = level + 1; + gpio_set_level(TEST_GPIO_INPUT_OUTPUT_IO1, level * 0.2); + if (level > 10) { + break; + } + vTaskDelay(100 / portTICK_PERIOD_MS); + } + vTaskDelay(100 / portTICK_PERIOD_MS); + // For falling edge in GPIO_INTR_ANYEDGE + while (1) { + level = level - 1; + gpio_set_level(TEST_GPIO_INPUT_OUTPUT_IO1, level / 5); + if (level < 0) { + break; + } + vTaskDelay(100 / portTICK_PERIOD_MS); + } + vTaskDelay(100 / portTICK_PERIOD_MS); + TEST_ASSERT_EQUAL_INT(2, edge_intr_times); + gpio_isr_handler_remove(TEST_GPIO_INPUT_OUTPUT_IO1); + gpio_uninstall_isr_service(); +} + +TEST_CASE("GPIO_input_high_level_trigger_cut_the_interrupt_source_exit_interrupt_test", "[gpio]") +{ + level_intr_times = 0; + test_gpio_config_mode_input_output(TEST_GPIO_INPUT_OUTPUT_IO1); + TEST_ESP_OK(gpio_set_level(TEST_GPIO_INPUT_OUTPUT_IO1, 0)); + + gpio_set_intr_type(TEST_GPIO_INPUT_OUTPUT_IO1, GPIO_INTR_HIGH_LEVEL); + gpio_install_isr_service(0); + gpio_isr_handler_add(TEST_GPIO_INPUT_OUTPUT_IO1, gpio_isr_level_handler2, (void *) TEST_GPIO_INPUT_OUTPUT_IO1); + gpio_set_level(TEST_GPIO_INPUT_OUTPUT_IO1, 1); + vTaskDelay(100 / portTICK_PERIOD_MS); + TEST_ASSERT_EQUAL_INT_MESSAGE(1, level_intr_times, "go into high-level interrupt more than once with cut interrupt source way"); + gpio_isr_handler_remove(TEST_GPIO_INPUT_OUTPUT_IO1); + gpio_uninstall_isr_service(); +} + +TEST_CASE("GPIO_low_level_interrupt_test", "[gpio]") +{ + disable_intr_times = 0; + test_gpio_config_mode_input_output(TEST_GPIO_INPUT_OUTPUT_IO1); + TEST_ESP_OK(gpio_set_level(TEST_GPIO_INPUT_OUTPUT_IO1, 1)); + + gpio_set_intr_type(TEST_GPIO_INPUT_OUTPUT_IO1, GPIO_INTR_LOW_LEVEL); + gpio_install_isr_service(0); + gpio_isr_handler_add(TEST_GPIO_INPUT_OUTPUT_IO1, gpio_isr_level_handler, (void *) TEST_GPIO_INPUT_OUTPUT_IO1); + gpio_set_level(TEST_GPIO_INPUT_OUTPUT_IO1, 0); + printf("get level:%d\n", gpio_get_level(TEST_GPIO_INPUT_OUTPUT_IO1)); + vTaskDelay(100 / portTICK_PERIOD_MS); + TEST_ASSERT_EQUAL_INT_MESSAGE(1, disable_intr_times, "go into low-level interrupt more than once with disable way"); + gpio_isr_handler_remove(TEST_GPIO_INPUT_OUTPUT_IO1); + gpio_uninstall_isr_service(); +} + +TEST_CASE("GPIO_multi-level_trigger_cut_the_interrupt_source_exit_interrupt_test", "[gpio]") +{ + level_intr_times = 0; + test_gpio_config_mode_input_output(TEST_GPIO_INPUT_OUTPUT_IO1); + TEST_ESP_OK(gpio_set_level(TEST_GPIO_INPUT_OUTPUT_IO1, 0)); + + gpio_set_intr_type(TEST_GPIO_INPUT_OUTPUT_IO1, GPIO_INTR_HIGH_LEVEL); + gpio_install_isr_service(0); + gpio_isr_handler_add(TEST_GPIO_INPUT_OUTPUT_IO1, gpio_isr_level_handler2, (void *) TEST_GPIO_INPUT_OUTPUT_IO1); + gpio_set_level(TEST_GPIO_INPUT_OUTPUT_IO1, 1); + vTaskDelay(100 / portTICK_PERIOD_MS); + TEST_ASSERT_EQUAL_INT_MESSAGE(1, level_intr_times, "go into high-level interrupt more than once with cut interrupt source way"); + gpio_set_level(TEST_GPIO_INPUT_OUTPUT_IO1, 1); + vTaskDelay(200 / portTICK_PERIOD_MS); + TEST_ASSERT_EQUAL_INT_MESSAGE(2, level_intr_times, "go into high-level interrupt more than once with cut interrupt source way"); + gpio_isr_handler_remove(TEST_GPIO_INPUT_OUTPUT_IO1); + gpio_uninstall_isr_service(); +} + +TEST_CASE("GPIO_enable_and_disable_interrupt_test", "[gpio]") +{ + disable_intr_times = 0; + test_gpio_config_mode_input_output(TEST_GPIO_INPUT_OUTPUT_IO1); + TEST_ESP_OK(gpio_set_level(TEST_GPIO_INPUT_OUTPUT_IO1, 0)); + + TEST_ESP_OK(gpio_set_intr_type(TEST_GPIO_INPUT_OUTPUT_IO1, GPIO_INTR_HIGH_LEVEL)); + TEST_ESP_OK(gpio_install_isr_service(0)); + TEST_ESP_OK(gpio_isr_handler_add(TEST_GPIO_INPUT_OUTPUT_IO1, gpio_isr_level_handler, (void *) TEST_GPIO_INPUT_OUTPUT_IO1)); + TEST_ESP_OK(gpio_set_level(TEST_GPIO_INPUT_OUTPUT_IO1, 1)); + TEST_ESP_OK(gpio_isr_handler_remove(TEST_GPIO_INPUT_OUTPUT_IO1)); + TEST_ESP_OK(gpio_set_level(TEST_GPIO_INPUT_OUTPUT_IO1, 0)); + vTaskDelay(100 / portTICK_PERIOD_MS); + TEST_ASSERT_EQUAL_INT_MESSAGE(1, disable_intr_times, "go into high-level interrupt more than once with disable way"); + + // Interrupt disabled now + TEST_ESP_OK(gpio_intr_disable(TEST_GPIO_INPUT_OUTPUT_IO1)); + TEST_ESP_OK(gpio_set_level(TEST_GPIO_INPUT_OUTPUT_IO1, 1)); + vTaskDelay(100 / portTICK_PERIOD_MS); + TEST_ASSERT_EQUAL_INT_MESSAGE(1, disable_intr_times, "disable interrupt does not work, still go into interrupt!"); + + // Uninstall interrupt service + gpio_uninstall_isr_service(); + TEST_ASSERT(gpio_isr_handler_add(TEST_GPIO_INPUT_OUTPUT_IO1, gpio_isr_level_handler, (void *) TEST_GPIO_INPUT_OUTPUT_IO1) == ESP_ERR_INVALID_STATE); + TEST_ASSERT(gpio_isr_handler_remove(TEST_GPIO_INPUT_OUTPUT_IO1) == ESP_ERR_INVALID_STATE); +} + +TEST_CASE("GPIO_repeatedly_call_service_and_isr_has_no_memory_leak_test", "[gpio][timeout=90]") +{ + test_gpio_config_mode_input_output(TEST_GPIO_INPUT_OUTPUT_IO1); + TEST_ESP_OK(gpio_set_level(TEST_GPIO_INPUT_OUTPUT_IO1, 0)); + + // Rising edge intr + uint32_t size = esp_get_free_heap_size(); + for (int i = 0; i < 1000; i++) { + TEST_ESP_OK(gpio_set_intr_type(TEST_GPIO_INPUT_OUTPUT_IO1, GPIO_INTR_POSEDGE)); + TEST_ESP_OK(gpio_install_isr_service(0)); + TEST_ESP_OK(gpio_isr_handler_add(TEST_GPIO_INPUT_OUTPUT_IO1, gpio_isr_edge_handler, (void *) TEST_GPIO_INPUT_OUTPUT_IO1)); + gpio_set_level(TEST_GPIO_INPUT_OUTPUT_IO1, 1); + TEST_ESP_OK(gpio_isr_handler_remove(TEST_GPIO_INPUT_OUTPUT_IO1)); + gpio_set_level(TEST_GPIO_INPUT_OUTPUT_IO1, 0); + gpio_uninstall_isr_service(); + } + TEST_ASSERT_INT32_WITHIN(100, size, esp_get_free_heap_size()); +} + +typedef struct { + int gpio_num; + int isr_cnt; +} gpio_isr_param_t; + +static void gpio_isr_per_pin_handler(void *arg) +{ + gpio_isr_param_t *param = (gpio_isr_param_t *)arg; + esp_rom_printf("GPIO[%d] intr, val: %d\n", param->gpio_num, gpio_get_level(param->gpio_num)); + param->isr_cnt++; +} + +/** The old GPIO interrupt service routine used to poll the interrupt raw status register to find the GPIO that + * triggered the interrupt. But this will incorrectly handle the interrupt disabled GPIOs, because the raw interrupt + * status register can still be set when the trigger signal arrives, even if the interrupt is disabled. + * + * Do the following steps: + * 1. Configure TEST_GPIO_INPUT_OUTPUT_IO1 and TEST_GPIO_INPUT_OUTPUT_IO2 input_output mode. + * 2. Enable TEST_GPIO_INPUT_OUTPUT_IO1 dual edge triggered interrupt, enable TEST_GPIO_INPUT_OUTPUT_IO2 falling edge triggered interrupt. + * 3. Trigger TEST_GPIO_INPUT_OUTPUT_IO1 interrupt, then disable TEST_GPIO_INPUT_OUTPUT_IO1 interrupt, and then trigger TEST_GPIO_INPUT_OUTPUT_IO1 interrupt again (This time will not respond to the interrupt). + * 4. Trigger TEST_GPIO_INPUT_OUTPUT_IO2 interrupt. + * + * If the bug is not fixed, you will see, in the step 4, the interrupt of TEST_GPIO_INPUT_OUTPUT_IO1 will also respond. + */ +TEST_CASE("GPIO_isr_responses_to_correct_gpios_test", "[gpio]") +{ + gpio_isr_param_t io1_param = { + .gpio_num = TEST_GPIO_INPUT_OUTPUT_IO1, + .isr_cnt = 0, + }; + gpio_isr_param_t io2_param = { + .gpio_num = TEST_GPIO_INPUT_OUTPUT_IO2, + .isr_cnt = 0, + }; + test_gpio_config_mode_input_output(TEST_GPIO_INPUT_OUTPUT_IO1); + test_gpio_config_mode_input_output(TEST_GPIO_INPUT_OUTPUT_IO2); + TEST_ESP_OK(gpio_set_level(TEST_GPIO_INPUT_OUTPUT_IO1, 0)); + TEST_ESP_OK(gpio_set_level(TEST_GPIO_INPUT_OUTPUT_IO2, 0)); + TEST_ESP_OK(gpio_install_isr_service(0)); + TEST_ESP_OK(gpio_set_intr_type(TEST_GPIO_INPUT_OUTPUT_IO1, GPIO_INTR_ANYEDGE)); + TEST_ESP_OK(gpio_set_intr_type(TEST_GPIO_INPUT_OUTPUT_IO2, GPIO_INTR_NEGEDGE)); + TEST_ESP_OK(gpio_isr_handler_add(TEST_GPIO_INPUT_OUTPUT_IO1, gpio_isr_per_pin_handler, (void *) &io1_param)); + TEST_ESP_OK(gpio_isr_handler_add(TEST_GPIO_INPUT_OUTPUT_IO2, gpio_isr_per_pin_handler, (void *) &io2_param)); + + printf("Triggering the interrupt of GPIO%d\n", TEST_GPIO_INPUT_OUTPUT_IO1); + // Rising edge + TEST_ESP_OK(gpio_set_level(TEST_GPIO_INPUT_OUTPUT_IO1, 1)); + vTaskDelay(100 / portTICK_PERIOD_MS); + printf("Disable the interrupt of GPIO%d\n", TEST_GPIO_INPUT_OUTPUT_IO1); + // Disable TEST_GPIO_INPUT_OUTPUT_IO1 interrupt, TEST_GPIO_INPUT_OUTPUT_IO1 will not respond to the next falling edge interrupt + TEST_ESP_OK(gpio_intr_disable(TEST_GPIO_INPUT_OUTPUT_IO1)); + vTaskDelay(100 / portTICK_PERIOD_MS); + // Falling edge + TEST_ESP_OK(gpio_set_level(TEST_GPIO_INPUT_OUTPUT_IO1, 0)); + + printf("Triggering the interrupt of GPIO%d\n", TEST_GPIO_INPUT_OUTPUT_IO2); + TEST_ESP_OK(gpio_set_level(TEST_GPIO_INPUT_OUTPUT_IO2, 1)); + vTaskDelay(100 / portTICK_PERIOD_MS); + // Falling edge + TEST_ESP_OK(gpio_set_level(TEST_GPIO_INPUT_OUTPUT_IO2, 0)); + vTaskDelay(100 / portTICK_PERIOD_MS); + + TEST_ESP_OK(gpio_isr_handler_remove(TEST_GPIO_INPUT_OUTPUT_IO1)); + TEST_ESP_OK(gpio_isr_handler_remove(TEST_GPIO_INPUT_OUTPUT_IO2)); + gpio_uninstall_isr_service(); + TEST_ASSERT((io1_param.isr_cnt == 1) && (io2_param.isr_cnt == 1)); +} + +#if !CONFIG_FREERTOS_UNICORE +#include "esp_ipc.h" + +static void install_isr_service_task(void *arg) +{ + uint32_t gpio_num = (uint32_t) arg; + // Rising edge intr + TEST_ESP_OK(gpio_set_intr_type(gpio_num, GPIO_INTR_POSEDGE)); + TEST_ESP_OK(gpio_install_isr_service(0)); + gpio_isr_handler_add(gpio_num, gpio_isr_edge_handler, (void *) gpio_num); + vTaskSuspend(NULL); +} + +TEST_CASE("GPIO_interrupt_on_other_CPUs_test", "[gpio]") +{ + TaskHandle_t gpio_task_handle; + test_gpio_config_mode_input_output(TEST_GPIO_INPUT_OUTPUT_IO1); + + for (int cpu_num = 1; cpu_num < portNUM_PROCESSORS; ++cpu_num) { + // We assume unit-test task is running on core 0, so we install gpio interrupt on other cores + edge_intr_times = 0; + TEST_ESP_OK(gpio_set_level(TEST_GPIO_INPUT_OUTPUT_IO1, 0)); + xTaskCreatePinnedToCore(install_isr_service_task, "install_isr_service_task", 2048, (void *) TEST_GPIO_INPUT_OUTPUT_IO1, 1, &gpio_task_handle, cpu_num); + + vTaskDelay(200 / portTICK_PERIOD_MS); + TEST_ESP_OK(gpio_set_level(TEST_GPIO_INPUT_OUTPUT_IO1, 1)); + vTaskDelay(100 / portTICK_PERIOD_MS); + TEST_ASSERT_EQUAL_INT(1, edge_intr_times); + gpio_isr_handler_remove(TEST_GPIO_INPUT_OUTPUT_IO1); + gpio_uninstall_isr_service(); + unity_utils_task_delete(gpio_task_handle); + } +} + +static void gpio_intr_enable_task(void *param) +{ + int gpio_num = (int) param; + TEST_ESP_OK(gpio_intr_enable(gpio_num)); +} + +/** Test the GPIO Interrupt Enable API with dual core enabled. The GPIO ISR service routine is registered on one core. + * When the GPIO interrupt on another core is enabled, the GPIO interrupt will be lost. + * Note. This is only a problem for ESP32. On ESP32S3, interrupt enable is effective to both cores, therefore, no matter + * which core the interrupt service is installed on, the GPIO interrupt won't be lost. + * + * First on the core 0, do the following steps: + * 1. Configure TEST_GPIO_INPUT_OUTPUT_IO1 input_output mode, and enable the falling edge interrupt mode. + * 2. Trigger TEST_GPIO_INPUT_OUTPUT_IO1 interrupt and check if the interrupt responds correctly. + * 3. Disable TEST_GPIO_INPUT_OUTPUT_IO1 interrupt + * Then on the core 1, do the following steps: + * 1. Enable TEST_GPIO_INPUT_OUTPUT_IO1 interrupt again. + * 2. Trigger TEST_GPIO_INPUT_OUTPUT_IO1 interrupt and check if the interrupt responds correctly. + */ +TEST_CASE("GPIO_crosscore_interrupt_test", "[gpio]") +{ + edge_intr_times = 0; + test_gpio_config_mode_input_output(TEST_GPIO_INPUT_OUTPUT_IO1); + TEST_ESP_OK(gpio_set_level(TEST_GPIO_INPUT_OUTPUT_IO1, 0)); + + TEST_ESP_OK(gpio_set_intr_type(TEST_GPIO_INPUT_OUTPUT_IO1, GPIO_INTR_NEGEDGE)); + // GPIO interrupt service installed on core 0 + TEST_ESP_OK(gpio_install_isr_service(0)); + TEST_ESP_OK(gpio_isr_handler_add(TEST_GPIO_INPUT_OUTPUT_IO1, gpio_isr_edge_handler, (void *) TEST_GPIO_INPUT_OUTPUT_IO1)); + vTaskDelay(1000 / portTICK_PERIOD_MS); + TEST_ESP_OK(gpio_set_level(TEST_GPIO_INPUT_OUTPUT_IO1, 1)); + vTaskDelay(100 / portTICK_PERIOD_MS); + TEST_ESP_OK(gpio_set_level(TEST_GPIO_INPUT_OUTPUT_IO1, 0)); + vTaskDelay(100 / portTICK_PERIOD_MS); + TEST_ESP_OK(gpio_intr_disable(TEST_GPIO_INPUT_OUTPUT_IO1)); + TEST_ASSERT(edge_intr_times == 1); + // Here, interrupt is enabling from core 1, but since the isr is installed on core 0, core 0 interrupt enable bit + // will still be set instead of core 1 interrupt enable bit + esp_ipc_call_blocking((xPortGetCoreID() == 0), gpio_intr_enable_task, (void *) TEST_GPIO_INPUT_OUTPUT_IO1); + vTaskDelay(1000 / portTICK_PERIOD_MS); + TEST_ESP_OK(gpio_set_level(TEST_GPIO_INPUT_OUTPUT_IO1, 1)); + vTaskDelay(100 / portTICK_PERIOD_MS); + TEST_ESP_OK(gpio_set_level(TEST_GPIO_INPUT_OUTPUT_IO1, 0)); + vTaskDelay(100 / portTICK_PERIOD_MS); + TEST_ESP_OK(gpio_intr_disable(TEST_GPIO_INPUT_OUTPUT_IO1)); + TEST_ESP_OK(gpio_isr_handler_remove(TEST_GPIO_INPUT_OUTPUT_IO1)); + gpio_uninstall_isr_service(); + TEST_ASSERT(edge_intr_times == 2); +} +#endif //!CONFIG_FREERTOS_UNICORE + +#if CONFIG_GPIO_CTRL_FUNC_IN_IRAM +static volatile DRAM_ATTR bool isr_triggered = false; + +static void IRAM_ATTR gpio_isr_level_iram_handler(void *arg) +{ + uint32_t gpio_num = (uint32_t) arg; + isr_triggered = true; + gpio_intr_disable(gpio_num); +} + +static void IRAM_ATTR gpio_wait_intr_done_task(void *arg) +{ + SemaphoreHandle_t sem = (SemaphoreHandle_t) arg; + spi_flash_guard_get()->start(); // Disables flash cache + // Since interrupt service is installed on core 0, we enable the gpio intr on core 0 + gpio_ll_intr_enable_on_core(&GPIO, 0, TEST_GPIO_INPUT_OUTPUT_IO1); + // Wait until interrupt triggered + while (!isr_triggered) { + ; + } + spi_flash_guard_get()->end(); // Re-enables flash cache + xSemaphoreGive(sem); + vTaskSuspend(NULL); +} + +TEST_CASE("GPIO_iram_interrupt_safe_test", "[gpio]") +{ + SemaphoreHandle_t done_sem = xSemaphoreCreateBinary(); + TaskHandle_t task_handle; + TEST_ASSERT_NOT_NULL(done_sem); + test_gpio_config_mode_input_output(TEST_GPIO_INPUT_OUTPUT_IO1); + TEST_ESP_OK(gpio_set_level(TEST_GPIO_INPUT_OUTPUT_IO1, 0)); + + TEST_ESP_OK(gpio_set_intr_type(TEST_GPIO_INPUT_OUTPUT_IO1, GPIO_INTR_HIGH_LEVEL)); + // We assume unit-test task is running on core 0, so interrupt service is installed on core 0 + TEST_ESP_OK(gpio_install_isr_service(ESP_INTR_FLAG_IRAM)); + TEST_ESP_OK(gpio_isr_handler_add(TEST_GPIO_INPUT_OUTPUT_IO1, gpio_isr_level_iram_handler, (void *) TEST_GPIO_INPUT_OUTPUT_IO1)); + // Disable intr and set pin level high, such that once the intr is re-enabled, it will trigger isr + TEST_ESP_OK(gpio_intr_disable(TEST_GPIO_INPUT_OUTPUT_IO1)); + TEST_ESP_OK(gpio_set_level(TEST_GPIO_INPUT_OUTPUT_IO1, 1)); + xTaskCreate(gpio_wait_intr_done_task, "gpio_wait_intr_done_task", 2048, done_sem, 1, &task_handle); + + xSemaphoreTake(done_sem, portMAX_DELAY); + gpio_isr_handler_remove(TEST_GPIO_INPUT_OUTPUT_IO1); + gpio_uninstall_isr_service(); + vSemaphoreDelete(done_sem); + unity_utils_task_delete(task_handle); +} +#endif + +#if TEST_GPIO_INTERNAL_ROUTING +// Inter-connect input pin and output pin through an internal signal +static void gpio_interconnect_input_output_pin(uint32_t input_pin, uint32_t output_pin, uint32_t signal_idx) +{ + // signal256 -> output pin -> signal_idx -> input_pin + // Set output pin IE to be able to connect to the signal + PIN_INPUT_ENABLE(GPIO_PIN_MUX_REG[output_pin]); + esp_rom_gpio_connect_in_signal(output_pin, signal_idx, 0); + // Input pin OE to be able to connect to the signal is done by the esp_rom_gpio_connect_out_signal function + esp_rom_gpio_connect_out_signal(input_pin, signal_idx, 0, 0); +} +#endif + +TEST_CASE("GPIO_set_output_level_get_input_level_test", "[gpio]") +{ + gpio_config_t output_io = test_init_io(TEST_GPIO_EXT_OUT_IO); + gpio_config(&output_io); + gpio_config_t input_io = test_init_io(TEST_GPIO_EXT_IN_IO); + input_io.mode = GPIO_MODE_INPUT; + gpio_config(&input_io); + +#if TEST_GPIO_INTERNAL_ROUTING + gpio_interconnect_input_output_pin(TEST_GPIO_EXT_IN_IO, TEST_GPIO_EXT_OUT_IO, TEST_GPIO_SIGNAL_IDX); +#endif + + gpio_set_level(TEST_GPIO_EXT_OUT_IO, 0); + vTaskDelay(100 / portTICK_PERIOD_MS); + // tested voltage is around 0v + TEST_ASSERT_EQUAL_INT_MESSAGE(0, gpio_get_level(TEST_GPIO_EXT_IN_IO), "get level error! the level should be low!"); + gpio_set_level(TEST_GPIO_EXT_OUT_IO, 1); + vTaskDelay(100 / portTICK_PERIOD_MS); + // tested voltage is around 3.3v + TEST_ASSERT_EQUAL_INT_MESSAGE(1, gpio_get_level(TEST_GPIO_EXT_IN_IO), "get level error! the level should be high!"); +} + +// This test routes constant-high/low signal to pins, another way is to directly connect TEST_GPIO_EXT_IN_IO to +// 3.3v or GND pin +TEST_CASE("GPIO_get_level_from_fixed_voltage_test", "[gpio]") +{ +#if !TEST_GPIO_INTERNAL_ROUTING + // If TEST_GPIO_EXT_OUT_IO is connected to TEST_GPIO_EXT_IN_IO, prevent being affected + gpio_set_direction(TEST_GPIO_EXT_OUT_IO, GPIO_MODE_DISABLE); +#endif + gpio_config_t input_io = test_init_io(TEST_GPIO_EXT_IN_IO); + input_io.mode = GPIO_MODE_INPUT; + gpio_config(&input_io); + esp_rom_gpio_connect_out_signal(TEST_GPIO_EXT_IN_IO, TEST_GPIO_SIGNAL_IDX, 0, 0); + + // Connect TEST_GPIO_EXT_IN_IO to a constant-high signal (to simulate connection to 3.3v) + esp_rom_gpio_connect_in_signal(GPIO_MATRIX_CONST_ONE_INPUT, TEST_GPIO_SIGNAL_IDX, 0); + int level1 = gpio_get_level(TEST_GPIO_EXT_IN_IO); + printf("TEST_GPIO_EXT_IN_IO(GPIO%d)'s level is: %d\n", TEST_GPIO_EXT_IN_IO, level1); + TEST_ASSERT_EQUAL_INT_MESSAGE(1, level1, "get level error! the level should be high!"); + + // Connect TEST_GPIO_EXT_IN_IO to a constant-low signal (to simulate connection to GND) + esp_rom_gpio_connect_in_signal(GPIO_MATRIX_CONST_ZERO_INPUT, TEST_GPIO_SIGNAL_IDX, 0); + int level2 = gpio_get_level(TEST_GPIO_EXT_IN_IO); + printf("TEST_GPIO_EXT_IN_IO(GPIO%d)'s level is: %d\n", TEST_GPIO_EXT_IN_IO, level2); + TEST_ASSERT_EQUAL_INT_MESSAGE(0, level2, "get level error! the level should be low!"); +} + +TEST_CASE("GPIO_io_pull_up/down_function", "[gpio]") +{ + // First, ensure that the output IO will not affect the level + gpio_config_t io_conf = test_init_io(TEST_GPIO_EXT_OUT_IO); + gpio_config(&io_conf); + gpio_set_direction(TEST_GPIO_EXT_OUT_IO, GPIO_MODE_INPUT); + io_conf = test_init_io(TEST_GPIO_EXT_IN_IO); + gpio_config(&io_conf); + gpio_set_direction(TEST_GPIO_EXT_IN_IO, GPIO_MODE_INPUT); + TEST_ESP_OK(gpio_pullup_en(TEST_GPIO_EXT_IN_IO)); // pull up first + vTaskDelay(100 / portTICK_PERIOD_MS); + TEST_ASSERT_EQUAL_INT_MESSAGE(1, gpio_get_level(TEST_GPIO_EXT_IN_IO), "gpio_pullup_en error, it can't pull up"); + TEST_ESP_OK(gpio_pulldown_dis(TEST_GPIO_EXT_IN_IO)); //can't be pull down + vTaskDelay(100 / portTICK_PERIOD_MS); + TEST_ASSERT_EQUAL_INT_MESSAGE(1, gpio_get_level(TEST_GPIO_EXT_IN_IO), "gpio_pulldown_dis error, it can pull down"); + TEST_ESP_OK(gpio_pulldown_en(TEST_GPIO_EXT_IN_IO)); // can be pull down now + vTaskDelay(100 / portTICK_PERIOD_MS); + TEST_ASSERT_EQUAL_INT_MESSAGE(0, gpio_get_level(TEST_GPIO_EXT_IN_IO), "gpio_pulldown_en error, it can't pull down"); + TEST_ESP_OK(gpio_pullup_dis(TEST_GPIO_EXT_IN_IO)); // can't be pull up + vTaskDelay(100 / portTICK_PERIOD_MS); + TEST_ASSERT_EQUAL_INT_MESSAGE(0, gpio_get_level(TEST_GPIO_EXT_IN_IO), "gpio_pullup_dis error, it can pull up"); +} + +// This test case tests whether gpio_set_level() outputs correctly with all gpio modes (gpio_mode_t) +TEST_CASE("GPIO_mode_test", "[gpio]") +{ + gpio_config_t output_io = test_init_io(TEST_GPIO_EXT_OUT_IO); + gpio_config_t input_io = test_init_io(TEST_GPIO_EXT_IN_IO); + gpio_config(&output_io); + gpio_config(&input_io); + int level = gpio_get_level(TEST_GPIO_EXT_IN_IO); + + // Disable mode + gpio_set_direction(TEST_GPIO_EXT_OUT_IO, GPIO_MODE_DISABLE); + gpio_set_direction(TEST_GPIO_EXT_IN_IO, GPIO_MODE_OUTPUT); +#if TEST_GPIO_INTERNAL_ROUTING + gpio_interconnect_input_output_pin(TEST_GPIO_EXT_IN_IO, TEST_GPIO_EXT_OUT_IO, TEST_GPIO_SIGNAL_IDX); +#endif + gpio_set_level(TEST_GPIO_EXT_OUT_IO, !level); + TEST_ASSERT_EQUAL_INT_MESSAGE(level, gpio_get_level(TEST_GPIO_EXT_IN_IO), "direction GPIO_MODE_DISABLE set error, it can output"); + + // Output mode + gpio_set_direction(TEST_GPIO_EXT_OUT_IO, GPIO_MODE_OUTPUT); + gpio_set_direction(TEST_GPIO_EXT_IN_IO, GPIO_MODE_INPUT); +#if TEST_GPIO_INTERNAL_ROUTING + gpio_interconnect_input_output_pin(TEST_GPIO_EXT_IN_IO, TEST_GPIO_EXT_OUT_IO, TEST_GPIO_SIGNAL_IDX); +#endif + gpio_set_level(TEST_GPIO_EXT_OUT_IO, 1); + TEST_ASSERT_EQUAL_INT_MESSAGE(1, gpio_get_level(TEST_GPIO_EXT_IN_IO), "direction GPIO_MODE_OUTPUT set error, it can't output"); + gpio_set_level(TEST_GPIO_EXT_OUT_IO, 0); + TEST_ASSERT_EQUAL_INT_MESSAGE(0, gpio_get_level(TEST_GPIO_EXT_IN_IO), "direction GPIO_MODE_OUTPUT set error, it can't output"); + + // Open drain mode(output), can just output low level + gpio_set_direction(TEST_GPIO_EXT_OUT_IO, GPIO_MODE_OUTPUT_OD); + gpio_set_direction(TEST_GPIO_EXT_IN_IO, GPIO_MODE_INPUT); +#if TEST_GPIO_INTERNAL_ROUTING + gpio_interconnect_input_output_pin(TEST_GPIO_EXT_IN_IO, TEST_GPIO_EXT_OUT_IO, TEST_GPIO_SIGNAL_IDX); +#endif + // Outputs high level: w/ pull up, then must read high level; w/ pull down, then must read low level + gpio_set_level(TEST_GPIO_EXT_OUT_IO, 1); + gpio_set_pull_mode(TEST_GPIO_EXT_OUT_IO, GPIO_PULLUP_ONLY); + TEST_ASSERT_EQUAL_INT_MESSAGE(1, gpio_get_level(TEST_GPIO_EXT_IN_IO), "direction GPIO_MODE_OUTPUT_OD with GPIO_PULLUP_ONLY set error, it outputs low level"); + gpio_set_pull_mode(TEST_GPIO_EXT_OUT_IO, GPIO_PULLDOWN_ONLY); + TEST_ASSERT_EQUAL_INT_MESSAGE(0, gpio_get_level(TEST_GPIO_EXT_IN_IO), "direction GPIO_MODE_OUTPUT_OD with GPIO_PULLDOWN_ONLY set error, it outputs high level"); + // Outputs low level: must read low level + gpio_set_level(TEST_GPIO_EXT_OUT_IO, 0); + gpio_set_pull_mode(TEST_GPIO_EXT_OUT_IO, GPIO_FLOATING); + TEST_ASSERT_EQUAL_INT_MESSAGE(0, gpio_get_level(TEST_GPIO_EXT_IN_IO), "direction GPIO_MODE_OUTPUT_OD set error, it outputs high level"); + + // Open drain mode(output and input), can just output low level + gpio_set_direction(TEST_GPIO_EXT_OUT_IO, GPIO_MODE_INPUT_OUTPUT_OD); + gpio_set_direction(TEST_GPIO_EXT_IN_IO, GPIO_MODE_INPUT); +#if TEST_GPIO_INTERNAL_ROUTING + gpio_interconnect_input_output_pin(TEST_GPIO_EXT_IN_IO, TEST_GPIO_EXT_OUT_IO, TEST_GPIO_SIGNAL_IDX); +#endif + // Outputs high level: w/ pull up, then must read high level; w/ pull down, then must read low level + gpio_set_level(TEST_GPIO_EXT_OUT_IO, 1); + gpio_set_pull_mode(TEST_GPIO_EXT_OUT_IO, GPIO_PULLUP_ONLY); + TEST_ASSERT_EQUAL_INT_MESSAGE(1, gpio_get_level(TEST_GPIO_EXT_IN_IO), "direction GPIO_MODE_INPUT_OUTPUT_OD with GPIO_PULLUP_ONLY set error, it outputs low level"); + gpio_set_pull_mode(TEST_GPIO_EXT_OUT_IO, GPIO_PULLDOWN_ONLY); + TEST_ASSERT_EQUAL_INT_MESSAGE(0, gpio_get_level(TEST_GPIO_EXT_IN_IO), "direction GPIO_MODE_INPUT_OUTPUT_OD with GPIO_PULLDOWN_ONLY set error, it outputs high level"); + // Outputs low level: must read low level + gpio_set_level(TEST_GPIO_EXT_OUT_IO, 0); + gpio_set_pull_mode(TEST_GPIO_EXT_OUT_IO, GPIO_FLOATING); + TEST_ASSERT_EQUAL_INT_MESSAGE(0, gpio_get_level(TEST_GPIO_EXT_IN_IO), "direction GPIO_MODE_INPUT_OUTPUT_OD set error, it outputs high level"); + + // GPIO_MODE_INPUT_OUTPUT mode + level = gpio_get_level(TEST_GPIO_EXT_IN_IO); + gpio_set_direction(TEST_GPIO_EXT_OUT_IO, GPIO_MODE_INPUT_OUTPUT); + gpio_set_direction(TEST_GPIO_EXT_IN_IO, GPIO_MODE_INPUT); +#if TEST_GPIO_INTERNAL_ROUTING + gpio_interconnect_input_output_pin(TEST_GPIO_EXT_IN_IO, TEST_GPIO_EXT_OUT_IO, TEST_GPIO_SIGNAL_IDX); +#endif + gpio_set_level(TEST_GPIO_EXT_OUT_IO, !level); + TEST_ASSERT_EQUAL_INT_MESSAGE(!level, gpio_get_level(TEST_GPIO_EXT_IN_IO), "direction GPIO_MODE_INPUT_OUTPUT set error, it gives incorrect output"); +} + +static void prompt_to_continue(const char *str) +{ + printf("%s , please press \"Enter\" to go on!\n", str); + char sign[5] = {0}; + while (strlen(sign) == 0) { + /* Flush anything already in the RX buffer */ + while (esp_rom_uart_rx_one_char((uint8_t *) sign) == 0) { + } + /* Read line */ + esp_rom_uart_rx_string((uint8_t *) sign, sizeof(sign) - 1); + } +} + +// This case needs the resistance to pull up the voltage or pull down the voltage +// Ignored in CI because the voltage needs to be tested with multimeter +TEST_CASE_CI_IGNORE("GPIO_verify_only_the_gpio_with_input_ability_can_be_set_pull/down", "[gpio]") +{ + gpio_config_t output_io = test_init_io(TEST_GPIO_EXT_OUT_IO); + gpio_config_t input_io = test_init_io(TEST_GPIO_EXT_IN_IO); + gpio_config(&output_io); + input_io.mode = GPIO_MODE_INPUT; + gpio_config(&input_io); + + printf("pull up test!\n"); + // pull up test + gpio_set_direction(TEST_GPIO_EXT_OUT_IO, GPIO_MODE_OUTPUT); + TEST_ESP_OK(gpio_set_pull_mode(TEST_GPIO_EXT_OUT_IO, GPIO_PULLUP_ONLY)); + prompt_to_continue("mode: GPIO_MODE_OUTPUT"); + + gpio_set_direction(TEST_GPIO_EXT_OUT_IO, GPIO_MODE_OUTPUT_OD); + TEST_ESP_OK(gpio_set_pull_mode(TEST_GPIO_EXT_OUT_IO, GPIO_PULLUP_ONLY)); + + // open drain just can output low level + gpio_set_direction(TEST_GPIO_EXT_OUT_IO, GPIO_MODE_INPUT_OUTPUT_OD); + TEST_ESP_OK(gpio_set_pull_mode(TEST_GPIO_EXT_OUT_IO, GPIO_PULLUP_ONLY)); + prompt_to_continue("mode: GPIO_MODE_OUTPUT_OD"); + + gpio_set_direction(TEST_GPIO_EXT_OUT_IO, GPIO_MODE_INPUT_OUTPUT); + TEST_ESP_OK(gpio_set_pull_mode(TEST_GPIO_EXT_OUT_IO, GPIO_PULLUP_ONLY)); + prompt_to_continue("mode: GPIO_MODE_INPUT_OUTPUT"); + + gpio_set_direction(TEST_GPIO_EXT_OUT_IO, GPIO_MODE_INPUT); + TEST_ESP_OK(gpio_set_pull_mode(TEST_GPIO_EXT_OUT_IO, GPIO_PULLUP_ONLY)); + prompt_to_continue("mode: GPIO_MODE_INPUT"); + + // after pull up the level is high now + // pull down test + printf("pull down test!\n"); + + gpio_set_direction(TEST_GPIO_EXT_OUT_IO, GPIO_MODE_OUTPUT); + TEST_ESP_OK(gpio_set_pull_mode(TEST_GPIO_EXT_OUT_IO, GPIO_PULLDOWN_ONLY)); + prompt_to_continue("mode: GPIO_MODE_OUTPUT"); + + gpio_set_direction(TEST_GPIO_EXT_OUT_IO, GPIO_MODE_OUTPUT_OD); + TEST_ESP_OK(gpio_set_pull_mode(TEST_GPIO_EXT_OUT_IO, GPIO_PULLDOWN_ONLY)); + prompt_to_continue("mode: GPIO_MODE_OUTPUT_OD"); + + gpio_set_direction(TEST_GPIO_EXT_OUT_IO, GPIO_MODE_INPUT_OUTPUT_OD); + TEST_ESP_OK(gpio_set_pull_mode(TEST_GPIO_EXT_OUT_IO, GPIO_PULLDOWN_ONLY)); + prompt_to_continue("mode: GPIO_MODE_INPUT_OUTPUT_OD"); + + gpio_set_direction(TEST_GPIO_EXT_OUT_IO, GPIO_MODE_INPUT_OUTPUT); + TEST_ESP_OK(gpio_set_pull_mode(TEST_GPIO_EXT_OUT_IO, GPIO_PULLDOWN_ONLY)); + prompt_to_continue("mode: GPIO_MODE_INPUT_OUTPUT"); + + gpio_set_direction(TEST_GPIO_EXT_OUT_IO, GPIO_MODE_INPUT); + TEST_ESP_OK(gpio_set_pull_mode(TEST_GPIO_EXT_OUT_IO, GPIO_PULLDOWN_ONLY)); + prompt_to_continue("mode: GPIO_MODE_INPUT"); +} + +static void drive_capability_set_get(gpio_num_t num, gpio_drive_cap_t capability) +{ + gpio_config_t pad_io = test_init_io(num); + TEST_ESP_OK(gpio_config(&pad_io)); + TEST_ASSERT(gpio_set_drive_capability(num, GPIO_DRIVE_CAP_MAX) == ESP_ERR_INVALID_ARG); + + gpio_drive_cap_t cap; + TEST_ESP_OK(gpio_set_drive_capability(num, capability)); + TEST_ESP_OK(gpio_get_drive_capability(num, &cap)); + TEST_ASSERT_EQUAL_INT(capability, cap); +} + +/** + * There are 5 situation for the GPIO drive capability: + * 1. GPIO drive weak capability test + * 2. GPIO drive stronger capability test + * 3. GPIO drive default capability test + * 4. GPIO drive default capability test2 + * 5. GPIO drive strongest capability test + * + * How to test: + * when testing, use the sliding resistor and a multimeter + * adjust the resistor from low to high, 0-10k + * watch the current change + * the current test result: + * weak capability: (0.32-10.1)mA + * stronger capability: (0.32-20.0)mA + * default capability: (0.33-39.8)mA + * default capability2: (0.33-39.9)mA + * strongest capability: (0.33-64.2)mA + * + * the data shows: + * weak capability < stronger capability < default capability = default capability2 < strongest capability + * + * all of these cases should be ignored that it will not run in CI + */ +TEST_CASE_CI_IGNORE("GPIO_drive_capability_test", "[gpio]") +{ + printf("weak capability test! please view the current change!\n"); + drive_capability_set_get(TEST_GPIO_EXT_OUT_IO, GPIO_DRIVE_CAP_0); + prompt_to_continue("If this test finishes"); + + printf("stronger capability test! please view the current change!\n"); + drive_capability_set_get(TEST_GPIO_EXT_OUT_IO, GPIO_DRIVE_CAP_1); + prompt_to_continue("If this test finishes"); + + printf("default capability test! please view the current change!\n"); + drive_capability_set_get(TEST_GPIO_EXT_OUT_IO, GPIO_DRIVE_CAP_2); + prompt_to_continue("If this test finishes"); + + printf("default capability2 test! please view the current change!\n"); + drive_capability_set_get(TEST_GPIO_EXT_OUT_IO, GPIO_DRIVE_CAP_DEFAULT); + prompt_to_continue("If this test finishes"); + + printf("strongest capability test! please view the current change!\n"); + drive_capability_set_get(TEST_GPIO_EXT_OUT_IO, GPIO_DRIVE_CAP_3); + prompt_to_continue("If this test finishes"); +} + +#if SOC_USB_SERIAL_JTAG_SUPPORTED +TEST_CASE("GPIO_input_and_output_of_USB_pins_test", "[gpio]") +{ + const int test_pins[] = {USB_DM_GPIO_NUM, USB_DM_GPIO_NUM}; + gpio_config_t io_conf = { + .intr_type = GPIO_INTR_DISABLE, + .mode = GPIO_MODE_INPUT_OUTPUT, + .pin_bit_mask = (BIT64(test_pins[0]) | BIT64(test_pins[1])), + .pull_down_en = 0, + .pull_up_en = 0, + }; + gpio_config(&io_conf); + + for (int i = 0; i < sizeof(test_pins) / sizeof(int); i++) { + int pin = test_pins[i]; + // test pin + gpio_set_level(pin, 0); + // tested voltage is around 0v + esp_rom_delay_us(10); + TEST_ASSERT_EQUAL_INT_MESSAGE(0, gpio_get_level(pin), "get level error! the level should be low!"); + gpio_set_level(pin, 1); + esp_rom_delay_us(10); + // tested voltage is around 3.3v + TEST_ASSERT_EQUAL_INT_MESSAGE(1, gpio_get_level(pin), "get level error! the level should be high!"); + gpio_set_level(pin, 0); + esp_rom_delay_us(10); + // tested voltage is around 0v + TEST_ASSERT_EQUAL_INT_MESSAGE(0, gpio_get_level(pin), "get level error! the level should be low!"); + gpio_set_level(pin, 1); + esp_rom_delay_us(10); + // tested voltage is around 3.3v + TEST_ASSERT_EQUAL_INT_MESSAGE(1, gpio_get_level(pin), "get level error! the level should be high!"); + } +} +#endif //SOC_USB_SERIAL_JTAG_SUPPORTED + +// Ignored in CI because it needs manually connect TEST_GPIO_INPUT_LEVEL_LOW_PIN to 3.3v to wake up from light sleep +TEST_CASE_CI_IGNORE("GPIO_light_sleep_wake_up_test", "[gpio]") +{ + gpio_config_t io_config = test_init_io(TEST_GPIO_INPUT_LEVEL_LOW_PIN); + io_config.mode = GPIO_MODE_INPUT; + io_config.pull_down_en = 1; + gpio_config(&io_config); + TEST_ESP_OK(gpio_wakeup_enable(TEST_GPIO_INPUT_LEVEL_LOW_PIN, GPIO_INTR_HIGH_LEVEL)); + TEST_ESP_OK(esp_sleep_enable_gpio_wakeup()); + printf("Entering light sleep... Please connect GPIO%d to 3.3v to wake up...\n", TEST_GPIO_INPUT_LEVEL_LOW_PIN); + // Wait for the complete line to be printed before entering sleep + vTaskDelay(1000 / portTICK_PERIOD_MS); + esp_light_sleep_start(); + printf("Waked up from light sleep\n"); + TEST_ASSERT(esp_sleep_get_wakeup_cause() == ESP_SLEEP_WAKEUP_GPIO); +} diff --git a/components/driver/test_apps/gpio/main/test_gpio.h b/components/driver/test_apps/gpio/main/test_gpio.h new file mode 100644 index 0000000000..6ab38136ea --- /dev/null +++ b/components/driver/test_apps/gpio/main/test_gpio.h @@ -0,0 +1,53 @@ +/* + * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include "sdkconfig.h" +#include "soc/gpio_sig_map.h" + +#ifdef __cplusplus +extern "C" { +#endif + +// GPIO self-test pins (GPIO_MODE_INPUT_OUTPUT) +#define TEST_GPIO_INPUT_OUTPUT_IO1 (4) +#define TEST_GPIO_INPUT_OUTPUT_IO2 (5) + +#if CONFIG_IDF_TARGET_ESP32 +#define TEST_GPIO_EXT_OUT_IO (18) +#define TEST_GPIO_EXT_IN_IO (19) +#define TEST_GPIO_INPUT_ONLY_PIN (34) +#define TEST_GPIO_INPUT_LEVEL_LOW_PIN (4) +#define TEST_GPIO_SIGNAL_IDX (SIG_IN_FUNC224_IDX) +#elif CONFIG_IDF_TARGET_ESP32S2 +#define TEST_GPIO_EXT_OUT_IO (17) +#define TEST_GPIO_EXT_IN_IO (21) +#define TEST_GPIO_INPUT_ONLY_PIN (46) +#define TEST_GPIO_INPUT_LEVEL_LOW_PIN (1) +#define TEST_GPIO_SIGNAL_IDX (SIG_IN_FUNC223_IDX) +#elif CONFIG_IDF_TARGET_ESP32S3 +#define TEST_GPIO_EXT_OUT_IO (17) +#define TEST_GPIO_EXT_IN_IO (21) +#define TEST_GPIO_INPUT_LEVEL_LOW_PIN (1) +#define TEST_GPIO_SIGNAL_IDX (SIG_IN_FUNC208_IDX) +#elif CONFIG_IDF_TARGET_ESP32C3 +#define TEST_GPIO_EXT_OUT_IO (2) +#define TEST_GPIO_EXT_IN_IO (3) +#define TEST_GPIO_INPUT_LEVEL_LOW_PIN (1) +#define TEST_GPIO_SIGNAL_IDX (SIG_IN_FUNC97_IDX) +#elif CONFIG_IDF_TARGET_ESP32C2 +#define TEST_GPIO_EXT_OUT_IO (2) +#define TEST_GPIO_EXT_IN_IO (3) +#define TEST_GPIO_INPUT_LEVEL_LOW_PIN (1) +#define TEST_GPIO_SIGNAL_IDX (SIG_IN_FUNC97_IDX) +#elif CONFIG_IDF_TARGET_ESP32H2 +#define TEST_GPIO_EXT_OUT_IO (6) +#define TEST_GPIO_EXT_IN_IO (7) +#define TEST_GPIO_INPUT_LEVEL_LOW_PIN (1) +#define TEST_GPIO_SIGNAL_IDX (SIG_IN_FUNC97_IDX) +#endif + +#ifdef __cplusplus +} +#endif diff --git a/components/driver/test/test_sigmadelta.c b/components/driver/test_apps/gpio/main/test_sigmadelta.c similarity index 89% rename from components/driver/test/test_sigmadelta.c rename to components/driver/test_apps/gpio/main/test_sigmadelta.c index 76fe8578db..6c25e57bf2 100644 --- a/components/driver/test/test_sigmadelta.c +++ b/components/driver/test_apps/gpio/main/test_sigmadelta.c @@ -3,16 +3,18 @@ * * SPDX-License-Identifier: Apache-2.0 */ + #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "unity.h" -#include "test_utils.h" #include "soc/soc_caps.h" -#if SOC_SIGMADELTA_SUPPORTED #include "driver/sigmadelta.h" +void test_app_include_sigmadelta(void) +{ +} -TEST_CASE("SigmaDelta config test", "[sigma_delta]") +TEST_CASE("SigmaDelta_config_test", "[sigma_delta]") { sigmadelta_config_t sigmadelta_cfg = { .sigmadelta_prescale = 80, @@ -30,7 +32,7 @@ TEST_CASE("SigmaDelta config test", "[sigma_delta]") // connect GPIO4 with LED positive pin, and the GND pin connect LED negative pin // logic analyzer help also to see the wave form(more standard and accurate) -TEST_CASE("SigmaDelta pin, duty, prescale set", "[sigma_delta][ignore]") +TEST_CASE("SigmaDelta_pin_duty_prescale_set", "[sigma_delta][ignore]") { sigmadelta_config_t sigmadelta_cfg = { .channel = 0, @@ -66,5 +68,3 @@ TEST_CASE("SigmaDelta pin, duty, prescale set", "[sigma_delta][ignore]") TEST_ESP_OK(sigmadelta_set_pin(sigmadelta_cfg.channel, 5)); vTaskDelay(3000 / portTICK_PERIOD_MS); } - -#endif // SOC_SIGMADELTA_SUPPORTED diff --git a/components/driver/test_apps/gpio/pytest_gpio.py b/components/driver/test_apps/gpio/pytest_gpio.py new file mode 100644 index 0000000000..3583b10e75 --- /dev/null +++ b/components/driver/test_apps/gpio/pytest_gpio.py @@ -0,0 +1,21 @@ +# SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: CC0-1.0 + +import pytest +from pytest_embedded import Dut + + +@pytest.mark.supported_targets +@pytest.mark.generic +@pytest.mark.parametrize( + 'config', + [ + 'iram_safe', + 'release', + ], + indirect=True, +) +def test_gpio(dut: Dut) -> None: + dut.expect_exact('Press ENTER to see the list of tests') + dut.write('*') + dut.expect_unity_test_output() diff --git a/components/driver/test_apps/gpio/sdkconfig.ci.iram_safe b/components/driver/test_apps/gpio/sdkconfig.ci.iram_safe new file mode 100644 index 0000000000..abfc89e440 --- /dev/null +++ b/components/driver/test_apps/gpio/sdkconfig.ci.iram_safe @@ -0,0 +1,4 @@ +CONFIG_COMPILER_DUMP_RTL_FILES=y +CONFIG_GPIO_CTRL_FUNC_IN_IRAM=y +# silent the error check, as the error string are stored in rodata, causing RTL check failure +CONFIG_COMPILER_OPTIMIZATION_CHECKS_SILENT=y diff --git a/components/driver/test_apps/gpio/sdkconfig.ci.release b/components/driver/test_apps/gpio/sdkconfig.ci.release new file mode 100644 index 0000000000..91d93f163e --- /dev/null +++ b/components/driver/test_apps/gpio/sdkconfig.ci.release @@ -0,0 +1,5 @@ +CONFIG_PM_ENABLE=y +CONFIG_FREERTOS_USE_TICKLESS_IDLE=y +CONFIG_COMPILER_OPTIMIZATION_SIZE=y +CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_SIZE=y +CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT=y diff --git a/components/driver/test_apps/gpio/sdkconfig.defaults b/components/driver/test_apps/gpio/sdkconfig.defaults new file mode 100644 index 0000000000..b308cb2ddd --- /dev/null +++ b/components/driver/test_apps/gpio/sdkconfig.defaults @@ -0,0 +1,2 @@ +CONFIG_FREERTOS_HZ=1000 +CONFIG_ESP_TASK_WDT=n diff --git a/components/esp_rom/include/esp32h2/rom/gpio.h b/components/esp_rom/include/esp32h2/rom/gpio.h index 2969d14fc0..be60742711 100644 --- a/components/esp_rom/include/esp32h2/rom/gpio.h +++ b/components/esp_rom/include/esp32h2/rom/gpio.h @@ -1,16 +1,8 @@ -// Copyright 2020 Espressif Systems (Shanghai) PTE LTD -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +/* + * SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ #ifndef _ROM_GPIO_H_ #define _ROM_GPIO_H_ @@ -20,6 +12,7 @@ #include "esp_attr.h" #include "soc/gpio_reg.h" +#include "sdkconfig.h" #ifdef __cplusplus extern "C" { @@ -39,8 +32,13 @@ extern "C" { #define GPIO_ID_PIN(n) (GPIO_ID_PIN0+(n)) #define GPIO_PIN_ADDR(i) (GPIO_PIN0_REG + i*4) +#if CONFIG_IDF_TARGET_ESP32H2_BETA_VERSION_1 #define GPIO_FUNC_IN_HIGH 0x38 #define GPIO_FUNC_IN_LOW 0x3C +#elif CONFIG_IDF_TARGET_ESP32H2_BETA_VERSION_2 +#define GPIO_FUNC_IN_HIGH 0x1E +#define GPIO_FUNC_IN_LOW 0x1F +#endif #define GPIO_ID_IS_PIN_REGISTER(reg_id) \ ((reg_id >= GPIO_ID_PIN0) && (reg_id <= GPIO_ID_PIN(GPIO_PIN_COUNT-1))) diff --git a/components/esp_wifi/test/test_wifi_init.c b/components/esp_wifi/test/test_wifi_init.c index 2d13fa6022..660a817ac4 100644 --- a/components/esp_wifi/test/test_wifi_init.c +++ b/components/esp_wifi/test/test_wifi_init.c @@ -7,6 +7,7 @@ #include "nvs_flash.h" #include "test_utils.h" #include "freertos/event_groups.h" +#include "unity_test_utils.h" #define GOT_IP_EVENT 0x00000001 #define DISCONNECT_EVENT 0x00000002 @@ -116,7 +117,7 @@ TEST_CASE("wifi driver can start on APP CPU", "[wifi_init]") xSemaphoreTake(sema, portMAX_DELAY); vSemaphoreDelete(sema); sema = NULL; - test_utils_task_delete(th); + unity_utils_task_delete(th); } static void wifi_start_stop_task(void* arg) @@ -169,7 +170,7 @@ TEST_CASE("Calling esp_wifi_stop() with start", "[wifi_init]") xSemaphoreTake(sema, portMAX_DELAY); vSemaphoreDelete(sema); sema = NULL; - test_utils_task_delete(th); + unity_utils_task_delete(th); } static void wifi_stop_task(void* arg) @@ -218,7 +219,7 @@ TEST_CASE("Calling esp_wifi_stop() without start", "[wifi_init]") xSemaphoreTake(sema, portMAX_DELAY); vSemaphoreDelete(sema); sema = NULL; - test_utils_task_delete(th); + unity_utils_task_delete(th); } static void wifi_deinit_task(void* arg) @@ -271,5 +272,5 @@ TEST_CASE("Calling esp_wifi_deinit() without stop", "[wifi_init]") xSemaphoreTake(sema, portMAX_DELAY); vSemaphoreDelete(sema); sema = NULL; - test_utils_task_delete(th); + unity_utils_task_delete(th); } diff --git a/components/hal/gpio_hal.c b/components/hal/gpio_hal.c index 9907104ab3..6d9bd0a772 100644 --- a/components/hal/gpio_hal.c +++ b/components/hal/gpio_hal.c @@ -1,16 +1,8 @@ -// Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +/* + * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ // The HAL layer for GPIO (common part) diff --git a/components/hal/linker.lf b/components/hal/linker.lf index cf8dd38d5e..695b7be44f 100644 --- a/components/hal/linker.lf +++ b/components/hal/linker.lf @@ -24,3 +24,5 @@ entries: systimer_hal (noflash) if GPTIMER_CTRL_FUNC_IN_IRAM = y: timer_hal_iram (noflash) + if GPIO_CTRL_FUNC_IN_IRAM = y: + gpio_hal: gpio_hal_intr_disable (noflash) diff --git a/components/sdmmc/test/test_sd.c b/components/sdmmc/test/test_sd.c index 25070abd04..6d49dfec22 100644 --- a/components/sdmmc/test/test_sd.c +++ b/components/sdmmc/test/test_sd.c @@ -24,6 +24,8 @@ #include "esp_heap_caps.h" #include "esp_rom_gpio.h" #include "test_utils.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" #include "soc/gpio_sig_map.h" #include "soc/gpio_reg.h" diff --git a/components/soc/esp32c3/include/soc/io_mux_reg.h b/components/soc/esp32c3/include/soc/io_mux_reg.h index 69d2ae4bb7..6d5ae6da2f 100644 --- a/components/soc/esp32c3/include/soc/io_mux_reg.h +++ b/components/soc/esp32c3/include/soc/io_mux_reg.h @@ -130,6 +130,9 @@ #define SD_DATA2_GPIO_NUM 9 #define SD_DATA3_GPIO_NUM 10 +#define USB_DM_GPIO_NUM 18 +#define USB_DP_GPIO_NUM 19 + #define MAX_RTC_GPIO_NUM 5 #define MAX_PAD_GPIO_NUM 21 #define MAX_GPIO_NUM 25 diff --git a/components/soc/esp32h2/include/rev1/soc/io_mux_reg.h b/components/soc/esp32h2/include/rev1/soc/io_mux_reg.h index 96ec302766..276c526990 100644 --- a/components/soc/esp32h2/include/rev1/soc/io_mux_reg.h +++ b/components/soc/esp32h2/include/rev1/soc/io_mux_reg.h @@ -149,6 +149,9 @@ #define SD_DATA2_GPIO_NUM 9 #define SD_DATA3_GPIO_NUM 10 +#define USB_DM_GPIO_NUM 18 +#define USB_DP_GPIO_NUM 19 + #define MAX_RTC_GPIO_NUM 5 #define MAX_PAD_GPIO_NUM 40 #define MAX_GPIO_NUM 44 diff --git a/components/soc/esp32h2/include/rev2/soc/io_mux_reg.h b/components/soc/esp32h2/include/rev2/soc/io_mux_reg.h index 26d6fba79c..829adedcf3 100644 --- a/components/soc/esp32h2/include/rev2/soc/io_mux_reg.h +++ b/components/soc/esp32h2/include/rev2/soc/io_mux_reg.h @@ -124,6 +124,9 @@ #define SPI_D_GPIO_NUM 18 #define SPI_Q_GPIO_NUM 14 +#define USB_DM_GPIO_NUM 24 +#define USB_DP_GPIO_NUM 25 + #define MAX_RTC_GPIO_NUM 12 // GPIO7~12 are the rtc_io pads #define MAX_PAD_GPIO_NUM 25 #define MAX_GPIO_NUM 29 diff --git a/components/soc/esp32h2/include/soc/gpio_pins.h b/components/soc/esp32h2/include/soc/gpio_pins.h index f5a35b9975..c7b38724d0 100644 --- a/components/soc/esp32h2/include/soc/gpio_pins.h +++ b/components/soc/esp32h2/include/soc/gpio_pins.h @@ -1,26 +1,25 @@ -// Copyright 2020 Espressif Systems (Shanghai) PTE LTD -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +/* + * SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ #pragma once +#include "sdkconfig.h" + #ifdef __cplusplus extern "C" { #endif +#if CONFIG_IDF_TARGET_ESP32H2_BETA_VERSION_1 +#define GPIO_MATRIX_CONST_ONE_INPUT (0x38) +#define GPIO_MATRIX_CONST_ZERO_INPUT (0x3C) +#elif CONFIG_IDF_TARGET_ESP32H2_BETA_VERSION_2 #define GPIO_MATRIX_CONST_ONE_INPUT (0x1E) #define GPIO_MATRIX_CONST_ZERO_INPUT (0x1F) +#endif #ifdef __cplusplus } diff --git a/components/soc/esp32s3/include/soc/io_mux_reg.h b/components/soc/esp32s3/include/soc/io_mux_reg.h index 1cde913790..f8456b33b7 100644 --- a/components/soc/esp32s3/include/soc/io_mux_reg.h +++ b/components/soc/esp32s3/include/soc/io_mux_reg.h @@ -154,6 +154,8 @@ #define SD_DATA1_GPIO_NUM 14 #define SD_DATA2_GPIO_NUM 9 #define SD_DATA3_GPIO_NUM 10 +#define USB_DM_GPIO_NUM 19 +#define USB_DP_GPIO_NUM 20 #define MAX_RTC_GPIO_NUM 21 #define MAX_PAD_GPIO_NUM 48 diff --git a/components/spi_flash/test/test_read_write.c b/components/spi_flash/test/test_read_write.c index e0028c0cd1..590a38b429 100644 --- a/components/spi_flash/test/test_read_write.c +++ b/components/spi_flash/test/test_read_write.c @@ -17,6 +17,7 @@ #include #include "../cache_utils.h" #include "soc/timer_periph.h" +#include "esp_attr.h" #include "esp_heap_caps.h" #include "esp_rom_spiflash.h" #if CONFIG_IDF_TARGET_ESP32 diff --git a/components/tcp_transport/test/test_transport_fixtures.c b/components/tcp_transport/test/test_transport_fixtures.c index 1ad70ddbd7..c1bfad87d8 100644 --- a/components/tcp_transport/test/test_transport_fixtures.c +++ b/components/tcp_transport/test/test_transport_fixtures.c @@ -8,6 +8,7 @@ #include "lwip/sockets.h" #include "freertos/event_groups.h" #include "tcp_transport_fixtures.h" +#include "unity_test_utils.h" // This is a private API of the tcp transport, but needed for socket operation tests int esp_transport_get_socket(esp_transport_handle_t t); @@ -228,8 +229,8 @@ static void connect_test_teardown(tcp_connect_test_t t) vTaskSuspend(t->tcp_connect_task); vTaskSuspend(t->listener_task); vEventGroupDelete(t->tcp_connect_done); - test_utils_task_delete(t->tcp_connect_task); - test_utils_task_delete(t->listener_task); + unity_utils_task_delete(t->tcp_connect_task); + unity_utils_task_delete(t->listener_task); free(t); } diff --git a/components/unity/CMakeLists.txt b/components/unity/CMakeLists.txt index 14184db294..14a33632cd 100644 --- a/components/unity/CMakeLists.txt +++ b/components/unity/CMakeLists.txt @@ -15,6 +15,7 @@ endif() if(CONFIG_UNITY_ENABLE_IDF_TEST_RUNNER) list(APPEND srcs "unity_runner.c") + list(APPEND srcs "unity_utils_freertos.c") endif() if(CONFIG_UNITY_ENABLE_FIXTURE) diff --git a/components/unity/include/unity_test_runner.h b/components/unity/include/unity_test_runner.h index ac504a29ee..7587b6d249 100644 --- a/components/unity/include/unity_test_runner.h +++ b/components/unity/include/unity_test_runner.h @@ -1,16 +1,8 @@ -// Copyright 2016-2018 Espressif Systems (Shanghai) PTE LTD -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +/* + * SPDX-FileCopyrightText: 2016-2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ #pragma once #include @@ -131,7 +123,6 @@ void unity_testcase_register(test_desc_t* desc); } - /* * First argument is a free-form description, * second argument is (by convention) a list of identifiers, each one in square brackets. @@ -157,6 +148,18 @@ void unity_testcase_register(test_desc_t* desc); unity_testcase_register( & UNITY_TEST_UID(test_desc_) ); \ } + +/* + Test case macro to be ignored in CI. + Tests will still be built (to check for compile error) but not linked if IDF_CI_BUILD. + */ +#if IDF_CI_BUILD +#define TEST_CASE_CI_IGNORE(name_, desc_) \ + __attribute__((unused)) static void UNITY_TEST_UID(test_func_) (void) +#else +#define TEST_CASE_CI_IGNORE(name_, desc_) TEST_CASE(name_, desc_) +#endif + /** * Note: initialization of test_desc_t fields above has to be done exactly * in the same order as the fields are declared in the structure. diff --git a/components/unity/include/unity_test_utils.h b/components/unity/include/unity_test_utils.h new file mode 100644 index 0000000000..f508ecaa5f --- /dev/null +++ b/components/unity/include/unity_test_utils.h @@ -0,0 +1,26 @@ +/* + * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Delete task ensuring dynamic memory (for stack, tcb etc.) gets freed up immediately + * + * @param[in] thandle Handle of task to be deleted (should not be NULL or self handle) + */ +void unity_utils_task_delete(TaskHandle_t thandle); + +#ifdef __cplusplus +} +#endif diff --git a/components/unity/unity_utils_freertos.c b/components/unity/unity_utils_freertos.c new file mode 100644 index 0000000000..a9616e46b2 --- /dev/null +++ b/components/unity/unity_utils_freertos.c @@ -0,0 +1,69 @@ +/* + * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "unity.h" +#include "unity_test_utils.h" +#include "freertos/semphr.h" +#include "sdkconfig.h" +#if !CONFIG_FREERTOS_UNICORE +#include "esp_ipc.h" +#include "esp_freertos_hooks.h" +#endif + +#if !CONFIG_FREERTOS_UNICORE +static SemaphoreHandle_t test_sem; + +static bool idle_hook_func(void) +{ + if (test_sem) { + xSemaphoreGive(test_sem); + } + return true; +} + +static void task_delete_func(void *arg) +{ + vTaskDelete(arg); +} +#endif // !CONFIG_FREERTOS_UNICORE + +void unity_utils_task_delete(TaskHandle_t thandle) +{ + /* Self deletion can not free up associated task dynamic memory immediately, + * hence not recommended for test scenarios */ + TEST_ASSERT_NOT_NULL_MESSAGE(thandle, "unity_utils_task_delete: handle is NULL"); + TEST_ASSERT_NOT_EQUAL_MESSAGE(thandle, xTaskGetCurrentTaskHandle(), "unity_utils_task_delete: handle is of currently executing task"); + +#if CONFIG_FREERTOS_UNICORE + vTaskDelete(thandle); +#else // CONFIG_FREERTOS_UNICORE + const BaseType_t tsk_affinity = xTaskGetAffinity(thandle); + const BaseType_t core_id = xPortGetCoreID(); + + printf("Task_affinity: 0x%x, current_core: %d\n", tsk_affinity, core_id); + + if (tsk_affinity == tskNO_AFFINITY) { + /* For no affinity case, we wait for idle hook to trigger on different core */ + esp_err_t ret = esp_register_freertos_idle_hook_for_cpu(idle_hook_func, !core_id); + TEST_ASSERT_EQUAL_MESSAGE(ret, ESP_OK, "unity_utils_task_delete: failed to register idle hook"); + vTaskDelete(thandle); + test_sem = xSemaphoreCreateBinary(); + TEST_ASSERT_NOT_NULL_MESSAGE(test_sem, "unity_utils_task_delete: failed to create semaphore"); + xSemaphoreTake(test_sem, portMAX_DELAY); + esp_deregister_freertos_idle_hook_for_cpu(idle_hook_func, !core_id); + vSemaphoreDelete(test_sem); + test_sem = NULL; + } else if (tsk_affinity != core_id) { + /* Task affinity and current core are differnt, schedule IPC call (to delete task) + * on core where task is pinned to */ + esp_ipc_call_blocking(tsk_affinity, task_delete_func, thandle); + } else { + /* Task affinity and current core are same, so we can safely proceed for deletion */ + vTaskDelete(thandle); + } +#endif // !CONFIG_FREERTOS_UNICORE +} diff --git a/tools/ci/check_copyright_ignore.txt b/tools/ci/check_copyright_ignore.txt index 12d159e302..b945905369 100644 --- a/tools/ci/check_copyright_ignore.txt +++ b/tools/ci/check_copyright_ignore.txt @@ -580,7 +580,6 @@ components/esp_rom/include/esp32h2/rom/digital_signature.h components/esp_rom/include/esp32h2/rom/efuse.h components/esp_rom/include/esp32h2/rom/esp_flash.h components/esp_rom/include/esp32h2/rom/ets_sys.h -components/esp_rom/include/esp32h2/rom/gpio.h components/esp_rom/include/esp32h2/rom/hmac.h components/esp_rom/include/esp32h2/rom/libc_stubs.h components/esp_rom/include/esp32h2/rom/lldesc.h @@ -905,7 +904,6 @@ components/hal/esp32s3/include/hal/uhci_ll.h components/hal/esp32s3/include/hal/usb_ll.h components/hal/esp32s3/include/hal/usb_serial_jtag_ll.h components/hal/esp32s3/interrupt_descriptor_table.c -components/hal/gpio_hal.c components/hal/include/hal/aes_hal.h components/hal/include/hal/aes_types.h components/hal/include/hal/brownout_hal.h @@ -1383,7 +1381,6 @@ components/soc/esp32h2/include/soc/efuse_reg.h components/soc/esp32h2/include/soc/efuse_struct.h components/soc/esp32h2/include/soc/extmem_reg.h components/soc/esp32h2/include/soc/fe_reg.h -components/soc/esp32h2/include/soc/gpio_pins.h components/soc/esp32h2/include/soc/gpio_sd_reg.h components/soc/esp32h2/include/soc/gpio_sd_struct.h components/soc/esp32h2/include/soc/hwcrypto_reg.h @@ -1683,7 +1680,6 @@ components/ulp/test/esp32/test_ulp_as.c components/unity/include/priv/setjmp.h components/unity/include/unity_config.h components/unity/include/unity_fixture_extras.h -components/unity/include/unity_test_runner.h components/unity/unity_runner.c components/usb/test/hcd/test_hcd_ctrl.c components/vfs/include/esp_vfs_common.h diff --git a/tools/unit-test-app/components/test_utils/include/test_utils.h b/tools/unit-test-app/components/test_utils/include/test_utils.h index a739eb1fd8..6526c88d1b 100644 --- a/tools/unit-test-app/components/test_utils/include/test_utils.h +++ b/tools/unit-test-app/components/test_utils/include/test_utils.h @@ -11,8 +11,6 @@ #include #include #include "sdkconfig.h" -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" #include "unity.h" #include "soc/soc_caps.h" /* include performance pass standards header file */ @@ -237,14 +235,6 @@ test_utils_exhaust_memory_rec test_utils_exhaust_memory(uint32_t caps, size_t li */ void test_utils_free_exhausted_memory(test_utils_exhaust_memory_rec rec); - -/** - * @brief Delete task ensuring dynamic memory (for stack, tcb etc.) gets freed up immediately - * - * @param[in] thandle Handle of task to be deleted (should not be NULL or self handle) - */ -void test_utils_task_delete(TaskHandle_t thandle); - #ifdef __cplusplus } #endif diff --git a/tools/unit-test-app/components/test_utils/test_utils.c b/tools/unit-test-app/components/test_utils/test_utils.c index c2d4763403..e09a07c2f8 100644 --- a/tools/unit-test-app/components/test_utils/test_utils.c +++ b/tools/unit-test-app/components/test_utils/test_utils.c @@ -13,10 +13,6 @@ #include "lwip/sockets.h" #include "sdkconfig.h" #include "memory_checks.h" -#if !CONFIG_FREERTOS_UNICORE -#include "esp_ipc.h" -#include "esp_freertos_hooks.h" -#endif const esp_partition_t *get_test_data_partition(void) { @@ -147,57 +143,3 @@ void test_utils_free_exhausted_memory(test_utils_exhaust_memory_rec rec) } free(rec); } - -#if !CONFIG_FREERTOS_UNICORE -static SemaphoreHandle_t test_sem; - -static bool test_idle_hook_func(void) -{ - if (test_sem) { - xSemaphoreGive(test_sem); - } - return true; -} - -static void test_task_delete_func(void *arg) -{ - vTaskDelete(arg); -} -#endif // !CONFIG_FREERTOS_UNICORE - -void test_utils_task_delete(TaskHandle_t thandle) -{ - /* Self deletion can not free up associated task dynamic memory immediately, - * hence not recommended for test scenarios */ - TEST_ASSERT_NOT_NULL_MESSAGE(thandle, "test_utils_task_delete: handle is NULL"); - TEST_ASSERT_NOT_EQUAL_MESSAGE(thandle, xTaskGetCurrentTaskHandle(), "test_utils_task_delete: handle is of currently executing task"); - -#if CONFIG_FREERTOS_UNICORE - vTaskDelete(thandle); -#else // CONFIG_FREERTOS_UNICORE - const BaseType_t tsk_affinity = xTaskGetAffinity(thandle); - const BaseType_t core_id = xPortGetCoreID(); - - printf("Task_affinity: 0x%x, current_core: %d\n", tsk_affinity, core_id); - - if (tsk_affinity == tskNO_AFFINITY) { - /* For no affinity case, we wait for idle hook to trigger on different core */ - esp_err_t ret = esp_register_freertos_idle_hook_for_cpu(test_idle_hook_func, !core_id); - TEST_ASSERT_EQUAL_MESSAGE(ret, ESP_OK, "test_utils_task_delete: failed to register idle hook"); - vTaskDelete(thandle); - test_sem = xSemaphoreCreateBinary(); - TEST_ASSERT_NOT_NULL_MESSAGE(test_sem, "test_utils_task_delete: failed to create semaphore"); - xSemaphoreTake(test_sem, portMAX_DELAY); - esp_deregister_freertos_idle_hook_for_cpu(test_idle_hook_func, !core_id); - vSemaphoreDelete(test_sem); - test_sem = NULL; - } else if (tsk_affinity != core_id) { - /* Task affinity and current core are differnt, schedule IPC call (to delete task) - * on core where task is pinned to */ - esp_ipc_call_blocking(tsk_affinity, test_task_delete_func, thandle); - } else { - /* Task affinity and current core are same, so we can safely proceed for deletion */ - vTaskDelete(thandle); - } -#endif // !CONFIG_FREERTOS_UNICORE -}