2525#include "soc/gpio_pins.h"
2626#include "hal/pcnt_hal.h"
2727#include "hal/pcnt_ll.h"
28+ #include "driver/gpio.h"
29+ #include "driver/pulse_cnt.h"
2830#include "esp_private/esp_clk.h"
2931#include "esp_private/periph_ctrl.h"
3032#include "esp_private/sleep_retention.h"
31- #include "driver/gpio.h"
3233#include "esp_private/gpio.h"
33- #include "driver/pulse_cnt .h"
34+ #include "esp_private/esp_clk_tree_common .h"
3435#include "esp_memory_utils.h"
3536
3637// If ISR handler is allowed to run whilst cache is disabled,
4950
5051#define PCNT_ALLOW_INTR_PRIORITY_MASK ESP_INTR_FLAG_LOWMED
5152
53+ #if SOC_PERIPH_CLK_CTRL_SHARED
54+ #define PCNT_CLOCK_SRC_ATOMIC () PERIPH_RCC_ATOMIC()
55+ #else
56+ #define PCNT_CLOCK_SRC_ATOMIC ()
57+ #endif
58+
5259#if !SOC_RCC_IS_INDEPENDENT
5360#define PCNT_RCC_ATOMIC () PERIPH_RCC_ATOMIC()
5461#else
@@ -75,6 +82,7 @@ struct pcnt_platform_t {
7582struct pcnt_group_t {
7683 int group_id ; // Group ID, index from 0
7784 int intr_priority ; // PCNT interrupt priority
85+ pcnt_clock_source_t clk_src ; // PCNT clock source
7886 portMUX_TYPE spinlock ; // to protect per-group register level concurrent access
7987 pcnt_hal_context_t hal ;
8088 pcnt_unit_t * units [SOC_PCNT_ATTR (UNITS_PER_INST )]; // array of PCNT units
@@ -140,6 +148,7 @@ static pcnt_platform_t s_platform;
140148static pcnt_group_t * pcnt_acquire_group_handle (int group_id );
141149static void pcnt_release_group_handle (pcnt_group_t * group );
142150static void pcnt_default_isr (void * args );
151+ static esp_err_t pcnt_select_periph_clock (pcnt_unit_t * unit , pcnt_clock_source_t clk_src );
143152
144153static esp_err_t pcnt_register_to_group (pcnt_unit_t * unit )
145154{
@@ -222,6 +231,10 @@ esp_err_t pcnt_new_unit(const pcnt_unit_config_t *config, pcnt_unit_handle_t *re
222231 int group_id = group -> group_id ;
223232 int unit_id = unit -> unit_id ;
224233
234+ pcnt_clock_source_t pcnt_clk_src = config -> clk_src ? config -> clk_src : PCNT_CLK_SRC_DEFAULT ;
235+ ESP_GOTO_ON_ERROR (pcnt_select_periph_clock (unit , pcnt_clk_src ), err , TAG , "select periph clock failed" );
236+ ESP_GOTO_ON_ERROR (esp_clk_tree_enable_src ((soc_module_clk_t )group -> clk_src , true), err , TAG , "clock source enable failed" );
237+
225238 // if interrupt priority specified before, it cannot be changed until the group is released
226239 // check if the new priority specified consistents with the old one
227240 bool intr_priority_conflict = false;
@@ -323,6 +336,8 @@ esp_err_t pcnt_del_unit(pcnt_unit_handle_t unit)
323336#endif // SOC_PCNT_SUPPORT_CLEAR_SIGNAL
324337
325338 ESP_LOGD (TAG , "del unit (%d,%d)" , group_id , unit_id );
339+ // disable clock source
340+ ESP_RETURN_ON_ERROR (esp_clk_tree_enable_src ((soc_module_clk_t )group -> clk_src , false), TAG , "clock source disable failed" );
326341 // recycle memory resource
327342 ESP_RETURN_ON_ERROR (pcnt_destroy (unit ), TAG , "destroy pcnt unit failed" );
328343 return ESP_OK ;
@@ -930,14 +945,6 @@ static pcnt_group_t *pcnt_acquire_group_handle(int group_id)
930945 _lock_release (& s_platform .mutex );
931946
932947 if (new_group ) {
933- #if CONFIG_PM_ENABLE
934- // PCNT uses the APB as its function clock,
935- // and its filter module is sensitive to the clock frequency
936- // thus we choose the APM_MAX lock to prevent the function clock from being changed
937- if (esp_pm_lock_create (ESP_PM_APB_FREQ_MAX , 0 , pcnt_periph_signals .groups [group_id ].module_name , & group -> pm_lock ) != ESP_OK ) {
938- ESP_LOGW (TAG , "create pm lock failed" );
939- }
940- #endif
941948 ESP_LOGD (TAG , "new group (%d) at %p" , group_id , group );
942949 }
943950
@@ -975,6 +982,48 @@ static void pcnt_release_group_handle(pcnt_group_t *group)
975982 }
976983}
977984
985+ static esp_err_t pcnt_select_periph_clock (pcnt_unit_t * unit , pcnt_clock_source_t clk_src )
986+ {
987+ esp_err_t ret = ESP_OK ;
988+ pcnt_group_t * group = unit -> group ;
989+ bool clock_selection_conflict = false;
990+ bool do_clock_init = false;
991+ // group clock source is shared by all units
992+ portENTER_CRITICAL (& group -> spinlock );
993+ if (group -> clk_src == 0 ) {
994+ group -> clk_src = clk_src ;
995+ do_clock_init = true;
996+ } else {
997+ clock_selection_conflict = (group -> clk_src != clk_src );
998+ }
999+ portEXIT_CRITICAL (& group -> spinlock );
1000+ ESP_RETURN_ON_FALSE (!clock_selection_conflict , ESP_ERR_INVALID_ARG , TAG ,
1001+ "group clock conflict, already is %d but attempt to %d" , group -> clk_src , clk_src );
1002+
1003+ if (do_clock_init ) {
1004+
1005+ #if CONFIG_PM_ENABLE
1006+ // PCNT filter module is sensitive to the clock frequency
1007+ // to make the pcnt works reliable, the source clock must stay alive and unchanged
1008+ esp_pm_lock_type_t pm_lock_type = ESP_PM_NO_LIGHT_SLEEP ;
1009+ #if PCNT_LL_CLOCK_SUPPORT_APB
1010+ if (clk_src == PCNT_CLK_SRC_APB ) {
1011+ // APB clock frequency can be changed during DFS
1012+ // thus we choose the APM_MAX lock to prevent the function clock from being changed
1013+ pm_lock_type = ESP_PM_APB_FREQ_MAX ;
1014+ }
1015+ #endif // PCNT_LL_CLOCK_SUPPORT_APB
1016+ ret = esp_pm_lock_create (pm_lock_type , 0 , soc_pcnt_signals [group -> group_id ].module_name , & group -> pm_lock );
1017+ ESP_RETURN_ON_ERROR (ret , TAG , "create pm lock failed" );
1018+ #endif // CONFIG_PM_ENABLE
1019+
1020+ PCNT_CLOCK_SRC_ATOMIC () {
1021+ pcnt_ll_set_clock_source (group -> hal .dev , clk_src );
1022+ }
1023+ }
1024+ return ret ;
1025+ }
1026+
9781027IRAM_ATTR static void pcnt_default_isr (void * args )
9791028{
9801029 bool need_yield = false;
0 commit comments