From 3779757cd30a79fc7b97e1b8d222cb84fb74e7e5 Mon Sep 17 00:00:00 2001 From: Chen Jichang Date: Mon, 9 Dec 2024 19:05:04 +0800 Subject: [PATCH] feat(pcnt): support step_notify on esp32h2 eco5 --- components/esp_driver_pcnt/src/pulse_cnt.c | 7 +- .../test_apps/pulse_cnt/main/test_pulse_cnt.c | 260 +++++++++--------- components/hal/esp32c5/include/hal/pcnt_ll.h | 10 + components/hal/esp32h2/include/hal/pcnt_ll.h | 61 +++- .../esp32h2/include/soc/Kconfig.soc_caps.in | 4 + components/soc/esp32h2/include/soc/soc_caps.h | 1 + .../soc/esp32h2/register/soc/pcnt_struct.h | 53 +++- 7 files changed, 260 insertions(+), 136 deletions(-) diff --git a/components/esp_driver_pcnt/src/pulse_cnt.c b/components/esp_driver_pcnt/src/pulse_cnt.c index e2accb936f..2dc6e5e2eb 100644 --- a/components/esp_driver_pcnt/src/pulse_cnt.c +++ b/components/esp_driver_pcnt/src/pulse_cnt.c @@ -667,8 +667,10 @@ esp_err_t pcnt_unit_remove_watch_point(pcnt_unit_handle_t unit, int watch_point) #if SOC_PCNT_SUPPORT_STEP_NOTIFY esp_err_t pcnt_unit_add_watch_step(pcnt_unit_handle_t unit, int step_interval) { - pcnt_group_t *group = NULL; - + pcnt_group_t *group = unit->group; + if (!pcnt_ll_is_step_notify_supported(group->group_id)) { + ESP_RETURN_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, TAG, "watch step is not supported by this chip revision"); + } ESP_RETURN_ON_FALSE(unit, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); ESP_RETURN_ON_FALSE((step_interval > 0 && unit->flags.en_step_notify_up) || (step_interval < 0 && unit->flags.en_step_notify_down), ESP_ERR_INVALID_ARG, TAG, "invalid step interval"); @@ -677,7 +679,6 @@ esp_err_t pcnt_unit_add_watch_step(pcnt_unit_handle_t unit, int step_interval) ESP_RETURN_ON_FALSE(unit->step_interval == 0, ESP_ERR_INVALID_STATE, TAG, "watch step has been set to %d already", unit->step_interval); - group = unit->group; unit->step_interval = step_interval; pcnt_ll_set_step_value(group->hal.dev, unit->unit_id, step_interval); // different units are mixing in the same register, so we use the group's spinlock here diff --git a/components/esp_driver_pcnt/test_apps/pulse_cnt/main/test_pulse_cnt.c b/components/esp_driver_pcnt/test_apps/pulse_cnt/main/test_pulse_cnt.c index 61952df1a2..51c09a1122 100644 --- a/components/esp_driver_pcnt/test_apps/pulse_cnt/main/test_pulse_cnt.c +++ b/components/esp_driver_pcnt/test_apps/pulse_cnt/main/test_pulse_cnt.c @@ -14,6 +14,7 @@ #include "soc/soc_caps.h" #include "esp_attr.h" #include "test_pulse_cnt_board.h" +#include "hal/pcnt_ll.h" TEST_CASE("pcnt_unit_install_uninstall", "[pcnt]") { @@ -677,145 +678,148 @@ TEST_CASE("pcnt overflow accumulation", "[pcnt]") #if SOC_PCNT_SUPPORT_STEP_NOTIFY 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); + if (pcnt_ll_is_step_notify_supported(0)) { // for ESP32H2, only support in chip version v1.2 and above + test_gpio_init_for_simulation(TEST_PCNT_GPIO_A); + test_gpio_init_for_simulation(TEST_PCNT_GPIO_B); - pcnt_unit_config_t unit_config = { - .low_limit = -100, - .high_limit = 100, - .flags = { - .en_step_notify_down = true, - }, - }; + pcnt_unit_config_t unit_config = { + .low_limit = -100, + .high_limit = 100, + .flags = { + .en_step_notify_down = 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 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 two pcnt channels with different edge/level action\r\n"); - pcnt_chan_config_t channel_config = { - .edge_gpio_num = TEST_PCNT_GPIO_A, - .level_gpio_num = TEST_PCNT_GPIO_B, - }; - pcnt_channel_handle_t channelA = NULL; - TEST_ESP_OK(pcnt_new_channel(unit, &channel_config, &channelA)); - TEST_ESP_OK(pcnt_channel_set_edge_action(channelA, PCNT_CHANNEL_EDGE_ACTION_DECREASE, PCNT_CHANNEL_EDGE_ACTION_INCREASE)); - TEST_ESP_OK(pcnt_channel_set_level_action(channelA, PCNT_CHANNEL_LEVEL_ACTION_KEEP, PCNT_CHANNEL_LEVEL_ACTION_INVERSE)); - // switch edge gpio and level gpio, the assign to another channel in the same unit - pcnt_channel_handle_t channelB = NULL; - channel_config.edge_gpio_num = TEST_PCNT_GPIO_B; - channel_config.level_gpio_num = TEST_PCNT_GPIO_A; - TEST_ESP_OK(pcnt_new_channel(unit, &channel_config, &channelB)); - TEST_ESP_OK(pcnt_channel_set_edge_action(channelB, PCNT_CHANNEL_EDGE_ACTION_INCREASE, PCNT_CHANNEL_EDGE_ACTION_DECREASE)); - TEST_ESP_OK(pcnt_channel_set_level_action(channelB, PCNT_CHANNEL_LEVEL_ACTION_KEEP, PCNT_CHANNEL_LEVEL_ACTION_INVERSE)); + printf("install two pcnt channels with different edge/level action\r\n"); + pcnt_chan_config_t channel_config = { + .edge_gpio_num = TEST_PCNT_GPIO_A, + .level_gpio_num = TEST_PCNT_GPIO_B, + }; + pcnt_channel_handle_t channelA = NULL; + TEST_ESP_OK(pcnt_new_channel(unit, &channel_config, &channelA)); + TEST_ESP_OK(pcnt_channel_set_edge_action(channelA, PCNT_CHANNEL_EDGE_ACTION_DECREASE, PCNT_CHANNEL_EDGE_ACTION_INCREASE)); + TEST_ESP_OK(pcnt_channel_set_level_action(channelA, PCNT_CHANNEL_LEVEL_ACTION_KEEP, PCNT_CHANNEL_LEVEL_ACTION_INVERSE)); + // switch edge gpio and level gpio, the assign to another channel in the same unit + pcnt_channel_handle_t channelB = NULL; + channel_config.edge_gpio_num = TEST_PCNT_GPIO_B; + channel_config.level_gpio_num = TEST_PCNT_GPIO_A; + TEST_ESP_OK(pcnt_new_channel(unit, &channel_config, &channelB)); + TEST_ESP_OK(pcnt_channel_set_edge_action(channelB, PCNT_CHANNEL_EDGE_ACTION_INCREASE, PCNT_CHANNEL_EDGE_ACTION_DECREASE)); + TEST_ESP_OK(pcnt_channel_set_level_action(channelB, PCNT_CHANNEL_LEVEL_ACTION_KEEP, PCNT_CHANNEL_LEVEL_ACTION_INVERSE)); - // ensure the simulation signal in a stable state - TEST_ESP_OK(gpio_set_level(TEST_PCNT_GPIO_A, 1)); - TEST_ESP_OK(gpio_set_level(TEST_PCNT_GPIO_B, 1)); + // ensure the simulation signal in a stable state + TEST_ESP_OK(gpio_set_level(TEST_PCNT_GPIO_A, 1)); + TEST_ESP_OK(gpio_set_level(TEST_PCNT_GPIO_B, 1)); - pcnt_event_callbacks_t cbs = { - .on_reach = test_pcnt_quadrature_reach_watch_point, - }; - test_pcnt_quadrature_context_t user_data = { - .index = 0, - .triggered_watch_values = {0}, - }; - TEST_ESP_OK(pcnt_unit_register_event_callbacks(unit, &cbs, &user_data)); + pcnt_event_callbacks_t cbs = { + .on_reach = test_pcnt_quadrature_reach_watch_point, + }; + test_pcnt_quadrature_context_t user_data = { + .index = 0, + .triggered_watch_values = {0}, + }; + TEST_ESP_OK(pcnt_unit_register_event_callbacks(unit, &cbs, &user_data)); - printf("add watch step and point\r\n"); - TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, pcnt_unit_add_watch_step(unit, 0)); - TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, pcnt_unit_add_watch_step(unit, 20)); - TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, pcnt_unit_add_watch_step(unit, -120)); - TEST_ESP_OK(pcnt_unit_add_watch_step(unit, -25)); - TEST_ASSERT_EQUAL(ESP_ERR_INVALID_STATE, pcnt_unit_add_watch_step(unit, -100)); - TEST_ESP_OK(pcnt_unit_add_watch_point(unit, -100)); - TEST_ESP_OK(pcnt_unit_add_watch_point(unit, 0)); - TEST_ESP_OK(pcnt_unit_add_watch_point(unit, -50)); - TEST_ESP_OK(pcnt_unit_add_watch_point(unit, 11)); + printf("add watch step and point\r\n"); + TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, pcnt_unit_add_watch_step(unit, 0)); + TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, pcnt_unit_add_watch_step(unit, 20)); + TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, pcnt_unit_add_watch_step(unit, -120)); + TEST_ESP_OK(pcnt_unit_add_watch_step(unit, -25)); + TEST_ASSERT_EQUAL(ESP_ERR_INVALID_STATE, pcnt_unit_add_watch_step(unit, -100)); + TEST_ESP_OK(pcnt_unit_add_watch_point(unit, -100)); + TEST_ESP_OK(pcnt_unit_add_watch_point(unit, 0)); + TEST_ESP_OK(pcnt_unit_add_watch_point(unit, -50)); + TEST_ESP_OK(pcnt_unit_add_watch_point(unit, 11)); - TEST_ESP_OK(pcnt_unit_enable(unit)); - TEST_ESP_OK(pcnt_unit_start(unit)); + TEST_ESP_OK(pcnt_unit_enable(unit)); + TEST_ESP_OK(pcnt_unit_start(unit)); - printf("simulating quadrature signals and count down\r\n"); - test_gpio_simulate_quadrature_signals(TEST_PCNT_GPIO_A, TEST_PCNT_GPIO_B, 25); // 25*(-4) = -100 -> 0 + printf("simulating quadrature signals and count down\r\n"); + test_gpio_simulate_quadrature_signals(TEST_PCNT_GPIO_A, TEST_PCNT_GPIO_B, 25); // 25*(-4) = -100 -> 0 - int count_value; - TEST_ESP_OK(pcnt_unit_get_count(unit, &count_value)); - printf("counter stopped at %d\r\n", count_value); + int count_value; + TEST_ESP_OK(pcnt_unit_get_count(unit, &count_value)); + printf("counter stopped at %d\r\n", count_value); - for (int i = 0 ; i < user_data.index; i++) { - printf("%d:%d\r\n", i, user_data.triggered_watch_values[i]); + for (int i = 0 ; i < user_data.index; i++) { + printf("%d:%d\r\n", i, user_data.triggered_watch_values[i]); + } + TEST_ASSERT_EQUAL(0, count_value); + TEST_ASSERT_EQUAL(5, user_data.index); + TEST_ASSERT_EQUAL(-25, user_data.triggered_watch_values[0]); // step point (-25*1) + TEST_ASSERT_EQUAL(-50, user_data.triggered_watch_values[1]); // step point && watch point + TEST_ASSERT_EQUAL(-75, user_data.triggered_watch_values[2]); // step point (-25*3) + TEST_ASSERT_EQUAL(-100, user_data.triggered_watch_values[3]);// step point && watch point + TEST_ASSERT_EQUAL(0, user_data.triggered_watch_values[4]); // watch point (overflow zero cross) + + printf("simulating quadrature signals and count up\r\n"); + user_data.index = 0; + test_gpio_simulate_quadrature_signals(TEST_PCNT_GPIO_B, TEST_PCNT_GPIO_A, 3); // 0+3*4 = 12 + TEST_ESP_OK(pcnt_unit_get_count(unit, &count_value)); + printf("counter stopped at %d\r\n", count_value); + for (int i = 0 ; i < user_data.index; i++) { + printf("%d:%d\r\n", i, user_data.triggered_watch_values[i]); + } + TEST_ASSERT_EQUAL(12, count_value); + TEST_ASSERT_EQUAL(1, user_data.index); + TEST_ASSERT_EQUAL(11, user_data.triggered_watch_values[0]); // watch point + + printf("simulating quadrature signals and count down again\r\n"); + user_data.index = 0; + test_gpio_simulate_quadrature_signals(TEST_PCNT_GPIO_A, TEST_PCNT_GPIO_B, 13); // 12-13*4 = -40 + TEST_ESP_OK(pcnt_unit_get_count(unit, &count_value)); + printf("counter stopped at %d\r\n", count_value); + for (int i = 0 ; i < user_data.index; i++) { + printf("%d:%d\r\n", i, user_data.triggered_watch_values[i]); + } + TEST_ASSERT_EQUAL(-40, count_value); + TEST_ASSERT_EQUAL(3, user_data.index); + TEST_ASSERT_EQUAL(11, user_data.triggered_watch_values[0]); // watch point + TEST_ASSERT_EQUAL(0, user_data.triggered_watch_values[1]); // watch point (zero cross) + TEST_ASSERT_EQUAL(-25, user_data.triggered_watch_values[2]);// step point (-25*1) + + // before change step interval, the next step point should be -25-25=-50 + printf("change step interval\r\n"); + TEST_ESP_OK(pcnt_unit_remove_watch_step(unit)); + TEST_ESP_OK(pcnt_unit_add_watch_step(unit, -20)); + // but after change, the next step point is -25-20=-45 (-25 is the last active step point) + + printf("simulating quadrature signals and count down\r\n"); + user_data.index = 0; + test_gpio_simulate_quadrature_signals(TEST_PCNT_GPIO_A, TEST_PCNT_GPIO_B, 20); // -40+20*(-4) = -120 -> -20 + TEST_ESP_OK(pcnt_unit_get_count(unit, &count_value)); + printf("counter stopped at %d\r\n", count_value); + + for (int i = 0 ; i < user_data.index; i++) { + printf("%d:%d\r\n", i, user_data.triggered_watch_values[i]); + } + TEST_ASSERT_EQUAL(-20, count_value); + TEST_ASSERT_EQUAL(7, user_data.index); + TEST_ASSERT_EQUAL(-45, user_data.triggered_watch_values[0]); // watch step + TEST_ASSERT_EQUAL(-50, user_data.triggered_watch_values[1]); // watch point + TEST_ASSERT_EQUAL(-65, user_data.triggered_watch_values[2]); // step point (-45-20) + TEST_ASSERT_EQUAL(-85, user_data.triggered_watch_values[3]); // step point (-65-20) + TEST_ASSERT_EQUAL(-100, user_data.triggered_watch_values[4]);// step && watch point + TEST_ASSERT_EQUAL(0, user_data.triggered_watch_values[5]); // watch point (overflow) + TEST_ASSERT_EQUAL(-20, user_data.triggered_watch_values[6]); // step point (0-20) + + printf("remove step_notify and uninstall channels\r\n"); + TEST_ESP_OK(pcnt_unit_remove_watch_step(unit)); + TEST_ASSERT_EQUAL(ESP_ERR_INVALID_STATE, pcnt_unit_remove_watch_step(unit)); + TEST_ESP_OK(pcnt_del_channel(channelA)); + TEST_ESP_OK(pcnt_del_channel(channelB)); + TEST_ESP_OK(pcnt_unit_stop(unit)); + TEST_ESP_OK(pcnt_unit_disable(unit)); + TEST_ESP_OK(pcnt_del_unit(unit)); } - TEST_ASSERT_EQUAL(0, count_value); - TEST_ASSERT_EQUAL(5, user_data.index); - TEST_ASSERT_EQUAL(-25, user_data.triggered_watch_values[0]); // step point (-25*1) - TEST_ASSERT_EQUAL(-50, user_data.triggered_watch_values[1]); // step point && watch point - TEST_ASSERT_EQUAL(-75, user_data.triggered_watch_values[2]); // step point (-25*3) - TEST_ASSERT_EQUAL(-100, user_data.triggered_watch_values[3]);// step point && watch point - TEST_ASSERT_EQUAL(0, user_data.triggered_watch_values[4]); // watch point (overflow zero cross) - printf("simulating quadrature signals and count up\r\n"); - user_data.index = 0; - test_gpio_simulate_quadrature_signals(TEST_PCNT_GPIO_B, TEST_PCNT_GPIO_A, 3); // 0+3*4 = 12 - TEST_ESP_OK(pcnt_unit_get_count(unit, &count_value)); - printf("counter stopped at %d\r\n", count_value); - for (int i = 0 ; i < user_data.index; i++) { - printf("%d:%d\r\n", i, user_data.triggered_watch_values[i]); - } - TEST_ASSERT_EQUAL(12, count_value); - TEST_ASSERT_EQUAL(1, user_data.index); - TEST_ASSERT_EQUAL(11, user_data.triggered_watch_values[0]); // watch point - - printf("simulating quadrature signals and count down again\r\n"); - user_data.index = 0; - test_gpio_simulate_quadrature_signals(TEST_PCNT_GPIO_A, TEST_PCNT_GPIO_B, 13); // 12-13*4 = -40 - TEST_ESP_OK(pcnt_unit_get_count(unit, &count_value)); - printf("counter stopped at %d\r\n", count_value); - for (int i = 0 ; i < user_data.index; i++) { - printf("%d:%d\r\n", i, user_data.triggered_watch_values[i]); - } - TEST_ASSERT_EQUAL(-40, count_value); - TEST_ASSERT_EQUAL(3, user_data.index); - TEST_ASSERT_EQUAL(11, user_data.triggered_watch_values[0]); // watch point - TEST_ASSERT_EQUAL(0, user_data.triggered_watch_values[1]); // watch point (zero cross) - TEST_ASSERT_EQUAL(-25, user_data.triggered_watch_values[2]);// step point (-25*1) - - // before change step interval, the next step point should be -25-25=-50 - printf("change step interval\r\n"); - TEST_ESP_OK(pcnt_unit_remove_watch_step(unit)); - TEST_ESP_OK(pcnt_unit_add_watch_step(unit, -20)); - // but after change, the next step point is -25-20=-45 (-25 is the last active step point) - - printf("simulating quadrature signals and count down\r\n"); - user_data.index = 0; - test_gpio_simulate_quadrature_signals(TEST_PCNT_GPIO_A, TEST_PCNT_GPIO_B, 20); // -40+20*(-4) = -120 -> -20 - TEST_ESP_OK(pcnt_unit_get_count(unit, &count_value)); - printf("counter stopped at %d\r\n", count_value); - - for (int i = 0 ; i < user_data.index; i++) { - printf("%d:%d\r\n", i, user_data.triggered_watch_values[i]); - } - TEST_ASSERT_EQUAL(-20, count_value); - TEST_ASSERT_EQUAL(7, user_data.index); - TEST_ASSERT_EQUAL(-45, user_data.triggered_watch_values[0]); // watch step - TEST_ASSERT_EQUAL(-50, user_data.triggered_watch_values[1]); // watch point - TEST_ASSERT_EQUAL(-65, user_data.triggered_watch_values[2]); // step point (-45-20) - TEST_ASSERT_EQUAL(-85, user_data.triggered_watch_values[3]); // step point (-65-20) - TEST_ASSERT_EQUAL(-100, user_data.triggered_watch_values[4]);// step && watch point - TEST_ASSERT_EQUAL(0, user_data.triggered_watch_values[5]); // watch point (overflow) - TEST_ASSERT_EQUAL(-20, user_data.triggered_watch_values[6]); // step point (0-20) - - printf("remove step_notify and uninstall channels\r\n"); - TEST_ESP_OK(pcnt_unit_remove_watch_step(unit)); - TEST_ASSERT_EQUAL(ESP_ERR_INVALID_STATE, pcnt_unit_remove_watch_step(unit)); - TEST_ESP_OK(pcnt_del_channel(channelA)); - TEST_ESP_OK(pcnt_del_channel(channelB)); - 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_STEP_NOTIFY diff --git a/components/hal/esp32c5/include/hal/pcnt_ll.h b/components/hal/esp32c5/include/hal/pcnt_ll.h index a1de9bd8de..491303725c 100644 --- a/components/hal/esp32c5/include/hal/pcnt_ll.h +++ b/components/hal/esp32c5/include/hal/pcnt_ll.h @@ -497,6 +497,16 @@ static inline void pcnt_ll_reset_register(int group_id) PCR.pcnt_conf.pcnt_rst_en = 0; } +/** + * @brief Check if the step notify is supported + */ +static inline bool pcnt_ll_is_step_notify_supported(int group_id) +{ + (void)group_id; + return true; +} + + #ifdef __cplusplus } #endif diff --git a/components/hal/esp32h2/include/hal/pcnt_ll.h b/components/hal/esp32h2/include/hal/pcnt_ll.h index 23fc40f672..f8b21a1642 100644 --- a/components/hal/esp32h2/include/hal/pcnt_ll.h +++ b/components/hal/esp32h2/include/hal/pcnt_ll.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -19,6 +19,9 @@ #include #include "soc/pcnt_struct.h" #include "hal/pcnt_types.h" +#include "hal/misc.h" +#include "hal/efuse_hal.h" +#include "soc/chip_revision.h" #include "soc/pcr_struct.h" #ifdef __cplusplus @@ -40,6 +43,12 @@ typedef enum { PCNT_LL_WATCH_EVENT_MAX } pcnt_ll_watch_event_id_t; +typedef enum { + PCNT_LL_STEP_EVENT_REACH_LIMIT = PCNT_LL_WATCH_EVENT_MAX, + PCNT_LL_STEP_EVENT_REACH_INTERVAL +} pcnt_ll_step_event_id_t; + +#define PCNT_LL_STEP_NOTIFY_DIR_LIMIT 1 #define PCNT_LL_WATCH_EVENT_MASK ((1 << PCNT_LL_WATCH_EVENT_MAX) - 1) #define PCNT_LL_UNIT_WATCH_EVENT(unit_id) (1 << (unit_id)) @@ -137,6 +146,46 @@ static inline void pcnt_ll_clear_count(pcnt_dev_t *hw, uint32_t 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. @@ -458,6 +507,16 @@ static inline void pcnt_ll_reset_register(int group_id) PCR.pcnt_conf.pcnt_rst_en = 0; } +/** + * @brief Check if the step notify is supported + */ +static inline bool pcnt_ll_is_step_notify_supported(int group_id) +{ + (void)group_id; + return ESP_CHIP_REV_ABOVE(efuse_hal_chip_revision(), 102); +} + + #ifdef __cplusplus } #endif diff --git a/components/soc/esp32h2/include/soc/Kconfig.soc_caps.in b/components/soc/esp32h2/include/soc/Kconfig.soc_caps.in index 5e9a4985c5..05347ab3d2 100644 --- a/components/soc/esp32h2/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32h2/include/soc/Kconfig.soc_caps.in @@ -763,6 +763,10 @@ config SOC_PCNT_SUPPORT_RUNTIME_THRES_UPDATE bool default y +config SOC_PCNT_SUPPORT_STEP_NOTIFY + bool + default y + config SOC_PCNT_SUPPORT_SLEEP_RETENTION bool default y diff --git a/components/soc/esp32h2/include/soc/soc_caps.h b/components/soc/esp32h2/include/soc/soc_caps.h index 000d6592c1..3bcd2de4a2 100644 --- a/components/soc/esp32h2/include/soc/soc_caps.h +++ b/components/soc/esp32h2/include/soc/soc_caps.h @@ -302,6 +302,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_STEP_NOTIFY 1 /*!< Only avliable in chip version above 1.2*/ #define SOC_PCNT_SUPPORT_SLEEP_RETENTION 1 /*!< The sleep retention feature can help back up PCNT registers before sleep */ /*--------------------------- RMT CAPS ---------------------------------------*/ diff --git a/components/soc/esp32h2/register/soc/pcnt_struct.h b/components/soc/esp32h2/register/soc/pcnt_struct.h index 4c372320c5..b67c3aedb9 100644 --- a/components/soc/esp32h2/register/soc/pcnt_struct.h +++ b/components/soc/esp32h2/register/soc/pcnt_struct.h @@ -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 */ @@ -181,7 +181,23 @@ typedef union { * Set this bit to freeze unit 3's counter. */ uint32_t cnt_pause_u3:1; - uint32_t reserved_8:8; + /** dalta_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; + * 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; + * 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; + * Configures this bit to enable unit 3's step comparator. + */ + uint32_t dalta_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 * and written by application. 0: the registers can not be read or written by @@ -193,6 +209,22 @@ typedef union { uint32_t val; } pcnt_ctrl_reg_t; +/** Type of change_conf register + * Configuration register for unit $n's step value. + */ +typedef union { + struct { + /** cnt_step : R/W; bitpos: [15:0]; default: 0; + * Configures the step value for unit n. + */ + 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:16; + }; + uint32_t val; +} pcnt_un_change_conf_reg_t; /** Group: Status Register */ /** Type of un_cnt register @@ -250,7 +282,19 @@ typedef union { * valid. 0: others */ uint32_t cnt_thr_zero_lat:1; - uint32_t reserved_7:25; + /** cnt_thr_step_lim_lat_un : RO; bitpos: [7]; default: 0; + * The latched value of step counter limit event of PCNT_Un when step counter event + * interrupt is valid. 1: the current pulse counter equals to reg_cnt_step_lim and + * step counter event is valid. 0: others + */ + uint32_t cnt_thr_step_lim_lat_un:1; + /** cnt_thr_step_lat_un : RO; bitpos: [8]; default: 0; + * The latched value of step counter event of PCNT_Un when step counter event + * interrupt is valid. 1: the current pulse counter increment equals to reg_cnt_step + * and step counter event is valid. 0: others + */ + uint32_t cnt_thr_step_lat_un:1; + uint32_t reserved_7:23; }; uint32_t val; } pcnt_un_status_reg_t; @@ -389,7 +433,8 @@ typedef struct pcnt_dev_t { volatile pcnt_int_clr_reg_t int_clr; volatile pcnt_un_status_reg_t status_unit[4]; volatile pcnt_ctrl_reg_t ctrl; - uint32_t reserved_064[38]; + volatile pcnt_un_change_conf_reg_t change_conf_unit[4]; // Note the unit order is 3210 + uint32_t reserved_064[34]; volatile pcnt_date_reg_t date; } pcnt_dev_t;