Merge branch 'refactor/gpio_unit_test' into 'master'

gpio: Clean up unit tests and enable ci ut on some previously disabled test cases

Closes IDF-4620

See merge request espressif/esp-idf!17417
This commit is contained in:
Song Ruo Jing
2022-04-01 18:38:19 +08:00
36 changed files with 1211 additions and 1043 deletions

View File

@@ -176,6 +176,8 @@ before_script:
scapy scapy
google-api-python-client google-api-python-client
- cd $IDF_PATH - cd $IDF_PATH
- export EXTRA_CFLAGS=${PEDANTIC_CFLAGS}
- export EXTRA_CXXFLAGS=${PEDANTIC_CXXFLAGS}
default: default:
retry: retry:

View File

@@ -186,6 +186,13 @@ menu "Driver configurations"
pullup/pulldown mode in sleep. pullup/pulldown mode in sleep.
If this option is selected, chip will automatically emulate the behaviour of switching, If this option is selected, chip will automatically emulate the behaviour of switching,
and about 450B of source codes would be placed into IRAM. 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 endmenu # GPIO Configuration
menu "GDMA Configuration" menu "GDMA Configuration"

View File

@@ -90,6 +90,8 @@ esp_err_t gpio_intr_enable(gpio_num_t gpio_num);
/** /**
* @brief Disable GPIO module interrupt signal * @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); * @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 * @return
@@ -102,6 +104,8 @@ esp_err_t gpio_intr_disable(gpio_num_t gpio_num);
/** /**
* @brief GPIO set output level * @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 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 * @param level Output level. 0: low ; 1: high
* *

View File

@@ -17,3 +17,6 @@ entries:
pulse_cnt: pcnt_unit_stop (noflash) pulse_cnt: pcnt_unit_stop (noflash)
pulse_cnt: pcnt_unit_clear_count (noflash) pulse_cnt: pcnt_unit_clear_count (noflash)
pulse_cnt: pcnt_unit_get_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)

View File

@@ -11,6 +11,7 @@
#include "test_utils.h" #include "test_utils.h"
#include "esp_adc_cal.h" #include "esp_adc_cal.h"
#include "driver/adc_common.h" #include "driver/adc_common.h"
#include "esp_cpu.h"
__attribute__((unused)) static const char *TAG = "ADC"; __attribute__((unused)) static const char *TAG = "ADC";

View File

@@ -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 <stdio.h>
#include <string.h>
#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 capability<stronger capability<default capability=default capability2<strongest capability
*
* all of these cases should be ignored that it will not run in CI
*/
// drive capability test
TEST_CASE("GPIO drive capability test", "[gpio][ignore]")
{
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 !CONFIG_FREERTOS_UNICORE
void gpio_enable_task(void *param)
{
int gpio_num = (int)param;
TEST_ESP_OK(gpio_intr_enable(gpio_num));
vTaskDelete(NULL);
}
/** 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.
* First on the core 0, Do the following steps:
* 1. Configure the GPIO9 input_output mode, and enable the rising edge interrupt mode.
* 2. Trigger the GPIO9 interrupt and check if the interrupt responds correctly.
* 3. Disable the GPIO9 interrupt
* Then on the core 1, Do the following steps:
* 1. Enable the GPIO9 interrupt again.
* 2. Trigger the GPIO9 interrupt and check if the interrupt responds correctly.
*
*/
TEST_CASE("GPIO Enable/Disable interrupt on multiple cores", "[gpio][ignore]")
{
gpio_config_t io_conf;
io_conf.intr_type = GPIO_INTR_NEGEDGE;
io_conf.mode = GPIO_MODE_INPUT_OUTPUT;
io_conf.pin_bit_mask = (1ULL << TEST_IO_9);
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_install_isr_service(0));
TEST_ESP_OK(gpio_isr_handler_add(TEST_IO_9, gpio_isr_edge_handler, (void *) TEST_IO_9));
vTaskDelay(1000 / portTICK_PERIOD_MS);
TEST_ESP_OK(gpio_set_level(TEST_IO_9, 1));
vTaskDelay(100 / portTICK_PERIOD_MS);
TEST_ESP_OK(gpio_set_level(TEST_IO_9, 0));
vTaskDelay(100 / portTICK_PERIOD_MS);
TEST_ESP_OK(gpio_intr_disable(TEST_IO_9));
TEST_ASSERT(edge_intr_times == 1);
xTaskCreatePinnedToCore(gpio_enable_task, "gpio_enable_task", 1024 * 4, (void *)TEST_IO_9, 8, NULL, (xPortGetCoreID() == 0));
vTaskDelay(1000 / portTICK_PERIOD_MS);
TEST_ESP_OK(gpio_set_level(TEST_IO_9, 1));
vTaskDelay(100 / portTICK_PERIOD_MS);
TEST_ESP_OK(gpio_set_level(TEST_IO_9, 0));
vTaskDelay(100 / portTICK_PERIOD_MS);
TEST_ESP_OK(gpio_intr_disable(TEST_IO_9));
TEST_ESP_OK(gpio_isr_handler_remove(TEST_IO_9));
gpio_uninstall_isr_service();
TEST_ASSERT(edge_intr_times == 2);
}
#endif //!CONFIG_FREERTOS_UNICORE
typedef struct {
int gpio_num;
int isr_cnt;
} gpio_isr_param_t;
static void gpio_isr_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 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

