Skip to content

Commit 4312734

Browse files
committed
Merge branch 'feat/re_support_pcnt_on_c5' into 'master'
feat(pcnt): re-support pcnt on ESP32-C5 V1.0 Closes IDF-8683, IDF-12831, and IDF-12634 See merge request espressif/esp-idf!38547
2 parents bf1b165 + ca981fd commit 4312734

File tree

13 files changed

+767
-2517
lines changed

13 files changed

+767
-2517
lines changed

components/esp_driver_pcnt/include/driver/pulse_cnt.h

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
2+
* SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
33
*
44
* SPDX-License-Identifier: Apache-2.0
55
*/
@@ -316,7 +316,7 @@ esp_err_t pcnt_unit_remove_watch_point(pcnt_unit_handle_t unit, int watch_point)
316316
* @brief Add a step notify for PCNT unit, PCNT will generate an event when the incremental(can be positive or negative) of counter value reaches the step interval
317317
*
318318
* @param[in] unit PCNT unit handle created by `pcnt_new_unit()`
319-
* @param[in] step_interval PCNT step notify interval value
319+
* @param[in] step_interval PCNT step notify interval value. Positive value means step forward, negative value means step backward.
320320
* @return
321321
* - ESP_OK: Add step notify successfully
322322
* - ESP_ERR_INVALID_ARG: Add step notify failed because of invalid argument (e.g. the value incremental to be watched is out of the limitation set in `pcnt_unit_config_t`)
@@ -325,17 +325,35 @@ esp_err_t pcnt_unit_remove_watch_point(pcnt_unit_handle_t unit, int watch_point)
325325
*/
326326
esp_err_t pcnt_unit_add_watch_step(pcnt_unit_handle_t unit, int step_interval);
327327

328+
/**
329+
* @brief Remove all step notify for PCNT unit
330+
*
331+
* @param[in] unit PCNT unit handle created by `pcnt_new_unit()`
332+
* @return
333+
* - ESP_OK: Remove step notify successfully
334+
* - ESP_ERR_INVALID_ARG: Remove step notify failed because of invalid argument
335+
* - ESP_ERR_INVALID_STATE: Remove step notify failed because the step notify was not added by `pcnt_unit_add_watch_step()` yet
336+
* - ESP_FAIL: Remove step notify failed because of other error
337+
*/
338+
esp_err_t pcnt_unit_remove_all_watch_step(pcnt_unit_handle_t unit);
339+
328340
/**
329341
* @brief Remove a step notify for PCNT unit
330342
*
331343
* @param[in] unit PCNT unit handle created by `pcnt_new_unit()`
344+
* @param[in] step_interval Step notify interval value
332345
* @return
333346
* - ESP_OK: Remove step notify successfully
334347
* - ESP_ERR_INVALID_ARG: Remove step notify failed because of invalid argument
335348
* - ESP_ERR_INVALID_STATE: Remove step notify failed because the step notify was not added by `pcnt_unit_add_watch_step()` yet
336349
* - ESP_FAIL: Remove step notify failed because of other error
337350
*/
338-
esp_err_t pcnt_unit_remove_watch_step(pcnt_unit_handle_t unit);
351+
esp_err_t pcnt_unit_remove_single_watch_step(pcnt_unit_handle_t unit, int step_interval);
352+
353+
#define _pcnt_unit_remove_all_watch_step(unit) pcnt_unit_remove_all_watch_step(unit)
354+
#define _pcnt_unit_remove_single_watch_step(unit, step_interval) pcnt_unit_remove_single_watch_step(unit, step_interval)
355+
#define _pcnt_get_remove_func(_1, _2, FUNC, ...) FUNC
356+
#define pcnt_unit_remove_watch_step(...) _pcnt_get_remove_func(__VA_ARGS__, _pcnt_unit_remove_single_watch_step, _pcnt_unit_remove_all_watch_step)(__VA_ARGS__)
339357

