From 3fed3cf50e1f8b8ec550372802e2883ec9c55884 Mon Sep 17 00:00:00 2001 From: Chen Jichang Date: Tue, 1 Aug 2023 15:00:05 +0800 Subject: [PATCH] feat(pcnt): add support for ESP32P4 --- components/driver/Kconfig | 33 +- components/driver/pcnt/Kconfig.pcnt | 32 ++ .../driver/pcnt/include/driver/pulse_cnt.h | 8 + components/driver/pcnt/pulse_cnt.c | 35 +- .../test_apps/pulse_cnt/main/test_pulse_cnt.c | 84 +++- .../pulse_cnt/main/test_pulse_cnt_board.h | 3 +- .../hal/esp32p4/include/hal/clk_gate_ll.h | 3 + components/hal/esp32p4/include/hal/pcnt_ll.h | 467 ++++++++++++++++++ .../esp32p4/include/soc/Kconfig.soc_caps.in | 8 + components/soc/esp32p4/include/soc/pcnt_reg.h | 40 +- .../soc/esp32p4/include/soc/pcnt_struct.h | 229 ++++----- components/soc/esp32p4/include/soc/soc_caps.h | 5 +- components/soc/esp32p4/pcnt_periph.c | 61 ++- components/soc/include/soc/pcnt_periph.h | 1 + docs/en/api-reference/peripherals/pcnt.rst | 6 + docs/zh_CN/api-reference/peripherals/pcnt.rst | 6 + 16 files changed, 816 insertions(+), 205 deletions(-) create mode 100644 components/driver/pcnt/Kconfig.pcnt create mode 100644 components/hal/esp32p4/include/hal/pcnt_ll.h diff --git a/components/driver/Kconfig b/components/driver/Kconfig index 7c68ec740f..1db06b0c00 100644 --- a/components/driver/Kconfig +++ b/components/driver/Kconfig @@ -308,38 +308,7 @@ menu "Driver Configurations" orsource "./gptimer/Kconfig.gptimer" - menu "PCNT Configuration" - depends on SOC_PCNT_SUPPORTED - config PCNT_CTRL_FUNC_IN_IRAM - bool "Place PCNT control functions into IRAM" - default n - help - Place PCNT control functions (like start/stop) into IRAM, - so that these functions can be IRAM-safe and able to be called in the other IRAM interrupt context. - Enabling this option can improve driver performance as well. - - config PCNT_ISR_IRAM_SAFE - bool "PCNT ISR IRAM-Safe" - default n - help - Ensure the PCNT interrupt is IRAM-Safe by allowing the interrupt handler to be - executable when the cache is disabled (e.g. SPI Flash write). - - config PCNT_SUPPRESS_DEPRECATE_WARN - bool "Suppress legacy driver deprecated warning" - default n - help - Wether to suppress the deprecation warnings when using legacy PCNT driver (driver/pcnt.h). - If you want to continue using the legacy driver, and don't want to see related deprecation warnings, - you can enable this option. - - config PCNT_ENABLE_DEBUG_LOG - bool "Enable debug log" - default n - help - Wether to enable the debug log message for PCNT driver. - Note that, this option only controls the PCNT driver log, won't affect other drivers. - endmenu # PCNT Configuration + orsource "./pcnt/Kconfig.pcnt" menu "RMT Configuration" depends on SOC_RMT_SUPPORTED diff --git a/components/driver/pcnt/Kconfig.pcnt b/components/driver/pcnt/Kconfig.pcnt new file mode 100644 index 0000000000..4a01e45141 --- /dev/null +++ b/components/driver/pcnt/Kconfig.pcnt @@ -0,0 +1,32 @@ +menu "PCNT Configuration" + depends on SOC_PCNT_SUPPORTED + config PCNT_CTRL_FUNC_IN_IRAM + bool "Place PCNT control functions into IRAM" + default n + help + Place PCNT control functions (like start/stop) into IRAM, + so that these functions can be IRAM-safe and able to be called in the other IRAM interrupt context. + Enabling this option can improve driver performance as well. + + config PCNT_ISR_IRAM_SAFE + bool "PCNT ISR IRAM-Safe" + default n + help + Ensure the PCNT interrupt is IRAM-Safe by allowing the interrupt handler to be + executable when the cache is disabled (e.g. SPI Flash write). + + config PCNT_SUPPRESS_DEPRECATE_WARN + bool "Suppress legacy driver deprecated warning" + default n + help + Wether to suppress the deprecation warnings when using legacy PCNT driver (driver/pcnt.h). + If you want to continue using the legacy driver, and don't want to see related deprecation warnings, + you can enable this option. + + config PCNT_ENABLE_DEBUG_LOG + bool "Enable debug log" + default n + help + Wether to enable the debug log message for PCNT driver. + Note that, this option only controls the PCNT driver log, won't affect other drivers. +endmenu # PCNT Configuration diff --git a/components/driver/pcnt/include/driver/pulse_cnt.h b/components/driver/pcnt/include/driver/pulse_cnt.h index 8766e42de4..b1ad664fe1 100644 --- a/components/driver/pcnt/include/driver/pulse_cnt.h +++ b/components/driver/pcnt/include/driver/pulse_cnt.h @@ -61,8 +61,15 @@ typedef struct { typedef struct { int low_limit; /*!< Low limitation of the count unit, should be lower than 0 */ int high_limit; /*!< High limitation of the count unit, should be higher than 0 */ +#if SOC_PCNT_SUPPORT_ZERO_INPUT + int zero_input_gpio_num; /*!< GPIO number used by the clear signal, the default active level is high, input mode with pull down enabled. Set to -1 if unused */ +#endif struct { uint32_t accum_count: 1; /*!< Whether to accumulate the count value when overflows at the high/low limit */ +#if SOC_PCNT_SUPPORT_ZERO_INPUT + uint32_t invert_zero_input: 1; /*!< Invert the zero 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 */ +#endif } flags; /*!< Extra flags */ } pcnt_unit_config_t; @@ -253,6 +260,7 @@ esp_err_t pcnt_unit_register_event_callbacks(pcnt_unit_handle_t unit, const pcnt /** * @brief Add a watch point for PCNT unit, PCNT will generate an event when the counter value reaches the watch point value * + * * @param[in] unit PCNT unit handle created by `pcnt_new_unit()` * @param[in] watch_point Value to be watched * @return diff --git a/components/driver/pcnt/pulse_cnt.c b/components/driver/pcnt/pulse_cnt.c index ee19430e3e..8c2e393072 100644 --- a/components/driver/pcnt/pulse_cnt.c +++ b/components/driver/pcnt/pulse_cnt.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2021-2023 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -84,6 +84,7 @@ struct pcnt_unit_t { int unit_id; // allocated unit numerical ID int low_limit; // low limit value int high_limit; // high limit value + int zero_input_gpio_num; // which gpio clear signal input int accum_value; // accumulated count value pcnt_chan_t *channels[SOC_PCNT_CHANNELS_PER_UNIT]; // array of PCNT channels pcnt_watch_point_t watchers[PCNT_LL_WATCH_EVENT_MAX]; // array of PCNT watchers @@ -226,6 +227,30 @@ esp_err_t pcnt_new_unit(const pcnt_unit_config_t *config, pcnt_unit_handle_t *re for (int i = 0; i < PCNT_LL_WATCH_EVENT_MAX; i++) { unit->watchers[i].event_id = PCNT_LL_WATCH_EVENT_INVALID; // invalid all watch point } + +#if SOC_PCNT_SUPPORT_ZERO_INPUT + // 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 = true, + .pull_up_en = false, + }; + + if (config->zero_input_gpio_num >= 0) { + if (config->flags.invert_zero_input) { + gpio_conf.pull_down_en = false; + gpio_conf.pull_up_en = true; + } + gpio_conf.pin_bit_mask = 1ULL << config->zero_input_gpio_num; + ESP_GOTO_ON_ERROR(gpio_config(&gpio_conf), err, TAG, "config zero GPIO failed"); + esp_rom_gpio_connect_in_signal(config->zero_input_gpio_num, + pcnt_periph_signals.groups[group_id].units[unit_id].clear_sig, + config->flags.invert_zero_input); + } + unit->zero_input_gpio_num = config->zero_input_gpio_num; +#endif // SOC_PCNT_SUPPORT_ZERO_INPUT + ESP_LOGD(TAG, "new pcnt unit (%d,%d) at %p, count range:[%d,%d]", group_id, unit_id, unit, unit->low_limit, unit->high_limit); *ret_unit = unit; return ESP_OK; @@ -249,6 +274,12 @@ esp_err_t pcnt_del_unit(pcnt_unit_handle_t unit) ESP_RETURN_ON_FALSE(!unit->channels[i], ESP_ERR_INVALID_STATE, TAG, "channel %d still in working", i); } +#if SOC_PCNT_SUPPORT_ZERO_INPUT + if (unit->zero_input_gpio_num >= 0) { + gpio_reset_pin(unit->zero_input_gpio_num); + } +#endif // SOC_PCNT_SUPPORT_ZERO_INPUT + ESP_LOGD(TAG, "del unit (%d,%d)", group_id, unit_id); // recycle memory resource ESP_RETURN_ON_ERROR(pcnt_destroy(unit), TAG, "destroy pcnt unit failed"); @@ -742,6 +773,8 @@ IRAM_ATTR static void pcnt_default_isr(void *args) uint32_t intr_status = pcnt_ll_get_intr_status(group->hal.dev); if (intr_status & PCNT_LL_UNIT_WATCH_EVENT(unit_id)) { pcnt_ll_clear_intr_status(group->hal.dev, PCNT_LL_UNIT_WATCH_EVENT(unit_id)); + + // points watcher event uint32_t event_status = pcnt_ll_get_event_status(group->hal.dev, unit_id); // iter on each event_id while (event_status) { diff --git a/components/driver/test_apps/pulse_cnt/main/test_pulse_cnt.c b/components/driver/test_apps/pulse_cnt/main/test_pulse_cnt.c index e94944a8f3..fd3b37f039 100644 --- a/components/driver/test_apps/pulse_cnt/main/test_pulse_cnt.c +++ b/components/driver/test_apps/pulse_cnt/main/test_pulse_cnt.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -20,6 +20,9 @@ TEST_CASE("pcnt_unit_install_uninstall", "[pcnt]") pcnt_unit_config_t unit_config = { .low_limit = -100, .high_limit = 100, +#if SOC_PCNT_SUPPORT_ZERO_INPUT + .zero_input_gpio_num = -1, +#endif }; pcnt_unit_handle_t units[SOC_PCNT_UNITS_PER_GROUP]; int count_value = 0; @@ -77,6 +80,9 @@ TEST_CASE("pcnt_channel_install_uninstall", "[pcnt]") pcnt_unit_config_t unit_config = { .low_limit = -100, .high_limit = 100, +#if SOC_PCNT_SUPPORT_ZERO_INPUT + .zero_input_gpio_num = -1, +#endif }; pcnt_chan_config_t chan_config = { .edge_gpio_num = TEST_PCNT_GPIO_A, // only detect edge signal in this case @@ -165,6 +171,9 @@ TEST_CASE("pcnt_multiple_units_pulse_count", "[pcnt]") pcnt_unit_config_t unit_config = { .low_limit = -100, .high_limit = 100, +#if SOC_PCNT_SUPPORT_ZERO_INPUT + .zero_input_gpio_num = -1, +#endif }; pcnt_unit_handle_t units[2]; for (int i = 0; i < 2; i++) { @@ -229,7 +238,10 @@ TEST_CASE("pcnt_quadrature_decode_event", "[pcnt]") { pcnt_unit_config_t unit_config = { .low_limit = -100, - .high_limit = 100 + .high_limit = 100, +#if SOC_PCNT_SUPPORT_ZERO_INPUT + .zero_input_gpio_num = -1, +#endif }; printf("install pcnt unit\r\n"); @@ -352,7 +364,10 @@ TEST_CASE("pcnt_zero_cross_mode", "[pcnt]") { pcnt_unit_config_t unit_config = { .low_limit = -100, - .high_limit = 100 + .high_limit = 100, +#if SOC_PCNT_SUPPORT_ZERO_INPUT + .zero_input_gpio_num = -1, +#endif }; printf("install pcnt unit\r\n"); @@ -445,6 +460,9 @@ TEST_CASE("pcnt_virtual_io", "[pcnt]") pcnt_unit_config_t unit_config = { .low_limit = -100, .high_limit = 100, +#if SOC_PCNT_SUPPORT_ZERO_INPUT + .zero_input_gpio_num = -1, +#endif }; pcnt_chan_config_t chan_config = { .edge_gpio_num = TEST_PCNT_GPIO_A, // only detect edge signal in this case @@ -490,3 +508,63 @@ TEST_CASE("pcnt_virtual_io", "[pcnt]") TEST_ESP_OK(pcnt_del_channel(chan)); TEST_ESP_OK(pcnt_del_unit(unit)); } + +#if SOC_PCNT_SUPPORT_ZERO_INPUT +TEST_CASE("pcnt_zero_input_signal", "[pcnt]") +{ + pcnt_unit_config_t unit_config = { + .low_limit = -1000, + .high_limit = 1000, + .zero_input_gpio_num = TEST_PCNT_GPIO_Z, + .flags.io_loop_back = true, + }; + + printf("install pcnt unit\r\n"); + pcnt_unit_handle_t unit = NULL; + TEST_ESP_OK(pcnt_new_unit(&unit_config, &unit)); + pcnt_glitch_filter_config_t filter_config = { + .max_glitch_ns = 1000, + }; + TEST_ESP_OK(pcnt_unit_set_glitch_filter(unit, &filter_config)); + + printf("install pcnt channels\r\n"); + pcnt_chan_config_t chan_config = { + .level_gpio_num = -1, + .edge_gpio_num = TEST_PCNT_GPIO_A, + .flags.io_loop_back = true, + }; + pcnt_channel_handle_t channel; + + TEST_ESP_OK(pcnt_new_channel(unit, &chan_config, &channel)); + TEST_ESP_OK(pcnt_channel_set_edge_action(channel, PCNT_CHANNEL_EDGE_ACTION_INCREASE, PCNT_CHANNEL_EDGE_ACTION_HOLD)); + TEST_ESP_OK(pcnt_channel_set_level_action(channel, PCNT_CHANNEL_LEVEL_ACTION_KEEP, PCNT_CHANNEL_LEVEL_ACTION_KEEP)); + + printf("enable and start unit\r\n"); + + TEST_ESP_OK(pcnt_unit_enable(unit)); + TEST_ESP_OK(pcnt_unit_start(unit)); + TEST_ESP_OK(gpio_set_level(TEST_PCNT_GPIO_Z, 0)); + + // trigger 10 rising edge on GPIO + test_gpio_simulate_rising_edge(TEST_PCNT_GPIO_A, 10); + + int count_value = 0; + + TEST_ESP_OK(pcnt_unit_get_count(unit, &count_value)); + printf("count_value=%d\r\n", count_value); + TEST_ASSERT_EQUAL(10, count_value); + + printf("simulating zero input signal\r\n"); + TEST_ESP_OK(gpio_set_level(TEST_PCNT_GPIO_Z, 1)); + TEST_ESP_OK(gpio_set_level(TEST_PCNT_GPIO_Z, 0)); + + TEST_ESP_OK(pcnt_unit_get_count(unit, &count_value)); + printf("count_value=%d\r\n", count_value); + TEST_ASSERT_EQUAL(0, count_value); // 0 after rst_sig + + TEST_ESP_OK(pcnt_del_channel(channel)); + TEST_ESP_OK(pcnt_unit_stop(unit)); + TEST_ESP_OK(pcnt_unit_disable(unit)); + TEST_ESP_OK(pcnt_del_unit(unit)); +} +#endif // SOC_PCNT_SUPPORT_ZERO_INPUT diff --git a/components/driver/test_apps/pulse_cnt/main/test_pulse_cnt_board.h b/components/driver/test_apps/pulse_cnt/main/test_pulse_cnt_board.h index 88eadf5999..0f6385c0e1 100644 --- a/components/driver/test_apps/pulse_cnt/main/test_pulse_cnt_board.h +++ b/components/driver/test_apps/pulse_cnt/main/test_pulse_cnt_board.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -11,6 +11,7 @@ extern "C" { #define TEST_PCNT_GPIO_A 0 #define TEST_PCNT_GPIO_B 2 +#define TEST_PCNT_GPIO_Z 4 #if CONFIG_PCNT_ISR_IRAM_SAFE #define TEST_PCNT_CALLBACK_ATTR IRAM_ATTR diff --git a/components/hal/esp32p4/include/hal/clk_gate_ll.h b/components/hal/esp32p4/include/hal/clk_gate_ll.h index cb847f3a97..951e0a92d4 100644 --- a/components/hal/esp32p4/include/hal/clk_gate_ll.h +++ b/components/hal/esp32p4/include/hal/clk_gate_ll.h @@ -112,6 +112,8 @@ static inline uint32_t periph_ll_get_clk_en_mask(periph_module_t periph) return HP_SYS_CLKRST_REG_CRYPTO_ECDSA_CLK_EN; case PERIPH_ISP_MODULE: return HP_SYS_CLKRST_REG_ISP_CLK_EN; + case PERIPH_PCNT_MODULE: + return HP_SYS_CLKRST_REG_PCNT_APB_CLK_EN; default: return 0; } @@ -264,6 +266,7 @@ static inline uint32_t periph_ll_get_clk_en_reg(periph_module_t periph) return HP_SYS_CLKRST_PERI_CLK_CTRL119_REG; case PERIPH_MCPWM0_MODULE: case PERIPH_MCPWM1_MODULE: + case PERIPH_PCNT_MODULE: return HP_SYS_CLKRST_SOC_CLK_CTRL2_REG; case PERIPH_TIMG0_MODULE: return HP_SYS_CLKRST_PERI_CLK_CTRL20_REG; diff --git a/components/hal/esp32p4/include/hal/pcnt_ll.h b/components/hal/esp32p4/include/hal/pcnt_ll.h new file mode 100644 index 0000000000..d4881aa2cb --- /dev/null +++ b/components/hal/esp32p4/include/hal/pcnt_ll.h @@ -0,0 +1,467 @@ +/* + * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/******************************************************************************* + * NOTICE + * The hal is not public api, don't use in application code. + * See readme.md in hal/include/hal/readme.md + ******************************************************************************/ + +// The LL layer for ESP32-P4 PCNT register operations + +#pragma once + +#include +#include +#include +#include "soc/pcnt_struct.h" +#include "hal/pcnt_types.h" +#include "hal/misc.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define PCNT_LL_GET_HW(num) (((num) == 0) ? (&PCNT) : NULL) +#define PCNT_LL_MAX_GLITCH_WIDTH 1023 +#define PCNT_LL_MAX_LIM SHRT_MAX +#define PCNT_LL_MIN_LIN SHRT_MIN + +typedef enum { + PCNT_LL_WATCH_EVENT_INVALID = -1, + PCNT_LL_WATCH_EVENT_THRES1, + PCNT_LL_WATCH_EVENT_THRES0, + PCNT_LL_WATCH_EVENT_LOW_LIMIT, + PCNT_LL_WATCH_EVENT_HIGH_LIMIT, + PCNT_LL_WATCH_EVENT_ZERO_CROSS, + PCNT_LL_WATCH_EVENT_MAX +} pcnt_ll_watch_event_id_t; + +#define PCNT_LL_WATCH_EVENT_MASK ((1 << PCNT_LL_WATCH_EVENT_MAX) - 1) +#define PCNT_LL_UNIT_WATCH_EVENT(unit_id) (1 << (unit_id)) + +/** + * @brief Set PCNT channel edge action + * + * @param hw Peripheral PCNT hardware instance address. + * @param unit PCNT unit number + * @param channel PCNT channel number + * @param pos_act Counter action when detecting positive edge + * @param neg_act Counter action when detecting negative edge + */ +static inline void pcnt_ll_set_edge_action(pcnt_dev_t *hw, uint32_t unit, uint32_t channel, pcnt_channel_edge_action_t pos_act, pcnt_channel_edge_action_t neg_act) +{ + if (channel == 0) { + hw->conf_unit[unit].conf0.ch0_pos_mode = pos_act; + hw->conf_unit[unit].conf0.ch0_neg_mode = neg_act; + } else { + hw->conf_unit[unit].conf0.ch1_pos_mode = pos_act; + hw->conf_unit[unit].conf0.ch1_neg_mode = neg_act; + } +} + +/** + * @brief Set PCNT channel level action + * + * @param hw Peripheral PCNT hardware instance address. + * @param unit PCNT unit number + * @param channel PCNT channel number + * @param high_act Counter action when control signal is high level + * @param low_act Counter action when control signal is low level + */ +static inline void pcnt_ll_set_level_action(pcnt_dev_t *hw, uint32_t unit, uint32_t channel, pcnt_channel_level_action_t high_act, pcnt_channel_level_action_t low_act) +{ + if (channel == 0) { + hw->conf_unit[unit].conf0.ch0_hctrl_mode = high_act; + hw->conf_unit[unit].conf0.ch0_lctrl_mode = low_act; + } else { + hw->conf_unit[unit].conf0.ch1_hctrl_mode = high_act; + hw->conf_unit[unit].conf0.ch1_lctrl_mode = low_act; + } +} + +/** + * @brief Get pulse counter value + * + * @param hw Peripheral PCNT hardware instance address. + * @param unit Pulse Counter unit number + * @return PCNT count value (a signed integer) + */ +__attribute__((always_inline)) +static inline int pcnt_ll_get_count(pcnt_dev_t *hw, uint32_t unit) +{ + pcnt_un_cnt_reg_t cnt_reg = hw->cnt_unit[unit]; + int16_t value = cnt_reg.pulse_cnt; + return value; +} + +/** + * @brief Pause PCNT counter of PCNT unit + * + * @param hw Peripheral PCNT hardware instance address. + * @param unit PCNT unit number + */ +__attribute__((always_inline)) +static inline void pcnt_ll_stop_count(pcnt_dev_t *hw, uint32_t unit) +{ + hw->ctrl.val |= 1 << (2 * unit + 1); +} + +/** + * @brief Resume counting for PCNT counter + * + * @param hw Peripheral PCNT hardware instance address. + * @param unit PCNT unit number, select from uint32_t + */ +__attribute__((always_inline)) +static inline void pcnt_ll_start_count(pcnt_dev_t *hw, uint32_t unit) +{ + hw->ctrl.val &= ~(1 << (2 * unit + 1)); +} + +/** + * @brief Clear PCNT counter value to zero + * + * @param hw Peripheral PCNT hardware instance address. + * @param unit PCNT unit number, select from uint32_t + */ +__attribute__((always_inline)) +static inline void pcnt_ll_clear_count(pcnt_dev_t *hw, uint32_t unit) +{ + hw->ctrl.val |= 1 << (2 * unit); + hw->ctrl.val &= ~(1 << (2 * unit)); +} + +/** + * @brief Enable PCNT step comparator event + * + * @param hw Peripheral PCNT hardware instance address. + * @param unit PCNT unit number + * @param enable true to enable, false to disable + */ +static inline void pcnt_ll_enable_step_notify(pcnt_dev_t *hw, uint32_t unit, bool enable) +{ + if (enable) { + hw->ctrl.val |= 1 << (8 + unit); + } else { + hw->ctrl.val &= ~(1 << (8 + unit)); + } +} + +/** + * @brief Set PCNT step value + * + * @param hw Peripheral PCNT hardware instance address. + * @param unit PCNT unit number + * @param value PCNT step value + */ +static inline void pcnt_ll_set_step_value(pcnt_dev_t *hw, uint32_t unit, int value) +{ + HAL_FORCE_MODIFY_U32_REG_FIELD(hw->change_conf_unit[3 - unit], cnt_step, value); +} + +/** + * @brief Set PCNT step limit value + * + * @param hw Peripheral PCNT hardware instance address. + * @param unit PCNT unit number + * @param value PCNT step limit value + */ +static inline void pcnt_ll_set_step_limit_value(pcnt_dev_t *hw, uint32_t unit, int value) +{ + HAL_FORCE_MODIFY_U32_REG_FIELD(hw->change_conf_unit[3 - unit], cnt_step_lim, value); +} + +/** + * @brief Enable PCNT interrupt for PCNT unit + * @note Each PCNT unit has five watch point events that share the same interrupt bit. + * + * @param hw Peripheral PCNT hardware instance address. + * @param unit_mask PCNT units mask + * @param enable True to enable interrupt, False to disable interrupt + */ +static inline void pcnt_ll_enable_intr(pcnt_dev_t *hw, uint32_t unit_mask, bool enable) +{ + if (enable) { + hw->int_ena.val |= unit_mask; + } else { + hw->int_ena.val &= ~unit_mask; + } +} + +/** + * @brief Get PCNT interrupt status + * + * @param hw Peripheral PCNT hardware instance address. + * @return Interrupt status word + */ +__attribute__((always_inline)) +static inline uint32_t pcnt_ll_get_intr_status(pcnt_dev_t *hw) +{ + return hw->int_st.val; +} + +/** + * @brief Clear PCNT interrupt status + * + * @param hw Peripheral PCNT hardware instance address. + * @param status value to clear interrupt status + */ +__attribute__((always_inline)) +static inline void pcnt_ll_clear_intr_status(pcnt_dev_t *hw, uint32_t status) +{ + hw->int_clr.val = status; +} + +/** + * @brief Enable PCNT high limit event + * + * @param hw Peripheral PCNT hardware instance address. + * @param unit PCNT unit number + * @param enable true to enable, false to disable + */ +static inline void pcnt_ll_enable_high_limit_event(pcnt_dev_t *hw, uint32_t unit, bool enable) +{ + hw->conf_unit[unit].conf0.thr_h_lim_en = enable; +} + +/** + * @brief Enable PCNT low limit event + * + * @param hw Peripheral PCNT hardware instance address. + * @param unit PCNT unit number + * @param enable true to enable, false to disable + */ +static inline void pcnt_ll_enable_low_limit_event(pcnt_dev_t *hw, uint32_t unit, bool enable) +{ + hw->conf_unit[unit].conf0.thr_l_lim_en = enable; +} + +/** + * @brief Enable PCNT zero cross event + * + * @param hw Peripheral PCNT hardware instance address. + * @param unit PCNT unit number + * @param enable true to enable, false to disable + */ +static inline void pcnt_ll_enable_zero_cross_event(pcnt_dev_t *hw, uint32_t unit, bool enable) +{ + hw->conf_unit[unit].conf0.thr_zero_en = enable; +} + +/** + * @brief Enable PCNT threshold event + * + * @param hw Peripheral PCNT hardware instance address. + * @param unit PCNT unit number + * @param thres Threshold ID + * @param enable true to enable, false to disable + */ +static inline void pcnt_ll_enable_thres_event(pcnt_dev_t *hw, uint32_t unit, uint32_t thres, bool enable) +{ + if (thres == 0) { + hw->conf_unit[unit].conf0.thr_thres0_en = enable; + } else { + hw->conf_unit[unit].conf0.thr_thres1_en = enable; + } +} + +/** + * @brief Disable all PCNT threshold events + * + * @param hw Peripheral PCNT hardware instance address. + * @param unit unit number + */ +static inline void pcnt_ll_disable_all_events(pcnt_dev_t *hw, uint32_t unit) +{ + hw->conf_unit[unit].conf0.val &= ~(PCNT_LL_WATCH_EVENT_MASK << 11); +} + +/** + * @brief Set PCNT high limit value + * + * @param hw Peripheral PCNT hardware instance address. + * @param unit PCNT unit number + * @param value PCNT high limit value + */ +static inline void pcnt_ll_set_high_limit_value(pcnt_dev_t *hw, uint32_t unit, int value) +{ + pcnt_un_conf2_reg_t conf2_reg = hw->conf_unit[unit].conf2; + conf2_reg.cnt_h_lim = value; + hw->conf_unit[unit].conf2 = conf2_reg; +} + +/** + * @brief Set PCNT low limit value + * + * @param hw Peripheral PCNT hardware instance address. + * @param unit PCNT unit number + * @param value PCNT low limit value + */ +static inline void pcnt_ll_set_low_limit_value(pcnt_dev_t *hw, uint32_t unit, int value) +{ + pcnt_un_conf2_reg_t conf2_reg = hw->conf_unit[unit].conf2; + conf2_reg.cnt_l_lim = value; + hw->conf_unit[unit].conf2 = conf2_reg; +} + +/** + * @brief Set PCNT threshold value + * + * @param hw Peripheral PCNT hardware instance address. + * @param unit PCNT unit number + * @param thres Threshold ID + * @param value PCNT threshold value + */ +static inline void pcnt_ll_set_thres_value(pcnt_dev_t *hw, uint32_t unit, uint32_t thres, int value) +{ + pcnt_un_conf1_reg_t conf1_reg = hw->conf_unit[unit].conf1; + if (thres == 0) { + conf1_reg.cnt_thres0 = value; + } else { + conf1_reg.cnt_thres1 = value; + } + hw->conf_unit[unit].conf1 = conf1_reg; +} + +/** + * @brief Get PCNT high limit value + * + * @param hw Peripheral PCNT hardware instance address. + * @param unit PCNT unit number + * @return PCNT high limit value + */ +static inline int pcnt_ll_get_high_limit_value(pcnt_dev_t *hw, uint32_t unit) +{ + pcnt_un_conf2_reg_t conf2_reg = hw->conf_unit[unit].conf2; + int16_t value = conf2_reg.cnt_h_lim ; + return value; +} + +/** + * @brief Get PCNT low limit value + * + * @param hw Peripheral PCNT hardware instance address. + * @param unit PCNT unit number + * @return PCNT high limit value + */ +static inline int pcnt_ll_get_low_limit_value(pcnt_dev_t *hw, uint32_t unit) +{ + pcnt_un_conf2_reg_t conf2_reg = hw->conf_unit[unit].conf2; + int16_t value = conf2_reg.cnt_l_lim ; + return value; +} + +/** + * @brief Get PCNT threshold value + * + * @param hw Peripheral PCNT hardware instance address. + * @param unit PCNT unit number + * @param thres Threshold ID + * @return PCNT threshold value + */ +static inline int pcnt_ll_get_thres_value(pcnt_dev_t *hw, uint32_t unit, uint32_t thres) +{ + int16_t value; + pcnt_un_conf1_reg_t conf1_reg = hw->conf_unit[unit].conf1; + if (thres == 0) { + value = conf1_reg.cnt_thres0 ; + } else { + value = conf1_reg.cnt_thres1 ; + } + return value; +} + +/** + * @brief Get PCNT unit runtime status + * + * @param hw Peripheral PCNT hardware instance address. + * @param unit PCNT unit number + * @return PCNT unit runtime status + */ +static inline uint32_t pcnt_ll_get_unit_status(pcnt_dev_t *hw, uint32_t unit) +{ + return hw->status_unit[unit].val; +} + +/** + * @brief Get PCNT zero cross mode + * + * @param hw Peripheral PCNT hardware instance address. + * @param unit PCNT unit number + * @return Zero cross mode + */ +__attribute__((always_inline)) +static inline pcnt_unit_zero_cross_mode_t pcnt_ll_get_zero_cross_mode(pcnt_dev_t *hw, uint32_t unit) +{ + return hw->status_unit[unit].val & 0x03; +} + +/** + * @brief Get PCNT event status + * + * @param hw Peripheral PCNT hardware instance address. + * @param unit PCNT unit number + * @return Event status word + */ +__attribute__((always_inline)) +static inline uint32_t pcnt_ll_get_event_status(pcnt_dev_t *hw, uint32_t unit) +{ + return hw->status_unit[unit].val >> 2; +} + +/** + * @brief Set PCNT glitch filter threshold + * + * @param hw Peripheral PCNT hardware instance address. + * @param unit PCNT unit number + * @param filter_val PCNT signal filter value, counter in APB_CLK cycles. + * Any pulses lasting shorter than this will be ignored when the filter is enabled. + */ +static inline void pcnt_ll_set_glitch_filter_thres(pcnt_dev_t *hw, uint32_t unit, uint32_t filter_val) +{ + hw->conf_unit[unit].conf0.filter_thres = filter_val; +} + +/** + * @brief Get PCNT glitch filter threshold + * + * @param hw Peripheral PCNT hardware instance address. + * @param unit PCNT unit number + * @return glitch filter threshold + */ +static inline uint32_t pcnt_ll_get_glitch_filter_thres(pcnt_dev_t *hw, uint32_t unit) +{ + return hw->conf_unit[unit].conf0.filter_thres ; +} + +/** + * @brief Enable PCNT glitch filter + * + * @param hw Peripheral PCNT hardware instance address. + * @param unit PCNT unit number + * @param enable True to enable the filter, False to disable the filter + */ +static inline void pcnt_ll_enable_glitch_filter(pcnt_dev_t *hw, uint32_t unit, bool enable) +{ + hw->conf_unit[unit].conf0.filter_en = enable; +} + +/** + * @brief Get interrupt status register address. + * + * @param hw Beginning address of the peripheral registers. + * + * @return Interrupt status register address + */ +static inline volatile void *pcnt_ll_get_intr_status_reg(pcnt_dev_t *hw) +{ + return &hw->int_st.val; +} + +#ifdef __cplusplus +} +#endif diff --git a/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in b/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in index b43748bbb9..99c0b82770 100644 --- a/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in @@ -23,6 +23,10 @@ config SOC_GPTIMER_SUPPORTED bool default y +config SOC_PCNT_SUPPORTED + bool + default y + config SOC_MCPWM_SUPPORTED bool default y @@ -435,6 +439,10 @@ config SOC_PCNT_SUPPORT_RUNTIME_THRES_UPDATE bool default y +config SOC_PCNT_SUPPORT_ZERO_INPUT + bool + default y + config SOC_RMT_GROUPS int default 1 diff --git a/components/soc/esp32p4/include/soc/pcnt_reg.h b/components/soc/esp32p4/include/soc/pcnt_reg.h index 7cbfedda60..3a7e3821c0 100644 --- a/components/soc/esp32p4/include/soc/pcnt_reg.h +++ b/components/soc/esp32p4/include/soc/pcnt_reg.h @@ -1215,34 +1215,34 @@ extern "C" { #define PCNT_CNT_PAUSE_U3_M (PCNT_CNT_PAUSE_U3_V << PCNT_CNT_PAUSE_U3_S) #define PCNT_CNT_PAUSE_U3_V 0x00000001U #define PCNT_CNT_PAUSE_U3_S 7 -/** PCNT_DALTA_CHANGE_EN_U0 : R/W; bitpos: [8]; default: 0; +/** PCNT_DELTA_CHANGE_EN_U0 : R/W; bitpos: [8]; default: 0; * Configures this bit to enable unit 0's step comparator. */ -#define PCNT_DALTA_CHANGE_EN_U0 (BIT(8)) -#define PCNT_DALTA_CHANGE_EN_U0_M (PCNT_DALTA_CHANGE_EN_U0_V << PCNT_DALTA_CHANGE_EN_U0_S) -#define PCNT_DALTA_CHANGE_EN_U0_V 0x00000001U -#define PCNT_DALTA_CHANGE_EN_U0_S 8 -/** PCNT_DALTA_CHANGE_EN_U1 : R/W; bitpos: [9]; default: 0; +#define PCNT_DELTA_CHANGE_EN_U0 (BIT(8)) +#define PCNT_DELTA_CHANGE_EN_U0_M (PCNT_DELTA_CHANGE_EN_U0_V << PCNT_DELTA_CHANGE_EN_U0_S) +#define PCNT_DELTA_CHANGE_EN_U0_V 0x00000001U +#define PCNT_DELTA_CHANGE_EN_U0_S 8 +/** PCNT_DELTA_CHANGE_EN_U1 : R/W; bitpos: [9]; default: 0; * Configures this bit to enable unit 1's step comparator. */ -#define PCNT_DALTA_CHANGE_EN_U1 (BIT(9)) -#define PCNT_DALTA_CHANGE_EN_U1_M (PCNT_DALTA_CHANGE_EN_U1_V << PCNT_DALTA_CHANGE_EN_U1_S) -#define PCNT_DALTA_CHANGE_EN_U1_V 0x00000001U -#define PCNT_DALTA_CHANGE_EN_U1_S 9 -/** PCNT_DALTA_CHANGE_EN_U2 : R/W; bitpos: [10]; default: 0; +#define PCNT_DELTA_CHANGE_EN_U1 (BIT(9)) +#define PCNT_DELTA_CHANGE_EN_U1_M (PCNT_DELTA_CHANGE_EN_U1_V << PCNT_DELTA_CHANGE_EN_U1_S) +#define PCNT_DELTA_CHANGE_EN_U1_V 0x00000001U +#define PCNT_DELTA_CHANGE_EN_U1_S 9 +/** PCNT_DELTA_CHANGE_EN_U2 : R/W; bitpos: [10]; default: 0; * Configures this bit to enable unit 2's step comparator. */ -#define PCNT_DALTA_CHANGE_EN_U2 (BIT(10)) -#define PCNT_DALTA_CHANGE_EN_U2_M (PCNT_DALTA_CHANGE_EN_U2_V << PCNT_DALTA_CHANGE_EN_U2_S) -#define PCNT_DALTA_CHANGE_EN_U2_V 0x00000001U -#define PCNT_DALTA_CHANGE_EN_U2_S 10 -/** PCNT_DALTA_CHANGE_EN_U3 : R/W; bitpos: [11]; default: 0; +#define PCNT_DELTA_CHANGE_EN_U2 (BIT(10)) +#define PCNT_DELTA_CHANGE_EN_U2_M (PCNT_DELTA_CHANGE_EN_U2_V << PCNT_DELTA_CHANGE_EN_U2_S) +#define PCNT_DELTA_CHANGE_EN_U2_V 0x00000001U +#define PCNT_DELTA_CHANGE_EN_U2_S 10 +/** PCNT_DELTA_CHANGE_EN_U3 : R/W; bitpos: [11]; default: 0; * Configures this bit to enable unit 3's step comparator. */ -#define PCNT_DALTA_CHANGE_EN_U3 (BIT(11)) -#define PCNT_DALTA_CHANGE_EN_U3_M (PCNT_DALTA_CHANGE_EN_U3_V << PCNT_DALTA_CHANGE_EN_U3_S) -#define PCNT_DALTA_CHANGE_EN_U3_V 0x00000001U -#define PCNT_DALTA_CHANGE_EN_U3_S 11 +#define PCNT_DELTA_CHANGE_EN_U3 (BIT(11)) +#define PCNT_DELTA_CHANGE_EN_U3_M (PCNT_DELTA_CHANGE_EN_U3_V << PCNT_DELTA_CHANGE_EN_U3_S) +#define PCNT_DELTA_CHANGE_EN_U3_V 0x00000001U +#define PCNT_DELTA_CHANGE_EN_U3_S 11 /** PCNT_CLK_EN : R/W; bitpos: [16]; default: 0; * The registers clock gate enable signal of PCNT module. 1: the registers can be read * and written by application. 0: the registers can not be read or written by diff --git a/components/soc/esp32p4/include/soc/pcnt_struct.h b/components/soc/esp32p4/include/soc/pcnt_struct.h index e424d22588..90961854fd 100644 --- a/components/soc/esp32p4/include/soc/pcnt_struct.h +++ b/components/soc/esp32p4/include/soc/pcnt_struct.h @@ -16,98 +16,98 @@ extern "C" { */ typedef union { struct { - /** filter_thres_un : R/W; bitpos: [9:0]; default: 16; + /** filter_thres : R/W; bitpos: [9:0]; default: 16; * This sets the maximum threshold, in APB_CLK cycles, for the filter. * * Any pulses with width less than this will be ignored when the filter is enabled. */ - uint32_t filter_thres_un:10; - /** filter_en_un : R/W; bitpos: [10]; default: 1; + uint32_t filter_thres:10; + /** filter_en : R/W; bitpos: [10]; default: 1; * This is the enable bit for unit n's input filter. */ - uint32_t filter_en_un:1; - /** thr_zero_en_un : R/W; bitpos: [11]; default: 1; + uint32_t filter_en:1; + /** thr_zero_en : R/W; bitpos: [11]; default: 1; * This is the enable bit for unit n's zero comparator. */ - uint32_t thr_zero_en_un:1; - /** thr_h_lim_en_un : R/W; bitpos: [12]; default: 1; + uint32_t thr_zero_en:1; + /** thr_h_lim_en : R/W; bitpos: [12]; default: 1; * This is the enable bit for unit n's thr_h_lim comparator. Configures it to enable * the high limit interrupt. */ - uint32_t thr_h_lim_en_un:1; - /** thr_l_lim_en_un : R/W; bitpos: [13]; default: 1; + uint32_t thr_h_lim_en:1; + /** thr_l_lim_en : R/W; bitpos: [13]; default: 1; * This is the enable bit for unit n's thr_l_lim comparator. Configures it to enable * the low limit interrupt. */ - uint32_t thr_l_lim_en_un:1; - /** thr_thres0_en_un : R/W; bitpos: [14]; default: 0; + uint32_t thr_l_lim_en:1; + /** thr_thres0_en : R/W; bitpos: [14]; default: 0; * This is the enable bit for unit n's thres0 comparator. */ - uint32_t thr_thres0_en_un:1; - /** thr_thres1_en_un : R/W; bitpos: [15]; default: 0; + uint32_t thr_thres0_en:1; + /** thr_thres1_en : R/W; bitpos: [15]; default: 0; * This is the enable bit for unit n's thres1 comparator. */ - uint32_t thr_thres1_en_un:1; - /** ch0_neg_mode_un : R/W; bitpos: [17:16]; default: 0; + uint32_t thr_thres1_en:1; + /** ch0_neg_mode : R/W; bitpos: [17:16]; default: 0; * This register sets the behavior when the signal input of channel 0 detects a * negative edge. * * 1: Increase the counter.2: Decrease the counter.0, 3: No effect on counter */ - uint32_t ch0_neg_mode_un:2; - /** ch0_pos_mode_un : R/W; bitpos: [19:18]; default: 0; + uint32_t ch0_neg_mode:2; + /** ch0_pos_mode : R/W; bitpos: [19:18]; default: 0; * This register sets the behavior when the signal input of channel 0 detects a * positive edge. * * 1: Increase the counter.2: Decrease the counter.0, 3: No effect on counter */ - uint32_t ch0_pos_mode_un:2; - /** ch0_hctrl_mode_un : R/W; bitpos: [21:20]; default: 0; + uint32_t ch0_pos_mode:2; + /** ch0_hctrl_mode : R/W; bitpos: [21:20]; default: 0; * This register configures how the CHn_POS_MODE/CHn_NEG_MODE settings will be * modified when the control signal is high. * * 0: No modification.1: Invert behavior (increase -> decrease, decrease -> * increase).2, 3: Inhibit counter modification */ - uint32_t ch0_hctrl_mode_un:2; - /** ch0_lctrl_mode_un : R/W; bitpos: [23:22]; default: 0; + uint32_t ch0_hctrl_mode:2; + /** ch0_lctrl_mode : R/W; bitpos: [23:22]; default: 0; * This register configures how the CHn_POS_MODE/CHn_NEG_MODE settings will be * modified when the control signal is low. * * 0: No modification.1: Invert behavior (increase -> decrease, decrease -> * increase).2, 3: Inhibit counter modification */ - uint32_t ch0_lctrl_mode_un:2; - /** ch1_neg_mode_un : R/W; bitpos: [25:24]; default: 0; + uint32_t ch0_lctrl_mode:2; + /** ch1_neg_mode : R/W; bitpos: [25:24]; default: 0; * This register sets the behavior when the signal input of channel 1 detects a * negative edge. * * 1: Increment the counter.2: Decrement the counter.0, 3: No effect on counter */ - uint32_t ch1_neg_mode_un:2; - /** ch1_pos_mode_un : R/W; bitpos: [27:26]; default: 0; + uint32_t ch1_neg_mode:2; + /** ch1_pos_mode : R/W; bitpos: [27:26]; default: 0; * This register sets the behavior when the signal input of channel 1 detects a * positive edge. * * 1: Increment the counter.2: Decrement the counter.0, 3: No effect on counter */ - uint32_t ch1_pos_mode_un:2; - /** ch1_hctrl_mode_un : R/W; bitpos: [29:28]; default: 0; + uint32_t ch1_pos_mode:2; + /** ch1_hctrl_mode : R/W; bitpos: [29:28]; default: 0; * This register configures how the CHn_POS_MODE/CHn_NEG_MODE settings will be * modified when the control signal is high. * * 0: No modification.1: Invert behavior (increase -> decrease, decrease -> * increase).2, 3: Inhibit counter modification */ - uint32_t ch1_hctrl_mode_un:2; - /** ch1_lctrl_mode_un : R/W; bitpos: [31:30]; default: 0; + uint32_t ch1_hctrl_mode:2; + /** ch1_lctrl_mode : R/W; bitpos: [31:30]; default: 0; * This register configures how the CHn_POS_MODE/CHn_NEG_MODE settings will be * modified when the control signal is low. * * 0: No modification.1: Invert behavior (increase -> decrease, decrease -> * increase).2, 3: Inhibit counter modification */ - uint32_t ch1_lctrl_mode_un:2; + uint32_t ch1_lctrl_mode:2; }; uint32_t val; } pcnt_un_conf0_reg_t; @@ -117,14 +117,14 @@ typedef union { */ typedef union { struct { - /** cnt_thres0_un : R/W; bitpos: [15:0]; default: 0; + /** cnt_thres0 : R/W; bitpos: [15:0]; default: 0; * This register is used to configure the thres0 value for unit n. */ - uint32_t cnt_thres0_un:16; - /** cnt_thres1_un : R/W; bitpos: [31:16]; default: 0; + uint32_t cnt_thres0:16; + /** cnt_thres1 : R/W; bitpos: [31:16]; default: 0; * This register is used to configure the thres1 value for unit n. */ - uint32_t cnt_thres1_un:16; + uint32_t cnt_thres1:16; }; uint32_t val; } pcnt_un_conf1_reg_t; @@ -134,16 +134,16 @@ typedef union { */ typedef union { struct { - /** cnt_h_lim_un : R/W; bitpos: [15:0]; default: 0; + /** cnt_h_lim : R/W; bitpos: [15:0]; default: 0; * This register is used to configure the thr_h_lim value for unit n. When pcnt * reaches this value, the counter will be cleared to 0. */ - uint32_t cnt_h_lim_un:16; - /** cnt_l_lim_un : R/W; bitpos: [31:16]; default: 0; + uint32_t cnt_h_lim:16; + /** cnt_l_lim : R/W; bitpos: [31:16]; default: 0; * This register is used to configure the thr_l_lim value for unit n. When pcnt * reaches this value, the counter will be cleared to 0. */ - uint32_t cnt_l_lim_un:16; + uint32_t cnt_l_lim:16; }; uint32_t val; } pcnt_un_conf2_reg_t; @@ -185,22 +185,22 @@ typedef union { * Set this bit to freeze unit 3's counter. */ uint32_t cnt_pause_u3:1; - /** dalta_change_en_u0 : R/W; bitpos: [8]; default: 0; + /** delta_change_en_u0 : R/W; bitpos: [8]; default: 0; * Configures this bit to enable unit 0's step comparator. */ - uint32_t dalta_change_en_u0:1; - /** dalta_change_en_u1 : R/W; bitpos: [9]; default: 0; + uint32_t delta_change_en_u0:1; + /** delta_change_en_u1 : R/W; bitpos: [9]; default: 0; * Configures this bit to enable unit 1's step comparator. */ - uint32_t dalta_change_en_u1:1; - /** dalta_change_en_u2 : R/W; bitpos: [10]; default: 0; + uint32_t delta_change_en_u1:1; + /** delta_change_en_u2 : R/W; bitpos: [10]; default: 0; * Configures this bit to enable unit 2's step comparator. */ - uint32_t dalta_change_en_u2:1; - /** dalta_change_en_u3 : R/W; bitpos: [11]; default: 0; + uint32_t delta_change_en_u2:1; + /** delta_change_en_u3 : R/W; bitpos: [11]; default: 0; * Configures this bit to enable unit 3's step comparator. */ - uint32_t dalta_change_en_u3:1; + uint32_t delta_change_en_u3:1; uint32_t reserved_12:4; /** clk_en : R/W; bitpos: [16]; default: 0; * The registers clock gate enable signal of PCNT module. 1: the registers can be read @@ -213,73 +213,22 @@ typedef union { uint32_t val; } pcnt_ctrl_reg_t; -/** Type of u3_change_conf register +/** Type of change_conf register * Configuration register for unit $n's step value. */ typedef union { struct { - /** cnt_step_u3 : R/W; bitpos: [15:0]; default: 0; - * Configures the step value for unit 3. + /** cnt_step : R/W; bitpos: [15:0]; default: 0; + * Configures the step value for unit n. */ - uint32_t cnt_step_u3:16; - /** cnt_step_lim_u3 : R/W; bitpos: [31:16]; default: 0; - * Configures the step limit value for unit 3. + uint32_t cnt_step:16; + /** cnt_step_lim : R/W; bitpos: [31:16]; default: 0; + * Configures the step limit value for unit n. */ - uint32_t cnt_step_lim_u3:16; + uint32_t cnt_step_lim:16; }; uint32_t val; -} pcnt_u3_change_conf_reg_t; - -/** Type of u2_change_conf register - * Configuration register for unit $n's step value. - */ -typedef union { - struct { - /** cnt_step_u2 : R/W; bitpos: [15:0]; default: 0; - * Configures the step value for unit 2. - */ - uint32_t cnt_step_u2:16; - /** cnt_step_lim_u2 : R/W; bitpos: [31:16]; default: 0; - * Configures the step limit value for unit 2. - */ - uint32_t cnt_step_lim_u2:16; - }; - uint32_t val; -} pcnt_u2_change_conf_reg_t; - -/** Type of u1_change_conf register - * Configuration register for unit $n's step value. - */ -typedef union { - struct { - /** cnt_step_u1 : R/W; bitpos: [15:0]; default: 0; - * Configures the step value for unit 1. - */ - uint32_t cnt_step_u1:16; - /** cnt_step_lim_u1 : R/W; bitpos: [31:16]; default: 0; - * Configures the step limit value for unit 1. - */ - uint32_t cnt_step_lim_u1:16; - }; - uint32_t val; -} pcnt_u1_change_conf_reg_t; - -/** Type of u0_change_conf register - * Configuration register for unit $n's step value. - */ -typedef union { - struct { - /** cnt_step_u0 : R/W; bitpos: [15:0]; default: 0; - * Configures the step value for unit 0. - */ - uint32_t cnt_step_u0:16; - /** cnt_step_lim_u0 : R/W; bitpos: [31:16]; default: 0; - * Configures the step limit value for unit 0. - */ - uint32_t cnt_step_lim_u0:16; - }; - uint32_t val; -} pcnt_u0_change_conf_reg_t; +} pcnt_un_change_conf_reg_t; /** Group: Status Register */ @@ -288,10 +237,10 @@ typedef union { */ typedef union { struct { - /** pulse_cnt_un : RO; bitpos: [15:0]; default: 0; + /** pulse_cnt : RO; bitpos: [15:0]; default: 0; * This register stores the current pulse count value for unit n. */ - uint32_t pulse_cnt_un:16; + uint32_t pulse_cnt:16; uint32_t reserved_16:16; }; uint32_t val; @@ -302,42 +251,42 @@ typedef union { */ typedef union { struct { - /** cnt_thr_zero_mode_un : RO; bitpos: [1:0]; default: 0; - * The pulse counter status of PCNT_Un corresponding to 0. 0: pulse counter decreases + /** cnt_thr_zero_mode : RO; bitpos: [1:0]; default: 0; + * The pulse counter status of PCNT corresponding to 0. 0: pulse counter decreases * from positive to 0. 1: pulse counter increases from negative to 0. 2: pulse counter * is negative. 3: pulse counter is positive. */ - uint32_t cnt_thr_zero_mode_un:2; - /** cnt_thr_thres1_lat_un : RO; bitpos: [2]; default: 0; - * The latched value of thres1 event of PCNT_Un when threshold event interrupt is + uint32_t cnt_thr_zero_mode:2; + /** cnt_thr_thres1_lat : RO; bitpos: [2]; default: 0; + * The latched value of thres1 event of PCNT when threshold event interrupt is * valid. 1: the current pulse counter equals to thres1 and thres1 event is valid. 0: * others */ - uint32_t cnt_thr_thres1_lat_un:1; - /** cnt_thr_thres0_lat_un : RO; bitpos: [3]; default: 0; - * The latched value of thres0 event of PCNT_Un when threshold event interrupt is + uint32_t cnt_thr_thres1_lat:1; + /** cnt_thr_thres0_lat : RO; bitpos: [3]; default: 0; + * The latched value of thres0 event of PCNT when threshold event interrupt is * valid. 1: the current pulse counter equals to thres0 and thres0 event is valid. 0: * others */ - uint32_t cnt_thr_thres0_lat_un:1; - /** cnt_thr_l_lim_lat_un : RO; bitpos: [4]; default: 0; - * The latched value of low limit event of PCNT_Un when threshold event interrupt is + uint32_t cnt_thr_thres0_lat:1; + /** cnt_thr_l_lim_lat : RO; bitpos: [4]; default: 0; + * The latched value of low limit event of PCNT when threshold event interrupt is * valid. 1: the current pulse counter equals to thr_l_lim and low limit event is * valid. 0: others */ - uint32_t cnt_thr_l_lim_lat_un:1; - /** cnt_thr_h_lim_lat_un : RO; bitpos: [5]; default: 0; - * The latched value of high limit event of PCNT_Un when threshold event interrupt is + uint32_t cnt_thr_l_lim_lat:1; + /** cnt_thr_h_lim_lat : RO; bitpos: [5]; default: 0; + * The latched value of high limit event of PCNT when threshold event interrupt is * valid. 1: the current pulse counter equals to thr_h_lim and high limit event is * valid. 0: others */ - uint32_t cnt_thr_h_lim_lat_un:1; - /** cnt_thr_zero_lat_un : RO; bitpos: [6]; default: 0; - * The latched value of zero threshold event of PCNT_Un when threshold event interrupt + uint32_t cnt_thr_h_lim_lat:1; + /** cnt_thr_zero_lat : RO; bitpos: [6]; default: 0; + * The latched value of zero threshold event of PCNT when threshold event interrupt * is valid. 1: the current pulse counter equals to 0 and zero threshold event is * valid. 0: others */ - uint32_t cnt_thr_zero_lat_un:1; + uint32_t cnt_thr_zero_lat:1; uint32_t reserved_7:25; }; uint32_t val; @@ -464,35 +413,25 @@ typedef union { uint32_t val; } pcnt_date_reg_t; - -typedef struct { - volatile pcnt_un_conf0_reg_t u0_conf0; - volatile pcnt_un_conf1_reg_t u0_conf1; - volatile pcnt_un_conf2_reg_t u0_conf2; - volatile pcnt_un_conf0_reg_t u1_conf0; - volatile pcnt_un_conf1_reg_t u1_conf1; - volatile pcnt_un_conf2_reg_t u1_conf2; - volatile pcnt_un_conf0_reg_t u2_conf0; - volatile pcnt_un_conf1_reg_t u2_conf1; - volatile pcnt_un_conf2_reg_t u2_conf2; - volatile pcnt_un_conf0_reg_t u3_conf0; - volatile pcnt_un_conf1_reg_t u3_conf1; - volatile pcnt_un_conf2_reg_t u3_conf2; - volatile pcnt_un_cnt_reg_t un_cnt[4]; +typedef struct pcnt_dev_t { + volatile struct { + pcnt_un_conf0_reg_t conf0; + pcnt_un_conf1_reg_t conf1; + pcnt_un_conf2_reg_t conf2; + } conf_unit[4]; + volatile pcnt_un_cnt_reg_t cnt_unit[4]; volatile pcnt_int_raw_reg_t int_raw; volatile pcnt_int_st_reg_t int_st; volatile pcnt_int_ena_reg_t int_ena; volatile pcnt_int_clr_reg_t int_clr; - volatile pcnt_un_status_reg_t un_status[4]; + volatile pcnt_un_status_reg_t status_unit[4]; volatile pcnt_ctrl_reg_t ctrl; - volatile pcnt_u3_change_conf_reg_t u3_change_conf; - volatile pcnt_u2_change_conf_reg_t u2_change_conf; - volatile pcnt_u1_change_conf_reg_t u1_change_conf; - volatile pcnt_u0_change_conf_reg_t u0_change_conf; + volatile pcnt_un_change_conf_reg_t change_conf_unit[4]; // Note the unit order is 3210 uint32_t reserved_074[34]; volatile pcnt_date_reg_t date; } pcnt_dev_t; +extern pcnt_dev_t PCNT; #ifndef __cplusplus _Static_assert(sizeof(pcnt_dev_t) == 0x100, "Invalid size of pcnt_dev_t structure"); diff --git a/components/soc/esp32p4/include/soc/soc_caps.h b/components/soc/esp32p4/include/soc/soc_caps.h index ecbae66121..2415ece599 100644 --- a/components/soc/esp32p4/include/soc/soc_caps.h +++ b/components/soc/esp32p4/include/soc/soc_caps.h @@ -33,8 +33,8 @@ #define SOC_AHB_GDMA_SUPPORTED 1 #define SOC_AXI_GDMA_SUPPORTED 1 #define SOC_GPTIMER_SUPPORTED 1 -// #define SOC_PCNT_SUPPORTED 1 //TODO: IDF-7475 -#define SOC_MCPWM_SUPPORTED 1 +#define SOC_PCNT_SUPPORTED 1 +#define SOC_MCPWM_SUPPORTED 1 // #define SOC_TWAI_SUPPORTED 1 //TODO: IDF-7470 // #define SOC_ETM_SUPPORTED 1 //TODO: IDF-7478 // #define SOC_PARLIO_SUPPORTED 1 //TODO: IDF-7471, TODO: IDF-7472 @@ -255,6 +255,7 @@ #define SOC_PCNT_CHANNELS_PER_UNIT 2 #define SOC_PCNT_THRES_POINT_PER_UNIT 2 #define SOC_PCNT_SUPPORT_RUNTIME_THRES_UPDATE 1 +#define SOC_PCNT_SUPPORT_ZERO_INPUT 1 /*!< Support encoder with Zero phase input */ /*--------------------------- RMT CAPS ---------------------------------------*/ #define SOC_RMT_GROUPS 1U /*!< One RMT group */ diff --git a/components/soc/esp32p4/pcnt_periph.c b/components/soc/esp32p4/pcnt_periph.c index ca289c2e28..8d1afb6ed2 100644 --- a/components/soc/esp32p4/pcnt_periph.c +++ b/components/soc/esp32p4/pcnt_periph.c @@ -8,5 +8,64 @@ #include "soc/gpio_sig_map.h" const pcnt_signal_conn_t pcnt_periph_signals = { - + .groups = { + [0] = { + .module = PERIPH_PCNT_MODULE, + .irq = ETS_PCNT_INTR_SOURCE, + .units = { + [0] = { + .channels = { + [0] = { + .control_sig = PCNT_CTRL_CH0_PAD_IN0_IDX, + .pulse_sig = PCNT_SIG_CH0_PAD_IN0_IDX + }, + [1] = { + .control_sig = PCNT_CTRL_CH1_PAD_IN0_IDX, + .pulse_sig = PCNT_SIG_CH1_PAD_IN0_IDX + } + }, + .clear_sig = PCNT_RST_PAD_IN0_IDX + }, + [1] = { + .channels = { + [0] = { + .control_sig = PCNT_CTRL_CH0_PAD_IN1_IDX, + .pulse_sig = PCNT_SIG_CH0_PAD_IN1_IDX, + }, + [1] = { + .control_sig = PCNT_CTRL_CH1_PAD_IN1_IDX, + .pulse_sig = PCNT_SIG_CH1_PAD_IN1_IDX + } + }, + .clear_sig = PCNT_RST_PAD_IN1_IDX + }, + [2] = { + .channels = { + [0] = { + .control_sig = PCNT_CTRL_CH0_PAD_IN2_IDX, + .pulse_sig = PCNT_SIG_CH0_PAD_IN2_IDX, + }, + [1] = { + .control_sig = PCNT_CTRL_CH1_PAD_IN2_IDX, + .pulse_sig = PCNT_SIG_CH1_PAD_IN2_IDX + } + }, + .clear_sig = PCNT_RST_PAD_IN2_IDX + }, + [3] = { + .channels = { + [0] = { + .control_sig = PCNT_CTRL_CH0_PAD_IN3_IDX, + .pulse_sig = PCNT_SIG_CH0_PAD_IN3_IDX, + }, + [1] = { + .control_sig = PCNT_CTRL_CH1_PAD_IN3_IDX, + .pulse_sig = PCNT_SIG_CH1_PAD_IN3_IDX + } + }, + .clear_sig = PCNT_RST_PAD_IN3_IDX + } + } + } + } }; diff --git a/components/soc/include/soc/pcnt_periph.h b/components/soc/include/soc/pcnt_periph.h index f73aefcd82..16ced33c43 100644 --- a/components/soc/include/soc/pcnt_periph.h +++ b/components/soc/include/soc/pcnt_periph.h @@ -23,6 +23,7 @@ typedef struct { const uint32_t pulse_sig; const uint32_t control_sig; } channels[SOC_PCNT_CHANNELS_PER_UNIT]; + const uint32_t clear_sig; } units[SOC_PCNT_UNITS_PER_GROUP]; const uint32_t irq; const periph_module_t module; diff --git a/docs/en/api-reference/peripherals/pcnt.rst b/docs/en/api-reference/peripherals/pcnt.rst index 978818c4e8..c1bdaaa9bf 100644 --- a/docs/en/api-reference/peripherals/pcnt.rst +++ b/docs/en/api-reference/peripherals/pcnt.rst @@ -47,6 +47,12 @@ To install a PCNT unit, there's a configuration structure that needs to be given - :cpp:member:`pcnt_unit_config_t::low_limit` and :cpp:member:`pcnt_unit_config_t::high_limit` specify the range for the internal hardware counter. The counter will reset to zero automatically when it crosses either the high or low limit. - :cpp:member:`pcnt_unit_config_t::accum_count` sets whether to create an internal accumulator for the counter. This is helpful when you want to extend the counter's width, which by default is 16bit at most, defined in the hardware. See also :ref:`pcnt-compensate-overflow-loss` for how to use this feature to compensate the overflow loss. +.. only:: SOC_PCNT_SUPPORT_ZERO_INPUT + + - :cpp:member:`pcnt_unit_config_t::zero_input_gpio_num` specify the GPIO numbers used by **zero** type signal. The default active level is high, and the input mode is pull-down enabled. Please note, it can be assigned to `-1` if it's not actually used, and GPIO will not be initialized. + - :cpp:member:`pcnt_unit_config_t::invert_zero_input` 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 invert. + - :cpp:member:`pcnt_unit_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 zreo pulse signals by function :cpp:func:`gpio_set_level` on the same GPIO. + Unit allocation and initialization is done by calling a function :cpp:func:`pcnt_new_unit` with :cpp:type:`pcnt_unit_config_t` as an input parameter. The function will return a PCNT unit handle only when it runs correctly. Specifically, when there are no more free PCNT units in the pool (i.e. unit resources have been used up), then this function will return :c:macro:`ESP_ERR_NOT_FOUND` error. The total number of available PCNT units is recorded by :c:macro:`SOC_PCNT_UNITS_PER_GROUP` for reference. If a previously created PCNT unit is no longer needed, it's recommended to recycle the resource by calling :cpp:func:`pcnt_del_unit`. Which in return allows the underlying unit hardware to be used for other purposes. Before deleting a PCNT unit, one should ensure the following prerequisites: diff --git a/docs/zh_CN/api-reference/peripherals/pcnt.rst b/docs/zh_CN/api-reference/peripherals/pcnt.rst index e5d9b71a80..c195fe1f9d 100644 --- a/docs/zh_CN/api-reference/peripherals/pcnt.rst +++ b/docs/zh_CN/api-reference/peripherals/pcnt.rst @@ -47,6 +47,12 @@ PCNT 单元和通道分别用 :cpp:type:`pcnt_unit_handle_t` 与 :cpp:type:`pcnt - :cpp:member:`pcnt_unit_config_t::low_limit` 与 :cpp:member:`pcnt_unit_config_t::high_limit` 用于指定内部计数器的最小值和最大值。当计数器超过任一限值时,计数器将归零。 - :cpp:member:`pcnt_unit_config_t::accum_count` 用于设置是否需要软件在硬件计数值溢出的时候进行累加保存,这有助于“拓宽”计数器的实际位宽。默认情况下,计数器的位宽最高只有 16 比特。请参考 :ref:`pcnt-compensate-overflow-loss` 了解如何利用此功能来补偿硬件计数器的溢出损失。 +.. only:: SOC_PCNT_SUPPORT_ZERO_INPUT + + - :cpp:member:`pcnt_unit_config_t::zero_input_gpio_num` 用于指定 **清零** 信号对应的 GPIO 编号。默认有效电平为高,使能下拉输入。请注意,这个参数未被使用时,可以设置为 `-1`,初始化时将不会分配 GPIO。 + - :cpp:member:`pcnt_unit_config_t::invert_zero_input` 用于确定信号在输入 PCNT 之前是否需要被翻转,信号翻转由 GPIO 矩阵 (不是 PCNT 单元) 执行。翻转时使能上拉输入。 + - :cpp:member:`pcnt_unit_config_t::io_loop_back` 仅用于调试,它可以使能 GPIO 的输入和输出路径。这样,就可以通过调用位于同一 GPIO 上的函数 :cpp:func:`gpio_set_level` 来模拟脉冲清零信号。 + 调用函数 :cpp:func:`pcnt_new_unit` 并将 :cpp:type:`pcnt_unit_config_t` 作为其输入值,可对 PCNT 单元进行分配和初始化。该函数正常运行时,会返回一个 PCNT 单元句柄。没有可用的 PCNT 单元时(即 PCNT 单元全部被占用),该函数会返回错误 :c:macro:`ESP_ERR_NOT_FOUND`。可用的 PCNT 单元总数记录在 :c:macro:`SOC_PCNT_UNITS_PER_GROUP` 中,以供参考。 如果不再需要之前创建的某个 PCNT 单元,建议通过调用 :cpp:func:`pcnt_del_unit` 来回收该单元,从而该单元可用于其他用途。删除某个 PCNT 单元之前,需要满足以下条件: