脉冲计数器 (PCNT)

概述

PCNT 用于统计输入信号的上升沿和/或下降沿的数量。ESP32-P4 集成了多个脉冲计数单元,1 每个单元都是包含多个通道的独立计数器。通道可独立配置为统计上升沿或下降沿数量的递增计数器或递减计数器。

PCNT 通道可检测 边沿 信号及 电平 信号。对于比较简单的应用,检测边沿信号就足够了。PCNT 通道可检测上升沿信号、下降沿信号,同时也能设置为递增计数,递减计数,或停止计数。电平信号就是所谓的 控制信号,可用来控制边沿信号的计数模式。通过设置电平信号与边沿信号的检测模式,PCNT 单元可用作 正交解码器

每个 PCNT 单元还包含一个滤波器,用于滤除线路毛刺。

PCNT 模块通常用于:

  • 对一段时间内的脉冲计数,进而计算得到周期信号的频率;

  • 对正交信号进行解码,进而获得速度和方向信息。

功能描述

PCNT 的功能从以下几个方面进行说明:

  • 分配资源 - 说明如何通过配置分配 PCNT 单元和通道,以及在相应操作完成之后,如何回收单元和通道。

  • 设置通道操作 - 说明如何设置通道针对不同信号沿和电平进行操作。

  • PCNT 观察点 - 说明如何配置观察点,即当计数达到某个数值时,命令 PCNT 单元触发某个事件。

  • 注册事件回调函数 - 说明如何将您的代码挂载到观察点事件的回调函数上。

  • 设置毛刺滤波器 - 说明如何使能毛刺滤波器并设置其时序参数。

  • 使用外部清零信号 - 说明如何使能外部清零信号并设置其参数。

  • 使能和禁用单元 - 说明如何使能和关闭 PCNT 单元。

  • 控制单元 IO 操作 - 说明 PCNT 单元的 IO 控制功能,例如使能毛刺滤波器,开启和停用 PCNT 单元,获取和清除计数。

  • 电源管理 - 说明哪些功能会阻止芯片进入低功耗模式。

  • 支持 IRAM 安全中断 - 说明在缓存禁用的情况下,如何执行 PCNT 中断和 IO 控制功能。

  • 支持线程安全 - 列出线程安全的 API。

  • 支持的 Kconfig 选项 - 列出了支持的 Kconfig 选项,这些选项可实现不同的驱动效果。

分配资源

PCNT 单元和通道分别用 pcnt_unit_handle_tpcnt_channel_handle_t 表示。所有的可用单元和通道都由驱动在资源池中进行维护,无需了解底层实例 ID。

安装 PCNT 单元

安装 PCNT 单元时,需要先完成配置 pcnt_unit_config_t

备注

由于所有 PCNT 单元共享一个中断源,安装多个 PCNT 单元时请确保每个单元的中断优先级 pcnt_unit_config_t::intr_priority 一致。

调用函数 pcnt_new_unit() 并将 pcnt_unit_config_t 作为其输入值,可对 PCNT 单元进行分配和初始化。该函数正常运行时,会返回一个 PCNT 单元句柄。没有可用的 PCNT 单元时(即 PCNT 单元全部被占用),该函数会返回错误 ESP_ERR_NOT_FOUND。可用的 PCNT 单元总数记录在 SOC_PCNT_UNITS_PER_GROUP 中,以供参考。

如果不再需要之前创建的某个 PCNT 单元,建议通过调用 pcnt_del_unit() 来回收该单元,从而该单元可用于其他用途。删除某个 PCNT 单元之前,需要满足以下条件:

#define EXAMPLE_PCNT_HIGH_LIMIT 100
#define EXAMPLE_PCNT_LOW_LIMIT  -100

pcnt_unit_config_t unit_config = {
    .high_limit = EXAMPLE_PCNT_HIGH_LIMIT,
    .low_limit = EXAMPLE_PCNT_LOW_LIMIT,
};
pcnt_unit_handle_t pcnt_unit = NULL;
ESP_ERROR_CHECK(pcnt_new_unit(&unit_config, &pcnt_unit));

安装 PCNT 通道

安装 PCNT 通道时,需要先初始化 pcnt_chan_config_t,然后调用 pcnt_new_channel()。对 pcnt_chan_config_t 配置如下所示:

