Merge branch 'feature/gpio_add_direction' into 'master'

feat(gpio): allow appending mode to IOs

See merge request espressif/esp-idf!33181
This commit is contained in:
Song Ruo Jing 2024-09-12 18:20:11 +08:00
commit 2cef80666b
28 changed files with 349 additions and 88 deletions

View File

@ -0,0 +1,25 @@
# GPIO API usage in Peripheral Drivers
When a peripheral driver initializes IOs, the general rule is to append necessary configurations onto the IO, instead of using any GPIO APIs that overwrite the current IO setting, such as `gpio_config`, `gpio_set_direction`, `gpio_set_pull_mode`.
Use `gpio_pullup_en` and `gpio_pulldown_en` to enable the internal pull-up/pull-down resistors if necessary.
Use `gpio_od_enable` to configure an output IO as open-drain mode if necessary.
## Configure an IO as a peripheral signal output
There is no need to enable the output for the IO explicitly, it is done internally in the following function calls.
If the signal is routed through IO MUX to the pin, then only needs to call `gpio_iomux_out` to select the IO MUX function index.
If the signal is routed through GPIO Matrix to the pin, then first call `gpio_func_sel` to let the pin use `PIN_FUNC_GPIO` function, follow by calling `esp_rom_gpio_connect_out_signal` to connect the signal.
When a peripheral driver does de-initialization, to de-configure the pin as the peripheral signal output, a call to `gpio_output_disable` is enough. It will disconnect the signal internally.
## Configure an IO as a peripheral signal input
If the signal is routed through IO MUX to the pin, then call `gpio_iomux_out` to select the IO MUX function index, and also call `gpio_iomux_in` to direct the signal to IO MUX. Input will be enabled for the IO internally.
If the signal is routed through GPIO Matrix to the pin, then first call `gpio_func_sel` to let the pin use `PIN_FUNC_GPIO` function, follow by calling `gpio_input_enable` and `esp_rom_gpio_connect_in_signal` to enable the input and connect the signal to the pin.
When a peripheral driver does de-initialization, to de-configure the pin as the peripheral signal input, use `esp_rom_gpio_connect_in_signal` to connect the signal to CONST_ONE or CONST_ZERO, so that it is disconnected from the pin. It is not desired to call `gpio_input_disable`, because there might be other drivers still using this pin as an input.

View File