340358
/**
341359
* @brief Create PCNT channel for specific unit, each PCNT has several channels associated with it

components/esp_driver_pcnt/src/pulse_cnt.c

Lines changed: 90 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,16 @@ typedef struct {
9292
int watch_point_value; // value to be watched
9393
} pcnt_watch_point_t;
9494

95+
typedef struct {
96+
#if PCNT_LL_STEP_NOTIFY_DIR_LIMIT
97+
int step_interval; // step interval
98+
int step_limit; // step limit value
99+
#else
100+
int step_interval_forward; // step interval for forward direction
101+
int step_interval_backward; // step interval for backward direction
102+
#endif
103+
} pcnt_step_interval_t;
104+
95105
typedef enum {
96106
PCNT_UNIT_FSM_INIT,
97107
PCNT_UNIT_FSM_ENABLE,
@@ -103,10 +113,9 @@ struct pcnt_unit_t {
103113
int unit_id; // allocated unit numerical ID
104114
int low_limit; // low limit value
105115
int high_limit; // high limit value
106-
int step_limit; // step limit value
107116
int clear_signal_gpio_num; // which gpio clear signal input
108117
int accum_value; // accumulated count value
109-
int step_interval; // PCNT step notify interval value
118+
pcnt_step_interval_t step_info; // step interval info
110119
pcnt_chan_t *channels[SOC_PCNT_CHANNELS_PER_UNIT]; // array of PCNT channels
111120
pcnt_watch_point_t watchers[PCNT_LL_WATCH_EVENT_MAX]; // array of PCNT watchers
112121
intr_handle_t intr; // interrupt handle
@@ -281,11 +290,11 @@ esp_err_t pcnt_new_unit(const pcnt_unit_config_t *config, pcnt_unit_handle_t *re
281290
unit->flags.en_step_notify_up = config->flags.en_step_notify_up;
282291
#if PCNT_LL_STEP_NOTIFY_DIR_LIMIT
283292
if (config->flags.en_step_notify_up) {
284-
unit->step_limit = config->high_limit;
293+
unit->step_info.step_limit = config->high_limit;
285294
} else if (config->flags.en_step_notify_down) {
286-
unit->step_limit = config->low_limit;
295+
unit->step_info.step_limit = config->low_limit;
287296
}
288-
pcnt_ll_set_step_limit_value(group->hal.dev, unit_id, unit->step_limit);
297+
pcnt_ll_set_step_limit_value(group->hal.dev, unit_id, unit->step_info.step_limit);
289298
#endif
290299
#endif
291300

@@ -679,11 +688,26 @@ esp_err_t pcnt_unit_add_watch_step(pcnt_unit_handle_t unit, int step_interval)
679688
ESP_ERR_INVALID_ARG, TAG, "invalid step interval");
680689
ESP_RETURN_ON_FALSE(step_interval >= unit->low_limit && step_interval <= unit->high_limit,
681690
ESP_ERR_INVALID_ARG, TAG, "step interval out of range [%d,%d]", unit->low_limit, unit->high_limit);
682-
ESP_RETURN_ON_FALSE(unit->step_interval == 0,
683-
ESP_ERR_INVALID_STATE, TAG, "watch step has been set to %d already", unit->step_interval);
684691

685-
unit->step_interval = step_interval;
686-
pcnt_ll_set_step_value(group->hal.dev, unit->unit_id, step_interval);
692+
#if PCNT_LL_STEP_NOTIFY_DIR_LIMIT
693+
ESP_RETURN_ON_FALSE(unit->step_info.step_interval == 0,
694+
ESP_ERR_INVALID_STATE, TAG, "watch step has been set to %d already", unit->step_info.step_interval);
695+
pcnt_ll_set_step_value(group->hal.dev, unit->unit_id, step_interval > 0 ? PCNT_STEP_FORWARD : PCNT_STEP_BACKWARD, (uint16_t)step_interval);
696+
unit->step_info.step_interval = step_interval;
697+
#else
698+
if (step_interval > 0) {
699+
ESP_RETURN_ON_FALSE(unit->step_info.step_interval_forward == 0,
700+
ESP_ERR_INVALID_STATE, TAG, "watch step in forward has been set to %d already", unit->step_info.step_interval_forward);
701+
pcnt_ll_set_step_value(group->hal.dev, unit->unit_id, PCNT_STEP_FORWARD, (uint16_t)step_interval);
702+
unit->step_info.step_interval_forward = step_interval;
703+
} else {
704+
ESP_RETURN_ON_FALSE(unit->step_info.step_interval_backward == 0,
705+
ESP_ERR_INVALID_STATE, TAG, "watch step in backward has been set to %d already", unit->step_info.step_interval_backward);
706+
pcnt_ll_set_step_value(group->hal.dev, unit->unit_id, PCNT_STEP_BACKWARD, (uint16_t)step_interval);
707+
unit->step_info.step_interval_backward = step_interval;
708+
}
709+
#endif //PCNT_LL_STEP_NOTIFY_DIR_LIMIT
710+
687711
// different units are mixing in the same register, so we use the group's spinlock here
688712
portENTER_CRITICAL(&group->spinlock);
689713
pcnt_ll_enable_step_notify(group->hal.dev, unit->unit_id, true);
@@ -692,21 +716,57 @@ esp_err_t pcnt_unit_add_watch_step(pcnt_unit_handle_t unit, int step_interval)
692716
return ESP_OK;
693717
}
694718

695-
esp_err_t pcnt_unit_remove_watch_step(pcnt_unit_handle_t unit)
719+
esp_err_t pcnt_unit_remove_all_watch_step(pcnt_unit_handle_t unit)
696720
{
697721
pcnt_group_t *group = NULL;
698722
ESP_RETURN_ON_FALSE(unit, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
699723
group = unit->group;
700-
ESP_RETURN_ON_FALSE(unit->step_interval != 0, ESP_ERR_INVALID_STATE, TAG, "watch step not added yet");
701-
702-
unit->step_interval = 0;
724+
#if PCNT_LL_STEP_NOTIFY_DIR_LIMIT
725+
ESP_RETURN_ON_FALSE(unit->step_info.step_interval != 0, ESP_ERR_INVALID_STATE, TAG, "watch step not added yet");
726+
unit->step_info.step_interval = 0;
727+
#else
728+
ESP_RETURN_ON_FALSE(unit->step_info.step_interval_forward != 0 || unit->step_info.step_interval_backward != 0,
729+
ESP_ERR_INVALID_STATE, TAG, "watch step in forward or backward not added yet");
730+
unit->step_info.step_interval_forward = 0;
731+
unit->step_info.step_interval_backward = 0;
732+
#endif
703733

704734
portENTER_CRITICAL(&group->spinlock);
705735
pcnt_ll_enable_step_notify(group->hal.dev, unit->unit_id, false);
706736
portEXIT_CRITICAL(&group->spinlock);
707737

708738
return ESP_OK;
709739
}
740+
741+
esp_err_t pcnt_unit_remove_single_watch_step(pcnt_unit_handle_t unit, int step_interval)
742+
{
743+
pcnt_group_t *group = NULL;
744+
ESP_RETURN_ON_FALSE(unit, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
745+
group = unit->group;
746+
#if PCNT_LL_STEP_NOTIFY_DIR_LIMIT
747+
ESP_RETURN_ON_FALSE(unit->step_info.step_interval == step_interval, ESP_ERR_INVALID_STATE, TAG, "watch step %d not added yet", step_interval);
748+
unit->step_info.step_interval = 0;
749+
portENTER_CRITICAL(&group->spinlock);
750+
pcnt_ll_enable_step_notify(group->hal.dev, unit->unit_id, false);
751+
portEXIT_CRITICAL(&group->spinlock);
752+
#else
753+
if (step_interval > 0) {
754+
ESP_RETURN_ON_FALSE(unit->step_info.step_interval_forward == step_interval, ESP_ERR_INVALID_STATE, TAG, "watch step %d not added yet", step_interval);
755+
pcnt_ll_set_step_value(group->hal.dev, unit->unit_id, PCNT_STEP_FORWARD, 0);
756+
unit->step_info.step_interval_forward = 0;
757+
} else {
758+
ESP_RETURN_ON_FALSE(unit->step_info.step_interval_backward == step_interval, ESP_ERR_INVALID_STATE, TAG, "watch step %d not added yet", step_interval);
759+
pcnt_ll_set_step_value(group->hal.dev, unit->unit_id, PCNT_STEP_BACKWARD, 0);
760+
unit->step_info.step_interval_backward = 0;
761+
}
762+
if (unit->step_info.step_interval_forward == 0 && unit->step_info.step_interval_backward == 0) {
763+
portENTER_CRITICAL(&group->spinlock);
764+
pcnt_ll_enable_step_notify(group->hal.dev, unit->unit_id, false);
765+
portEXIT_CRITICAL(&group->spinlock);
766+
}
767+
#endif
768+
return ESP_OK;
769+
}
710770
#endif //SOC_PCNT_SUPPORT_STEP_NOTIFY
711771

712772
esp_err_t pcnt_new_channel(pcnt_unit_handle_t unit, const pcnt_chan_config_t *config, pcnt_channel_handle_t *ret_chan)
@@ -962,6 +1022,7 @@ IRAM_ATTR static void pcnt_default_isr(void *args)
9621022
// using while loop so that we don't miss any event
9631023
while (event_status) {
9641024
#if SOC_PCNT_SUPPORT_STEP_NOTIFY
1025+
#if PCNT_LL_STEP_NOTIFY_DIR_LIMIT
9651026
// step event has higher priority than pointer event
9661027
if (event_status & (BIT(PCNT_LL_STEP_EVENT_REACH_INTERVAL) | BIT(PCNT_LL_STEP_EVENT_REACH_LIMIT))) {
9671028
if (event_status & BIT(PCNT_LL_STEP_EVENT_REACH_INTERVAL)) {
@@ -972,13 +1033,27 @@ IRAM_ATTR static void pcnt_default_isr(void *args)
9721033
if (event_status & BIT(PCNT_LL_STEP_EVENT_REACH_LIMIT)) {
9731034
event_status &= ~BIT(PCNT_LL_STEP_EVENT_REACH_LIMIT);
9741035
// adjust current count value to the step limit
975-
count_value = unit->step_limit;
1036+
count_value = unit->step_info.step_limit;
9761037
if (unit->flags.accum_count) {
9771038
portENTER_CRITICAL_ISR(&unit->spinlock);
978-
unit->accum_value += unit->step_limit;
1039+
unit->accum_value += unit->step_info.step_limit;
9791040
portEXIT_CRITICAL_ISR(&unit->spinlock);
9801041
}
9811042
}
1043+
#else
1044+
// step event has higher priority than pointer event
1045+
if (event_status & (BIT(PCNT_LL_STEP_EVENT_REACH_INTERVAL_FORWARD) | BIT(PCNT_LL_STEP_EVENT_REACH_INTERVAL_BACKWARD))) {
1046+
if (event_status & BIT(PCNT_LL_STEP_EVENT_REACH_INTERVAL_FORWARD)) {
1047+
event_status &= ~BIT(PCNT_LL_STEP_EVENT_REACH_INTERVAL_FORWARD);
1048+
// align current count value to the step interval
1049+
count_value = pcnt_ll_get_count(group->hal.dev, unit_id);
1050+
}
1051+
if (event_status & BIT(PCNT_LL_STEP_EVENT_REACH_INTERVAL_BACKWARD)) {
1052+
event_status &= ~BIT(PCNT_LL_STEP_EVENT_REACH_INTERVAL_BACKWARD);
1053+
// align current count value to the step interval
1054+
count_value = pcnt_ll_get_count(group->hal.dev, unit_id);
1055+
}
1056+
#endif // PCNT_LL_STEP_NOTIFY_DIR_LIMIT
9821057
// step event may happen with other pointer event at the same time, we don't need to process them again
9831058
event_status &= ~(BIT(PCNT_LL_WATCH_EVENT_LOW_LIMIT) | BIT(PCNT_LL_WATCH_EVENT_HIGH_LIMIT) |
9841059
BIT(PCNT_LL_WATCH_EVENT_THRES0) | BIT(PCNT_LL_WATCH_EVENT_THRES1));

0 commit comments

Comments
 (0)