调用函数 pcnt_new_channel(),将 pcnt_chan_config_t 作为输入值并调用 pcnt_new_unit() 返回的 PCNT 单元句柄,可对 PCNT 通道进行分配和初始化。如果该函数正常运行,会返回一个 PCNT 通道句柄。如果没有可用的 PCNT 通道(PCNT 通道资源全部被占用),该函数会返回错误 ESP_ERR_NOT_FOUND。可用的 PCNT 通道总数记录在 SOC_PCNT_CHANNELS_PER_UNIT,以供参考。注意,为某个单元安装 PCNT 通道时,应确保该单元处于初始状态,否则函数 pcnt_new_channel() 会返回错误 ESP_ERR_INVALID_STATE

如果不再需要之前创建的某个 PCNT 通道,建议通过调用 pcnt_del_channel() 回收该通道,从而该通道可用于其他用途。

#define EXAMPLE_CHAN_GPIO_A 0
#define EXAMPLE_CHAN_GPIO_B 2

pcnt_chan_config_t chan_config = {
    .edge_gpio_num = EXAMPLE_CHAN_GPIO_A,
    .level_gpio_num = EXAMPLE_CHAN_GPIO_B,
};
pcnt_channel_handle_t pcnt_chan = NULL;
ESP_ERROR_CHECK(pcnt_new_channel(pcnt_unit, &chan_config, &pcnt_chan));

设置通道操作

当输入脉冲信号切换时,PCNT 通道会增加,减少或停止计数。边沿信号及电平信号可设置为不同的计数器操作。

// decrease the counter on rising edge, increase the counter on falling edge
ESP_ERROR_CHECK(pcnt_channel_set_edge_action(pcnt_chan, PCNT_CHANNEL_EDGE_ACTION_DECREASE, PCNT_CHANNEL_EDGE_ACTION_INCREASE));
// keep the counting mode when the control signal is high level, and reverse the counting mode when the control signal is low level
ESP_ERROR_CHECK(pcnt_channel_set_level_action(pcnt_chan, PCNT_CHANNEL_LEVEL_ACTION_KEEP, PCNT_CHANNEL_LEVEL_ACTION_INVERSE));

PCNT 观察点

PCNT 单元可被设置为观察几个特定的数值,这些被观察的数值被称为 观察点。观察点不能超过 pcnt_unit_config_t 设置的范围,最小值和最大值分别为 pcnt_unit_config_t::low_limitpcnt_unit_config_t::high_limit。当计数器到达任一观察点时,会触发一个观察事件,如果在 pcnt_unit_register_event_callbacks() 注册过事件回调函数,该事件就会通过中断发送通知。关于如何注册事件回调函数,请参考 注册事件回调函数

观察点分别可以通过 pcnt_unit_add_watch_point()pcnt_unit_remove_watch_point() 进行添加和删除。常用的观察点包括 过零最大/最小计数 以及其他的阈值。可用的观察点是有限的,如果 pcnt_unit_add_watch_point() 无法获得空闲硬件资源来存储观察点,会返回错误 ESP_ERR_NOT_FOUND。不能多次添加同一个观察点,否则将返回错误 ESP_ERR_INVALID_STATE

建议通过 pcnt_unit_remove_watch_point() 删除未使用的观察点来回收资源。

// add zero across watch point
ESP_ERROR_CHECK(pcnt_unit_add_watch_point(pcnt_unit, 0));
// add high limit watch point
ESP_ERROR_CHECK(pcnt_unit_add_watch_point(pcnt_unit, EXAMPLE_PCNT_HIGH_LIMIT));

注册事件回调函数

当 PCNT 单元的数值达到任一使能的观察点的数值时,会触发相应的事件并通过中断通知 CPU。如果要在事件触发时执行相关函数,可通过调用 pcnt_unit_register_event_callbacks() 将函数挂载到中断服务程序 (ISR) 上。pcnt_event_callbacks_t 列出了所有支持的事件回调函数:

  • pcnt_event_callbacks_t::on_reach 用于为观察点事件设置回调函数。由于该回调函数是在 ISR 的上下文中被调用的,必须确保该函数不会阻塞调用的任务,(例如,可确保只有以 ISR 为后缀的 FreeRTOS API 才能在函数中调用)。pcnt_watch_cb_t 中声明了该回调函数的原型。

可通过 user_ctx 将函数上下文保存到 pcnt_unit_register_event_callbacks() 中,这些数据会直接传递给回调函数。