View File

@@ -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()

View File

@@ -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})

View File

@@ -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();
}

View File

@@ -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 * SPDX-License-Identifier: Apache-2.0
*/ */
#include "freertos/FreeRTOS.h" #include "freertos/FreeRTOS.h"
#include "freertos/task.h" #include "freertos/task.h"
#include "freertos/semphr.h" #include "freertos/semphr.h"
#include "unity.h" #include "unity.h"
#include "unity_test_utils.h"
#include "esp_rom_sys.h" #include "esp_rom_sys.h"
#include "soc/soc_caps.h" #include "soc/soc_caps.h"
#include "hal/cpu_ll.h" #include "hal/cpu_ll.h"
#include "driver/gpio.h" #include "driver/gpio.h"
#if SOC_DEDICATED_GPIO_SUPPORTED
#include "driver/dedic_gpio.h" #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 test_gpios[SOC_DEDIC_GPIO_OUT_CHANNELS_NUM / 2] = {0};
const int test2_gpios[SOC_DEDIC_GPIO_OUT_CHANNELS_NUM / 2 + 1] = {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)); TEST_ESP_OK(dedic_gpio_del_bundle(bundleB));
xSemaphoreGive(ctx->sem); 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); 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++) { for (int i = 0; i < SOC_CPU_CORES_NUM; i++) {
int start_gpio = i * TEST_GPIO_GROUP_SIZE; 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, .sem = sem,
.gpios = {start_gpio, start_gpio + 1, start_gpio + 2, start_gpio + 3} .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++) { for (int i = 0; i < SOC_CPU_CORES_NUM; i++) {
xSemaphoreTake(sem, pdMS_TO_TICKS(1000)); xSemaphoreTake(sem, pdMS_TO_TICKS(1000));
} }
vSemaphoreDelete(sem); 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) 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(); 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)); TEST_ESP_OK(dedic_gpio_del_bundle(bundle));
vSemaphoreDelete(sem); vSemaphoreDelete(sem);
} }
#endif // #if SOC_DEDICATED_GPIO_SUPPORTED

View File

@@ -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 <stdio.h>
#include <string.h>
#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);
}

View File

@@ -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

View File

@@ -3,16 +3,18 @@
* *
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
#include "freertos/FreeRTOS.h" #include "freertos/FreeRTOS.h"
#include "freertos/task.h" #include "freertos/task.h"
#include "unity.h" #include "unity.h"
#include "test_utils.h"
#include "soc/soc_caps.h" #include "soc/soc_caps.h"
#if SOC_SIGMADELTA_SUPPORTED
#include "driver/sigmadelta.h" #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_config_t sigmadelta_cfg = {
.sigmadelta_prescale = 80, .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 // 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) // 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 = { sigmadelta_config_t sigmadelta_cfg = {
.channel = 0, .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)); TEST_ESP_OK(sigmadelta_set_pin(sigmadelta_cfg.channel, 5));
vTaskDelay(3000 / portTICK_PERIOD_MS); vTaskDelay(3000 / portTICK_PERIOD_MS);
} }
#endif // SOC_SIGMADELTA_SUPPORTED

