diff --git a/components/driver/pcnt/include/driver/pulse_cnt.h b/components/driver/pcnt/include/driver/pulse_cnt.h index b4d7f3dc4e51..166d5ce5ae7c 100644 --- a/components/driver/pcnt/include/driver/pulse_cnt.h +++ b/components/driver/pcnt/include/driver/pulse_cnt.h @@ -63,15 +63,8 @@ typedef struct { int high_limit; /*!< High limitation of the count unit, should be higher than 0 */ int intr_priority; /*!< PCNT interrupt priority, if set to 0, the driver will try to allocate an interrupt with a relative low priority (1,2,3) */ -#if SOC_PCNT_SUPPORT_ZERO_INPUT - int zero_input_gpio_num; /*!< GPIO number used by the clear signal, the default active level is high, input mode with pull down enabled. Set to -1 if unused */ -#endif struct { uint32_t accum_count: 1; /*!< Whether to accumulate the count value when overflows at the high/low limit */ -#if SOC_PCNT_SUPPORT_ZERO_INPUT - uint32_t invert_zero_input: 1; /*!< Invert the zero input signal and set input mode with pull up.*/ - uint32_t io_loop_back: 1; /*!< For debug/test, the signal output from the GPIO will be fed to the input path as well */ -#endif } flags; /*!< Extra flags */ } pcnt_unit_config_t; @@ -146,6 +139,34 @@ esp_err_t pcnt_del_unit(pcnt_unit_handle_t unit); */ esp_err_t pcnt_unit_set_glitch_filter(pcnt_unit_handle_t unit, const pcnt_glitch_filter_config_t *config); +#if SOC_PCNT_SUPPORT_CLEAR_SIGNAL +/** + * @brief PCNT clear signal configuration + */ +typedef struct { + int clear_signal_gpio_num; /*!< GPIO number used by the clear signal, the default active level is high, input mode with pull down enabled */ + struct { + uint32_t invert_clear_signal: 1; /*!< Invert the clear input signal and set input mode with pull up */ + uint32_t io_loop_back: 1; /*!< For debug/test, the signal output from the GPIO will be fed to the input path as well */ + } flags; /*!< clear signal config flags */ +} pcnt_clear_signal_config_t; + +/** + * @brief Set clear signal for PCNT unit + * + * @note The function of clear signal is the same as `pcnt_unit_clear_count()`. High-level Active + * + * @param[in] unit PCNT unit handle created by `pcnt_new_unit()` + * @param[in] config PCNT clear signal configuration, set config to NULL means disabling the clear signal + * @return + * - ESP_OK: Set clear signal successfully + * - ESP_ERR_INVALID_ARG: Set clear signal failed because of invalid argument + * - ESP_ERR_INVALID_STATE: Set clear signal failed because set clear signal repeatly or disable clear signal before set it + * - 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 + /** * @brief Enable the PCNT unit * diff --git a/components/driver/pcnt/pulse_cnt.c b/components/driver/pcnt/pulse_cnt.c index 952daddb072d..b7721dd5ce36 100644 --- a/components/driver/pcnt/pulse_cnt.c +++ b/components/driver/pcnt/pulse_cnt.c @@ -87,7 +87,7 @@ struct pcnt_unit_t { int unit_id; // allocated unit numerical ID int low_limit; // low limit value int high_limit; // high limit value - int zero_input_gpio_num; // which gpio clear signal input + int clear_signal_gpio_num; // which gpio clear signal input int accum_value; // accumulated count value pcnt_chan_t *channels[SOC_PCNT_CHANNELS_PER_UNIT]; // array of PCNT channels pcnt_watch_point_t watchers[PCNT_LL_WATCH_EVENT_MAX]; // array of PCNT watchers @@ -235,6 +235,7 @@ esp_err_t pcnt_new_unit(const pcnt_unit_config_t *config, pcnt_unit_handle_t *re unit->high_limit = config->high_limit; unit->low_limit = config->low_limit; unit->accum_value = 0; + unit->clear_signal_gpio_num = -1; unit->flags.accum_count = config->flags.accum_count; // clear/pause register is shared by all units, so using group's spinlock @@ -253,29 +254,6 @@ esp_err_t pcnt_new_unit(const pcnt_unit_config_t *config, pcnt_unit_handle_t *re unit->watchers[i].event_id = PCNT_LL_WATCH_EVENT_INVALID; // invalid all watch point } -#if SOC_PCNT_SUPPORT_ZERO_INPUT - // GPIO configuration - gpio_config_t gpio_conf = { - .intr_type = GPIO_INTR_DISABLE, - .mode = GPIO_MODE_INPUT | (config->flags.io_loop_back ? GPIO_MODE_OUTPUT : 0), // also enable the output path if `io_loop_back` is enabled - .pull_down_en = true, - .pull_up_en = false, - }; - - if (config->zero_input_gpio_num >= 0) { - if (config->flags.invert_zero_input) { - gpio_conf.pull_down_en = false; - gpio_conf.pull_up_en = true; - } - gpio_conf.pin_bit_mask = 1ULL << config->zero_input_gpio_num; - ESP_GOTO_ON_ERROR(gpio_config(&gpio_conf), err, TAG, "config zero GPIO failed"); - esp_rom_gpio_connect_in_signal(config->zero_input_gpio_num, - pcnt_periph_signals.groups[group_id].units[unit_id].clear_sig, - config->flags.invert_zero_input); - } - unit->zero_input_gpio_num = config->zero_input_gpio_num; -#endif // SOC_PCNT_SUPPORT_ZERO_INPUT - ESP_LOGD(TAG, "new pcnt unit (%d,%d) at %p, count range:[%d,%d]", group_id, unit_id, unit, unit->low_limit, unit->high_limit); *ret_unit = unit; return ESP_OK; @@ -299,11 +277,11 @@ esp_err_t pcnt_del_unit(pcnt_unit_handle_t unit) ESP_RETURN_ON_FALSE(!unit->channels[i], ESP_ERR_INVALID_STATE, TAG, "channel %d still in working", i); } -#if SOC_PCNT_SUPPORT_ZERO_INPUT - if (unit->zero_input_gpio_num >= 0) { - gpio_reset_pin(unit->zero_input_gpio_num); +#if SOC_PCNT_SUPPORT_CLEAR_SIGNAL + if (unit->clear_signal_gpio_num >= 0) { + gpio_reset_pin(unit->clear_signal_gpio_num); } -#endif // SOC_PCNT_SUPPORT_ZERO_INPUT +#endif // SOC_PCNT_SUPPORT_CLEAR_SIGNAL ESP_LOGD(TAG, "del unit (%d,%d)", group_id, unit_id); // recycle memory resource @@ -311,6 +289,40 @@ esp_err_t pcnt_del_unit(pcnt_unit_handle_t unit) return ESP_OK; } +#if SOC_PCNT_SUPPORT_CLEAR_SIGNAL +esp_err_t pcnt_unit_set_clear_signal(pcnt_unit_handle_t unit, const pcnt_clear_signal_config_t *config) +{ + ESP_RETURN_ON_FALSE(unit, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); + pcnt_group_t *group = unit->group; + int group_id = group->group_id; + int unit_id = unit->unit_id; + + if (config) { + gpio_config_t gpio_conf = { + .intr_type = GPIO_INTR_DISABLE, + .mode = GPIO_MODE_INPUT | (config->flags.io_loop_back ? GPIO_MODE_OUTPUT : 0), // also enable the output path if `io_loop_back` is enabled + .pull_down_en = true, + .pull_up_en = false, + }; + if (config->flags.invert_clear_signal) { + gpio_conf.pull_down_en = false; + gpio_conf.pull_up_en = true; + } + gpio_conf.pin_bit_mask = 1ULL << config->clear_signal_gpio_num; + ESP_RETURN_ON_ERROR(gpio_config(&gpio_conf), TAG, "config zero signal GPIO failed"); + esp_rom_gpio_connect_in_signal(config->clear_signal_gpio_num, + pcnt_periph_signals.groups[group_id].units[unit_id].clear_sig, + config->flags.invert_clear_signal); + unit->clear_signal_gpio_num = config->clear_signal_gpio_num; + } else { + ESP_RETURN_ON_FALSE(unit->clear_signal_gpio_num >= 0, ESP_ERR_INVALID_STATE, TAG, "zero signal not set yet"); + gpio_reset_pin(unit->clear_signal_gpio_num); + unit->clear_signal_gpio_num = -1; + } + return ESP_OK; +} +#endif // SOC_PCNT_SUPPORT_CLEAR_SIGNAL + esp_err_t pcnt_unit_set_glitch_filter(pcnt_unit_handle_t unit, const pcnt_glitch_filter_config_t *config) { pcnt_group_t *group = NULL; diff --git a/components/driver/test_apps/pulse_cnt/main/test_pulse_cnt.c b/components/driver/test_apps/pulse_cnt/main/test_pulse_cnt.c index aafa6526d16c..8acdcdaf7c8e 100644 --- a/components/driver/test_apps/pulse_cnt/main/test_pulse_cnt.c +++ b/components/driver/test_apps/pulse_cnt/main/test_pulse_cnt.c @@ -21,9 +21,6 @@ TEST_CASE("pcnt_unit_install_uninstall", "[pcnt]") .low_limit = -100, .high_limit = 100, .intr_priority = 0, -#if SOC_PCNT_SUPPORT_ZERO_INPUT - .zero_input_gpio_num = -1, -#endif }; pcnt_unit_handle_t units[SOC_PCNT_UNITS_PER_GROUP]; int count_value = 0; @@ -91,9 +88,6 @@ TEST_CASE("pcnt_channel_install_uninstall", "[pcnt]") pcnt_unit_config_t unit_config = { .low_limit = -100, .high_limit = 100, -#if SOC_PCNT_SUPPORT_ZERO_INPUT - .zero_input_gpio_num = -1, -#endif }; pcnt_chan_config_t chan_config = { .edge_gpio_num = TEST_PCNT_GPIO_A, // only detect edge signal in this case @@ -182,9 +176,6 @@ TEST_CASE("pcnt_multiple_units_pulse_count", "[pcnt]") pcnt_unit_config_t unit_config = { .low_limit = -100, .high_limit = 100, -#if SOC_PCNT_SUPPORT_ZERO_INPUT - .zero_input_gpio_num = -1, -#endif }; pcnt_unit_handle_t units[2]; for (int i = 0; i < 2; i++) { @@ -250,9 +241,6 @@ TEST_CASE("pcnt_quadrature_decode_event", "[pcnt]") pcnt_unit_config_t unit_config = { .low_limit = -100, .high_limit = 100, -#if SOC_PCNT_SUPPORT_ZERO_INPUT - .zero_input_gpio_num = -1, -#endif }; printf("install pcnt unit\r\n"); @@ -376,9 +364,6 @@ TEST_CASE("pcnt_zero_cross_mode", "[pcnt]") pcnt_unit_config_t unit_config = { .low_limit = -100, .high_limit = 100, -#if SOC_PCNT_SUPPORT_ZERO_INPUT - .zero_input_gpio_num = -1, -#endif }; printf("install pcnt unit\r\n"); @@ -471,9 +456,6 @@ TEST_CASE("pcnt_virtual_io", "[pcnt]") pcnt_unit_config_t unit_config = { .low_limit = -100, .high_limit = 100, -#if SOC_PCNT_SUPPORT_ZERO_INPUT - .zero_input_gpio_num = -1, -#endif }; pcnt_chan_config_t chan_config = { .edge_gpio_num = TEST_PCNT_GPIO_A, // only detect edge signal in this case @@ -520,23 +502,24 @@ TEST_CASE("pcnt_virtual_io", "[pcnt]") TEST_ESP_OK(pcnt_del_unit(unit)); } -#if SOC_PCNT_SUPPORT_ZERO_INPUT +#if SOC_PCNT_SUPPORT_CLEAR_SIGNAL TEST_CASE("pcnt_zero_input_signal", "[pcnt]") { pcnt_unit_config_t unit_config = { .low_limit = -1000, .high_limit = 1000, - .zero_input_gpio_num = TEST_PCNT_GPIO_Z, - .flags.io_loop_back = 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, + + pcnt_clear_signal_config_t clear_signal_config = { + .clear_signal_gpio_num = TEST_PCNT_GPIO_Z, + .flags.io_loop_back = true, }; - TEST_ESP_OK(pcnt_unit_set_glitch_filter(unit, &filter_config)); + + TEST_ESP_OK(pcnt_unit_set_clear_signal(unit, &clear_signal_config)); printf("install pcnt channels\r\n"); pcnt_chan_config_t chan_config = { @@ -571,11 +554,30 @@ TEST_CASE("pcnt_zero_input_signal", "[pcnt]") TEST_ESP_OK(pcnt_unit_get_count(unit, &count_value)); printf("count_value=%d\r\n", count_value); - TEST_ASSERT_EQUAL(0, count_value); // 0 after rst_sig + TEST_ASSERT_EQUAL(0, count_value); // 0 after zero signal + + printf("remove zero signal\r\n"); + TEST_ESP_OK(pcnt_unit_set_clear_signal(unit, NULL)); + TEST_ESP_ERR(ESP_ERR_INVALID_STATE, pcnt_unit_set_clear_signal(unit, NULL)); + + // trigger 10 rising edge on GPIO + test_gpio_simulate_rising_edge(TEST_PCNT_GPIO_A, 10); + + TEST_ESP_OK(pcnt_unit_get_count(unit, &count_value)); + printf("count_value=%d\r\n", count_value); + TEST_ASSERT_EQUAL(10, count_value); + + printf("simulating zero input signal\r\n"); + TEST_ESP_OK(gpio_set_level(TEST_PCNT_GPIO_Z, 1)); + TEST_ESP_OK(gpio_set_level(TEST_PCNT_GPIO_Z, 0)); + + TEST_ESP_OK(pcnt_unit_get_count(unit, &count_value)); + printf("count_value=%d\r\n", count_value); + TEST_ASSERT_EQUAL(10, count_value); // 10 with no zero signal TEST_ESP_OK(pcnt_del_channel(channel)); TEST_ESP_OK(pcnt_unit_stop(unit)); TEST_ESP_OK(pcnt_unit_disable(unit)); TEST_ESP_OK(pcnt_del_unit(unit)); } -#endif // SOC_PCNT_SUPPORT_ZERO_INPUT +#endif // SOC_PCNT_SUPPORT_CLEAR_SIGNAL diff --git a/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in b/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in index 6b8cac1893b5..8c633dbc75be 100644 --- a/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in @@ -459,7 +459,7 @@ config SOC_PCNT_SUPPORT_RUNTIME_THRES_UPDATE bool default y -config SOC_PCNT_SUPPORT_ZERO_INPUT +config SOC_PCNT_SUPPORT_CLEAR_SIGNAL bool default y diff --git a/components/soc/esp32p4/include/soc/soc_caps.h b/components/soc/esp32p4/include/soc/soc_caps.h index b280502efc42..db52ad5b7561 100644 --- a/components/soc/esp32p4/include/soc/soc_caps.h +++ b/components/soc/esp32p4/include/soc/soc_caps.h @@ -262,7 +262,7 @@ #define SOC_PCNT_CHANNELS_PER_UNIT 2 #define SOC_PCNT_THRES_POINT_PER_UNIT 2 #define SOC_PCNT_SUPPORT_RUNTIME_THRES_UPDATE 1 -#define SOC_PCNT_SUPPORT_ZERO_INPUT 1 /*!< Support encoder with Zero phase input */ +#define SOC_PCNT_SUPPORT_CLEAR_SIGNAL 1 /*!< Support clear signal input */ /*--------------------------- RMT CAPS ---------------------------------------*/ #define SOC_RMT_GROUPS 1U /*!< One RMT group */ diff --git a/docs/en/api-reference/peripherals/pcnt.rst b/docs/en/api-reference/peripherals/pcnt.rst index a91f000550d6..8bc56a5b7b4b 100644 --- a/docs/en/api-reference/peripherals/pcnt.rst +++ b/docs/en/api-reference/peripherals/pcnt.rst @@ -20,17 +20,20 @@ Functional Overview Description of the PCNT functionality is divided into the following sections: -- :ref:`pcnt-resource-allocation` - covers how to allocate PCNT units and channels with properly set of configurations. It also covers how to recycle the resources when they finished working. -- :ref:`pcnt-setup-channel-actions` - covers how to configure the PCNT channel to behave on different signal edges and levels. -- :ref:`pcnt-watch-points` - describes how to configure PCNT watch points (i.e., tell PCNT unit to trigger an event when the count reaches a certain value). -- :ref:`pcnt-register-event-callbacks` - describes how to hook your specific code to the watch point event callback function. -- :ref:`pcnt-set-glitch-filter` - describes how to enable and set the timing parameters for the internal glitch filter. -- :ref:`pcnt-enable-disable-unit` - describes how to enable and disable the PCNT unit. -- :ref:`pcnt-unit-io-control` - describes IO control functions of PCNT unit, like enable glitch filter, start and stop unit, get and clear count value. -- :ref:`pcnt-power-management` - describes what functionality will prevent the chip from going into low power mode. -- :ref:`pcnt-iram-safe` - describes tips on how to make the PCNT interrupt and IO control functions work better along with a disabled cache. -- :ref:`pcnt-thread-safe` - lists which APIs are guaranteed to be thread safe by the driver. -- :ref:`pcnt-kconfig-options` - lists the supported Kconfig options that can be used to make a different effect on driver behavior. +.. list:: + + - :ref:`pcnt-resource-allocation` - covers how to allocate PCNT units and channels with properly set of configurations. It also covers how to recycle the resources when they finished working. + - :ref:`pcnt-setup-channel-actions` - covers how to configure the PCNT channel to behave on different signal edges and levels. + - :ref:`pcnt-watch-points` - describes how to configure PCNT watch points (i.e., tell PCNT unit to trigger an event when the count reaches a certain value). + - :ref:`pcnt-register-event-callbacks` - describes how to hook your specific code to the watch point event callback function. + - :ref:`pcnt-set-glitch-filter` - describes how to enable and set the timing parameters for the internal glitch filter. + :SOC_PCNT_SUPPORT_CLEAR_SIGNAL: - :ref:`pcnt-set-clear-signal` - describes how to set the parameters for the zero signal. + - :ref:`pcnt-enable-disable-unit` - describes how to enable and disable the PCNT unit. + - :ref:`pcnt-unit-io-control` - describes IO control functions of PCNT unit, like enable glitch filter, start and stop unit, get and clear count value. + - :ref:`pcnt-power-management` - describes what functionality will prevent the chip from going into low power mode. + - :ref:`pcnt-iram-safe` - describes tips on how to make the PCNT interrupt and IO control functions work better along with a disabled cache. + - :ref:`pcnt-thread-safe` - lists which APIs are guaranteed to be thread safe by the driver. + - :ref:`pcnt-kconfig-options` - lists the supported Kconfig options that can be used to make a different effect on driver behavior. .. _pcnt-resource-allocation: @@ -52,12 +55,6 @@ To install a PCNT unit, there's a configuration structure that needs to be given Since all PCNT units share the same interrupt source, when installing multiple PCNT units make sure that the interrupt priority :cpp:member:`pcnt_unit_config_t::intr_priority` is the same for each unit. -.. only:: SOC_PCNT_SUPPORT_ZERO_INPUT - - - :cpp:member:`pcnt_unit_config_t::zero_input_gpio_num` specify the GPIO numbers used by **zero** type signal. The default active level is high, and the input mode is pull-down enabled. Please note, it can be assigned to `-1` if it's not actually used, and GPIO will not be initialized. - - :cpp:member:`pcnt_unit_config_t::invert_zero_input` is used to decide whether to invert the input signal before it going into PCNT hardware. The invert is done by GPIO matrix instead of PCNT hardware. The input mode is pull-up enabled when the input signal is invert. - - :cpp:member:`pcnt_unit_config_t::io_loop_back` is for debug only, which enables both the GPIO's input and output paths. This can help to simulate the zreo pulse signals by function :cpp:func:`gpio_set_level` on the same GPIO. - Unit allocation and initialization is done by calling a function :cpp:func:`pcnt_new_unit` with :cpp:type:`pcnt_unit_config_t` as an input parameter. The function will return a PCNT unit handle only when it runs correctly. Specifically, when there are no more free PCNT units in the pool (i.e. unit resources have been used up), then this function will return :c:macro:`ESP_ERR_NOT_FOUND` error. The total number of available PCNT units is recorded by :c:macro:`SOC_PCNT_UNITS_PER_GROUP` for reference. If a previously created PCNT unit is no longer needed, it's recommended to recycle the resource by calling :cpp:func:`pcnt_del_unit`. Which in return allows the underlying unit hardware to be used for other purposes. Before deleting a PCNT unit, one should ensure the following prerequisites: @@ -204,7 +201,33 @@ This function should be called when the unit is in the init state. Otherwise, it }; ESP_ERROR_CHECK(pcnt_unit_set_glitch_filter(pcnt_unit, &filter_config)); -.. _pcnt-enable-disable-unit: +.. only:: SOC_PCNT_SUPPORT_CLEAR_SIGNAL + + .. _pcnt-set-clear-signal: + + Set Clear Signal + ^^^^^^^^^^^^^^^^ + + The PCNT unit can receive a zero signal from the GPIO. The parameters that can be configured for the zero signal are listed in :cpp:type:`pcnt_clear_signal_config_t`: + + - :cpp:member:`pcnt_clear_signal_config_t::zero_input_gpio_num` specify the GPIO numbers used by **zero** signal. The default active level is high, and the input mode is pull-down enabled. + - :cpp:member:`pcnt_clear_signal_config_t::invert_zero_input` is used to decide whether to invert the input signal before it going into PCNT hardware. The invert is done by GPIO matrix instead of PCNT hardware. The input mode is pull-up enabled when the input signal is invert. + - :cpp:member:`pcnt_clear_signal_config_t::io_loop_back` is for debug only, which enables both the GPIO's input and output paths. This can help to simulate the zreo pulse signals by function :cpp:func:`gpio_set_level` on the same GPIO. + + This signal acts in the same way as calling :cpp:func:`pcnt_unit_clear_count`, but is not subject to software latency, and is suitable for use in situations with high latency requirements. + + .. code:: c + + pcnt_clear_signal_config_t clear_signal_config = { + .clear_signal_gpio_num = PCNT_CLEAR_SIGNAL_GPIO, + }; + ESP_ERROR_CHECK(pcnt_unit_set_clear_signal(pcnt_unit, &clear_signal_config)); + + .. _pcnt-enable-disable-unit: + +.. only:: not SOC_PCNT_SUPPORT_CLEAR_SIGNAL + + .. _pcnt-enable-disable-unit: Enable and Disable Unit ^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/docs/zh_CN/api-reference/peripherals/pcnt.rst b/docs/zh_CN/api-reference/peripherals/pcnt.rst index 6428765f687c..3c163ac2aec0 100644 --- a/docs/zh_CN/api-reference/peripherals/pcnt.rst +++ b/docs/zh_CN/api-reference/peripherals/pcnt.rst @@ -20,17 +20,20 @@ PCNT 模块通常用于: PCNT 的功能从以下几个方面进行说明: -- :ref:`pcnt-resource-allocation` - 说明如何通过配置分配 PCNT 单元和通道,以及在相应操作完成之后,如何回收单元和通道。 -- :ref:`pcnt-setup-channel-actions` - 说明如何设置通道针对不同信号沿和电平进行操作。 -- :ref:`pcnt-watch-points` - 说明如何配置观察点,即当计数达到某个数值时,命令 PCNT 单元触发某个事件。 -- :ref:`pcnt-register-event-callbacks` - 说明如何将您的代码挂载到观察点事件的回调函数上。 -- :ref:`pcnt-set-glitch-filter` - 说明如何使能毛刺滤波器并设置其时序参数。 -- :ref:`pcnt-enable-disable-unit` - 说明如何使能和关闭 PCNT 单元。 -- :ref:`pcnt-unit-io-control` - 说明 PCNT 单元的 IO 控制功能,例如使能毛刺滤波器,开启和停用 PCNT 单元,获取和清除计数。 -- :ref:`pcnt-power-management` - 说明哪些功能会阻止芯片进入低功耗模式。 -- :ref:`pcnt-iram-safe` - 说明在缓存禁用的情况下,如何执行 PCNT 中断和 IO 控制功能。 -- :ref:`pcnt-thread-safe` - 列出线程安全的 API。 -- :ref:`pcnt-kconfig-options` - 列出了支持的 Kconfig 选项,这些选项可实现不同的驱动效果。 +.. list:: + + - :ref:`pcnt-resource-allocation` - 说明如何通过配置分配 PCNT 单元和通道,以及在相应操作完成之后,如何回收单元和通道。 + - :ref:`pcnt-setup-channel-actions` - 说明如何设置通道针对不同信号沿和电平进行操作。 + - :ref:`pcnt-watch-points` - 说明如何配置观察点,即当计数达到某个数值时,命令 PCNT 单元触发某个事件。 + - :ref:`pcnt-register-event-callbacks` - 说明如何将您的代码挂载到观察点事件的回调函数上。 + - :ref:`pcnt-set-glitch-filter` - 说明如何使能毛刺滤波器并设置其时序参数。 + :SOC_PCNT_SUPPORT_CLEAR_SIGNAL: - :ref:`pcnt-set-clear-signal` - 说明如何使能清零信号并设置其参数。 + - :ref:`pcnt-enable-disable-unit` - 说明如何使能和关闭 PCNT 单元。 + - :ref:`pcnt-unit-io-control` - 说明 PCNT 单元的 IO 控制功能,例如使能毛刺滤波器,开启和停用 PCNT 单元,获取和清除计数。 + - :ref:`pcnt-power-management` - 说明哪些功能会阻止芯片进入低功耗模式。 + - :ref:`pcnt-iram-safe` - 说明在缓存禁用的情况下,如何执行 PCNT 中断和 IO 控制功能。 + - :ref:`pcnt-thread-safe` - 列出线程安全的 API。 + - :ref:`pcnt-kconfig-options` - 列出了支持的 Kconfig 选项,这些选项可实现不同的驱动效果。 .. _pcnt-resource-allocation: @@ -52,12 +55,6 @@ PCNT 单元和通道分别用 :cpp:type:`pcnt_unit_handle_t` 与 :cpp:type:`pcnt 由于所有 PCNT 单元共享一个中断源,安装多个 PCNT 单元时请确保每个单元的中断优先级 :cpp:member:`pcnt_unit_config_t::intr_priority` 一致。 -.. only:: SOC_PCNT_SUPPORT_ZERO_INPUT - - - :cpp:member:`pcnt_unit_config_t::zero_input_gpio_num` 用于指定 **清零** 信号对应的 GPIO 编号。默认有效电平为高,使能下拉输入。请注意,这个参数未被使用时,可以设置为 `-1`,初始化时将不会分配 GPIO。 - - :cpp:member:`pcnt_unit_config_t::invert_zero_input` 用于确定信号在输入 PCNT 之前是否需要被翻转,信号翻转由 GPIO 矩阵 (不是 PCNT 单元) 执行。翻转时使能上拉输入。 - - :cpp:member:`pcnt_unit_config_t::io_loop_back` 仅用于调试,它可以使能 GPIO 的输入和输出路径。这样,就可以通过调用位于同一 GPIO 上的函数 :cpp:func:`gpio_set_level` 来模拟脉冲清零信号。 - 调用函数 :cpp:func:`pcnt_new_unit` 并将 :cpp:type:`pcnt_unit_config_t` 作为其输入值,可对 PCNT 单元进行分配和初始化。该函数正常运行时,会返回一个 PCNT 单元句柄。没有可用的 PCNT 单元时(即 PCNT 单元全部被占用),该函数会返回错误 :c:macro:`ESP_ERR_NOT_FOUND`。可用的 PCNT 单元总数记录在 :c:macro:`SOC_PCNT_UNITS_PER_GROUP` 中,以供参考。 如果不再需要之前创建的某个 PCNT 单元,建议通过调用 :cpp:func:`pcnt_del_unit` 来回收该单元,从而该单元可用于其他用途。删除某个 PCNT 单元之前,需要满足以下条件: @@ -204,7 +201,33 @@ PCNT 单元的滤波器可滤除信号中的短时毛刺,:cpp:type:`pcnt_glitc }; ESP_ERROR_CHECK(pcnt_unit_set_glitch_filter(pcnt_unit, &filter_config)); -.. _pcnt-enable-disable-unit: +.. only:: SOC_PCNT_SUPPORT_CLEAR_SIGNAL + + .. _pcnt-set-clear-signal: + + 设置清零信号 + ^^^^^^^^^^^^^^^^ + + PCNT 单元的可以接收来自 GPIO 的清零信号,:cpp:type:`pcnt_clear_signal_config_t` 中列出了清零信号的配置参数: + + - :cpp:member:`pcnt_clear_signal_config_t::zero_input_gpio_num` 用于指定 **清零** 信号对应的 GPIO 编号。默认有效电平为高,使能下拉输入。 + - :cpp:member:`pcnt_clear_signal_config_t::invert_zero_input` 用于确定信号在输入 PCNT 之前是否需要被翻转,信号翻转由 GPIO 矩阵 (不是 PCNT 单元) 执行。翻转时使能上拉输入。 + - :cpp:member:`pcnt_clear_signal_config_t::io_loop_back` 仅用于调试,它可以使能 GPIO 的输入和输出路径。这样,就可以通过调用位于同一 GPIO 上的函数 :cpp:func:`gpio_set_level` 来模拟脉冲清零信号。 + + 该信号作用与调用 :cpp:func:`pcnt_unit_clear_count` 相同,但不受软件延迟的限制,适用于对延迟要求较高的场合。 + + .. code:: c + + pcnt_clear_signal_config_t clear_signal_config = { + .clear_signal_gpio_num = PCNT_CLEAR_SIGNAL_GPIO, + }; + ESP_ERROR_CHECK(pcnt_unit_set_clear_signal(pcnt_unit, &clear_signal_config)); + + .. _pcnt-enable-disable-unit: + +.. only:: not SOC_PCNT_SUPPORT_CLEAR_SIGNAL + + .. _pcnt-enable-disable-unit: 使能和禁用单元 ^^^^^^^^^^^^^^^^^