驱动程序会将特定事件的数据写入回调函数中,例如,观察点事件数据被声明为 pcnt_watch_event_data_t

注册回调函数会导致中断服务延迟安装,因此回调函数只能在 PCNT 单元被 pcnt_unit_enable() 使能之前调用。否则,回调函数会返回错误 ESP_ERR_INVALID_STATE

static bool example_pcnt_on_reach(pcnt_unit_handle_t unit, const pcnt_watch_event_data_t *edata, void *user_ctx)
{
    BaseType_t high_task_wakeup;
    QueueHandle_t queue = (QueueHandle_t)user_ctx;
    // send watch point to queue, from this interrupt callback
    xQueueSendFromISR(queue, &(edata->watch_point_value), &high_task_wakeup);
    // return whether a high priority task has been waken up by this function
    return (high_task_wakeup == pdTRUE);
}

pcnt_event_callbacks_t cbs = {
    .on_reach = example_pcnt_on_reach,
};
QueueHandle_t queue = xQueueCreate(10, sizeof(int));
ESP_ERROR_CHECK(pcnt_unit_register_event_callbacks(pcnt_unit, &cbs, queue));

设置毛刺滤波器

PCNT 单元的滤波器可滤除信号中的短时毛刺,pcnt_glitch_filter_config_t 中列出了毛刺滤波器的配置参数:

  • pcnt_glitch_filter_config_t::max_glitch_ns 设置了最大的毛刺宽度,单位为纳秒。如果一个信号脉冲的宽度小于该数值,则该信号会被认定为噪声而不会触发计数器操作。

可通过调用 pcnt_unit_set_glitch_filter() 来使能毛刺滤波器,并对上述参数进行配置。之后,还可通过调用 pcnt_unit_set_glitch_filter() 来关闭毛刺滤波器,并将上述参数设置为 NULL

调用该函数时,PCNT 单元应处于初始状态。否则,函数将返回错误 ESP_ERR_INVALID_STATE

备注

毛刺滤波器的时钟信息来自 APB。为确保 PCNT 单元不会滤除脉冲信号,最大毛刺宽度应大于一个 APB_CLK 周期(如果 APB 的频率为 80 MHz,则最大毛刺宽度为 12.5 ns)。使能动态频率缩放 (DFS) 后,APB 的频率会发生变化,从而最大毛刺宽度也会发生变化,这会导致计数器无法正常工作。因此,驱动会为每个 PCNT 单元安装电源锁。关于 PCNT 驱动的电源管理的更多信息,请参考 电源管理

pcnt_glitch_filter_config_t filter_config = {
    .max_glitch_ns = 1000,
};
ESP_ERROR_CHECK(pcnt_unit_set_glitch_filter(pcnt_unit, &filter_config));

使用外部清零信号

PCNT 单元的可以接收来自 GPIO 的清零信号,pcnt_clear_signal_config_t 中列出了清零信号的配置参数:

该输入信号的作用与调用 pcnt_unit_clear_count() 函数相同,但它不受软件延迟的限制,更适用于需要低延迟的场合。请注意,该信号的翻转频率不能太高。

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 单元进行 IO 控制之前,需要通过调用函数 pcnt_unit_enable() 来使能该 PCNT 单元。该函数将完成以下操作:

  • 将 PCNT 单元的驱动状态从 初始 切换到 使能

  • 如果中断服务已经在 pcnt_unit_register_event_callbacks() 延迟安装,使能中断服务。

  • 如果电源管理锁已经安装,获取该电源管理锁。请参考 电源管理 获取更多信息。

调用函数 pcnt_unit_disable() 会进行相反的操作,即将 PCNT 单元的驱动状态切换回 初始 状态,禁用中断服务并释放电源管理锁。

控制单元 IO 操作

启用/停用及清零

通过调用 pcnt_unit_start() 可启用 PCNT 单元,根据不同脉冲信号进行递增或递减计数;通过调用 pcnt_unit_stop() 可停用 PCNT 单元,当前的计数值会保留;通过调用 pcnt_unit_clear_count() 可将计数器清零。

注意 pcnt_unit_start()pcnt_unit_stop() 应该在 PCNT 单元被 pcnt_unit_enable() 使能后调用,否则将返回错误 ESP_ERR_INVALID_STATE

获取计数器数值