View File

@@ -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()

View File

@@ -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

View File

@@ -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

View File

@@ -0,0 +1,2 @@
CONFIG_FREERTOS_HZ=1000
CONFIG_ESP_TASK_WDT=n

View File

@@ -1,16 +1,8 @@
// Copyright 2020 Espressif Systems (Shanghai) PTE LTD /*
// * SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD
// Licensed under the Apache License, Version 2.0 (the "License"); *
// you may not use this file except in compliance with the License. * SPDX-License-Identifier: Apache-2.0
// 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.
#ifndef _ROM_GPIO_H_ #ifndef _ROM_GPIO_H_
#define _ROM_GPIO_H_ #define _ROM_GPIO_H_
@@ -20,6 +12,7 @@
#include "esp_attr.h" #include "esp_attr.h"
#include "soc/gpio_reg.h" #include "soc/gpio_reg.h"
#include "sdkconfig.h"
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
@@ -39,8 +32,13 @@ extern "C" {
#define GPIO_ID_PIN(n) (GPIO_ID_PIN0+(n)) #define GPIO_ID_PIN(n) (GPIO_ID_PIN0+(n))
#define GPIO_PIN_ADDR(i) (GPIO_PIN0_REG + i*4) #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_HIGH 0x38
#define GPIO_FUNC_IN_LOW 0x3C #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) \ #define GPIO_ID_IS_PIN_REGISTER(reg_id) \
((reg_id >= GPIO_ID_PIN0) && (reg_id <= GPIO_ID_PIN(GPIO_PIN_COUNT-1))) ((reg_id >= GPIO_ID_PIN0) && (reg_id <= GPIO_ID_PIN(GPIO_PIN_COUNT-1)))

View File

@@ -7,6 +7,7 @@
#include "nvs_flash.h" #include "nvs_flash.h"
#include "test_utils.h" #include "test_utils.h"
#include "freertos/event_groups.h" #include "freertos/event_groups.h"
#include "unity_test_utils.h"
#define GOT_IP_EVENT 0x00000001 #define GOT_IP_EVENT 0x00000001
#define DISCONNECT_EVENT 0x00000002 #define DISCONNECT_EVENT 0x00000002
@@ -116,7 +117,7 @@ TEST_CASE("wifi driver can start on APP CPU", "[wifi_init]")
xSemaphoreTake(sema, portMAX_DELAY); xSemaphoreTake(sema, portMAX_DELAY);
vSemaphoreDelete(sema); vSemaphoreDelete(sema);
sema = NULL; sema = NULL;
test_utils_task_delete(th); unity_utils_task_delete(th);
} }
static void wifi_start_stop_task(void* arg) 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); xSemaphoreTake(sema, portMAX_DELAY);
vSemaphoreDelete(sema); vSemaphoreDelete(sema);
sema = NULL; sema = NULL;
test_utils_task_delete(th); unity_utils_task_delete(th);
} }
static void wifi_stop_task(void* arg) 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); xSemaphoreTake(sema, portMAX_DELAY);
vSemaphoreDelete(sema); vSemaphoreDelete(sema);
sema = NULL; sema = NULL;
test_utils_task_delete(th); unity_utils_task_delete(th);
} }
static void wifi_deinit_task(void* arg) 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); xSemaphoreTake(sema, portMAX_DELAY);
vSemaphoreDelete(sema); vSemaphoreDelete(sema);
sema = NULL; sema = NULL;
test_utils_task_delete(th); unity_utils_task_delete(th);
} }

View File

@@ -1,16 +1,8 @@
// Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD /*
// * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
// Licensed under the Apache License, Version 2.0 (the "License"); *
// you may not use this file except in compliance with the License. * SPDX-License-Identifier: Apache-2.0
// 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.
// The HAL layer for GPIO (common part) // The HAL layer for GPIO (common part)

View File

