Skip to content

Commit

Permalink
fix(pcnt): install the pm lock upon driver installation
Browse files Browse the repository at this point in the history
  • Loading branch information
suda-morris committed Oct 16, 2024
1 parent 5d3399f commit 33f8207
Show file tree
Hide file tree
Showing 3 changed files with 16 additions and 17 deletions.
17 changes: 8 additions & 9 deletions components/esp_driver_pcnt/src/pulse_cnt.c
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,14 @@ esp_err_t pcnt_new_unit(const pcnt_unit_config_t *config, pcnt_unit_handle_t *re
TAG, "install interrupt service failed");
}

// PCNT uses the APB as its function clock,
// and its filter module is sensitive to the clock frequency
#if CONFIG_PM_ENABLE
sprintf(unit->pm_lock_name, "pcnt_%d_%d", group_id, unit_id); // e.g. pcnt_0_0
ESP_GOTO_ON_ERROR(esp_pm_lock_create(ESP_PM_APB_FREQ_MAX, 0, unit->pm_lock_name, &unit->pm_lock), err, TAG, "install pm lock failed");
ESP_LOGD(TAG, "install APB_FREQ_MAX lock for unit (%d,%d)", group_id, unit_id);
#endif

// some events are enabled by default, disable them all
pcnt_ll_disable_all_events(group->hal.dev, unit_id);
// disable filter by default
Expand Down Expand Up @@ -340,15 +348,6 @@ esp_err_t pcnt_unit_set_glitch_filter(pcnt_unit_handle_t unit, const pcnt_glitch
if (config) {
glitch_filter_thres = esp_clk_apb_freq() / 1000000 * config->max_glitch_ns / 1000;
ESP_RETURN_ON_FALSE(glitch_filter_thres <= PCNT_LL_MAX_GLITCH_WIDTH, ESP_ERR_INVALID_ARG, TAG, "glitch width out of range");

// The filter module is working against APB clock, so lazy install PM lock
#if CONFIG_PM_ENABLE
if (!unit->pm_lock) {
sprintf(unit->pm_lock_name, "pcnt_%d_%d", group->group_id, unit->unit_id); // e.g. pcnt_0_0
ESP_RETURN_ON_ERROR(esp_pm_lock_create(ESP_PM_APB_FREQ_MAX, 0, unit->pm_lock_name, &unit->pm_lock), TAG, "install pm lock failed");
ESP_LOGD(TAG, "install APB_FREQ_MAX lock for unit (%d,%d)", group->group_id, unit->unit_id);
}
#endif
}

// filter control bit is mixed with other PCNT control bits in the same register
Expand Down
8 changes: 4 additions & 4 deletions docs/en/api-reference/peripherals/pcnt.rst
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ This function should be called when the unit is in the init state. Otherwise, it

.. note::

The glitch filter is clocked from APB. For the counter not to miss any pulses, the maximum glitch width should be longer than one APB_CLK cycle (usually 12.5 ns if APB equals 80 MHz). As the APB frequency would be changed after DFS (Dynamic Frequency Scaling) enabled, which means the filter does not work as expect in that case. So the driver installs a PM lock for PCNT unit during the first time you enable the glitch filter. For more information related to power management strategy used in PCNT driver, please see :ref:`pcnt-power-management`.
The glitch filter operates using the APB clock. To ensure the counter does not miss any pulses, the maximum glitch width should be longer than one APB_CLK cycle (typically 12.5 ns if APB is 80 MHz). Since the APB frequency can change with Dynamic Frequency Scaling (DFS), the filter may not function as expected in such cases. Therefore, the driver installs a power management lock for each PCNT unit. For more details on the power management strategy used in the PCNT driver, please refer to :ref:`pcnt-power-management`.

.. code:: c
Expand Down Expand Up @@ -236,7 +236,7 @@ Before doing IO control to the PCNT unit, you need to enable it first, by callin

* switches the PCNT driver state from **init** to **enable**.
* enables the interrupt service if it has been lazy installed in :cpp:func:`pcnt_unit_register_event_callbacks`.
* acquires a proper power management lock if it has been lazy installed in :cpp:func:`pcnt_unit_set_glitch_filter`. See also :ref:`pcnt-power-management` for more information.
* acquires a proper power management lock if it has been installed. See also :ref:`pcnt-power-management` for more information.

On the contrary, calling :cpp:func:`pcnt_unit_disable` will do the opposite, that is, put the PCNT driver back to the **init** state, disable the interrupts service and release the power management lock.

Expand Down Expand Up @@ -291,9 +291,9 @@ The internal hardware counter will be cleared to zero automatically when it reac
Power Management
^^^^^^^^^^^^^^^^

When power management is enabled (i.e., :ref:`CONFIG_PM_ENABLE` is on), the system will adjust the APB frequency before going into light sleep, thus potentially changing the behavior of PCNT glitch filter and leading to valid signal being treated as noise.
When power management is enabled (i.e., :ref:`CONFIG_PM_ENABLE` is on), the system adjusts the APB frequency before entering light sleep, which can cause the PCNT glitch filter to misinterpret valid signals as noise.

However, the driver can prevent the system from changing APB frequency by acquiring a power management lock of type :cpp:enumerator:`ESP_PM_APB_FREQ_MAX`. Whenever you enable the glitch filter by :cpp:func:`pcnt_unit_set_glitch_filter`, the driver guarantees that the power management lock is acquired after the PCNT unit is enabled by :cpp:func:`pcnt_unit_enable`. Likewise, the driver releases the lock after :cpp:func:`pcnt_unit_disable` is called.
To prevent this, the driver can acquire a power management lock of type :cpp:enumerator:`ESP_PM_APB_FREQ_MAX`, ensuring the APB frequency remains constant. This lock is acquired when the PCNT unit is enabled via :cpp:func:`pcnt_unit_enable` and released when the unit is disabled via :cpp:func:`pcnt_unit_disable`.

.. _pcnt-iram-safe:

Expand Down
8 changes: 4 additions & 4 deletions docs/zh_CN/api-reference/peripherals/pcnt.rst
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ PCNT 单元的滤波器可滤除信号中的短时毛刺,:cpp:type:`pcnt_glitc

.. note::

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

.. code:: c
Expand Down Expand Up @@ -236,7 +236,7 @@ PCNT 单元的滤波器可滤除信号中的短时毛刺,:cpp:type:`pcnt_glitc

* 将 PCNT 单元的驱动状态从 **初始** 切换到 **使能** 。
* 如果中断服务已经在 :cpp:func:`pcnt_unit_register_event_callbacks` 延迟安装,使能中断服务。
* 如果电源管理锁已经在 :cpp:func:`pcnt_unit_set_glitch_filter` 延迟安装,获取该电源管理锁。请参考 :ref:`pcnt-power-management` 获取更多信息。
* 如果电源管理锁已经安装,获取该电源管理锁。请参考 :ref:`pcnt-power-management` 获取更多信息。

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

Expand Down Expand Up @@ -291,9 +291,9 @@ PCNT 内部的硬件计数器会在计数达到高/低门限的时候自动清
电源管理
^^^^^^^^^^

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

驱动通过获取 :cpp:enumerator:`ESP_PM_APB_FREQ_MAX` 类型的电源管理锁来防止系统修改 APB 频率。每当通过 :cpp:func:`pcnt_unit_set_glitch_filter` 使能毛刺滤波器时,驱动可以保证系统在 :cpp:func:`pcnt_unit_enable` 使能 PCNT 单元后获取电源管理锁。而系统调用 :cpp:func:`pcnt_unit_disable` 之后,驱动会释放电源管理锁
为了防止这种情况发生,驱动程序可以获取类型为 :cpp:enumerator:`ESP_PM_APB_FREQ_MAX` 的电源管理锁,以确保 APB 频率保持不变。该锁在通过 :cpp:func:`pcnt_unit_enable` 使能 PCNT 单元时获取,并在通过 :cpp:func:`pcnt_unit_disable` 禁用单元时释放

.. _pcnt-iram-safe:

Expand Down

0 comments on commit 33f8207

Please sign in to comment.