调用 pcnt_unit_get_count() 可随时获取当前计数器的数值。返回的计数值是一个 带符号 的整型数,其符号反映了计数的方向。

int pulse_count = 0;
ESP_ERROR_CHECK(pcnt_unit_get_count(pcnt_unit, &pulse_count));

计数溢出补偿

PCNT 内部的硬件计数器会在计数达到高/低门限的时候自动清零。如果你想补偿该计数值的溢出损失,以期进一步拓宽计数器的实际位宽,你可以:

  1. 在安装 PCNT 计数单元的时候使能 pcnt_unit_config_t::accum_count 选项。

  2. 将高/低计数门限设置为 PCNT 观察点.

  3. 现在,pcnt_unit_get_count() 函数返回的计数值就会包含硬件计数器当前的计数值,累加上计数器溢出造成的损失。

备注

pcnt_unit_clear_count() 会复位该软件累加器。

电源管理

当电源管理使能(即 CONFIG_PM_ENABLE 开启)时,系统会在进入 Light-sleep 模式之前调整 APB 的频率,这可能导致 PCNT 毛刺滤波器将有效信号误认为噪声。

为了防止这种情况发生,驱动程序可以获取类型为 ESP_PM_APB_FREQ_MAX 的电源管理锁,以确保 APB 频率保持不变。该锁在通过 pcnt_unit_enable() 使能 PCNT 单元时获取,并在通过 pcnt_unit_disable() 禁用单元时释放。

支持 IRAM 安全中断

当缓存由于写入/擦除 flash 等原因被禁用时,PCNT 中断会默认被延迟。这会导致报警中断无法及时执行,从而无法满足实时性应用的要求。

Konfig 选项 CONFIG_PCNT_ISR_IRAM_SAFE 可以实现以下功能:

  1. 即使缓存被禁用也可以使能中断服务

  2. 将 ISR 使用的所有函数都放入 IRAM 中 2

  3. 将驱动对象放入 DRAM(防止驱动对象被意外映射到 PSRAM 中)

这样,在缓存被禁用时,中断也可运行,但是这也会增加 IRAM 的消耗。

另外一个 Konfig 选项 CONFIG_PCNT_CTRL_FUNC_IN_IRAM 也可以把常用的 IO 控制函数放在 IRAM 中。这样,当缓存禁用时,这些函数仍然可以执行。这些 IO 控制函数如下所示:

支持线程安全

驱动保证工厂函数 pcnt_new_unit()pcnt_new_channel() 是线程安全的,因此可以从 RTOS 任务中调用这些函数,而无需使用额外的电源管理锁。

以下函数可以在 ISR 上下文中运行,驱动可以防止这些函数在任务和 ISR 中同时被调用。

其他以 pcnt_unit_handle_tpcnt_channel_handle_t 作为第一个参数的函数被视为线程不安全函数,在多任务场景下应避免调用这些函数。

支持的 Kconfig 选项

应用实例

API 参考

Header File

  • components/esp_driver_pcnt/include/driver/pulse_cnt.h

  • This header file can be included with:

    #include "driver/pulse_cnt.h"
    
  • This header file is a part of the API provided by the esp_driver_pcnt component. To declare that your component depends on esp_driver_pcnt, add the following to your CMakeLists.txt:

    REQUIRES esp_driver_pcnt
    

    or

    PRIV_REQUIRES esp_driver_pcnt
    

Functions

esp_err_t pcnt_new_unit(const pcnt_unit_config_t *config, pcnt_unit_handle_t *ret_unit)

Create a new PCNT unit, and return the handle.

备注

The newly created PCNT unit is put in the init state.

参数
  • config -- [in] PCNT unit configuration

  • ret_unit -- [out] Returned PCNT unit handle

返回

  • ESP_OK: Create PCNT unit successfully

  • ESP_ERR_INVALID_ARG: Create PCNT unit failed because of invalid argument (e.g. high/low limit value out of the range)

  • ESP_ERR_NO_MEM: Create PCNT unit failed because out of memory

  • ESP_ERR_NOT_FOUND: Create PCNT unit failed because all PCNT units are used up and no more free one

  • ESP_FAIL: Create PCNT unit failed because of other error

esp_err_t pcnt_del_unit(pcnt_unit_handle_t unit)

Delete the PCNT unit handle.

备注

A PCNT unit can't be in the enable state when this function is invoked. See also pcnt_unit_disable() for how to disable a unit.