@@ -24,3 +24,5 @@ entries:
systimer_hal (noflash) systimer_hal (noflash)
if GPTIMER_CTRL_FUNC_IN_IRAM = y: if GPTIMER_CTRL_FUNC_IN_IRAM = y:
timer_hal_iram (noflash) timer_hal_iram (noflash)
if GPIO_CTRL_FUNC_IN_IRAM = y:
gpio_hal: gpio_hal_intr_disable (noflash)

View File

@@ -24,6 +24,8 @@
#include "esp_heap_caps.h" #include "esp_heap_caps.h"
#include "esp_rom_gpio.h" #include "esp_rom_gpio.h"
#include "test_utils.h" #include "test_utils.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "soc/gpio_sig_map.h" #include "soc/gpio_sig_map.h"
#include "soc/gpio_reg.h" #include "soc/gpio_reg.h"

View File

@@ -130,6 +130,9 @@
#define SD_DATA2_GPIO_NUM 9 #define SD_DATA2_GPIO_NUM 9
#define SD_DATA3_GPIO_NUM 10 #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_RTC_GPIO_NUM 5
#define MAX_PAD_GPIO_NUM 21 #define MAX_PAD_GPIO_NUM 21
#define MAX_GPIO_NUM 25 #define MAX_GPIO_NUM 25

View File

@@ -149,6 +149,9 @@
#define SD_DATA2_GPIO_NUM 9 #define SD_DATA2_GPIO_NUM 9
#define SD_DATA3_GPIO_NUM 10 #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_RTC_GPIO_NUM 5
#define MAX_PAD_GPIO_NUM 40 #define MAX_PAD_GPIO_NUM 40
#define MAX_GPIO_NUM 44 #define MAX_GPIO_NUM 44

View File

@@ -124,6 +124,9 @@
#define SPI_D_GPIO_NUM 18 #define SPI_D_GPIO_NUM 18
#define SPI_Q_GPIO_NUM 14 #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_RTC_GPIO_NUM 12 // GPIO7~12 are the rtc_io pads
#define MAX_PAD_GPIO_NUM 25 #define MAX_PAD_GPIO_NUM 25
#define MAX_GPIO_NUM 29 #define MAX_GPIO_NUM 29

View File

@@ -1,26 +1,25 @@
// Copyright 2020 Espressif Systems (Shanghai) PTE LTD /*
// * SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD
// Licensed under the Apache License, Version 2.0 (the "License"); *
// you may not use this file except in compliance with the License. * SPDX-License-Identifier: Apache-2.0
// 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.
#pragma once #pragma once
#include "sdkconfig.h"
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #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_ONE_INPUT (0x1E)
#define GPIO_MATRIX_CONST_ZERO_INPUT (0x1F) #define GPIO_MATRIX_CONST_ZERO_INPUT (0x1F)
#endif
#ifdef __cplusplus #ifdef __cplusplus
} }

View File

@@ -154,6 +154,8 @@
#define SD_DATA1_GPIO_NUM 14 #define SD_DATA1_GPIO_NUM 14
#define SD_DATA2_GPIO_NUM 9 #define SD_DATA2_GPIO_NUM 9
#define SD_DATA3_GPIO_NUM 10 #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_RTC_GPIO_NUM 21
#define MAX_PAD_GPIO_NUM 48 #define MAX_PAD_GPIO_NUM 48

View File

@@ -17,6 +17,7 @@
#include <esp_spi_flash.h> #include <esp_spi_flash.h>
#include "../cache_utils.h" #include "../cache_utils.h"
#include "soc/timer_periph.h" #include "soc/timer_periph.h"
#include "esp_attr.h"
#include "esp_heap_caps.h" #include "esp_heap_caps.h"
#include "esp_rom_spiflash.h" #include "esp_rom_spiflash.h"
#if CONFIG_IDF_TARGET_ESP32 #if CONFIG_IDF_TARGET_ESP32

View File

@@ -8,6 +8,7 @@
#include "lwip/sockets.h" #include "lwip/sockets.h"
#include "freertos/event_groups.h" #include "freertos/event_groups.h"
#include "tcp_transport_fixtures.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 // 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); 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->tcp_connect_task);
vTaskSuspend(t->listener_task); vTaskSuspend(t->listener_task);
vEventGroupDelete(t->tcp_connect_done); vEventGroupDelete(t->tcp_connect_done);
test_utils_task_delete(t->tcp_connect_task); unity_utils_task_delete(t->tcp_connect_task);
test_utils_task_delete(t->listener_task); unity_utils_task_delete(t->listener_task);
free(t); free(t);
} }

