From 6d223f1921380f79310aefbbe32466b677a23ff1 Mon Sep 17 00:00:00 2001 From: morris Date: Thu, 4 Jul 2024 18:24:15 +0800 Subject: [PATCH] refactor(pcnt): ISR handling --- .../include/driver/pulse_cnt.h | 8 +- components/esp_driver_pcnt/src/pulse_cnt.c | 124 ++++++----- .../test_apps/.build-test-rules.yml | 4 - .../test_apps/pulse_cnt/main/test_pulse_cnt.c | 203 ++++++++++++++---- .../test_apps/pulse_cnt/pytest_pulse_cnt.py | 2 +- components/hal/esp32/include/hal/pcnt_ll.h | 1 + components/hal/esp32c5/include/hal/pcnt_ll.h | 1 + components/hal/esp32c6/include/hal/pcnt_ll.h | 1 + components/hal/esp32h2/include/hal/pcnt_ll.h | 1 + components/hal/esp32p4/include/hal/pcnt_ll.h | 1 + components/hal/esp32s2/include/hal/pcnt_ll.h | 1 + components/hal/esp32s3/include/hal/pcnt_ll.h | 1 + components/hal/include/hal/pcnt_types.h | 1 + docs/en/api-reference/peripherals/pcnt.rst | 1 - docs/zh_CN/api-reference/peripherals/pcnt.rst | 1 - 15 files changed, 237 insertions(+), 114 deletions(-) diff --git a/components/esp_driver_pcnt/include/driver/pulse_cnt.h b/components/esp_driver_pcnt/include/driver/pulse_cnt.h index 24123b11ff..9163405529 100644 --- a/components/esp_driver_pcnt/include/driver/pulse_cnt.h +++ b/components/esp_driver_pcnt/include/driver/pulse_cnt.h @@ -66,9 +66,9 @@ typedef struct { struct { uint32_t accum_count: 1; /*!< Whether to accumulate the count value when overflows at the high/low limit */ #if SOC_PCNT_SUPPORT_STEP_NOTIFY - uint32_t en_step_notify_up: 1; /*!< Enable step notify in the positive direction*/ - uint32_t en_step_notify_down: 1; /*!< Enable step notify in the negative direction*/ -#endif + uint32_t en_step_notify_up: 1; /*!< Enable step notify in the positive direction */ + uint32_t en_step_notify_down: 1; /*!< Enable step notify in the negative direction */ +#endif // SOC_PCNT_SUPPORT_STEP_NOTIFY } flags; /*!< Extra flags */ } pcnt_unit_config_t; @@ -169,7 +169,7 @@ typedef struct { * - ESP_FAIL: Set clear signal failed because of other error */ esp_err_t pcnt_unit_set_clear_signal(pcnt_unit_handle_t unit, const pcnt_clear_signal_config_t *config); -#endif +#endif // SOC_PCNT_SUPPORT_CLEAR_SIGNAL /** * @brief Enable the PCNT unit diff --git a/components/esp_driver_pcnt/src/pulse_cnt.c b/components/esp_driver_pcnt/src/pulse_cnt.c index e25dafc00b..4daefa6cd1 100644 --- a/components/esp_driver_pcnt/src/pulse_cnt.c +++ b/components/esp_driver_pcnt/src/pulse_cnt.c @@ -201,7 +201,13 @@ esp_err_t pcnt_new_unit(const pcnt_unit_config_t *config, pcnt_unit_handle_t *re TAG, "invalid interrupt priority:%d", config->intr_priority); } #if PCNT_LL_STEP_NOTIFY_DIR_LIMIT - ESP_GOTO_ON_FALSE(!(config->flags.en_step_notify_up && config->flags.en_step_notify_down), ESP_ERR_NOT_SUPPORTED, err, TAG, "This target can only notify in one direction"); + ESP_GOTO_ON_FALSE(!(config->flags.en_step_notify_up && config->flags.en_step_notify_down), ESP_ERR_NOT_SUPPORTED, err, TAG, CONFIG_IDF_TARGET " can't trigger step notify in both direction"); + if (config->flags.en_step_notify_up) { + ESP_LOGW(TAG, "can't overflow at low limit: %d due to hardware limitation", config->low_limit); + } + if (config->flags.en_step_notify_down) { + ESP_LOGW(TAG, "can't overflow at high limit: %d due to hardware limitation", config->high_limit); + } #endif unit = heap_caps_calloc(1, sizeof(pcnt_unit_t), PCNT_MEM_ALLOC_CAPS); ESP_GOTO_ON_FALSE(unit, ESP_ERR_NO_MEM, err, TAG, "no mem for unit"); @@ -651,14 +657,10 @@ esp_err_t pcnt_unit_add_watch_step(pcnt_unit_handle_t unit, int step_interval) 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"); - ESP_RETURN_ON_FALSE(unit->flags.en_step_notify_up || unit->flags.en_step_notify_down, - ESP_ERR_INVALID_STATE, TAG, "step limit is not enabled yet"); - ESP_RETURN_ON_FALSE(unit->step_interval == 0, - ESP_ERR_INVALID_STATE, TAG, "watch step has been set to %d already", unit->step_interval); ESP_RETURN_ON_FALSE(step_interval >= unit->low_limit && step_interval <= unit->high_limit, ESP_ERR_INVALID_ARG, TAG, "step interval out of range [%d,%d]", unit->low_limit, unit->high_limit); - ESP_RETURN_ON_FALSE(unit->step_limit % step_interval == 0, - ESP_ERR_INVALID_ARG, TAG, "step interval should be a divisor of step limit"); + 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; @@ -893,70 +895,74 @@ IRAM_ATTR static void pcnt_default_isr(void *args) 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)); - // watcher event + // event status word contains information about the real watch event type uint32_t event_status = pcnt_ll_get_event_status(group->hal.dev, unit_id); - // use flags to avoid multiple callbacks in one point - bool is_limit_event __attribute__((unused)) = false; - bool is_step_event = false; + int count_value = 0; + pcnt_unit_zero_cross_mode_t zc_mode = PCNT_UNIT_ZERO_CROSS_INVALID; - // iter on each event_id + // using while loop so that we don't miss any event while (event_status) { - int watch_value = pcnt_ll_get_count(group->hal.dev, unit_id); - if (event_status & BIT(PCNT_LL_WATCH_EVENT_LOW_LIMIT)) { - event_status &= ~(BIT(PCNT_LL_WATCH_EVENT_LOW_LIMIT)); - is_limit_event = true; - if (unit->flags.accum_count) { - portENTER_CRITICAL_ISR(&unit->spinlock); - unit->accum_value += unit->low_limit; - portEXIT_CRITICAL_ISR(&unit->spinlock); - } - watch_value = unit->low_limit; - } else if (event_status & BIT(PCNT_LL_WATCH_EVENT_HIGH_LIMIT)) { - event_status &= ~(BIT(PCNT_LL_WATCH_EVENT_HIGH_LIMIT)); - is_limit_event = true; - if (unit->flags.accum_count) { - portENTER_CRITICAL_ISR(&unit->spinlock); - unit->accum_value += unit->high_limit; - portEXIT_CRITICAL_ISR(&unit->spinlock); - } - watch_value = unit->high_limit; - } #if SOC_PCNT_SUPPORT_STEP_NOTIFY - else if (event_status & BIT(PCNT_LL_STEP_EVENT_REACH_LIMIT)) { - event_status &= ~(BIT(PCNT_LL_STEP_EVENT_REACH_LIMIT)); - if (is_limit_event) { - continue; - } else if (unit->flags.accum_count) { - portENTER_CRITICAL_ISR(&unit->spinlock); - unit->accum_value += unit->step_limit; - portEXIT_CRITICAL_ISR(&unit->spinlock); + // step event has higher priority than pointer event + if (event_status & (BIT(PCNT_LL_STEP_EVENT_REACH_INTERVAL) | BIT(PCNT_LL_STEP_EVENT_REACH_LIMIT))) { + if (event_status & BIT(PCNT_LL_STEP_EVENT_REACH_INTERVAL)) { + event_status &= ~BIT(PCNT_LL_STEP_EVENT_REACH_INTERVAL); + // align current count value to the step interval + count_value = pcnt_ll_get_count(group->hal.dev, unit_id); } - watch_value = unit->step_limit; - } else if (event_status & BIT(PCNT_LL_STEP_EVENT_REACH_INTERVAL)) { - event_status &= ~(BIT(PCNT_LL_STEP_EVENT_REACH_INTERVAL)); - is_step_event = true; - } -#endif //SOC_PCNT_SUPPORT_STEP_NOTIFY - else if (event_status & BIT(PCNT_LL_WATCH_EVENT_ZERO_CROSS)) { - event_status &= ~(BIT(PCNT_LL_WATCH_EVENT_ZERO_CROSS)); - } else if (event_status & BIT(PCNT_LL_WATCH_EVENT_THRES0)) { - event_status &= ~(BIT(PCNT_LL_WATCH_EVENT_THRES0)); - if (is_step_event) { - continue; + if (event_status & BIT(PCNT_LL_STEP_EVENT_REACH_LIMIT)) { + event_status &= ~BIT(PCNT_LL_STEP_EVENT_REACH_LIMIT); + // adjust current count value to the step limit + count_value = unit->step_limit; + if (unit->flags.accum_count) { + portENTER_CRITICAL_ISR(&unit->spinlock); + unit->accum_value += unit->step_limit; + portEXIT_CRITICAL_ISR(&unit->spinlock); + } } - } else if (event_status & BIT(PCNT_LL_WATCH_EVENT_THRES1)) { - event_status &= ~(BIT(PCNT_LL_WATCH_EVENT_THRES1)); - if (is_step_event) { - continue; + // step event may happen with other pointer event at the same time, we don't need to process them again + event_status &= ~(BIT(PCNT_LL_WATCH_EVENT_LOW_LIMIT) | BIT(PCNT_LL_WATCH_EVENT_HIGH_LIMIT) | + BIT(PCNT_LL_WATCH_EVENT_THRES0) | BIT(PCNT_LL_WATCH_EVENT_THRES1)); + } else +#endif // SOC_PCNT_SUPPORT_STEP_NOTIFY + if (event_status & BIT(PCNT_LL_WATCH_EVENT_LOW_LIMIT)) { + event_status &= ~(BIT(PCNT_LL_WATCH_EVENT_LOW_LIMIT)); + // adjust the count value to reflect the actual watch point value + count_value = unit->low_limit; + if (unit->flags.accum_count) { + portENTER_CRITICAL_ISR(&unit->spinlock); + unit->accum_value += unit->low_limit; + portEXIT_CRITICAL_ISR(&unit->spinlock); + } + } else if (event_status & BIT(PCNT_LL_WATCH_EVENT_HIGH_LIMIT)) { + event_status &= ~(BIT(PCNT_LL_WATCH_EVENT_HIGH_LIMIT)); + // adjust the count value to reflect the actual watch point value + count_value = unit->high_limit; + if (unit->flags.accum_count) { + portENTER_CRITICAL_ISR(&unit->spinlock); + unit->accum_value += unit->high_limit; + portEXIT_CRITICAL_ISR(&unit->spinlock); + } + } else if (event_status & BIT(PCNT_LL_WATCH_EVENT_THRES0)) { + event_status &= ~(BIT(PCNT_LL_WATCH_EVENT_THRES0)); + // adjust the count value to reflect the actual watch point value + count_value = pcnt_ll_get_thres_value(group->hal.dev, unit_id, 0); + } else if (event_status & BIT(PCNT_LL_WATCH_EVENT_THRES1)) { + event_status &= ~(BIT(PCNT_LL_WATCH_EVENT_THRES1)); + // adjust the count value to reflect the actual watch point value + count_value = pcnt_ll_get_thres_value(group->hal.dev, unit_id, 1); + } else if (event_status & BIT(PCNT_LL_WATCH_EVENT_ZERO_CROSS)) { + event_status &= ~(BIT(PCNT_LL_WATCH_EVENT_ZERO_CROSS)); + count_value = 0; + zc_mode = pcnt_ll_get_zero_cross_mode(group->hal.dev, unit_id); } - } // invoked user registered callback if (on_reach) { pcnt_watch_event_data_t edata = { - .watch_point_value = watch_value, - .zero_cross_mode = pcnt_ll_get_zero_cross_mode(group->hal.dev, unit_id), + .watch_point_value = count_value, + .zero_cross_mode = zc_mode, }; if (on_reach(unit, &edata, unit->user_data)) { // check if we need to yield for high priority task diff --git a/components/esp_driver_pcnt/test_apps/.build-test-rules.yml b/components/esp_driver_pcnt/test_apps/.build-test-rules.yml index 80540d51eb..d94a1f9237 100644 --- a/components/esp_driver_pcnt/test_apps/.build-test-rules.yml +++ b/components/esp_driver_pcnt/test_apps/.build-test-rules.yml @@ -3,9 +3,5 @@ components/esp_driver_pcnt/test_apps/pulse_cnt: disable: - if: SOC_PCNT_SUPPORTED != 1 - disable_test: - - if: IDF_TARGET == "esp32c5" - temporary: true - reason: target test failed # TODO [ESP32C5] IDF-10342 depends_components: - esp_driver_pcnt 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 e60e15db02..91b026d294 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 @@ -282,7 +282,7 @@ TEST_CASE("pcnt_quadrature_decode_event", "[pcnt]") }; TEST_ESP_OK(pcnt_unit_register_event_callbacks(unit, &cbs, &user_data)); - printf("add watchpoints\r\n"); + printf("add watch points\r\n"); TEST_ESP_OK(pcnt_unit_add_watch_point(unit, 0)); TEST_ESP_OK(pcnt_unit_add_watch_point(unit, 100)); TEST_ESP_OK(pcnt_unit_add_watch_point(unit, -100)); @@ -310,7 +310,10 @@ TEST_CASE("pcnt_quadrature_decode_event", "[pcnt]") int count_value; printf("checking count value\r\n"); TEST_ESP_OK(pcnt_unit_get_count(unit, &count_value)); - printf("count_value=%d\r\n", 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); // 0-30*4+100 TEST_ASSERT_EQUAL(3, user_data.index); TEST_ASSERT_EQUAL(-50, user_data.triggered_watch_values[0]); @@ -324,7 +327,10 @@ TEST_CASE("pcnt_quadrature_decode_event", "[pcnt]") vTaskDelay(pdMS_TO_TICKS(100)); printf("checking count value\r\n"); TEST_ESP_OK(pcnt_unit_get_count(unit, &count_value)); - printf("count_value=%d\r\n", 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); // -20+40*4-100 TEST_ASSERT_EQUAL(4, user_data.index); TEST_ASSERT_EQUAL(0, user_data.triggered_watch_values[0]); @@ -332,7 +338,7 @@ TEST_CASE("pcnt_quadrature_decode_event", "[pcnt]") TEST_ASSERT_EQUAL(100, user_data.triggered_watch_values[2]); TEST_ASSERT_EQUAL(0, user_data.triggered_watch_values[3]); - printf("remove watchpoints and uninstall channels\r\n"); + printf("remove watch points and uninstall channels\r\n"); TEST_ESP_OK(pcnt_unit_remove_watch_point(unit, 0)); TEST_ESP_OK(pcnt_unit_remove_watch_point(unit, 100)); TEST_ESP_OK(pcnt_unit_remove_watch_point(unit, -100)); @@ -582,14 +588,17 @@ TEST_CASE("pcnt_zero_input_signal", "[pcnt]") } #endif // SOC_PCNT_SUPPORT_CLEAR_SIGNAL -#if SOC_PCNT_SUPPORT_STEP_NOTIFY -TEST_CASE("pcnt_step_notify_event", "[pcnt]") +TEST_CASE("pcnt overflow accumulation", "[pcnt]") { pcnt_unit_config_t unit_config = { .low_limit = -100, .high_limit = 100, - .flags.accum_count = true, - .flags.en_step_notify_down = true, + .flags = { + .accum_count = true, +#if SOC_PCNT_SUPPORT_STEP_NOTIFY + .en_step_notify_down = true, +#endif + }, }; printf("install pcnt unit\r\n"); @@ -608,8 +617,89 @@ TEST_CASE("pcnt_step_notify_event", "[pcnt]") }; 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_HOLD)); - TEST_ESP_OK(pcnt_channel_set_level_action(channelA, PCNT_CHANNEL_LEVEL_ACTION_KEEP, PCNT_CHANNEL_LEVEL_ACTION_HOLD)); + 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)); + + TEST_ESP_OK(pcnt_unit_add_watch_point(unit, -100)); + TEST_ESP_OK(pcnt_unit_add_watch_point(unit, 100)); + + // 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)); + + 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, 50); // 50*(-4) = -200 + + int count_value; + TEST_ESP_OK(pcnt_unit_get_count(unit, &count_value)); + printf("counter stopped at %d\r\n", count_value); + TEST_ASSERT_EQUAL(-200, count_value); + + printf("simulating quadrature signals and count up\r\n"); + test_gpio_simulate_quadrature_signals(TEST_PCNT_GPIO_B, TEST_PCNT_GPIO_A, 31); // -200+31*4 = -76 + TEST_ESP_OK(pcnt_unit_get_count(unit, &count_value)); + printf("counter stopped at %d\r\n", count_value); + TEST_ASSERT_EQUAL(-76, count_value); + + printf("simulating quadrature signals and count down again\r\n"); + test_gpio_simulate_quadrature_signals(TEST_PCNT_GPIO_A, TEST_PCNT_GPIO_B, 20); // -76-20*4 = -156 + TEST_ESP_OK(pcnt_unit_get_count(unit, &count_value)); + printf("counter stopped at %d\r\n", count_value); + TEST_ASSERT_EQUAL(-156, count_value); + + 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)); +} + +#if SOC_PCNT_SUPPORT_STEP_NOTIFY +TEST_CASE("pcnt_step_notify_event", "[pcnt]") +{ + 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 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, + .flags.io_loop_back = true, + }; + 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)); @@ -624,69 +714,94 @@ TEST_CASE("pcnt_step_notify_event", "[pcnt]") }; TEST_ESP_OK(pcnt_unit_register_event_callbacks(unit, &cbs, &user_data)); - printf("add step notify\r\n"); + 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_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, pcnt_unit_add_watch_step(unit, -30)); 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)); -#if !SOC_PCNT_SUPPORT_RUNTIME_THRES_UPDATE - // the above added watch point won't take effect at once, unless we clear the internal counter manually - TEST_ESP_OK(pcnt_unit_clear_count(unit)); -#endif + TEST_ESP_OK(pcnt_unit_add_watch_point(unit, 11)); 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 + int count_value; - - // trigger 150 rising edge on GPIO - test_gpio_simulate_rising_edge(TEST_PCNT_GPIO_A, 150); - - printf("checking count value\r\n"); TEST_ESP_OK(pcnt_unit_get_count(unit, &count_value)); - printf("count_value=%d\r\n", 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(-150, count_value); - TEST_ASSERT_EQUAL(7, user_data.index); - TEST_ASSERT_EQUAL(-25, user_data.triggered_watch_values[0]); - TEST_ASSERT_EQUAL(-50, user_data.triggered_watch_values[1]); - TEST_ASSERT_EQUAL(-75, user_data.triggered_watch_values[2]); - TEST_ASSERT_EQUAL(-100, user_data.triggered_watch_values[3]); - TEST_ASSERT_EQUAL(-0, user_data.triggered_watch_values[4]); - TEST_ASSERT_EQUAL(-25, user_data.triggered_watch_values[5]); - TEST_ASSERT_EQUAL(-50, user_data.triggered_watch_values[6]); + 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("add a new step interval\r\n"); - TEST_ESP_OK(pcnt_unit_remove_watch_step(unit)); - TEST_ESP_OK(pcnt_unit_clear_count(unit)); - TEST_ESP_OK(pcnt_unit_add_watch_step(unit, -100)); + printf("simulating quadrature signals and count up\r\n"); user_data.index = 0; - - test_gpio_simulate_rising_edge(TEST_PCNT_GPIO_A, 120); - - printf("checking count value\r\n"); + 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("count_value=%d\r\n", 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 - TEST_ASSERT_EQUAL(-120, count_value); + 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(-50, user_data.triggered_watch_values[0]); - TEST_ASSERT_EQUAL(-100, user_data.triggered_watch_values[1]); - TEST_ASSERT_EQUAL(0, user_data.triggered_watch_values[2]); + 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)); diff --git a/components/esp_driver_pcnt/test_apps/pulse_cnt/pytest_pulse_cnt.py b/components/esp_driver_pcnt/test_apps/pulse_cnt/pytest_pulse_cnt.py index b2b95bcccb..04566c1d5d 100644 --- a/components/esp_driver_pcnt/test_apps/pulse_cnt/pytest_pulse_cnt.py +++ b/components/esp_driver_pcnt/test_apps/pulse_cnt/pytest_pulse_cnt.py @@ -7,7 +7,7 @@ from pytest_embedded import Dut @pytest.mark.esp32 @pytest.mark.esp32s2 @pytest.mark.esp32s3 -# @pytest.mark.esp32c5 # TODO: [ESP32C5] IDF-10342 +@pytest.mark.esp32c5 @pytest.mark.esp32c6 @pytest.mark.esp32h2 @pytest.mark.esp32p4 diff --git a/components/hal/esp32/include/hal/pcnt_ll.h b/components/hal/esp32/include/hal/pcnt_ll.h index e54e97b050..be3d83241b 100644 --- a/components/hal/esp32/include/hal/pcnt_ll.h +++ b/components/hal/esp32/include/hal/pcnt_ll.h @@ -338,6 +338,7 @@ static inline int pcnt_ll_get_low_limit_value(pcnt_dev_t *hw, uint32_t unit) * @param thres Threshold ID * @return PCNT threshold value */ +__attribute__((always_inline)) static inline int pcnt_ll_get_thres_value(pcnt_dev_t *hw, uint32_t unit, uint32_t thres) { int16_t value; diff --git a/components/hal/esp32c5/include/hal/pcnt_ll.h b/components/hal/esp32c5/include/hal/pcnt_ll.h index adce896488..a1de9bd8de 100644 --- a/components/hal/esp32c5/include/hal/pcnt_ll.h +++ b/components/hal/esp32c5/include/hal/pcnt_ll.h @@ -374,6 +374,7 @@ static inline int pcnt_ll_get_low_limit_value(pcnt_dev_t *hw, uint32_t unit) * @param thres Threshold ID * @return PCNT threshold value */ +__attribute__((always_inline)) static inline int pcnt_ll_get_thres_value(pcnt_dev_t *hw, uint32_t unit, uint32_t thres) { int16_t value; diff --git a/components/hal/esp32c6/include/hal/pcnt_ll.h b/components/hal/esp32c6/include/hal/pcnt_ll.h index ade48358ae..4bf6469589 100644 --- a/components/hal/esp32c6/include/hal/pcnt_ll.h +++ b/components/hal/esp32c6/include/hal/pcnt_ll.h @@ -335,6 +335,7 @@ static inline int pcnt_ll_get_low_limit_value(pcnt_dev_t *hw, uint32_t unit) * @param thres Threshold ID * @return PCNT threshold value */ +__attribute__((always_inline)) static inline int pcnt_ll_get_thres_value(pcnt_dev_t *hw, uint32_t unit, uint32_t thres) { int16_t value; diff --git a/components/hal/esp32h2/include/hal/pcnt_ll.h b/components/hal/esp32h2/include/hal/pcnt_ll.h index 17c4b96212..23fc40f672 100644 --- a/components/hal/esp32h2/include/hal/pcnt_ll.h +++ b/components/hal/esp32h2/include/hal/pcnt_ll.h @@ -335,6 +335,7 @@ static inline int pcnt_ll_get_low_limit_value(pcnt_dev_t *hw, uint32_t unit) * @param thres Threshold ID * @return PCNT threshold value */ +__attribute__((always_inline)) static inline int pcnt_ll_get_thres_value(pcnt_dev_t *hw, uint32_t unit, uint32_t thres) { int16_t value; diff --git a/components/hal/esp32p4/include/hal/pcnt_ll.h b/components/hal/esp32p4/include/hal/pcnt_ll.h index 610dae51e5..c01ed77b4e 100644 --- a/components/hal/esp32p4/include/hal/pcnt_ll.h +++ b/components/hal/esp32p4/include/hal/pcnt_ll.h @@ -376,6 +376,7 @@ static inline int pcnt_ll_get_low_limit_value(pcnt_dev_t *hw, uint32_t unit) * @param thres Threshold ID * @return PCNT threshold value */ +__attribute__((always_inline)) static inline int pcnt_ll_get_thres_value(pcnt_dev_t *hw, uint32_t unit, uint32_t thres) { int16_t value; diff --git a/components/hal/esp32s2/include/hal/pcnt_ll.h b/components/hal/esp32s2/include/hal/pcnt_ll.h index fecf814652..6047342cab 100644 --- a/components/hal/esp32s2/include/hal/pcnt_ll.h +++ b/components/hal/esp32s2/include/hal/pcnt_ll.h @@ -336,6 +336,7 @@ static inline int pcnt_ll_get_low_limit_value(pcnt_dev_t *hw, uint32_t unit) * @param thres Threshold ID * @return PCNT threshold value */ +__attribute__((always_inline)) static inline int pcnt_ll_get_thres_value(pcnt_dev_t *hw, uint32_t unit, uint32_t thres) { int16_t value; diff --git a/components/hal/esp32s3/include/hal/pcnt_ll.h b/components/hal/esp32s3/include/hal/pcnt_ll.h index e9ec7e1a1c..d00563a2cd 100644 --- a/components/hal/esp32s3/include/hal/pcnt_ll.h +++ b/components/hal/esp32s3/include/hal/pcnt_ll.h @@ -335,6 +335,7 @@ static inline int pcnt_ll_get_low_limit_value(pcnt_dev_t *hw, uint32_t unit) * @param thres Threshold ID * @return PCNT threshold value */ +__attribute__((always_inline)) static inline int pcnt_ll_get_thres_value(pcnt_dev_t *hw, uint32_t unit, uint32_t thres) { int16_t value; diff --git a/components/hal/include/hal/pcnt_types.h b/components/hal/include/hal/pcnt_types.h index 78372a3171..0ce00e3566 100644 --- a/components/hal/include/hal/pcnt_types.h +++ b/components/hal/include/hal/pcnt_types.h @@ -36,6 +36,7 @@ typedef enum { PCNT_UNIT_ZERO_CROSS_NEG_ZERO, /*!< start from negative value, end to zero, i.e. -N->0 */ PCNT_UNIT_ZERO_CROSS_NEG_POS, /*!< start from negative value, end to positive value, i.e. -N->+M */ PCNT_UNIT_ZERO_CROSS_POS_NEG, /*!< start from positive value, end to negative value, i.e. +N->-M */ + PCNT_UNIT_ZERO_CROSS_INVALID, /*!< invalid zero cross mode */ } pcnt_unit_zero_cross_mode_t; #ifdef __cplusplus diff --git a/docs/en/api-reference/peripherals/pcnt.rst b/docs/en/api-reference/peripherals/pcnt.rst index 99e44fe7ba..d3998235c0 100644 --- a/docs/en/api-reference/peripherals/pcnt.rst +++ b/docs/en/api-reference/peripherals/pcnt.rst @@ -162,7 +162,6 @@ It is recommended to remove the unused watch point by :cpp:func:`pcnt_unit_remov .. note:: When a watch step and a watch point are triggered at the same time (i.e. at the same absolute point), the callback function only gets called by once. - The step interval must be a divisor of :cpp:member:`pcnt_unit_config_t::low_limit` or :cpp:member:`pcnt_unit_config_t::high_limit`. .. code:: c diff --git a/docs/zh_CN/api-reference/peripherals/pcnt.rst b/docs/zh_CN/api-reference/peripherals/pcnt.rst index 175010e404..565a8ddeea 100644 --- a/docs/zh_CN/api-reference/peripherals/pcnt.rst +++ b/docs/zh_CN/api-reference/peripherals/pcnt.rst @@ -162,7 +162,6 @@ PCNT 单元可被设置为观察几个特定的数值,这些被观察的数值 .. note:: 当观察步进和观察点同时被触发时,回调函数只会被调用一次。 - 步进间隔必须是 :cpp:member:`pcnt_unit_config_t::low_limit` 或 :cpp:member:`pcnt_unit_config_t::high_limit` 的因数。 .. code:: c