参数

unit -- [in] PCNT unit handle created by pcnt_new_unit()

返回

  • ESP_OK: Delete the PCNT unit successfully

  • ESP_ERR_INVALID_ARG: Delete the PCNT unit failed because of invalid argument

  • ESP_ERR_INVALID_STATE: Delete the PCNT unit failed because the unit is not in init state or some PCNT channel is still in working

  • ESP_FAIL: Delete the PCNT unit failed because of other error

esp_err_t pcnt_unit_set_glitch_filter(pcnt_unit_handle_t unit, const pcnt_glitch_filter_config_t *config)

Set glitch filter for PCNT unit.

备注

The glitch filter module is clocked from APB, and APB frequency can be changed during DFS, which in return make the filter out of action. So this function will lazy-install a PM lock internally when the power management is enabled. With this lock, the APB frequency won't be changed. The PM lock can be uninstalled in pcnt_del_unit().

备注

This function should be called when the PCNT unit is in the init state (i.e. before calling pcnt_unit_enable())

参数
  • unit -- [in] PCNT unit handle created by pcnt_new_unit()

  • config -- [in] PCNT filter configuration, set config to NULL means disabling the filter function

返回

  • ESP_OK: Set glitch filter successfully

  • ESP_ERR_INVALID_ARG: Set glitch filter failed because of invalid argument (e.g. glitch width is too big)

  • ESP_ERR_INVALID_STATE: Set glitch filter failed because the unit is not in the init state

  • ESP_FAIL: Set glitch filter 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)

Set clear signal for PCNT unit.

备注

The function of clear signal is the same as pcnt_unit_clear_count(). High-level Active

参数
  • unit -- [in] PCNT unit handle created by pcnt_new_unit()

  • config -- [in] PCNT clear signal configuration, set config to NULL means disabling the clear signal

返回

  • 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 repeatedly or disable clear signal before set it

  • ESP_FAIL: Set clear signal failed because of other error

esp_err_t pcnt_unit_enable(pcnt_unit_handle_t unit)

Enable the PCNT unit.

备注

This function will transit the unit state from init to enable.

备注

This function will enable the interrupt service, if it's lazy installed in pcnt_unit_register_event_callbacks().

备注

This function will acquire the PM lock if it's lazy installed in pcnt_unit_set_glitch_filter().

备注

Enable a PCNT unit doesn't mean to start it. See also pcnt_unit_start() for how to start the PCNT counter.

参数

unit -- [in] PCNT unit handle created by pcnt_new_unit()

返回

  • ESP_OK: Enable PCNT unit successfully

  • ESP_ERR_INVALID_ARG: Enable PCNT unit failed because of invalid argument

  • ESP_ERR_INVALID_STATE: Enable PCNT unit failed because the unit is already enabled

  • ESP_FAIL: Enable PCNT unit failed because of other error

esp_err_t pcnt_unit_disable(pcnt_unit_handle_t unit)

Disable the PCNT unit.

备注

This function will do the opposite work to the pcnt_unit_enable()

备注

Disable a PCNT unit doesn't mean to stop it. See also pcnt_unit_stop() for how to stop the PCNT counter.

参数

unit -- [in] PCNT unit handle created by pcnt_new_unit()

返回

  • ESP_OK: Disable PCNT unit successfully

  • ESP_ERR_INVALID_ARG: Disable PCNT unit failed because of invalid argument

  • ESP_ERR_INVALID_STATE: Disable PCNT unit failed because the unit is not enabled yet

  • ESP_FAIL: Disable PCNT unit failed because of other error

esp_err_t pcnt_unit_start(pcnt_unit_handle_t unit)

Start the PCNT unit, the counter will start to count according to the edge and/or level input signals.

备注

This function should be called when the unit is in the enable state (i.e. after calling pcnt_unit_enable())

备注

This function is allowed to run within ISR context

备注

This function will be placed into IRAM if CONFIG_PCNT_CTRL_FUNC_IN_IRAM is on, so that it's allowed to be executed when Cache is disabled

参数

unit -- [in] PCNT unit handle created by pcnt_new_unit()

返回

  • ESP_OK: Start PCNT unit successfully

  • ESP_ERR_INVALID_ARG: Start PCNT unit failed because of invalid argument

  • ESP_ERR_INVALID_STATE: Start PCNT unit failed because the unit is not enabled yet

  • ESP_FAIL: Start PCNT unit failed because of other error