@ -61,6 +61,8 @@ typedef struct {
* *
* @param pGPIOConfig Pointer to GPIO configure struct * @param pGPIOConfig Pointer to GPIO configure struct
* *
* @note This function always overwrite all the current IO configurations
*
* @return * @return
* - ESP_OK success * - ESP_OK success
* - ESP_ERR_INVALID_ARG Parameter error * - ESP_ERR_INVALID_ARG Parameter error
@ -157,11 +159,13 @@ int gpio_get_level(gpio_num_t gpio_num);
/** /**
* @brief GPIO set direction * @brief GPIO set direction
* *
* Configure GPIO direction,such as output_only,input_only,output_and_input * Configure GPIO mode,such as output_only,input_only,output_and_input
* *
* @param gpio_num Configure GPIO pins number, it should be GPIO number. If you want to set direction of e.g. GPIO16, gpio_num should be GPIO_NUM_16 (16); * @param gpio_num Configure GPIO pins number, it should be GPIO number. If you want to set direction of e.g. GPIO16, gpio_num should be GPIO_NUM_16 (16);
* @param mode GPIO direction * @param mode GPIO direction
* *
* @note This function always overwrite all the current modes that have applied on the IO pin
*
* @return * @return
* - ESP_OK Success * - ESP_OK Success
* - ESP_ERR_INVALID_ARG GPIO error * - ESP_ERR_INVALID_ARG GPIO error
@ -170,8 +174,20 @@ int gpio_get_level(gpio_num_t gpio_num);
esp_err_t gpio_set_direction(gpio_num_t gpio_num, gpio_mode_t mode); esp_err_t gpio_set_direction(gpio_num_t gpio_num, gpio_mode_t mode);
/** /**
* @brief Configure GPIO pull-up/pull-down resistors * @brief Enable input for an IO
* *
* @param gpio_num GPIO number
*
* @return
* - ESP_OK Success
* - ESP_ERR_INVALID_ARG GPIO number error
*/
esp_err_t gpio_input_enable(gpio_num_t gpio_num);
/**
* @brief Configure GPIO internal pull-up/pull-down resistors
*
* @note This function always overwrite the current pull-up/pull-down configurations
* @note ESP32: Only pins that support both input & output have integrated pull-up and pull-down resistors. Input-only GPIOs 34-39 do not. * @note ESP32: Only pins that support both input & output have integrated pull-up and pull-down resistors. Input-only GPIOs 34-39 do not.
* *
* @param gpio_num GPIO number. If you want to set pull up or down mode for e.g. GPIO16, gpio_num should be GPIO_NUM_16 (16); * @param gpio_num GPIO number. If you want to set pull up or down mode for e.g. GPIO16, gpio_num should be GPIO_NUM_16 (16);

View File

@ -52,6 +52,39 @@ esp_err_t gpio_sleep_pupd_config_unapply(gpio_num_t gpio_num);
*/ */
esp_err_t gpio_func_sel(gpio_num_t gpio_num, uint32_t func); esp_err_t gpio_func_sel(gpio_num_t gpio_num, uint32_t func);
/**
* @brief Disable output for an IO
*
* @param gpio_num GPIO number
*
* @return
* - ESP_OK Success
* - ESP_ERR_INVALID_ARG GPIO number error
*/
esp_err_t gpio_output_disable(gpio_num_t gpio_num);
/**
* @brief Enable open-drain for an IO
*
* @param gpio_num GPIO number
*
* @return
* - ESP_OK Success
* - ESP_ERR_INVALID_ARG GPIO number error
*/
esp_err_t gpio_od_disable(gpio_num_t gpio_num);
/**
* @brief Disable open-drain for an IO
*
* @param gpio_num GPIO number
*
* @return
* - ESP_OK Success
* - ESP_ERR_INVALID_ARG GPIO number error
*/
esp_err_t gpio_od_enable(gpio_num_t gpio_num);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View File

@ -20,7 +20,6 @@
#include "esp_log.h" #include "esp_log.h"
#include "esp_check.h" #include "esp_check.h"
#include "hal/gpio_hal.h" #include "hal/gpio_hal.h"
#include "esp_rom_gpio.h"
#include "esp_private/esp_gpio_reserve.h" #include "esp_private/esp_gpio_reserve.h"
#include "esp_private/io_mux.h" #include "esp_private/io_mux.h"
@ -197,36 +196,37 @@ static esp_err_t gpio_input_disable(gpio_num_t gpio_num)
return ESP_OK; return ESP_OK;
} }
static esp_err_t gpio_input_enable(gpio_num_t gpio_num) esp_err_t gpio_input_enable(gpio_num_t gpio_num)
{ {
GPIO_CHECK(GPIO_IS_VALID_GPIO(gpio_num), "GPIO number error", ESP_ERR_INVALID_ARG); GPIO_CHECK(GPIO_IS_VALID_GPIO(gpio_num), "GPIO number error", ESP_ERR_INVALID_ARG);
gpio_hal_input_enable(gpio_context.gpio_hal, gpio_num); gpio_hal_input_enable(gpio_context.gpio_hal, gpio_num);
return ESP_OK; return ESP_OK;
} }
static esp_err_t gpio_output_disable(gpio_num_t gpio_num) esp_err_t gpio_output_disable(gpio_num_t gpio_num)
{ {
GPIO_CHECK(GPIO_IS_VALID_GPIO(gpio_num), "GPIO number error", ESP_ERR_INVALID_ARG); GPIO_CHECK(GPIO_IS_VALID_GPIO(gpio_num), "GPIO number error", ESP_ERR_INVALID_ARG);
gpio_hal_output_disable(gpio_context.gpio_hal, gpio_num); gpio_hal_output_disable(gpio_context.gpio_hal, gpio_num);
gpio_hal_matrix_out_default(gpio_context.gpio_hal, gpio_num); // Ensure no other output signal is routed via GPIO matrix to this pin
return ESP_OK; return ESP_OK;
} }
static esp_err_t gpio_output_enable(gpio_num_t gpio_num) static esp_err_t gpio_output_enable(gpio_num_t gpio_num)
{ {
GPIO_CHECK(GPIO_IS_VALID_OUTPUT_GPIO(gpio_num), "GPIO output gpio_num error", ESP_ERR_INVALID_ARG); GPIO_CHECK(GPIO_IS_VALID_OUTPUT_GPIO(gpio_num), "GPIO output gpio_num error", ESP_ERR_INVALID_ARG);
gpio_hal_matrix_out_default(gpio_context.gpio_hal, gpio_num); // No peripheral output signal routed to the pin, just as a simple GPIO output
gpio_hal_output_enable(gpio_context.gpio_hal, gpio_num); gpio_hal_output_enable(gpio_context.gpio_hal, gpio_num);
esp_rom_gpio_connect_out_signal(gpio_num, SIG_GPIO_OUT_IDX, false, false);
return ESP_OK; return ESP_OK;
} }
static esp_err_t gpio_od_disable(gpio_num_t gpio_num) esp_err_t gpio_od_disable(gpio_num_t gpio_num)
{ {
GPIO_CHECK(GPIO_IS_VALID_GPIO(gpio_num), "GPIO number error", ESP_ERR_INVALID_ARG); GPIO_CHECK(GPIO_IS_VALID_GPIO(gpio_num), "GPIO number error", ESP_ERR_INVALID_ARG);
gpio_hal_od_disable(gpio_context.gpio_hal, gpio_num); gpio_hal_od_disable(gpio_context.gpio_hal, gpio_num);
return ESP_OK; return ESP_OK;
} }
static esp_err_t gpio_od_enable(gpio_num_t gpio_num) esp_err_t gpio_od_enable(gpio_num_t gpio_num)
{ {
GPIO_CHECK(GPIO_IS_VALID_GPIO(gpio_num), "GPIO number error", ESP_ERR_INVALID_ARG); GPIO_CHECK(GPIO_IS_VALID_GPIO(gpio_num), "GPIO number error", ESP_ERR_INVALID_ARG);
gpio_hal_od_enable(gpio_context.gpio_hal, gpio_num); gpio_hal_od_enable(gpio_context.gpio_hal, gpio_num);

View File

@ -238,7 +238,7 @@ TEST_CASE("RTCIO_output_hold_test", "[rtcio]")
#if SOC_DEEP_SLEEP_SUPPORTED && SOC_GPIO_SUPPORT_HOLD_IO_IN_DSLP #if SOC_DEEP_SLEEP_SUPPORTED && SOC_GPIO_SUPPORT_HOLD_IO_IN_DSLP
// It is not necessary to test every rtcio pin, it will take too much ci testing time for deep sleep // It is not necessary to test every rtcio pin, it will take too much ci testing time for deep sleep
// Only tests on s_test_map[TEST_RTCIO_DEEP_SLEEP_PIN_INDEX] pin // Only tests on s_test_map[TEST_RTCIO_DEEP_SLEEP_PIN_INDEX] pin
// These pads' default configuration is low level // The default configuration of these pads is low level
static void rtcio_deep_sleep_hold_test_first_stage(void) static void rtcio_deep_sleep_hold_test_first_stage(void)
{ {

View File

@ -652,8 +652,6 @@ esp_err_t ledc_timer_config(const ledc_timer_config_t *timer_conf)
esp_err_t _ledc_set_pin(int gpio_num, bool out_inv, ledc_mode_t speed_mode, ledc_channel_t channel) esp_err_t _ledc_set_pin(int gpio_num, bool out_inv, ledc_mode_t speed_mode, ledc_channel_t channel)
{ {
gpio_func_sel(gpio_num, PIN_FUNC_GPIO); gpio_func_sel(gpio_num, PIN_FUNC_GPIO);
gpio_set_level(gpio_num, out_inv);
gpio_set_direction(gpio_num, GPIO_MODE_OUTPUT);
// reserve the GPIO output path, because we don't expect another peripheral to signal to the same GPIO // reserve the GPIO output path, because we don't expect another peripheral to signal to the same GPIO
uint64_t old_gpio_rsv_mask = esp_gpio_reserve(BIT64(gpio_num)); uint64_t old_gpio_rsv_mask = esp_gpio_reserve(BIT64(gpio_num));
// check if the GPIO is already used by others, LEDC signal only uses the output path of the GPIO // check if the GPIO is already used by others, LEDC signal only uses the output path of the GPIO

View File

@ -83,7 +83,9 @@ typedef struct {
uint32_t invert_level_input: 1; /*!< Invert the input level signal */ uint32_t invert_level_input: 1; /*!< Invert the input level signal */
uint32_t virt_edge_io_level: 1; /*!< Virtual edge IO level, 0: low, 1: high. Only valid when edge_gpio_num is set to -1 */ uint32_t virt_edge_io_level: 1; /*!< Virtual edge IO level, 0: low, 1: high. Only valid when edge_gpio_num is set to -1 */
uint32_t virt_level_io_level: 1; /*!< Virtual level IO level, 0: low, 1: high. Only valid when level_gpio_num is set to -1 */ uint32_t virt_level_io_level: 1; /*!< Virtual level IO level, 0: low, 1: high. Only valid when level_gpio_num is set to -1 */
uint32_t io_loop_back: 1; /*!< For debug/test, the signal output from the GPIO will be fed to the input path as well */ uint32_t io_loop_back: 1; /*!< For debug/test, the signal output from the GPIO will be fed to the input path as well.
Note that this flag is deprecated, will be removed in IDF v6.0.
Instead, you can configure the output mode by calling gpio_config() first, and then do PCNT channel configuration. Necessary configurations for the IO to be used as the PCNT input will be appended. */
} flags; /*!< Channel config flags */ } flags; /*!< Channel config flags */
} pcnt_chan_config_t; } pcnt_chan_config_t;
@ -151,7 +153,9 @@ typedef struct {
int clear_signal_gpio_num; /*!< GPIO number used by the clear signal, the default active level is high, input mode with pull down enabled */ int clear_signal_gpio_num; /*!< GPIO number used by the clear signal, the default active level is high, input mode with pull down enabled */
struct { struct {
uint32_t invert_clear_signal: 1; /*!< Invert the clear input signal and set input mode with pull up */ uint32_t invert_clear_signal: 1; /*!< Invert the clear input signal and set input mode with pull up */
uint32_t io_loop_back: 1; /*!< For debug/test, the signal output from the GPIO will be fed to the input path as well */ uint32_t io_loop_back: 1; /*!< For debug/test, the signal output from the GPIO will be fed to the input path as well.
Note that this flag is deprecated, will be removed in IDF v6.0.
Instead, you can configure the output mode by calling gpio_config() first, and then do PCNT channel configuration. Necessary configurations for the IO to be used as the PCNT input will be appended. */
} flags; /*!< clear signal config flags */ } flags; /*!< clear signal config flags */
} pcnt_clear_signal_config_t; } pcnt_clear_signal_config_t;

View File

@ -29,6 +29,8 @@
#include "esp_private/esp_clk.h" #include "esp_private/esp_clk.h"
#include "esp_private/periph_ctrl.h" #include "esp_private/periph_ctrl.h"
#include "driver/gpio.h" #include "driver/gpio.h"
#include "esp_private/gpio.h"
#include "hal/gpio_ll.h" // for io_loop_back flag only
#include "driver/pulse_cnt.h" #include "driver/pulse_cnt.h"
#include "esp_memory_utils.h" #include "esp_memory_utils.h"
@ -329,27 +331,33 @@ esp_err_t pcnt_unit_set_clear_signal(pcnt_unit_handle_t unit, const pcnt_clear_s
pcnt_group_t *group = unit->group; pcnt_group_t *group = unit->group;
int group_id = group->group_id; int group_id = group->group_id;
int unit_id = unit->unit_id; int unit_id = unit->unit_id;
uint32_t clear_signal_idx = pcnt_periph_signals.groups[group_id].units[unit_id].clear_sig;
if (config) { if (config) {
gpio_config_t gpio_conf = { int io_num = config->clear_signal_gpio_num;
.intr_type = GPIO_INTR_DISABLE, ESP_RETURN_ON_FALSE(GPIO_IS_VALID_GPIO(io_num), ESP_ERR_INVALID_ARG, TAG, "gpio num is invalid");
.mode = GPIO_MODE_INPUT | (config->flags.io_loop_back ? GPIO_MODE_OUTPUT : 0), // also enable the output path if `io_loop_back` is enabled
.pull_down_en = true, if (!config->flags.invert_clear_signal) {
.pull_up_en = false, gpio_pulldown_en(io_num);
}; gpio_pullup_dis(io_num);
if (config->flags.invert_clear_signal) { } else {
gpio_conf.pull_down_en = false; gpio_pullup_en(io_num);
gpio_conf.pull_up_en = true; gpio_pulldown_dis(io_num);
} }
gpio_conf.pin_bit_mask = 1ULL << config->clear_signal_gpio_num; gpio_func_sel(io_num, PIN_FUNC_GPIO);
ESP_RETURN_ON_ERROR(gpio_config(&gpio_conf), TAG, "config zero signal GPIO failed"); gpio_input_enable(io_num);
esp_rom_gpio_connect_in_signal(config->clear_signal_gpio_num, esp_rom_gpio_connect_in_signal(io_num, clear_signal_idx, config->flags.invert_clear_signal);
pcnt_periph_signals.groups[group_id].units[unit_id].clear_sig,
config->flags.invert_clear_signal);
unit->clear_signal_gpio_num = config->clear_signal_gpio_num; unit->clear_signal_gpio_num = config->clear_signal_gpio_num;
// io_loop_back is a deprecated flag, workaround for compatibility
if (config->flags.io_loop_back) {
gpio_ll_output_enable(&GPIO, io_num);
}
} else { } else {
ESP_RETURN_ON_FALSE(unit->clear_signal_gpio_num >= 0, ESP_ERR_INVALID_STATE, TAG, "zero signal not set yet"); ESP_RETURN_ON_FALSE(unit->clear_signal_gpio_num >= 0, ESP_ERR_INVALID_STATE, TAG, "zero signal not set yet");
gpio_reset_pin(unit->clear_signal_gpio_num); esp_rom_gpio_connect_in_signal(GPIO_MATRIX_CONST_ZERO_INPUT, clear_signal_idx, 0);
gpio_pullup_dis(unit->clear_signal_gpio_num);
gpio_pulldown_dis(unit->clear_signal_gpio_num);
unit->clear_signal_gpio_num = -1; unit->clear_signal_gpio_num = -1;
} }
return ESP_OK; return ESP_OK;
@ -718,18 +726,19 @@ esp_err_t pcnt_new_channel(pcnt_unit_handle_t unit, const pcnt_chan_config_t *co
ESP_GOTO_ON_FALSE(channel_id != -1, ESP_ERR_NOT_FOUND, err, TAG, "no free channel in unit (%d,%d)", group_id, unit_id); ESP_GOTO_ON_FALSE(channel_id != -1, ESP_ERR_NOT_FOUND, err, TAG, "no free channel in unit (%d,%d)", group_id, unit_id);
// GPIO configuration // GPIO configuration
gpio_config_t gpio_conf = {
.intr_type = GPIO_INTR_DISABLE,
.mode = GPIO_MODE_INPUT | (config->flags.io_loop_back ? GPIO_MODE_OUTPUT : 0), // also enable the output path if `io_loop_back` is enabled
.pull_down_en = false,
.pull_up_en = true,
};
if (config->edge_gpio_num >= 0) { if (config->edge_gpio_num >= 0) {
gpio_conf.pin_bit_mask = 1ULL << config->edge_gpio_num; gpio_pullup_en(config->edge_gpio_num);
ESP_GOTO_ON_ERROR(gpio_config(&gpio_conf), err, TAG, "config edge GPIO failed"); gpio_pulldown_dis(config->edge_gpio_num);
gpio_func_sel(config->edge_gpio_num, PIN_FUNC_GPIO);
gpio_input_enable(config->edge_gpio_num);
esp_rom_gpio_connect_in_signal(config->edge_gpio_num, esp_rom_gpio_connect_in_signal(config->edge_gpio_num,
pcnt_periph_signals.groups[group_id].units[unit_id].channels[channel_id].pulse_sig, pcnt_periph_signals.groups[group_id].units[unit_id].channels[channel_id].pulse_sig,
config->flags.invert_edge_input); config->flags.invert_edge_input);
// io_loop_back is a deprecated flag, workaround for compatibility
if (config->flags.io_loop_back) {
gpio_ll_output_enable(&GPIO, config->edge_gpio_num);
}
} else { } else {
// using virtual IO // using virtual IO
esp_rom_gpio_connect_in_signal(config->flags.virt_edge_io_level ? GPIO_MATRIX_CONST_ONE_INPUT : GPIO_MATRIX_CONST_ZERO_INPUT, esp_rom_gpio_connect_in_signal(config->flags.virt_edge_io_level ? GPIO_MATRIX_CONST_ONE_INPUT : GPIO_MATRIX_CONST_ZERO_INPUT,
@ -738,11 +747,18 @@ esp_err_t pcnt_new_channel(pcnt_unit_handle_t unit, const pcnt_chan_config_t *co
} }
if (config->level_gpio_num >= 0) { if (config->level_gpio_num >= 0) {
gpio_conf.pin_bit_mask = 1ULL << config->level_gpio_num; gpio_pullup_en(config->level_gpio_num);
ESP_GOTO_ON_ERROR(gpio_config(&gpio_conf), err, TAG, "config level GPIO failed"); gpio_pulldown_dis(config->level_gpio_num);
gpio_func_sel(config->level_gpio_num, PIN_FUNC_GPIO);
gpio_input_enable(config->level_gpio_num);
esp_rom_gpio_connect_in_signal(config->level_gpio_num, esp_rom_gpio_connect_in_signal(config->level_gpio_num,
pcnt_periph_signals.groups[group_id].units[unit_id].channels[channel_id].control_sig, pcnt_periph_signals.groups[group_id].units[unit_id].channels[channel_id].control_sig,
config->flags.invert_level_input); config->flags.invert_level_input);
// io_loop_back is a deprecated flag, workaround for compatibility
if (config->flags.io_loop_back) {
gpio_ll_output_enable(&GPIO, config->level_gpio_num);
}
} else { } else {
// using virtual IO // using virtual IO
esp_rom_gpio_connect_in_signal(config->flags.virt_level_io_level ? GPIO_MATRIX_CONST_ONE_INPUT : GPIO_MATRIX_CONST_ZERO_INPUT, esp_rom_gpio_connect_in_signal(config->flags.virt_level_io_level ? GPIO_MATRIX_CONST_ONE_INPUT : GPIO_MATRIX_CONST_ZERO_INPUT,
@ -779,10 +795,18 @@ esp_err_t pcnt_del_channel(pcnt_channel_handle_t chan)
portEXIT_CRITICAL(&unit->spinlock); portEXIT_CRITICAL(&unit->spinlock);
if (chan->level_gpio_num >= 0) { if (chan->level_gpio_num >= 0) {
gpio_reset_pin(chan->level_gpio_num); esp_rom_gpio_connect_in_signal(GPIO_MATRIX_CONST_ONE_INPUT,
pcnt_periph_signals.groups[group_id].units[unit_id].channels[channel_id].control_sig,
0);
gpio_pullup_dis(chan->level_gpio_num);
gpio_pulldown_dis(chan->level_gpio_num);
} }
if (chan->edge_gpio_num >= 0) { if (chan->edge_gpio_num >= 0) {
gpio_reset_pin(chan->edge_gpio_num); esp_rom_gpio_connect_in_signal(GPIO_MATRIX_CONST_ONE_INPUT,
pcnt_periph_signals.groups[group_id].units[unit_id].channels[channel_id].pulse_sig,
0);
gpio_pullup_dis(chan->edge_gpio_num);
gpio_pulldown_dis(chan->edge_gpio_num);
} }
free(chan); free(chan);

View File

@ -85,6 +85,8 @@ TEST_CASE("pcnt_unit_install_uninstall", "[pcnt]")
TEST_CASE("pcnt_channel_install_uninstall", "[pcnt]") TEST_CASE("pcnt_channel_install_uninstall", "[pcnt]")
{ {
test_gpio_init_for_simulation(TEST_PCNT_GPIO_A);
pcnt_unit_config_t unit_config = { pcnt_unit_config_t unit_config = {
.low_limit = -100, .low_limit = -100,
.high_limit = 100, .high_limit = 100,
@ -92,7 +94,6 @@ TEST_CASE("pcnt_channel_install_uninstall", "[pcnt]")
pcnt_chan_config_t chan_config = { pcnt_chan_config_t chan_config = {
.edge_gpio_num = TEST_PCNT_GPIO_A, // only detect edge signal in this case .edge_gpio_num = TEST_PCNT_GPIO_A, // only detect edge signal in this case
.level_gpio_num = -1, .level_gpio_num = -1,
.flags.io_loop_back = true,
}; };
pcnt_unit_handle_t units[SOC_PCNT_UNITS_PER_GROUP]; pcnt_unit_handle_t units[SOC_PCNT_UNITS_PER_GROUP];
pcnt_channel_handle_t chans[SOC_PCNT_UNITS_PER_GROUP][SOC_PCNT_CHANNELS_PER_UNIT]; pcnt_channel_handle_t chans[SOC_PCNT_UNITS_PER_GROUP][SOC_PCNT_CHANNELS_PER_UNIT];
@ -172,6 +173,9 @@ TEST_CASE("pcnt_channel_install_uninstall", "[pcnt]")
TEST_CASE("pcnt_multiple_units_pulse_count", "[pcnt]") TEST_CASE("pcnt_multiple_units_pulse_count", "[pcnt]")
{ {
test_gpio_init_for_simulation(TEST_PCNT_GPIO_A);
test_gpio_init_for_simulation(TEST_PCNT_GPIO_B);
printf("install pcnt units\r\n"); printf("install pcnt units\r\n");
pcnt_unit_config_t unit_config = { pcnt_unit_config_t unit_config = {
.low_limit = -100, .low_limit = -100,
@ -186,7 +190,6 @@ TEST_CASE("pcnt_multiple_units_pulse_count", "[pcnt]")
const int channel_gpios[] = {TEST_PCNT_GPIO_A, TEST_PCNT_GPIO_B}; const int channel_gpios[] = {TEST_PCNT_GPIO_A, TEST_PCNT_GPIO_B};
pcnt_chan_config_t chan_config = { pcnt_chan_config_t chan_config = {
.level_gpio_num = -1, .level_gpio_num = -1,
.flags.io_loop_back = true,
}; };
pcnt_channel_handle_t chans[2]; pcnt_channel_handle_t chans[2];
for (int i = 0; i < 2; i++) { for (int i = 0; i < 2; i++) {
@ -238,6 +241,9 @@ static bool test_pcnt_quadrature_reach_watch_point(pcnt_unit_handle_t handle, co
TEST_CASE("pcnt_quadrature_decode_event", "[pcnt]") TEST_CASE("pcnt_quadrature_decode_event", "[pcnt]")
{ {
test_gpio_init_for_simulation(TEST_PCNT_GPIO_A);
test_gpio_init_for_simulation(TEST_PCNT_GPIO_B);
pcnt_unit_config_t unit_config = { pcnt_unit_config_t unit_config = {
.low_limit = -100, .low_limit = -100,
.high_limit = 100, .high_limit = 100,
@ -255,7 +261,6 @@ TEST_CASE("pcnt_quadrature_decode_event", "[pcnt]")
pcnt_chan_config_t channel_config = { pcnt_chan_config_t channel_config = {
.edge_gpio_num = TEST_PCNT_GPIO_A, .edge_gpio_num = TEST_PCNT_GPIO_A,
.level_gpio_num = TEST_PCNT_GPIO_B, .level_gpio_num = TEST_PCNT_GPIO_B,
.flags.io_loop_back = true,
}; };
pcnt_channel_handle_t channelA = NULL; pcnt_channel_handle_t channelA = NULL;
TEST_ESP_OK(pcnt_new_channel(unit, &channel_config, &channelA)); TEST_ESP_OK(pcnt_new_channel(unit, &channel_config, &channelA));
@ -367,6 +372,8 @@ static bool test_pcnt_on_zero_cross(pcnt_unit_handle_t handle, const pcnt_watch_
TEST_CASE("pcnt_zero_cross_mode", "[pcnt]") TEST_CASE("pcnt_zero_cross_mode", "[pcnt]")
{ {
test_gpio_init_for_simulation(TEST_PCNT_GPIO_A);
pcnt_unit_config_t unit_config = { pcnt_unit_config_t unit_config = {
.low_limit = -100, .low_limit = -100,
.high_limit = 100, .high_limit = 100,
@ -390,7 +397,6 @@ TEST_CASE("pcnt_zero_cross_mode", "[pcnt]")
pcnt_chan_config_t channel_config = { pcnt_chan_config_t channel_config = {
.edge_gpio_num = TEST_PCNT_GPIO_A, .edge_gpio_num = TEST_PCNT_GPIO_A,
.level_gpio_num = -1, .level_gpio_num = -1,
.flags.io_loop_back = true,
}; };
pcnt_channel_handle_t channelA = NULL; pcnt_channel_handle_t channelA = NULL;
pcnt_channel_handle_t channelB = NULL; pcnt_channel_handle_t channelB = NULL;
@ -459,6 +465,8 @@ TEST_CASE("pcnt_zero_cross_mode", "[pcnt]")
TEST_CASE("pcnt_virtual_io", "[pcnt]") TEST_CASE("pcnt_virtual_io", "[pcnt]")
{ {
test_gpio_init_for_simulation(TEST_PCNT_GPIO_A);
pcnt_unit_config_t unit_config = { pcnt_unit_config_t unit_config = {
.low_limit = -100, .low_limit = -100,
.high_limit = 100, .high_limit = 100,
@ -466,7 +474,6 @@ TEST_CASE("pcnt_virtual_io", "[pcnt]")
pcnt_chan_config_t chan_config = { pcnt_chan_config_t chan_config = {
.edge_gpio_num = TEST_PCNT_GPIO_A, // only detect edge signal in this case .edge_gpio_num = TEST_PCNT_GPIO_A, // only detect edge signal in this case
.level_gpio_num = -1, // level signal is connected to a virtual IO internally .level_gpio_num = -1, // level signal is connected to a virtual IO internally
.flags.io_loop_back = true,
.flags.virt_level_io_level = 1, // the level input is connected to high level, internally .flags.virt_level_io_level = 1, // the level input is connected to high level, internally
}; };
pcnt_unit_handle_t unit = NULL; pcnt_unit_handle_t unit = NULL;
@ -511,6 +518,9 @@ TEST_CASE("pcnt_virtual_io", "[pcnt]")
#if SOC_PCNT_SUPPORT_CLEAR_SIGNAL #if SOC_PCNT_SUPPORT_CLEAR_SIGNAL
TEST_CASE("pcnt_zero_input_signal", "[pcnt]") TEST_CASE("pcnt_zero_input_signal", "[pcnt]")
{ {
test_gpio_init_for_simulation(TEST_PCNT_GPIO_A);
test_gpio_init_for_simulation(TEST_PCNT_GPIO_Z);
pcnt_unit_config_t unit_config = { pcnt_unit_config_t unit_config = {
.low_limit = -1000, .low_limit = -1000,
.high_limit = 1000, .high_limit = 1000,
@ -522,7 +532,6 @@ TEST_CASE("pcnt_zero_input_signal", "[pcnt]")
pcnt_clear_signal_config_t clear_signal_config = { pcnt_clear_signal_config_t clear_signal_config = {
.clear_signal_gpio_num = TEST_PCNT_GPIO_Z, .clear_signal_gpio_num = TEST_PCNT_GPIO_Z,
.flags.io_loop_back = true,
}; };
TEST_ESP_OK(pcnt_unit_set_clear_signal(unit, &clear_signal_config)); TEST_ESP_OK(pcnt_unit_set_clear_signal(unit, &clear_signal_config));
@ -531,7 +540,6 @@ TEST_CASE("pcnt_zero_input_signal", "[pcnt]")
pcnt_chan_config_t chan_config = { pcnt_chan_config_t chan_config = {
.level_gpio_num = -1, .level_gpio_num = -1,
.edge_gpio_num = TEST_PCNT_GPIO_A, .edge_gpio_num = TEST_PCNT_GPIO_A,
.flags.io_loop_back = true,
}; };
pcnt_channel_handle_t channel; pcnt_channel_handle_t channel;
@ -590,6 +598,9 @@ TEST_CASE("pcnt_zero_input_signal", "[pcnt]")
TEST_CASE("pcnt overflow accumulation", "[pcnt]") TEST_CASE("pcnt overflow accumulation", "[pcnt]")
{ {
test_gpio_init_for_simulation(TEST_PCNT_GPIO_A);
test_gpio_init_for_simulation(TEST_PCNT_GPIO_B);
pcnt_unit_config_t unit_config = { pcnt_unit_config_t unit_config = {
.low_limit = -100, .low_limit = -100,
.high_limit = 100, .high_limit = 100,
@ -613,7 +624,6 @@ TEST_CASE("pcnt overflow accumulation", "[pcnt]")
pcnt_chan_config_t channel_config = { pcnt_chan_config_t channel_config = {
.edge_gpio_num = TEST_PCNT_GPIO_A, .edge_gpio_num = TEST_PCNT_GPIO_A,
.level_gpio_num = TEST_PCNT_GPIO_B, .level_gpio_num = TEST_PCNT_GPIO_B,
.flags.io_loop_back = true,
}; };
pcnt_channel_handle_t channelA = NULL; pcnt_channel_handle_t channelA = NULL;
TEST_ESP_OK(pcnt_new_channel(unit, &channel_config, &channelA)); TEST_ESP_OK(pcnt_new_channel(unit, &channel_config, &channelA));
@ -667,6 +677,9 @@ TEST_CASE("pcnt overflow accumulation", "[pcnt]")
#if SOC_PCNT_SUPPORT_STEP_NOTIFY #if SOC_PCNT_SUPPORT_STEP_NOTIFY
TEST_CASE("pcnt_step_notify_event", "[pcnt]") TEST_CASE("pcnt_step_notify_event", "[pcnt]")
{ {
test_gpio_init_for_simulation(TEST_PCNT_GPIO_A);
test_gpio_init_for_simulation(TEST_PCNT_GPIO_B);
pcnt_unit_config_t unit_config = { pcnt_unit_config_t unit_config = {
.low_limit = -100, .low_limit = -100,
.high_limit = 100, .high_limit = 100,
@ -687,7 +700,6 @@ TEST_CASE("pcnt_step_notify_event", "[pcnt]")
pcnt_chan_config_t channel_config = { pcnt_chan_config_t channel_config = {
.edge_gpio_num = TEST_PCNT_GPIO_A, .edge_gpio_num = TEST_PCNT_GPIO_A,
.level_gpio_num = TEST_PCNT_GPIO_B, .level_gpio_num = TEST_PCNT_GPIO_B,
.flags.io_loop_back = true,
}; };
pcnt_channel_handle_t channelA = NULL; pcnt_channel_handle_t channelA = NULL;
TEST_ESP_OK(pcnt_new_channel(unit, &channel_config, &channelA)); TEST_ESP_OK(pcnt_new_channel(unit, &channel_config, &channelA));

View File

@ -27,6 +27,7 @@ extern "C" {
#define TEST_PCNT_CALLBACK_ATTR #define TEST_PCNT_CALLBACK_ATTR
#endif // CONFIG_PCNT_ISR_IRAM_SAFE #endif // CONFIG_PCNT_ISR_IRAM_SAFE
void test_gpio_init_for_simulation(int gpio_sig);
void test_gpio_simulate_rising_edge(int gpio_sig, size_t times); void test_gpio_simulate_rising_edge(int gpio_sig, size_t times);
void test_gpio_simulate_quadrature_signals(int gpio_sig_a, int gpio_sig_b, size_t times); void test_gpio_simulate_quadrature_signals(int gpio_sig_a, int gpio_sig_b, size_t times);

View File

@ -35,6 +35,8 @@ static void IRAM_ATTR test_simulate_input_post_cache_disable(void *args)
TEST_CASE("pcnt_iram_interrupt_safe", "[pcnt]") TEST_CASE("pcnt_iram_interrupt_safe", "[pcnt]")
{ {
test_gpio_init_for_simulation(TEST_PCNT_GPIO_A);
pcnt_unit_config_t unit_config = { pcnt_unit_config_t unit_config = {
.low_limit = -100, .low_limit = -100,
.high_limit = 100 .high_limit = 100
@ -56,7 +58,6 @@ TEST_CASE("pcnt_iram_interrupt_safe", "[pcnt]")
pcnt_chan_config_t channel_config = { pcnt_chan_config_t channel_config = {
.edge_gpio_num = TEST_PCNT_GPIO_A, .edge_gpio_num = TEST_PCNT_GPIO_A,
.level_gpio_num = -1, .level_gpio_num = -1,
.flags.io_loop_back = true,
}; };
pcnt_channel_handle_t channelA = NULL; pcnt_channel_handle_t channelA = NULL;
pcnt_channel_handle_t channelB = NULL; pcnt_channel_handle_t channelB = NULL;

View File

@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD * SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
* *
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
@ -12,6 +12,16 @@
#include "esp_attr.h" #include "esp_attr.h"
#include "test_pulse_cnt_board.h" #include "test_pulse_cnt_board.h"
// helper function to initialize a gpio for simulation
void test_gpio_init_for_simulation(int gpio_sig)
{
gpio_config_t config = {
.mode = GPIO_MODE_OUTPUT,
.pin_bit_mask = 1ULL << gpio_sig,
};
gpio_config(&config);
}
// helper function to simulate several rising edges on gpio // helper function to simulate several rising edges on gpio
IRAM_ATTR void test_gpio_simulate_rising_edge(int gpio_sig, size_t times) IRAM_ATTR void test_gpio_simulate_rising_edge(int gpio_sig, size_t times)
{ {

View File

@ -376,10 +376,6 @@ static inline void gpio_ll_output_disable(gpio_dev_t *hw, uint32_t gpio_num)
} else { } else {
HAL_FORCE_MODIFY_U32_REG_FIELD(hw->enable1_w1tc, data, (0x1 << (gpio_num - 32))); HAL_FORCE_MODIFY_U32_REG_FIELD(hw->enable1_w1tc, data, (0x1 << (gpio_num - 32)));
} }
// Ensure no other output signal is routed via GPIO matrix to this pin
REG_WRITE(GPIO_FUNC0_OUT_SEL_CFG_REG + (gpio_num * 4),
SIG_GPIO_OUT_IDX);
} }
/** /**
@ -465,6 +461,18 @@ static inline void gpio_ll_od_enable(gpio_dev_t *hw, uint32_t gpio_num)
hw->pin[gpio_num].pad_driver = 1; hw->pin[gpio_num].pad_driver = 1;
} }
/**
* @brief Disconnect any peripheral output signal routed via GPIO matrix to the pin
*
* @param hw Peripheral GPIO hardware instance address.
* @param gpio_num GPIO number
*/
__attribute__((always_inline))
static inline void gpio_ll_matrix_out_default(gpio_dev_t *hw, uint32_t gpio_num)
{
REG_WRITE(GPIO_FUNC0_OUT_SEL_CFG_REG + (gpio_num * 4), SIG_GPIO_OUT_IDX);
}
/** /**
* @brief Select a function for the pin in the IOMUX * @brief Select a function for the pin in the IOMUX
* *

View File

@ -254,9 +254,6 @@ __attribute__((always_inline))
static inline void gpio_ll_output_disable(gpio_dev_t *hw, uint32_t gpio_num) static inline void gpio_ll_output_disable(gpio_dev_t *hw, uint32_t gpio_num)
{ {
hw->enable_w1tc.enable_w1tc = (0x1 << gpio_num); hw->enable_w1tc.enable_w1tc = (0x1 << gpio_num);
// Ensure no other output signal is routed via GPIO matrix to this pin
REG_WRITE(GPIO_FUNC0_OUT_SEL_CFG_REG + (gpio_num * 4),
SIG_GPIO_OUT_IDX);
} }
/** /**
@ -294,6 +291,21 @@ static inline void gpio_ll_od_enable(gpio_dev_t *hw, uint32_t gpio_num)
hw->pin[gpio_num].pad_driver = 1; hw->pin[gpio_num].pad_driver = 1;
} }
/**
* @brief Disconnect any peripheral output signal routed via GPIO matrix to the pin
*
* @param hw Peripheral GPIO hardware instance address.
* @param gpio_num GPIO number
*/
__attribute__((always_inline))
static inline void gpio_ll_matrix_out_default(gpio_dev_t *hw, uint32_t gpio_num)
{
gpio_func_out_sel_cfg_reg_t reg = {
.func_sel = SIG_GPIO_OUT_IDX,
};
hw->func_out_sel_cfg[gpio_num].val = reg.val;
}
/** /**
* @brief Select a function for the pin in the IOMUX * @brief Select a function for the pin in the IOMUX
* *

View File

@ -262,9 +262,6 @@ __attribute__((always_inline))
static inline void gpio_ll_output_disable(gpio_dev_t *hw, uint32_t gpio_num) static inline void gpio_ll_output_disable(gpio_dev_t *hw, uint32_t gpio_num)
{ {
hw->enable_w1tc.enable_w1tc = (0x1 << gpio_num); hw->enable_w1tc.enable_w1tc = (0x1 << gpio_num);
// Ensure no other output signal is routed via GPIO matrix to this pin
REG_WRITE(GPIO_FUNC0_OUT_SEL_CFG_REG + (gpio_num * 4),
SIG_GPIO_OUT_IDX);
} }
/** /**
@ -302,6 +299,18 @@ static inline void gpio_ll_od_enable(gpio_dev_t *hw, uint32_t gpio_num)
hw->pin[gpio_num].pad_driver = 1; hw->pin[gpio_num].pad_driver = 1;
} }
/**
* @brief Disconnect any peripheral output signal routed via GPIO matrix to the pin
*
* @param hw Peripheral GPIO hardware instance address.
* @param gpio_num GPIO number
*/
__attribute__((always_inline))
static inline void gpio_ll_matrix_out_default(gpio_dev_t *hw, uint32_t gpio_num)
{
REG_WRITE(GPIO_FUNC0_OUT_SEL_CFG_REG + (gpio_num * 4), SIG_GPIO_OUT_IDX);
}
/** /**
* @brief Select a function for the pin in the IOMUX * @brief Select a function for the pin in the IOMUX
* *

View File

@ -298,8 +298,6 @@ __attribute__((always_inline))
static inline void gpio_ll_output_disable(gpio_dev_t *hw, uint32_t gpio_num) static inline void gpio_ll_output_disable(gpio_dev_t *hw, uint32_t gpio_num)
{ {
hw->enable_w1tc.enable_w1tc = (0x1 << gpio_num); hw->enable_w1tc.enable_w1tc = (0x1 << gpio_num);
// Ensure no other output signal is routed via GPIO matrix to this pin
hw->func_out_sel_cfg[gpio_num].out_sel = SIG_GPIO_OUT_IDX;
} }
/** /**
@ -336,6 +334,21 @@ static inline void gpio_ll_od_enable(gpio_dev_t *hw, uint32_t gpio_num)
hw->pin[gpio_num].pad_driver = 1; hw->pin[gpio_num].pad_driver = 1;
} }
/**
* @brief Disconnect any peripheral output signal routed via GPIO matrix to the pin
*
* @param hw Peripheral GPIO hardware instance address.
* @param gpio_num GPIO number
*/
__attribute__((always_inline))
static inline void gpio_ll_matrix_out_default(gpio_dev_t *hw, uint32_t gpio_num)
{
gpio_func_out_sel_cfg_reg_t reg = {
.out_sel = SIG_GPIO_OUT_IDX,
};
hw->func_out_sel_cfg[gpio_num].val = reg.val;
}
/** /**
* @brief GPIO set output level * @brief GPIO set output level
* *

View File

@ -270,9 +270,6 @@ __attribute__((always_inline))
static inline void gpio_ll_output_disable(gpio_dev_t *hw, uint32_t gpio_num) static inline void gpio_ll_output_disable(gpio_dev_t *hw, uint32_t gpio_num)
{ {
hw->enable_w1tc.enable_w1tc = (0x1 << gpio_num); hw->enable_w1tc.enable_w1tc = (0x1 << gpio_num);
// Ensure no other output signal is routed via GPIO matrix to this pin
REG_WRITE(GPIO_FUNC0_OUT_SEL_CFG_REG + (gpio_num * 4),
SIG_GPIO_OUT_IDX);
} }
/** /**
@ -309,6 +306,21 @@ static inline void gpio_ll_od_enable(gpio_dev_t *hw, uint32_t gpio_num)
hw->pin[gpio_num].pad_driver = 1; hw->pin[gpio_num].pad_driver = 1;
} }
/**
* @brief Disconnect any peripheral output signal routed via GPIO matrix to the pin
*
* @param hw Peripheral GPIO hardware instance address.
* @param gpio_num GPIO number
*/
__attribute__((always_inline))
static inline void gpio_ll_matrix_out_default(gpio_dev_t *hw, uint32_t gpio_num)
{
gpio_func_out_sel_cfg_reg_t reg = {
.out_sel = SIG_GPIO_OUT_IDX,
};
hw->func_out_sel_cfg[gpio_num].val = reg.val;
}
/** /**
* @brief GPIO set output level * @brief GPIO set output level
* *

View File

@ -294,8 +294,6 @@ __attribute__((always_inline))
static inline void gpio_ll_output_disable(gpio_dev_t *hw, uint32_t gpio_num) static inline void gpio_ll_output_disable(gpio_dev_t *hw, uint32_t gpio_num)
{ {
hw->enable_w1tc.enable_w1tc = (0x1 << gpio_num); hw->enable_w1tc.enable_w1tc = (0x1 << gpio_num);
// Ensure no other output signal is routed via GPIO matrix to this pin
hw->funcn_out_sel_cfg[gpio_num].funcn_out_sel = SIG_GPIO_OUT_IDX;
} }
/** /**
@ -332,6 +330,21 @@ static inline void gpio_ll_od_enable(gpio_dev_t *hw, uint32_t gpio_num)
hw->pinn[gpio_num].pinn_pad_driver = 1; hw->pinn[gpio_num].pinn_pad_driver = 1;
} }
/**
* @brief Disconnect any peripheral output signal routed via GPIO matrix to the pin
*
* @param hw Peripheral GPIO hardware instance address.
* @param gpio_num GPIO number
*/
__attribute__((always_inline))
static inline void gpio_ll_matrix_out_default(gpio_dev_t *hw, uint32_t gpio_num)
{
gpio_funcn_out_sel_cfg_reg_t reg = {
.funcn_out_sel = SIG_GPIO_OUT_IDX,
};
hw->funcn_out_sel_cfg[gpio_num].val = reg.val;
}
/** /**
* @brief GPIO set output level * @brief GPIO set output level
* *

View File

@ -315,8 +315,6 @@ __attribute__((always_inline))
static inline void gpio_ll_output_disable(gpio_dev_t *hw, gpio_num_t gpio_num) static inline void gpio_ll_output_disable(gpio_dev_t *hw, gpio_num_t gpio_num)
{ {
hw->enable_w1tc.enable_w1tc = (0x1 << gpio_num); hw->enable_w1tc.enable_w1tc = (0x1 << gpio_num);
// Ensure no other output signal is routed via GPIO matrix to this pin
REG_WRITE(GPIO_FUNC0_OUT_SEL_CFG_REG + (gpio_num * 4), SIG_GPIO_OUT_IDX);
} }
/** /**
@ -353,6 +351,21 @@ static inline void gpio_ll_od_enable(gpio_dev_t *hw, gpio_num_t gpio_num)
hw->pin[gpio_num].pad_driver = 1; hw->pin[gpio_num].pad_driver = 1;
} }
/**
* @brief Disconnect any peripheral output signal routed via GPIO matrix to the pin
*
* @param hw Peripheral GPIO hardware instance address.
* @param gpio_num GPIO number
*/
__attribute__((always_inline))
static inline void gpio_ll_matrix_out_default(gpio_dev_t *hw, uint32_t gpio_num)
{
gpio_func_out_sel_cfg_reg_t reg = {
.out_sel = SIG_GPIO_OUT_IDX,
};
hw->func_out_sel_cfg[gpio_num].val = reg.val;
}
/** /**
* @brief GPIO set output level * @brief GPIO set output level
* *

View File

@ -333,8 +333,6 @@ static inline void gpio_ll_output_disable(gpio_dev_t *hw, uint32_t gpio_num)
} else { } else {
hw->enable1_w1tc.enable1_w1tc = (0x1 << (gpio_num - 32)); hw->enable1_w1tc.enable1_w1tc = (0x1 << (gpio_num - 32));
} }
// Ensure no other output signal is routed via GPIO matrix to this pin
hw->func_out_sel_cfg[gpio_num].out_sel = SIG_GPIO_OUT_IDX;
} }
/** /**
@ -375,6 +373,21 @@ static inline void gpio_ll_od_enable(gpio_dev_t *hw, uint32_t gpio_num)
hw->pin[gpio_num].pad_driver = 1; hw->pin[gpio_num].pad_driver = 1;
} }
/**
* @brief Disconnect any peripheral output signal routed via GPIO matrix to the pin
*
* @param hw Peripheral GPIO hardware instance address.
* @param gpio_num GPIO number
*/
__attribute__((always_inline))
static inline void gpio_ll_matrix_out_default(gpio_dev_t *hw, uint32_t gpio_num)
{
gpio_func_out_sel_cfg_reg_t reg = {
.out_sel = SIG_GPIO_OUT_IDX,
};
hw->func_out_sel_cfg[gpio_num].val = reg.val;
}
/** /**
* @brief GPIO set output level * @brief GPIO set output level
* *

View File

@ -260,10 +260,6 @@ static inline void gpio_ll_output_disable(gpio_dev_t *hw, uint32_t gpio_num)
} else { } else {
hw->enable1_w1tc.data = (0x1 << (gpio_num - 32)); hw->enable1_w1tc.data = (0x1 << (gpio_num - 32));
} }
// Ensure no other output signal is routed via GPIO matrix to this pin
REG_WRITE(GPIO_FUNC0_OUT_SEL_CFG_REG + (gpio_num * 4),
SIG_GPIO_OUT_IDX);
} }
/** /**
@ -305,6 +301,18 @@ static inline void gpio_ll_od_enable(gpio_dev_t *hw, uint32_t gpio_num)
hw->pin[gpio_num].pad_driver = 1; hw->pin[gpio_num].pad_driver = 1;
} }
/**
* @brief Disconnect any peripheral output signal routed via GPIO matrix to the pin
*
* @param hw Peripheral GPIO hardware instance address.
* @param gpio_num GPIO number
*/
__attribute__((always_inline))
static inline void gpio_ll_matrix_out_default(gpio_dev_t *hw, uint32_t gpio_num)
{
REG_WRITE(GPIO_FUNC0_OUT_SEL_CFG_REG + (gpio_num * 4), SIG_GPIO_OUT_IDX);
}
/** /**
* @brief Select a function for the pin in the IOMUX * @brief Select a function for the pin in the IOMUX
* *

View File

@ -273,10 +273,6 @@ static inline void gpio_ll_output_disable(gpio_dev_t *hw, uint32_t gpio_num)
} else { } else {
hw->enable1_w1tc.data = (0x1 << (gpio_num - 32)); hw->enable1_w1tc.data = (0x1 << (gpio_num - 32));
} }
// Ensure no other output signal is routed via GPIO matrix to this pin
REG_WRITE(GPIO_FUNC0_OUT_SEL_CFG_REG + (gpio_num * 4),
SIG_GPIO_OUT_IDX);
} }
/** /**
@ -318,6 +314,18 @@ static inline void gpio_ll_od_enable(gpio_dev_t *hw, uint32_t gpio_num)
hw->pin[gpio_num].pad_driver = 1; hw->pin[gpio_num].pad_driver = 1;
} }
/**
* @brief Disconnect any peripheral output signal routed via GPIO matrix to the pin
*
* @param hw Peripheral GPIO hardware instance address.
* @param gpio_num GPIO number
*/
__attribute__((always_inline))
static inline void gpio_ll_matrix_out_default(gpio_dev_t *hw, uint32_t gpio_num)
{
REG_WRITE(GPIO_FUNC0_OUT_SEL_CFG_REG + (gpio_num * 4), SIG_GPIO_OUT_IDX);
}
/** /**
* @brief Select a function for the pin in the IOMUX * @brief Select a function for the pin in the IOMUX
* *

View File

@ -185,6 +185,14 @@ void gpio_hal_intr_disable(gpio_hal_context_t *hal, uint32_t gpio_num);
*/ */
#define gpio_hal_od_enable(hal, gpio_num) gpio_ll_od_enable((hal)->dev, gpio_num) #define gpio_hal_od_enable(hal, gpio_num) gpio_ll_od_enable((hal)->dev, gpio_num)
/**
* @brief Disconnect any peripheral output signal routed via GPIO matrix to the pin
*
* @param hal Context of the HAL layer
* @param gpio_num GPIO number
*/
#define gpio_hal_matrix_out_default(hal, gpio_num) gpio_ll_matrix_out_default((hal)->dev, gpio_num)
/** /**
* @brief Select a function for the pin in the IOMUX * @brief Select a function for the pin in the IOMUX
* *

View File

@ -29,6 +29,18 @@ GPIO Summary
- Analog functions such as ADC/DAC/etc are in use - Analog functions such as ADC/DAC/etc are in use
:SOC_LP_PERIPHERALS_SUPPORTED: - LP peripherals, such as LP_UART, LP_I2C, are in use :SOC_LP_PERIPHERALS_SUPPORTED: - LP peripherals, such as LP_UART, LP_I2C, are in use
IO Configuration
----------------
An IO can be used in two ways:
- As a simple GPIO input to read the level on the pin, or as a simple GPIO output to output the desired level on the pin.
- As a peripheral signal input/output.
IDF peripheral drivers always take care of the necessary IO configurations that need to be applied onto the pins, so that they can be used as the peripheral signal inputs or outputs. This means the users usually only need to be responsible for configuring the IOs as simple inputs or outputs. :cpp:func:`gpio_config` is an all-in-one API that can be used to configure the I/O mode, internal pull-up/pull-down resistors, etc. for pins.
In some applications, an IO pin can serve dual purposes. For example, the IO, which outputs a LEDC PWM signal, can also act as a GPIO input to generate interrupts or GPIO ETM events. Careful handling on the configuration step is necessary for such dual use of IO pins cases. :cpp:func:`gpio_config` is an API that overwrites all the current configurations, so it must be called to set the pin mode to :cpp:enumerator:`gpio_mode_t::GPIO_MODE_INPUT` before calling the LEDC driver API which connects the output signal to the pin. As an alternative, if no other configuration is needed other than making the pin input enabled, :cpp:func:`gpio_input_enable` can be the one to call at any time to achieve the same purpose.
Check Current Configuration of IOs Check Current Configuration of IOs
---------------------------------- ----------------------------------
@ -75,7 +87,7 @@ If an IO pin is routed to a peripheral signal through the GPIO matrix, the signa
Do not rely on the default configurations values in the Technical Reference Manual, because it may be changed in the bootloader or application startup code before app_main. Do not rely on the default configurations values in the Technical Reference Manual, because it may be changed in the bootloader or application startup code before app_main.
.. only:: esp32c3 or esp32c6 or esp32h2 or esp32p4 or esp32s2 or esp32s3 or esp32c5 .. only:: esp32c3 or esp32c6 or esp32h2 or esp32p4 or esp32s2 or esp32s3 or esp32c5 or esp32c61
Configure USB PHY Pins to GPIO Configure USB PHY Pins to GPIO
------------------------------- -------------------------------

View File

@ -87,7 +87,6 @@ To install a PCNT channel, you must initialize a :cpp:type:`pcnt_chan_config_t`
- :cpp:member:`pcnt_chan_config_t::edge_gpio_num` and :cpp:member:`pcnt_chan_config_t::level_gpio_num` specify the GPIO numbers used by **edge** type signal and **level** type signal. Please note, either of them can be assigned to ``-1`` if it is not actually used, and thus it will become a **virtual IO**. For some simple pulse counting applications where one of the level/edge signals is fixed (i.e., never changes), you can reclaim a GPIO by setting the signal as a virtual IO on channel allocation. Setting the level/edge signal as a virtual IO causes that signal to be internally routed to a fixed High/Low logic level, thus allowing you to save a GPIO for other purposes. - :cpp:member:`pcnt_chan_config_t::edge_gpio_num` and :cpp:member:`pcnt_chan_config_t::level_gpio_num` specify the GPIO numbers used by **edge** type signal and **level** type signal. Please note, either of them can be assigned to ``-1`` if it is not actually used, and thus it will become a **virtual IO**. For some simple pulse counting applications where one of the level/edge signals is fixed (i.e., never changes), you can reclaim a GPIO by setting the signal as a virtual IO on channel allocation. Setting the level/edge signal as a virtual IO causes that signal to be internally routed to a fixed High/Low logic level, thus allowing you to save a GPIO for other purposes.
- :cpp:member:`pcnt_chan_config_t::virt_edge_io_level` and :cpp:member:`pcnt_chan_config_t::virt_level_io_level` specify the virtual IO level for **edge** and **level** input signal, to ensure a deterministic state for such control signal. Please note, they are only valid when either :cpp:member:`pcnt_chan_config_t::edge_gpio_num` or :cpp:member:`pcnt_chan_config_t::level_gpio_num` is assigned to ``-1``. - :cpp:member:`pcnt_chan_config_t::virt_edge_io_level` and :cpp:member:`pcnt_chan_config_t::virt_level_io_level` specify the virtual IO level for **edge** and **level** input signal, to ensure a deterministic state for such control signal. Please note, they are only valid when either :cpp:member:`pcnt_chan_config_t::edge_gpio_num` or :cpp:member:`pcnt_chan_config_t::level_gpio_num` is assigned to ``-1``.
- :cpp:member:`pcnt_chan_config_t::invert_edge_input` and :cpp:member:`pcnt_chan_config_t::invert_level_input` are used to decide whether to invert the input signals before they going into PCNT hardware. The invert is done by GPIO matrix instead of PCNT hardware. - :cpp:member:`pcnt_chan_config_t::invert_edge_input` and :cpp:member:`pcnt_chan_config_t::invert_level_input` are used to decide whether to invert the input signals before they going into PCNT hardware. The invert is done by GPIO matrix instead of PCNT hardware.
- :cpp:member:`pcnt_chan_config_t::io_loop_back` is for debug only, which enables both the GPIO's input and output paths. This can help to simulate the pulse signals by function :cpp:func:`gpio_set_level` on the same GPIO.
Channel allocating and initialization is done by calling a function :cpp:func:`pcnt_new_channel` with the above :cpp:type:`pcnt_chan_config_t` as an input parameter plus a PCNT unit handle returned from :cpp:func:`pcnt_new_unit`. This function will return a PCNT channel handle if it runs correctly. Specifically, when there are no more free PCNT channel within the unit (i.e., channel resources have been used up), then this function will return :c:macro:`ESP_ERR_NOT_FOUND` error. The total number of available PCNT channels within the unit is recorded by :c:macro:`SOC_PCNT_CHANNELS_PER_UNIT` for reference. Note that, when install a PCNT channel for a specific unit, one should ensure the unit is in the init state, otherwise this function will return :c:macro:`ESP_ERR_INVALID_STATE` error. Channel allocating and initialization is done by calling a function :cpp:func:`pcnt_new_channel` with the above :cpp:type:`pcnt_chan_config_t` as an input parameter plus a PCNT unit handle returned from :cpp:func:`pcnt_new_unit`. This function will return a PCNT channel handle if it runs correctly. Specifically, when there are no more free PCNT channel within the unit (i.e., channel resources have been used up), then this function will return :c:macro:`ESP_ERR_NOT_FOUND` error. The total number of available PCNT channels within the unit is recorded by :c:macro:`SOC_PCNT_CHANNELS_PER_UNIT` for reference. Note that, when install a PCNT channel for a specific unit, one should ensure the unit is in the init state, otherwise this function will return :c:macro:`ESP_ERR_INVALID_STATE` error.
@ -247,7 +246,6 @@ This function should be called when the unit is in the init state. Otherwise, it
- :cpp:member:`pcnt_clear_signal_config_t::clear_signal_gpio_num` specify the GPIO numbers used by **clear** signal. The default active level is high, and the input mode is pull-down enabled. - :cpp:member:`pcnt_clear_signal_config_t::clear_signal_gpio_num` specify the GPIO numbers used by **clear** signal. The default active level is high, and the input mode is pull-down enabled.
- :cpp:member:`pcnt_clear_signal_config_t::invert_clear_signal` is used to decide whether to invert the input signal before it going into PCNT hardware. The invert is done by GPIO matrix instead of PCNT hardware. The input mode is pull-up enabled when the input signal is inverted. - :cpp:member:`pcnt_clear_signal_config_t::invert_clear_signal` is used to decide whether to invert the input signal before it going into PCNT hardware. The invert is done by GPIO matrix instead of PCNT hardware. The input mode is pull-up enabled when the input signal is inverted.
- :cpp:member:`pcnt_clear_signal_config_t::io_loop_back` is for debug only, which enables both the GPIO's input and output paths. This can help to simulate the clear signal by function :cpp:func:`gpio_set_level` for the same GPIO.
This signal acts in the same way as calling :cpp:func:`pcnt_unit_clear_count`, but is not subject to software latency, and is suitable for use in situations with low latency requirements. Also please note, the flip frequency of this signal can not be too high. This signal acts in the same way as calling :cpp:func:`pcnt_unit_clear_count`, but is not subject to software latency, and is suitable for use in situations with low latency requirements. Also please note, the flip frequency of this signal can not be too high.

View File

@ -29,8 +29,20 @@ GPIO 汇总
- 使用 ADC/DAC 等模拟功能时 - 使用 ADC/DAC 等模拟功能时
:SOC_LP_PERIPHERALS_SUPPORTED: - 使用低功耗外设时,例如: LP_UART LP_I2C 等 :SOC_LP_PERIPHERALS_SUPPORTED: - 使用低功耗外设时,例如: LP_UART LP_I2C 等
IO 管脚配置
-----------
IO 可以有两种使用方式:
- 作为简单的 GPIO 输入读取引脚上的电平,或作为简单的 GPIO 输出以输出所需的电平。
- 作为外设信号的输入/输出。
IDF 外设驱动内部会处理需要应用到引脚上的必要 IO 配置,以便它们可以用作外设信号的输入或输出。这意味着用户通常自己只需负责将 IO 配置为简单的输入或输出。:cpp:func:`gpio_config` 是一个一体化的 API可用于配置 I/O 模式、内部上拉/下拉电阻等管脚设置。
在一些应用中IO 管脚可以同时发挥双重作用。例如,输出 LEDC PWM 信号的 IO 也可以作为 GPIO 输入生成中断或 GPIO ETM 事件。这种双重用途的 IO 管脚在配置时需要特别注意。由于 :cpp:func:`gpio_config` 是一个会覆盖所有当前配置的 API ,因此必须先调用它将管脚模式设置为 :cpp:enumerator:`gpio_mode_t::GPIO_MODE_INPUT`,然后才能调用 LEDC 驱动 API 将输出信号连接到引脚上。作为替代方案,如果除了使管脚输入启用之外不需要其他额外配置,可以随时调用 :cpp:func:`gpio_input_enable` 以实现相同的目的。
获取 IO 管脚实时配置状态 获取 IO 管脚实时配置状态
-------------------------------------------- ------------------------
GPIO 驱动提供了一个函数 :cpp:func:`gpio_dump_io_configuration` 用来输出指定管脚的实时配置状态,包括上下拉、输入输出使能、管脚映射等。例如,以下命令可用于输出 GPIO4GPIO8 与 GPIO26 的配置状态: GPIO 驱动提供了一个函数 :cpp:func:`gpio_dump_io_configuration` 用来输出指定管脚的实时配置状态,包括上下拉、输入输出使能、管脚映射等。例如,以下命令可用于输出 GPIO4GPIO8 与 GPIO26 的配置状态:
@ -75,7 +87,7 @@ GPIO 驱动提供了一个函数 :cpp:func:`gpio_dump_io_configuration` 用来
请不要依赖技术参考手册中记录的 GPIO 默认配置状态,因为特殊用途的 GPIO 可能会在 app_main 之前被引导程序或应用程序启动阶段的代码更改。 请不要依赖技术参考手册中记录的 GPIO 默认配置状态,因为特殊用途的 GPIO 可能会在 app_main 之前被引导程序或应用程序启动阶段的代码更改。
.. only:: esp32c3 or esp32c6 or esp32h2 or esp32p4 or esp32s2 or esp32s3 or esp32c5 .. only:: esp32c3 or esp32c6 or esp32h2 or esp32p4 or esp32s2 or esp32s3 or esp32c5 or esp32c61
配置 USB PHY 管脚 为普通 GPIO 管脚 配置 USB PHY 管脚 为普通 GPIO 管脚
--------------------------------------- ---------------------------------------

View File

@ -134,7 +134,7 @@
.. note:: .. note::
- 其中一些管脚被用作 Strapping 管脚,可用于选择加载芯片的启动模式等。详细信息可以在 `ESP32-C61 技术规格书 <{IDF_TARGET_DATASHEET_CN_URL}>`_ > ``Strapping 管脚`` 章节中找到 - 其中一些管脚被用作 Strapping 管脚,可用于选择加载芯片的启动模式等。详细信息请见 `ESP32-C61 技术规格书 <{IDF_TARGET_DATASHEET_CN_URL}>`_ > ``Strapping 管脚``
- SPI0/1GPIO14 ~ GPIO17 GPIO19 ~ GPIO21 通常用于 SPI flash不推荐用于其他用途。 - SPI0/1GPIO14 ~ GPIO17 GPIO19 ~ GPIO21 通常用于 SPI flash不推荐用于其他用途。
- USB-JTAGGPIO12 GPIO13 默认用于 USB-JTAG。如果将它们配置为普通 GPIO驱动程序将禁用 USB-JTAG 功能。 - USB-JTAGGPIO12 GPIO13 默认用于 USB-JTAG。如果将它们配置为普通 GPIO驱动程序将禁用 USB-JTAG 功能。

View File

@ -87,7 +87,6 @@ PCNT 单元和通道分别用 :cpp:type:`pcnt_unit_handle_t` 与 :cpp:type:`pcnt
- :cpp:member:`pcnt_chan_config_t::edge_gpio_num`:cpp:member:`pcnt_chan_config_t::level_gpio_num` 用于指定 **边沿** 信号和 **电平** 信号对应的 GPIO 编号。请注意,这两个参数未被使用时,可以设置为 `-1`,即成为 **虚拟 IO** 。对于一些简单的脉冲计数应用,电平信号或边沿信号是固定的(即不会发生改变),可将其设置为虚拟 IO然后该信号会被连接到一个固定的高/低逻辑电平,这样就可以在通道分配时回收一个 GPIO节省一个 GPIO 管脚资源。 - :cpp:member:`pcnt_chan_config_t::edge_gpio_num`:cpp:member:`pcnt_chan_config_t::level_gpio_num` 用于指定 **边沿** 信号和 **电平** 信号对应的 GPIO 编号。请注意,这两个参数未被使用时,可以设置为 `-1`,即成为 **虚拟 IO** 。对于一些简单的脉冲计数应用,电平信号或边沿信号是固定的(即不会发生改变),可将其设置为虚拟 IO然后该信号会被连接到一个固定的高/低逻辑电平,这样就可以在通道分配时回收一个 GPIO节省一个 GPIO 管脚资源。
- :cpp:member:`pcnt_chan_config_t::virt_edge_io_level`:cpp:member:`pcnt_chan_config_t::virt_level_io_level` 用于指定 **边沿** 信号和 **电平** 信号的虚拟 IO 电平,以保证这些控制信号处于确定状态。请注意,只有在 :cpp:member:`pcnt_chan_config_t::edge_gpio_num`:cpp:member:`pcnt_chan_config_t::level_gpio_num` 设置为 `-1` 时,这两个参数才有效。 - :cpp:member:`pcnt_chan_config_t::virt_edge_io_level`:cpp:member:`pcnt_chan_config_t::virt_level_io_level` 用于指定 **边沿** 信号和 **电平** 信号的虚拟 IO 电平,以保证这些控制信号处于确定状态。请注意,只有在 :cpp:member:`pcnt_chan_config_t::edge_gpio_num`:cpp:member:`pcnt_chan_config_t::level_gpio_num` 设置为 `-1` 时,这两个参数才有效。
- :cpp:member:`pcnt_chan_config_t::invert_edge_input`:cpp:member:`pcnt_chan_config_t::invert_level_input` 用于确定信号在输入 PCNT 之前是否需要被翻转,信号翻转由 GPIO 矩阵(不是 PCNT 单元)执行。 - :cpp:member:`pcnt_chan_config_t::invert_edge_input`:cpp:member:`pcnt_chan_config_t::invert_level_input` 用于确定信号在输入 PCNT 之前是否需要被翻转,信号翻转由 GPIO 矩阵(不是 PCNT 单元)执行。
- :cpp:member:`pcnt_chan_config_t::io_loop_back` 仅用于调试,它可以使能 GPIO 的输入和输出路径。这样,就可以通过调用位于同一 GPIO 上的函数 :cpp:func:`gpio_set_level` 来模拟脉冲信号。
调用函数 :cpp:func:`pcnt_new_channel`,将 :cpp:type:`pcnt_chan_config_t` 作为输入值并调用 :cpp:func:`pcnt_new_unit` 返回的 PCNT 单元句柄,可对 PCNT 通道进行分配和初始化。如果该函数正常运行,会返回一个 PCNT 通道句柄。如果没有可用的 PCNT 通道PCNT 通道资源全部被占用),该函数会返回错误 :c:macro:`ESP_ERR_NOT_FOUND`。可用的 PCNT 通道总数记录在 :c:macro:`SOC_PCNT_CHANNELS_PER_UNIT`,以供参考。注意,为某个单元安装 PCNT 通道时,应确保该单元处于初始状态,否则函数 :cpp:func:`pcnt_new_channel` 会返回错误 :c:macro:`ESP_ERR_INVALID_STATE` 调用函数 :cpp:func:`pcnt_new_channel`,将 :cpp:type:`pcnt_chan_config_t` 作为输入值并调用 :cpp:func:`pcnt_new_unit` 返回的 PCNT 单元句柄,可对 PCNT 通道进行分配和初始化。如果该函数正常运行,会返回一个 PCNT 通道句柄。如果没有可用的 PCNT 通道PCNT 通道资源全部被占用),该函数会返回错误 :c:macro:`ESP_ERR_NOT_FOUND`。可用的 PCNT 通道总数记录在 :c:macro:`SOC_PCNT_CHANNELS_PER_UNIT`,以供参考。注意,为某个单元安装 PCNT 通道时,应确保该单元处于初始状态,否则函数 :cpp:func:`pcnt_new_channel` 会返回错误 :c:macro:`ESP_ERR_INVALID_STATE`
@ -247,7 +246,6 @@ PCNT 单元的滤波器可滤除信号中的短时毛刺,:cpp:type:`pcnt_glitc
- :cpp:member:`pcnt_clear_signal_config_t::clear_signal_gpio_num` 用于指定 **清零** 信号对应的 GPIO 编号。默认有效电平为高,使能下拉输入。 - :cpp:member:`pcnt_clear_signal_config_t::clear_signal_gpio_num` 用于指定 **清零** 信号对应的 GPIO 编号。默认有效电平为高,使能下拉输入。
- :cpp:member:`pcnt_clear_signal_config_t::invert_clear_signal` 用于确定信号在输入 PCNT 之前是否需要被翻转,信号翻转由 GPIO 矩阵 (不是 PCNT 单元) 执行。驱动会使能上拉输入,以确保信号在未连接时保持高电平。 - :cpp:member:`pcnt_clear_signal_config_t::invert_clear_signal` 用于确定信号在输入 PCNT 之前是否需要被翻转,信号翻转由 GPIO 矩阵 (不是 PCNT 单元) 执行。驱动会使能上拉输入,以确保信号在未连接时保持高电平。
- :cpp:member:`pcnt_clear_signal_config_t::io_loop_back` 仅用于调试,它可以使能 GPIO 的输入和输出路径。这样,就可以通过 :cpp:func:`gpio_set_level` 函数来模拟外部输入的清零信号。
该输入信号的作用与调用 :cpp:func:`pcnt_unit_clear_count` 函数相同,但它不受软件延迟的限制,更适用于需要低延迟的场合。请注意,该信号的翻转频率不能太高。 该输入信号的作用与调用 :cpp:func:`pcnt_unit_clear_count` 函数相同,但它不受软件延迟的限制,更适用于需要低延迟的场合。请注意,该信号的翻转频率不能太高。