Merge branch 'refactor/pcnt_isr_handling' into 'master'

refactor(pcnt): ISR handling

Closes IDF-10329

See merge request espressif/esp-idf!31911
This commit is contained in:
morris 2024-07-09 10:12:18 +08:00
commit 7dc6a2f562
15 changed files with 237 additions and 114 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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));

View File

@ -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

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -162,7 +162,6 @@ PCNT 单元可被设置为观察几个特定的数值,这些被观察的数值
.. note::
当观察步进和观察点同时被触发时,回调函数只会被调用一次。
步进间隔必须是 :cpp:member:`pcnt_unit_config_t::low_limit`:cpp:member:`pcnt_unit_config_t::high_limit` 的因数。
.. code:: c