esp_err_t pcnt_unit_stop(pcnt_unit_handle_t unit)

Stop PCNT from counting.

备注

This function should be called when the unit is in the enable state (i.e. after calling pcnt_unit_enable())

备注

The stop operation won't clear the counter. Also see pcnt_unit_clear_count() for how to clear pulse count value.

备注

This function is allowed to run within ISR context

备注

This function will be placed into IRAM if CONFIG_PCNT_CTRL_FUNC_IN_IRAM, so that it is allowed to be executed when Cache is disabled

参数

unit -- [in] PCNT unit handle created by pcnt_new_unit()

返回

  • ESP_OK: Stop PCNT unit successfully

  • ESP_ERR_INVALID_ARG: Stop PCNT unit failed because of invalid argument

  • ESP_ERR_INVALID_STATE: Stop PCNT unit failed because the unit is not enabled yet

  • ESP_FAIL: Stop PCNT unit failed because of other error

esp_err_t pcnt_unit_clear_count(pcnt_unit_handle_t unit)

Clear PCNT pulse count value to zero.

备注

It's recommended to call this function after adding a watch point by pcnt_unit_add_watch_point(), so that the newly added watch point is effective immediately.

备注

This function is allowed to run within ISR context

备注

This function will be placed into IRAM if CONFIG_PCNT_CTRL_FUNC_IN_IRAM, so that it's allowed to be executed when Cache is disabled

参数

unit -- [in] PCNT unit handle created by pcnt_new_unit()

返回

  • ESP_OK: Clear PCNT pulse count successfully

  • ESP_ERR_INVALID_ARG: Clear PCNT pulse count failed because of invalid argument

  • ESP_FAIL: Clear PCNT pulse count failed because of other error

esp_err_t pcnt_unit_get_count(pcnt_unit_handle_t unit, int *value)

Get PCNT count value.

备注

This function is allowed to run within ISR context

备注

This function will be placed into IRAM if CONFIG_PCNT_CTRL_FUNC_IN_IRAM, so that it's allowed to be executed when Cache is disabled

参数
  • unit -- [in] PCNT unit handle created by pcnt_new_unit()

  • value -- [out] Returned count value

返回

  • ESP_OK: Get PCNT pulse count successfully

  • ESP_ERR_INVALID_ARG: Get PCNT pulse count failed because of invalid argument

  • ESP_FAIL: Get PCNT pulse count failed because of other error

esp_err_t pcnt_unit_register_event_callbacks(pcnt_unit_handle_t unit, const pcnt_event_callbacks_t *cbs, void *user_data)

Set event callbacks for PCNT unit.

备注

User registered callbacks are expected to be runnable within ISR context

备注

The first call to this function needs to be before the call to pcnt_unit_enable

备注

User can deregister a previously registered callback by calling this function and setting the callback member in the cbs structure to NULL.

参数
  • unit -- [in] PCNT unit handle created by pcnt_new_unit()

  • cbs -- [in] Group of callback functions

  • user_data -- [in] User data, which will be passed to callback functions directly

返回

  • ESP_OK: Set event callbacks successfully

  • ESP_ERR_INVALID_ARG: Set event callbacks failed because of invalid argument

  • ESP_ERR_INVALID_STATE: Set event callbacks failed because the unit is not in init state

  • ESP_FAIL: Set event callbacks failed because of other error

esp_err_t pcnt_unit_add_watch_point(pcnt_unit_handle_t unit, int watch_point)

Add a watch point for PCNT unit, PCNT will generate an event when the counter value reaches the watch point value.

参数
  • unit -- [in] PCNT unit handle created by pcnt_new_unit()

  • watch_point -- [in] Value to be watched

返回

  • ESP_OK: Add watch point successfully

  • ESP_ERR_INVALID_ARG: Add watch point failed because of invalid argument (e.g. the value to be watched is out of the limitation set in pcnt_unit_config_t)

  • ESP_ERR_INVALID_STATE: Add watch point failed because the same watch point has already been added

  • ESP_ERR_NOT_FOUND: Add watch point failed because no more hardware watch point can be configured

  • ESP_FAIL: Add watch point failed because of other error

esp_err_t pcnt_unit_remove_watch_point(pcnt_unit_handle_t unit, int watch_point)

