esp-idf/components/driver/test_apps/gpio/main/test_gpio.c

881 lines
39 KiB
C
Raw Normal View History

2021-05-23 19:06:17 -04:00
/*
2023-07-18 05:44:14 -04:00
* SPDX-FileCopyrightText: 2021-2023 Espressif Systems (Shanghai) CO LTD
2021-05-23 19:06:17 -04:00
*
* SPDX-License-Identifier: Apache-2.0
*/
2017-12-16 21:19:51 -05:00
/**
* 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
2017-12-16 21:19:51 -05:00
*/
2017-12-16 21:19:51 -05:00
#include <stdio.h>
#include <string.h>
#include "test_gpio.h"
2017-12-16 21:19:51 -05:00
#include "esp_system.h"
#include "esp_cpu.h"
2017-12-16 21:19:51 -05:00
#include "esp_sleep.h"
#include "unity.h"
#include "unity_test_utils.h"
2017-12-16 21:19:51 -05:00
#include "driver/gpio.h"
#include "hal/gpio_ll.h"
#include "soc/gpio_periph.h"
2017-12-16 21:19:51 -05:00
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "freertos/semphr.h"
2019-08-06 05:59:26 -04:00
#include "sdkconfig.h"
#include "esp_rom_uart.h"
#include "esp_rom_sys.h"
#include "spi_flash_mmap.h"
#include "esp_attr.h"
#include "esp_private/spi_flash_os.h"
2017-12-16 21:19:51 -05:00
// 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)
2017-12-16 21:19:51 -05:00
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
2017-12-16 21:19:51 -05:00
/**
* Do some initialization operation in this function
* @param num it is the destination GPIO wanted to be initialized
2017-12-16 21:19:51 -05:00
*/
static gpio_config_t test_init_io(gpio_num_t num)
2017-12-16 21:19:51 -05:00
{
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,
};
2017-12-16 21:19:51 -05:00
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)
2017-12-16 21:19:51 -05:00
{
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));
2017-12-16 21:19:51 -05:00
}
// test the basic configuration function with right parameters and error parameters
TEST_CASE("GPIO_config_parameters_test", "[gpio]")
2017-12-16 21:19:51 -05:00
{
gpio_config_t io_config = { 0 };
io_config.intr_type = GPIO_INTR_DISABLE;
2017-12-16 21:19:51 -05:00
// 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);
2017-12-16 21:19:51 -05:00
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);
2017-12-16 21:19:51 -05:00
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);
2017-12-16 21:19:51 -05:00
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
2017-12-16 21:19:51 -05:00
}
// 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, esp_cpu_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]")
2017-12-16 21:19:51 -05:00
{
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));
2017-12-16 21:19:51 -05:00
// Rising edge intr
TEST_ESP_OK(gpio_set_intr_type(TEST_GPIO_INPUT_OUTPUT_IO1, GPIO_INTR_POSEDGE));
2017-12-16 21:19:51 -05:00
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);
2017-12-16 21:19:51 -05:00
gpio_uninstall_isr_service();
}
TEST_CASE("GPIO_falling_edge_interrupt_test", "[gpio]")
2017-12-16 21:19:51 -05:00
{
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));
2017-12-16 21:19:51 -05:00
gpio_set_intr_type(TEST_GPIO_INPUT_OUTPUT_IO1, GPIO_INTR_NEGEDGE);
2017-12-16 21:19:51 -05:00
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);
2017-12-16 21:19:51 -05:00
gpio_uninstall_isr_service();
}
TEST_CASE("GPIO_both_rising_and_falling_edge_interrupt_test", "[gpio]")
2017-12-16 21:19:51 -05:00
{
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));
2017-12-16 21:19:51 -05:00
int level = 0;
gpio_set_intr_type(TEST_GPIO_INPUT_OUTPUT_IO1, GPIO_INTR_ANYEDGE);
2017-12-16 21:19:51 -05:00
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) {
2017-12-16 21:19:51 -05:00
level = level + 1;
gpio_set_level(TEST_GPIO_INPUT_OUTPUT_IO1, level * 0.2);
if (level > 10) {
2017-12-16 21:19:51 -05:00
break;
}
vTaskDelay(100 / portTICK_PERIOD_MS);
2017-12-16 21:19:51 -05:00
}
vTaskDelay(100 / portTICK_PERIOD_MS);
// For falling edge in GPIO_INTR_ANYEDGE
while (1) {
2017-12-16 21:19:51 -05:00
level = level - 1;
gpio_set_level(TEST_GPIO_INPUT_OUTPUT_IO1, level / 5);
if (level < 0) {
2017-12-16 21:19:51 -05:00
break;
}
vTaskDelay(100 / portTICK_PERIOD_MS);
2017-12-16 21:19:51 -05:00
}
vTaskDelay(100 / portTICK_PERIOD_MS);
TEST_ASSERT_EQUAL_INT(2, edge_intr_times);
gpio_isr_handler_remove(TEST_GPIO_INPUT_OUTPUT_IO1);
2017-12-16 21:19:51 -05:00
gpio_uninstall_isr_service();
}
TEST_CASE("GPIO_input_high_level_trigger_cut_the_interrupt_source_exit_interrupt_test", "[gpio]")
2017-12-16 21:19:51 -05:00
{
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));
2017-12-16 21:19:51 -05:00
gpio_set_intr_type(TEST_GPIO_INPUT_OUTPUT_IO1, GPIO_INTR_HIGH_LEVEL);
2017-12-16 21:19:51 -05:00
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);
2017-12-16 21:19:51 -05:00
gpio_uninstall_isr_service();
}
TEST_CASE("GPIO_low_level_interrupt_test", "[gpio]")
2017-12-16 21:19:51 -05:00
{
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));
2017-12-16 21:19:51 -05:00
gpio_set_intr_type(TEST_GPIO_INPUT_OUTPUT_IO1, GPIO_INTR_LOW_LEVEL);
2017-12-16 21:19:51 -05:00
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);
2017-12-16 21:19:51 -05:00
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]")
2017-12-16 21:19:51 -05:00
{
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++;
}
2017-12-16 21:19:51 -05:00
/** 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));
2017-12-16 21:19:51 -05:00
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));
2017-12-16 21:19:51 -05:00
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);
2017-12-16 21:19:51 -05:00
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));
2017-12-16 21:19:51 -05:00
}
#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;
2017-12-16 21:19:51 -05:00
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
2017-12-16 21:19:51 -05:00
gpio_set_level(TEST_GPIO_EXT_OUT_IO, 0);
vTaskDelay(100 / portTICK_PERIOD_MS);
2017-12-16 21:19:51 -05:00
// 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);
2017-12-16 21:19:51 -05:00
// 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!");
2017-12-16 21:19:51 -05:00
}
// 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]")
2017-12-16 21:19:51 -05:00
{
#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!");
2017-12-16 21:19:51 -05:00
// 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!");
2017-12-16 21:19:51 -05:00
}
TEST_CASE("GPIO_io_pull_up/down_function", "[gpio]")
2017-12-16 21:19:51 -05:00
{
// 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);
2017-12-16 21:19:51 -05:00
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");
2017-12-16 21:19:51 -05:00
}
// This test case tests whether gpio_set_level() outputs correctly with all gpio modes (gpio_mode_t)
TEST_CASE("GPIO_mode_test", "[gpio]")
2017-12-16 21:19:51 -05:00
{
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);
2017-12-16 21:19:51 -05:00
gpio_config(&output_io);
gpio_config(&input_io);
int level = gpio_get_level(TEST_GPIO_EXT_IN_IO);
2017-12-16 21:19:51 -05:00
// 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");
2017-12-16 21:19:51 -05:00
// 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");
2017-12-16 21:19:51 -05:00
// 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);
vTaskDelay(100 / portTICK_PERIOD_MS);
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);
vTaskDelay(100 / portTICK_PERIOD_MS);
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");
2017-12-16 21:19:51 -05:00
// 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_PULLDOWN_ONLY);
vTaskDelay(100 / portTICK_PERIOD_MS);
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");
gpio_set_pull_mode(TEST_GPIO_EXT_OUT_IO, GPIO_PULLUP_ONLY);
vTaskDelay(100 / portTICK_PERIOD_MS);
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");
// 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");
2017-12-16 21:19:51 -05:00
// 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");
2017-12-16 21:19:51 -05:00
}
static void prompt_to_continue(const char *str)
2017-12-16 21:19:51 -05:00
{
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);
2017-12-16 21:19:51 -05:00
}
}
// 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("GPIO_verify_only_the_gpio_with_input_ability_can_be_set_pull/down", "[gpio][ignore]")
2017-12-16 21:19:51 -05:00
{
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);
2017-12-16 21:19:51 -05:00
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));
2017-12-16 21:19:51 -05:00
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));
2017-12-16 21:19:51 -05:00
// 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));
2017-12-16 21:19:51 -05:00
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));
2017-12-16 21:19:51 -05:00
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));
2017-12-16 21:19:51 -05:00
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));
2017-12-16 21:19:51 -05:00
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));
2017-12-16 21:19:51 -05:00
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));
2017-12-16 21:19:51 -05:00
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));
2017-12-16 21:19:51 -05:00
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));
2017-12-16 21:19:51 -05:00
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);
}
2017-12-16 21:19:51 -05:00
/**
* 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
2017-12-16 21:19:51 -05:00
*
* all of these cases should be ignored that it will not run in CI
*/
TEST_CASE("GPIO_drive_capability_test", "[gpio][ignore]")
2017-12-16 21:19:51 -05:00
{
printf("weak capability test! please view the current change!\n");
drive_capability_set_get(TEST_GPIO_EXT_OUT_IO, GPIO_DRIVE_CAP_0);
2017-12-16 21:19:51 -05:00
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);
2017-12-16 21:19:51 -05:00
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);
2017-12-16 21:19:51 -05:00
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);
2017-12-16 21:19:51 -05:00
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);
2017-12-16 21:19:51 -05:00
prompt_to_continue("If this test finishes");
}
2023-07-18 05:44:14 -04:00
#if SOC_USB_SERIAL_JTAG_SUPPORTED // TODO: replace with a more proper soc_caps (USB_PHY_INTERNAL_NUM > 0)
TEST_CASE("GPIO_input_and_output_of_USB_pins_test", "[gpio]")
{
2023-07-18 05:44:14 -04:00
const int test_pins[] = {USB_INT_PHY0_DP_GPIO_NUM,
USB_INT_PHY0_DM_GPIO_NUM,
#if CONFIG_IDF_TARGET_ESP32P4 // TODO: Use proper soc_caps macro
USB_INT_PHY1_DP_GPIO_NUM,
USB_INT_PHY1_DM_GPIO_NUM
#endif
};
for (int i = 0; i < sizeof(test_pins) / sizeof(int); i++) {
int pin = test_pins[i];
2023-07-18 05:44:14 -04:00
gpio_config_t io_conf = {
.intr_type = GPIO_INTR_DISABLE,
.mode = GPIO_MODE_INPUT_OUTPUT,
.pin_bit_mask = BIT64(pin),
.pull_down_en = 0,
.pull_up_en = 0,
};
gpio_config(&io_conf);
// test pin
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!");
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!");
}
}
TEST_CASE("GPIO_USB_DP_pin_pullup_disable_test", "[gpio]")
{
// This test ensures the USB D+ pin pull-up can be disabled
// The pull-up value of the D+ pin is controlled by the pin's pull-up value together with the USB pull-up value.
// If any one of the pull-up value is 1, the pins pull-up resistor will be enabled.
// USB D+ pull-up value is default to 1 (USB_SERIAL_JTAG_DP_PULLUP)
// Therefore, when D+ pin's pull-up value is set to 0, it will also clear USB D+ pull-up value to allow
// its full functionality as a normal gpio pin
2023-07-18 05:44:14 -04:00
const int test_pins[] = {USB_INT_PHY0_DP_GPIO_NUM,
#if CONFIG_IDF_TARGET_ESP32P4 // TODO: Use proper soc_caps macro
USB_INT_PHY1_DP_GPIO_NUM,
#endif
};
2023-07-18 05:44:14 -04:00
for (int i = 0; i < sizeof(test_pins) / sizeof(int); i++) {
int pin = test_pins[i];
gpio_config_t input_io = test_init_io(pin);
input_io.mode = GPIO_MODE_INPUT;
input_io.pull_up_en = 0;
input_io.pull_down_en = 1;
gpio_config(&input_io);
TEST_ASSERT_EQUAL_INT(0, gpio_get_level(pin));
}
}
#endif //SOC_USB_SERIAL_JTAG_SUPPORTED
2023-07-18 05:44:14 -04:00
#if !TEMPORARY_DISABLED_FOR_TARGETS(ESP32P4) // TODO: IDF-7528 Remove when light sleep is supported on ESP32P4
// 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("GPIO_light_sleep_wake_up_test", "[gpio][ignore]")
{
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);
}
2023-07-18 05:44:14 -04:00
#endif //!TEMPORARY_DISABLED_FOR_TARGETS(...)