View File

@@ -15,6 +15,7 @@ endif()
if(CONFIG_UNITY_ENABLE_IDF_TEST_RUNNER) if(CONFIG_UNITY_ENABLE_IDF_TEST_RUNNER)
list(APPEND srcs "unity_runner.c") list(APPEND srcs "unity_runner.c")
list(APPEND srcs "unity_utils_freertos.c")
endif() endif()
if(CONFIG_UNITY_ENABLE_FIXTURE) if(CONFIG_UNITY_ENABLE_FIXTURE)

View File

@@ -1,16 +1,8 @@
// Copyright 2016-2018 Espressif Systems (Shanghai) PTE LTD /*
// * SPDX-FileCopyrightText: 2016-2022 Espressif Systems (Shanghai) CO LTD
// Licensed under the Apache License, Version 2.0 (the "License"); *
// you may not use this file except in compliance with the License. * SPDX-License-Identifier: Apache-2.0
// 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.
#pragma once #pragma once
#include <stdint.h> #include <stdint.h>
@@ -131,7 +123,6 @@ void unity_testcase_register(test_desc_t* desc);
} }
/* /*
* First argument is a free-form description, * First argument is a free-form description,
* second argument is (by convention) a list of identifiers, each one in square brackets. * 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_) ); \ 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 * 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. * in the same order as the fields are declared in the structure.

View File

@@ -0,0 +1,26 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdint.h>
#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

View File

@@ -0,0 +1,69 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <string.h>
#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
}

View File

@@ -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/efuse.h
components/esp_rom/include/esp32h2/rom/esp_flash.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/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/hmac.h
components/esp_rom/include/esp32h2/rom/libc_stubs.h components/esp_rom/include/esp32h2/rom/libc_stubs.h
components/esp_rom/include/esp32h2/rom/lldesc.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_ll.h
components/hal/esp32s3/include/hal/usb_serial_jtag_ll.h components/hal/esp32s3/include/hal/usb_serial_jtag_ll.h
components/hal/esp32s3/interrupt_descriptor_table.c components/hal/esp32s3/interrupt_descriptor_table.c
components/hal/gpio_hal.c
components/hal/include/hal/aes_hal.h components/hal/include/hal/aes_hal.h
components/hal/include/hal/aes_types.h components/hal/include/hal/aes_types.h
components/hal/include/hal/brownout_hal.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/efuse_struct.h
components/soc/esp32h2/include/soc/extmem_reg.h components/soc/esp32h2/include/soc/extmem_reg.h
components/soc/esp32h2/include/soc/fe_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_reg.h
components/soc/esp32h2/include/soc/gpio_sd_struct.h components/soc/esp32h2/include/soc/gpio_sd_struct.h
components/soc/esp32h2/include/soc/hwcrypto_reg.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/priv/setjmp.h
components/unity/include/unity_config.h components/unity/include/unity_config.h
components/unity/include/unity_fixture_extras.h components/unity/include/unity_fixture_extras.h
components/unity/include/unity_test_runner.h
components/unity/unity_runner.c components/unity/unity_runner.c
components/usb/test/hcd/test_hcd_ctrl.c components/usb/test/hcd/test_hcd_ctrl.c
components/vfs/include/esp_vfs_common.h components/vfs/include/esp_vfs_common.h

View File

@@ -11,8 +11,6 @@
#include <stdint.h> #include <stdint.h>
#include <esp_partition.h> #include <esp_partition.h>
#include "sdkconfig.h" #include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "unity.h" #include "unity.h"
#include "soc/soc_caps.h" #include "soc/soc_caps.h"
/* include performance pass standards header file */ /* 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); 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 #ifdef __cplusplus
} }
#endif #endif

View File

@@ -13,10 +13,6 @@
#include "lwip/sockets.h" #include "lwip/sockets.h"
#include "sdkconfig.h" #include "sdkconfig.h"
#include "memory_checks.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) 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); 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
}