Remove a watch point for PCNT unit.

参数
  • unit -- [in] PCNT unit handle created by pcnt_new_unit()

  • watch_point -- [in] Watch point value

返回

  • ESP_OK: Remove watch point successfully

  • ESP_ERR_INVALID_ARG: Remove watch point failed because of invalid argument

  • ESP_ERR_INVALID_STATE: Remove watch point failed because the watch point was not added by pcnt_unit_add_watch_point() yet

  • ESP_FAIL: Remove watch point failed because of other error

esp_err_t pcnt_new_channel(pcnt_unit_handle_t unit, const pcnt_chan_config_t *config, pcnt_channel_handle_t *ret_chan)

Create PCNT channel for specific unit, each PCNT has several channels associated with it.

备注

This function should be called when the unit is in init state (i.e. before calling pcnt_unit_enable())

参数
  • unit -- [in] PCNT unit handle created by pcnt_new_unit()

  • config -- [in] PCNT channel configuration

  • ret_chan -- [out] Returned channel handle

返回

  • ESP_OK: Create PCNT channel successfully

  • ESP_ERR_INVALID_ARG: Create PCNT channel failed because of invalid argument

  • ESP_ERR_NO_MEM: Create PCNT channel failed because of insufficient memory

  • ESP_ERR_NOT_FOUND: Create PCNT channel failed because all PCNT channels are used up and no more free one

  • ESP_ERR_INVALID_STATE: Create PCNT channel failed because the unit is not in the init state

  • ESP_FAIL: Create PCNT channel failed because of other error

esp_err_t pcnt_del_channel(pcnt_channel_handle_t chan)

Delete the PCNT channel.

参数

chan -- [in] PCNT channel handle created by pcnt_new_channel()

返回

  • ESP_OK: Delete the PCNT channel successfully

  • ESP_ERR_INVALID_ARG: Delete the PCNT channel failed because of invalid argument

  • ESP_FAIL: Delete the PCNT channel failed because of other error

esp_err_t pcnt_channel_set_edge_action(pcnt_channel_handle_t chan, pcnt_channel_edge_action_t pos_act, pcnt_channel_edge_action_t neg_act)

Set channel actions when edge signal changes (e.g. falling or rising edge occurred). The edge signal is input from the edge_gpio_num configured in pcnt_chan_config_t. We use these actions to control when and how to change the counter value.

参数
  • chan -- [in] PCNT channel handle created by pcnt_new_channel()

  • pos_act -- [in] Action on posedge signal

  • neg_act -- [in] Action on negedge signal

返回

  • ESP_OK: Set edge action for PCNT channel successfully

  • ESP_ERR_INVALID_ARG: Set edge action for PCNT channel failed because of invalid argument

  • ESP_FAIL: Set edge action for PCNT channel failed because of other error

esp_err_t pcnt_channel_set_level_action(pcnt_channel_handle_t chan, pcnt_channel_level_action_t high_act, pcnt_channel_level_action_t low_act)

Set channel actions when level signal changes (e.g. signal level goes from high to low). The level signal is input from the level_gpio_num configured in pcnt_chan_config_t. We use these actions to control when and how to change the counting mode.

参数
  • chan -- [in] PCNT channel handle created by pcnt_new_channel()

  • high_act -- [in] Action on high level signal

  • low_act -- [in] Action on low level signal

返回

  • ESP_OK: Set level action for PCNT channel successfully

  • ESP_ERR_INVALID_ARG: Set level action for PCNT channel failed because of invalid argument

  • ESP_FAIL: Set level action for PCNT channel failed because of other error

Structures

struct pcnt_watch_event_data_t

PCNT watch event data.

Public Members

int watch_point_value

Watch point value that triggered the event

pcnt_unit_zero_cross_mode_t zero_cross_mode

Zero cross mode

struct pcnt_event_callbacks_t

Group of supported PCNT callbacks.

备注

The callbacks are all running under ISR environment

备注

When CONFIG_PCNT_ISR_IRAM_SAFE is enabled, the callback itself and functions callbed by it should be placed in IRAM.

Public Members

pcnt_watch_cb_t on_reach

Called when PCNT unit counter reaches any watch point

struct pcnt_unit_config_t

PCNT unit configuration.

Public Members

int low_limit

Low limitation of the count unit, should be lower than 0

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)

uint32_t accum_count

