forked from espressif/esp-idf
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:
@@ -68,7 +68,7 @@ typedef struct {
|
|||||||
#if SOC_PCNT_SUPPORT_STEP_NOTIFY
|
#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_up: 1; /*!< Enable step notify in the positive direction */
|
||||||
uint32_t en_step_notify_down: 1; /*!< Enable step notify in the negative direction */
|
uint32_t en_step_notify_down: 1; /*!< Enable step notify in the negative direction */
|
||||||
#endif
|
#endif // SOC_PCNT_SUPPORT_STEP_NOTIFY
|
||||||
} flags; /*!< Extra flags */
|
} flags; /*!< Extra flags */
|
||||||
} pcnt_unit_config_t;
|
} pcnt_unit_config_t;
|
||||||
|
|
||||||
@@ -169,7 +169,7 @@ typedef struct {
|
|||||||
* - ESP_FAIL: Set clear signal failed because of other error
|
* - 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);
|
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
|
* @brief Enable the PCNT unit
|
||||||
|
@@ -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);
|
TAG, "invalid interrupt priority:%d", config->intr_priority);
|
||||||
}
|
}
|
||||||
#if PCNT_LL_STEP_NOTIFY_DIR_LIMIT
|
#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
|
#endif
|
||||||
unit = heap_caps_calloc(1, sizeof(pcnt_unit_t), PCNT_MEM_ALLOC_CAPS);
|
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");
|
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(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_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_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_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_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_RETURN_ON_FALSE(unit->step_interval == 0,
|
||||||
ESP_ERR_INVALID_ARG, TAG, "step interval should be a divisor of step limit");
|
ESP_ERR_INVALID_STATE, TAG, "watch step has been set to %d already", unit->step_interval);
|
||||||
|
|
||||||
group = unit->group;
|
group = unit->group;
|
||||||
unit->step_interval = step_interval;
|
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)) {
|
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));
|
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);
|
uint32_t event_status = pcnt_ll_get_event_status(group->hal.dev, unit_id);
|
||||||
|
|
||||||
// use flags to avoid multiple callbacks in one point
|
int count_value = 0;
|
||||||
bool is_limit_event __attribute__((unused)) = false;
|
pcnt_unit_zero_cross_mode_t zc_mode = PCNT_UNIT_ZERO_CROSS_INVALID;
|
||||||
bool is_step_event = false;
|
|
||||||
|
|
||||||
// iter on each event_id
|
// using while loop so that we don't miss any event
|
||||||
while (event_status) {
|
while (event_status) {
|
||||||
int watch_value = pcnt_ll_get_count(group->hal.dev, unit_id);
|
#if SOC_PCNT_SUPPORT_STEP_NOTIFY
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 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)) {
|
if (event_status & BIT(PCNT_LL_WATCH_EVENT_LOW_LIMIT)) {
|
||||||
event_status &= ~(BIT(PCNT_LL_WATCH_EVENT_LOW_LIMIT));
|
event_status &= ~(BIT(PCNT_LL_WATCH_EVENT_LOW_LIMIT));
|
||||||
is_limit_event = true;
|
// adjust the count value to reflect the actual watch point value
|
||||||
|
count_value = unit->low_limit;
|
||||||
if (unit->flags.accum_count) {
|
if (unit->flags.accum_count) {
|
||||||
portENTER_CRITICAL_ISR(&unit->spinlock);
|
portENTER_CRITICAL_ISR(&unit->spinlock);
|
||||||
unit->accum_value += unit->low_limit;
|
unit->accum_value += unit->low_limit;
|
||||||
portEXIT_CRITICAL_ISR(&unit->spinlock);
|
portEXIT_CRITICAL_ISR(&unit->spinlock);
|
||||||
}
|
}
|
||||||
watch_value = unit->low_limit;
|
|
||||||
} else if (event_status & BIT(PCNT_LL_WATCH_EVENT_HIGH_LIMIT)) {
|
} else if (event_status & BIT(PCNT_LL_WATCH_EVENT_HIGH_LIMIT)) {
|
||||||
event_status &= ~(BIT(PCNT_LL_WATCH_EVENT_HIGH_LIMIT));
|
event_status &= ~(BIT(PCNT_LL_WATCH_EVENT_HIGH_LIMIT));
|
||||||
is_limit_event = true;
|
// adjust the count value to reflect the actual watch point value
|
||||||
|
count_value = unit->high_limit;
|
||||||
if (unit->flags.accum_count) {
|
if (unit->flags.accum_count) {
|
||||||
portENTER_CRITICAL_ISR(&unit->spinlock);
|
portENTER_CRITICAL_ISR(&unit->spinlock);
|
||||||
unit->accum_value += unit->high_limit;
|
unit->accum_value += unit->high_limit;
|
||||||
portEXIT_CRITICAL_ISR(&unit->spinlock);
|
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);
|
|
||||||
}
|
|
||||||
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)) {
|
} else if (event_status & BIT(PCNT_LL_WATCH_EVENT_THRES0)) {
|
||||||
event_status &= ~(BIT(PCNT_LL_WATCH_EVENT_THRES0));
|
event_status &= ~(BIT(PCNT_LL_WATCH_EVENT_THRES0));
|
||||||
if (is_step_event) {
|
// adjust the count value to reflect the actual watch point value
|
||||||
continue;
|
count_value = pcnt_ll_get_thres_value(group->hal.dev, unit_id, 0);
|
||||||
}
|
|
||||||
} else if (event_status & BIT(PCNT_LL_WATCH_EVENT_THRES1)) {
|
} else if (event_status & BIT(PCNT_LL_WATCH_EVENT_THRES1)) {
|
||||||
event_status &= ~(BIT(PCNT_LL_WATCH_EVENT_THRES1));
|
event_status &= ~(BIT(PCNT_LL_WATCH_EVENT_THRES1));
|
||||||
if (is_step_event) {
|
// adjust the count value to reflect the actual watch point value
|
||||||
continue;
|
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
|
// invoked user registered callback
|
||||||
if (on_reach) {
|
if (on_reach) {
|
||||||
pcnt_watch_event_data_t edata = {
|
pcnt_watch_event_data_t edata = {
|
||||||
.watch_point_value = watch_value,
|
.watch_point_value = count_value,
|
||||||
.zero_cross_mode = pcnt_ll_get_zero_cross_mode(group->hal.dev, unit_id),
|
.zero_cross_mode = zc_mode,
|
||||||
};
|
};
|
||||||
if (on_reach(unit, &edata, unit->user_data)) {
|
if (on_reach(unit, &edata, unit->user_data)) {
|
||||||
// check if we need to yield for high priority task
|
// check if we need to yield for high priority task
|
||||||
|
@@ -3,9 +3,5 @@
|
|||||||
components/esp_driver_pcnt/test_apps/pulse_cnt:
|
components/esp_driver_pcnt/test_apps/pulse_cnt:
|
||||||
disable:
|
disable:
|
||||||
- if: SOC_PCNT_SUPPORTED != 1
|
- if: SOC_PCNT_SUPPORTED != 1
|
||||||
disable_test:
|
|
||||||
- if: IDF_TARGET == "esp32c5"
|
|
||||||
temporary: true
|
|
||||||
reason: target test failed # TODO [ESP32C5] IDF-10342
|
|
||||||
depends_components:
|
depends_components:
|
||||||
- esp_driver_pcnt
|
- esp_driver_pcnt
|
||||||
|
@@ -310,7 +310,10 @@ TEST_CASE("pcnt_quadrature_decode_event", "[pcnt]")
|
|||||||
int count_value;
|
int count_value;
|
||||||
printf("checking count value\r\n");
|
printf("checking count value\r\n");
|
||||||
TEST_ESP_OK(pcnt_unit_get_count(unit, &count_value));
|
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(-20, count_value); // 0-30*4+100
|
||||||
TEST_ASSERT_EQUAL(3, user_data.index);
|
TEST_ASSERT_EQUAL(3, user_data.index);
|
||||||
TEST_ASSERT_EQUAL(-50, user_data.triggered_watch_values[0]);
|
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));
|
vTaskDelay(pdMS_TO_TICKS(100));
|
||||||
printf("checking count value\r\n");
|
printf("checking count value\r\n");
|
||||||
TEST_ESP_OK(pcnt_unit_get_count(unit, &count_value));
|
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(40, count_value); // -20+40*4-100
|
||||||
TEST_ASSERT_EQUAL(4, user_data.index);
|
TEST_ASSERT_EQUAL(4, user_data.index);
|
||||||
TEST_ASSERT_EQUAL(0, user_data.triggered_watch_values[0]);
|
TEST_ASSERT_EQUAL(0, user_data.triggered_watch_values[0]);
|
||||||
@@ -582,14 +588,17 @@ TEST_CASE("pcnt_zero_input_signal", "[pcnt]")
|
|||||||
}
|
}
|
||||||
#endif // SOC_PCNT_SUPPORT_CLEAR_SIGNAL
|
#endif // SOC_PCNT_SUPPORT_CLEAR_SIGNAL
|
||||||
|
|
||||||
#if SOC_PCNT_SUPPORT_STEP_NOTIFY
|
TEST_CASE("pcnt overflow accumulation", "[pcnt]")
|
||||||
TEST_CASE("pcnt_step_notify_event", "[pcnt]")
|
|
||||||
{
|
{
|
||||||
pcnt_unit_config_t unit_config = {
|
pcnt_unit_config_t unit_config = {
|
||||||
.low_limit = -100,
|
.low_limit = -100,
|
||||||
.high_limit = 100,
|
.high_limit = 100,
|
||||||
.flags.accum_count = true,
|
.flags = {
|
||||||
.flags.en_step_notify_down = true,
|
.accum_count = true,
|
||||||
|
#if SOC_PCNT_SUPPORT_STEP_NOTIFY
|
||||||
|
.en_step_notify_down = true,
|
||||||
|
#endif
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
printf("install pcnt unit\r\n");
|
printf("install pcnt unit\r\n");
|
||||||
@@ -608,8 +617,89 @@ TEST_CASE("pcnt_step_notify_event", "[pcnt]")
|
|||||||
};
|
};
|
||||||
pcnt_channel_handle_t channelA = NULL;
|
pcnt_channel_handle_t channelA = NULL;
|
||||||
TEST_ESP_OK(pcnt_new_channel(unit, &channel_config, &channelA));
|
TEST_ESP_OK(pcnt_new_channel(unit, &channel_config, &channelA));
|
||||||
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_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_HOLD));
|
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
|
// 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_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));
|
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, 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, 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, -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_ESP_OK(pcnt_unit_add_watch_step(unit, -25));
|
||||||
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_STATE, pcnt_unit_add_watch_step(unit, -100));
|
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, -100));
|
||||||
TEST_ESP_OK(pcnt_unit_add_watch_point(unit, 0));
|
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, -50));
|
||||||
#if !SOC_PCNT_SUPPORT_RUNTIME_THRES_UPDATE
|
TEST_ESP_OK(pcnt_unit_add_watch_point(unit, 11));
|
||||||
// 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_enable(unit));
|
TEST_ESP_OK(pcnt_unit_enable(unit));
|
||||||
TEST_ESP_OK(pcnt_unit_start(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;
|
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));
|
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++) {
|
for (int i = 0 ; i < user_data.index; i++) {
|
||||||
printf("%d:%d\r\n", i, user_data.triggered_watch_values[i]);
|
printf("%d:%d\r\n", i, user_data.triggered_watch_values[i]);
|
||||||
}
|
}
|
||||||
TEST_ASSERT_EQUAL(-150, count_value);
|
TEST_ASSERT_EQUAL(0, count_value);
|
||||||
TEST_ASSERT_EQUAL(7, user_data.index);
|
TEST_ASSERT_EQUAL(5, user_data.index);
|
||||||
TEST_ASSERT_EQUAL(-25, user_data.triggered_watch_values[0]);
|
TEST_ASSERT_EQUAL(-25, user_data.triggered_watch_values[0]); // step point (-25*1)
|
||||||
TEST_ASSERT_EQUAL(-50, user_data.triggered_watch_values[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]);
|
TEST_ASSERT_EQUAL(-75, user_data.triggered_watch_values[2]); // step point (-25*3)
|
||||||
TEST_ASSERT_EQUAL(-100, user_data.triggered_watch_values[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]);
|
TEST_ASSERT_EQUAL(0, user_data.triggered_watch_values[4]); // watch point (overflow zero cross)
|
||||||
TEST_ASSERT_EQUAL(-25, user_data.triggered_watch_values[5]);
|
|
||||||
TEST_ASSERT_EQUAL(-50, user_data.triggered_watch_values[6]);
|
|
||||||
|
|
||||||
printf("add a new step interval\r\n");
|
printf("simulating quadrature signals and count up\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));
|
|
||||||
user_data.index = 0;
|
user_data.index = 0;
|
||||||
|
test_gpio_simulate_quadrature_signals(TEST_PCNT_GPIO_B, TEST_PCNT_GPIO_A, 3); // 0+3*4 = 12
|
||||||
test_gpio_simulate_rising_edge(TEST_PCNT_GPIO_A, 120);
|
|
||||||
|
|
||||||
printf("checking count value\r\n");
|
|
||||||
TEST_ESP_OK(pcnt_unit_get_count(unit, &count_value));
|
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++) {
|
for (int i = 0 ; i < user_data.index; i++) {
|
||||||
printf("%d:%d\r\n", i, user_data.triggered_watch_values[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(3, user_data.index);
|
||||||
TEST_ASSERT_EQUAL(-50, user_data.triggered_watch_values[0]);
|
TEST_ASSERT_EQUAL(11, user_data.triggered_watch_values[0]); // watch point
|
||||||
TEST_ASSERT_EQUAL(-100, user_data.triggered_watch_values[1]);
|
TEST_ASSERT_EQUAL(0, user_data.triggered_watch_values[1]); // watch point (zero cross)
|
||||||
TEST_ASSERT_EQUAL(0, user_data.triggered_watch_values[2]);
|
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");
|
printf("remove step_notify and uninstall channels\r\n");
|
||||||
TEST_ESP_OK(pcnt_unit_remove_watch_step(unit));
|
TEST_ESP_OK(pcnt_unit_remove_watch_step(unit));
|
||||||
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_STATE, 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(channelA));
|
||||||
|
TEST_ESP_OK(pcnt_del_channel(channelB));
|
||||||
TEST_ESP_OK(pcnt_unit_stop(unit));
|
TEST_ESP_OK(pcnt_unit_stop(unit));
|
||||||
TEST_ESP_OK(pcnt_unit_disable(unit));
|
TEST_ESP_OK(pcnt_unit_disable(unit));
|
||||||
TEST_ESP_OK(pcnt_del_unit(unit));
|
TEST_ESP_OK(pcnt_del_unit(unit));
|
||||||
|
@@ -7,7 +7,7 @@ from pytest_embedded import Dut
|
|||||||
@pytest.mark.esp32
|
@pytest.mark.esp32
|
||||||
@pytest.mark.esp32s2
|
@pytest.mark.esp32s2
|
||||||
@pytest.mark.esp32s3
|
@pytest.mark.esp32s3
|
||||||
# @pytest.mark.esp32c5 # TODO: [ESP32C5] IDF-10342
|
@pytest.mark.esp32c5
|
||||||
@pytest.mark.esp32c6
|
@pytest.mark.esp32c6
|
||||||
@pytest.mark.esp32h2
|
@pytest.mark.esp32h2
|
||||||
@pytest.mark.esp32p4
|
@pytest.mark.esp32p4
|
||||||
|
@@ -338,6 +338,7 @@ static inline int pcnt_ll_get_low_limit_value(pcnt_dev_t *hw, uint32_t unit)
|
|||||||
* @param thres Threshold ID
|
* @param thres Threshold ID
|
||||||
* @return PCNT threshold value
|
* @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)
|
static inline int pcnt_ll_get_thres_value(pcnt_dev_t *hw, uint32_t unit, uint32_t thres)
|
||||||
{
|
{
|
||||||
int16_t value;
|
int16_t value;
|
||||||
|
@@ -374,6 +374,7 @@ static inline int pcnt_ll_get_low_limit_value(pcnt_dev_t *hw, uint32_t unit)
|
|||||||
* @param thres Threshold ID
|
* @param thres Threshold ID
|
||||||
* @return PCNT threshold value
|
* @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)
|
static inline int pcnt_ll_get_thres_value(pcnt_dev_t *hw, uint32_t unit, uint32_t thres)
|
||||||
{
|
{
|
||||||
int16_t value;
|
int16_t value;
|
||||||
|
@@ -335,6 +335,7 @@ static inline int pcnt_ll_get_low_limit_value(pcnt_dev_t *hw, uint32_t unit)
|
|||||||
* @param thres Threshold ID
|
* @param thres Threshold ID
|
||||||
* @return PCNT threshold value
|
* @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)
|
static inline int pcnt_ll_get_thres_value(pcnt_dev_t *hw, uint32_t unit, uint32_t thres)
|
||||||
{
|
{
|
||||||
int16_t value;
|
int16_t value;
|
||||||
|
@@ -335,6 +335,7 @@ static inline int pcnt_ll_get_low_limit_value(pcnt_dev_t *hw, uint32_t unit)
|
|||||||
* @param thres Threshold ID
|
* @param thres Threshold ID
|
||||||
* @return PCNT threshold value
|
* @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)
|
static inline int pcnt_ll_get_thres_value(pcnt_dev_t *hw, uint32_t unit, uint32_t thres)
|
||||||
{
|
{
|
||||||
int16_t value;
|
int16_t value;
|
||||||
|
@@ -376,6 +376,7 @@ static inline int pcnt_ll_get_low_limit_value(pcnt_dev_t *hw, uint32_t unit)
|
|||||||
* @param thres Threshold ID
|
* @param thres Threshold ID
|
||||||
* @return PCNT threshold value
|
* @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)
|
static inline int pcnt_ll_get_thres_value(pcnt_dev_t *hw, uint32_t unit, uint32_t thres)
|
||||||
{
|
{
|
||||||
int16_t value;
|
int16_t value;
|
||||||
|
@@ -336,6 +336,7 @@ static inline int pcnt_ll_get_low_limit_value(pcnt_dev_t *hw, uint32_t unit)
|
|||||||
* @param thres Threshold ID
|
* @param thres Threshold ID
|
||||||
* @return PCNT threshold value
|
* @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)
|
static inline int pcnt_ll_get_thres_value(pcnt_dev_t *hw, uint32_t unit, uint32_t thres)
|
||||||
{
|
{
|
||||||
int16_t value;
|
int16_t value;
|
||||||
|
@@ -335,6 +335,7 @@ static inline int pcnt_ll_get_low_limit_value(pcnt_dev_t *hw, uint32_t unit)
|
|||||||
* @param thres Threshold ID
|
* @param thres Threshold ID
|
||||||
* @return PCNT threshold value
|
* @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)
|
static inline int pcnt_ll_get_thres_value(pcnt_dev_t *hw, uint32_t unit, uint32_t thres)
|
||||||
{
|
{
|
||||||
int16_t value;
|
int16_t value;
|
||||||
|
@@ -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_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_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_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;
|
} pcnt_unit_zero_cross_mode_t;
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
|
@@ -162,7 +162,6 @@ It is recommended to remove the unused watch point by :cpp:func:`pcnt_unit_remov
|
|||||||
.. note::
|
.. 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.
|
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
|
.. code:: c
|
||||||
|
|
||||||
|
@@ -162,7 +162,6 @@ PCNT 单元可被设置为观察几个特定的数值,这些被观察的数值
|
|||||||
.. note::
|
.. note::
|
||||||
|
|
||||||
当观察步进和观察点同时被触发时,回调函数只会被调用一次。
|
当观察步进和观察点同时被触发时,回调函数只会被调用一次。
|
||||||
步进间隔必须是 :cpp:member:`pcnt_unit_config_t::low_limit` 或 :cpp:member:`pcnt_unit_config_t::high_limit` 的因数。
|
|
||||||
|
|
||||||
.. code:: c
|
.. code:: c
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user