Whether to accumulate the count value when overflows at the high/low limit

struct pcnt_unit_config_t::[anonymous] flags

Extra flags

struct pcnt_chan_config_t

PCNT channel configuration.

Public Members

int edge_gpio_num

GPIO number used by the edge signal, input mode with pull up enabled. Set to -1 if unused

int level_gpio_num

GPIO number used by the level signal, input mode with pull up enabled. Set to -1 if unused

uint32_t invert_edge_input

Invert the input edge signal

uint32_t invert_level_input

Invert the input level signal

uint32_t virt_edge_io_level

Virtual edge IO level, 0: low, 1: high. Only valid when edge_gpio_num is set to -1

uint32_t virt_level_io_level

Virtual level IO level, 0: low, 1: high. Only valid when level_gpio_num is set to -1

uint32_t io_loop_back

For debug/test, the signal output from the GPIO will be fed to the input path as well

struct pcnt_chan_config_t::[anonymous] flags

Channel config flags

struct pcnt_glitch_filter_config_t

PCNT glitch filter configuration.

Public Members

uint32_t max_glitch_ns

Pulse width smaller than this threshold will be treated as glitch and ignored, in the unit of ns

struct pcnt_clear_signal_config_t

PCNT clear signal configuration.

Public Members

int clear_signal_gpio_num

GPIO number used by the clear signal, the default active level is high, input mode with pull down enabled

uint32_t invert_clear_signal

Invert the clear input signal and set input mode with pull up

uint32_t io_loop_back

For debug/test, the signal output from the GPIO will be fed to the input path as well

struct pcnt_clear_signal_config_t::[anonymous] flags

clear signal config flags

Type Definitions

typedef struct pcnt_unit_t *pcnt_unit_handle_t

Type of PCNT unit handle.

typedef struct pcnt_chan_t *pcnt_channel_handle_t

Type of PCNT channel handle.

typedef bool (*pcnt_watch_cb_t)(pcnt_unit_handle_t unit, const pcnt_watch_event_data_t *edata, void *user_ctx)

PCNT watch event callback prototype.

备注

The callback function is invoked from an ISR context, so it should meet the restrictions of not calling any blocking APIs when implementing the callback. e.g. must use ISR version of FreeRTOS APIs.

Param unit

[in] PCNT unit handle

Param edata

[in] PCNT event data, fed by the driver

Param user_ctx

[in] User data, passed from pcnt_unit_register_event_callbacks()

Return

Whether a high priority task has been woken up by this function

Header File

Enumerations

enum pcnt_channel_level_action_t

PCNT channel action on control level.

Values:

enumerator PCNT_CHANNEL_LEVEL_ACTION_KEEP

Keep current count mode

enumerator PCNT_CHANNEL_LEVEL_ACTION_INVERSE

Invert current count mode (increase -> decrease, decrease -> increase)

enumerator PCNT_CHANNEL_LEVEL_ACTION_HOLD

Hold current count value

enum pcnt_channel_edge_action_t

PCNT channel action on signal edge.

Values:

enumerator PCNT_CHANNEL_EDGE_ACTION_HOLD

Hold current count value

enumerator PCNT_CHANNEL_EDGE_ACTION_INCREASE

Increase count value

enumerator PCNT_CHANNEL_EDGE_ACTION_DECREASE

Decrease count value

enum pcnt_unit_zero_cross_mode_t

PCNT unit zero cross mode.

Values:

enumerator PCNT_UNIT_ZERO_CROSS_POS_ZERO

start from positive value, end to zero, i.e. +N->0

enumerator PCNT_UNIT_ZERO_CROSS_NEG_ZERO

start from negative value, end to zero, i.e. -N->0

enumerator PCNT_UNIT_ZERO_CROSS_NEG_POS

start from negative value, end to positive value, i.e. -N->+M

enumerator PCNT_UNIT_ZERO_CROSS_POS_NEG

start from positive value, end to negative value, i.e. +N->-M

1

在不同的 ESP 芯片系列中,PCNT 单元和通道的数量可能会有差异,具体信息请参考 [TRM]。驱动程序对通道申请数量不做限制,但当硬件资源用尽时,驱动程序将返回错误。因此分配资源时,应注意检查返回值,如 pcnt_new_unit()

2

pcnt_event_callbacks_t::on_reach 回调函数和其调用的函数也应该放在 IRAM 中。