Merge branch 'test/pcnt_step_notify' into 'master'

feat(pcnt): support for pcnt step_notify

Closes IDF-7984, IDFGH-8104, and IDFGH-10943

See merge request espressif/esp-idf!31466
This commit is contained in:
morris
2024-07-01 10:44:19 +08:00
9 changed files with 365 additions and 34 deletions
+45 -8
View File
@@ -25,6 +25,7 @@ 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).
:SOC_PCNT_SUPPORT_STEP_NOTIFY: - :ref:`pcnt-step-notify` - describes how to configure PCNT watch step (i.e., tell PCNT unit to trigger an event when the count increment 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 external clear signal.
@@ -47,9 +48,13 @@ Install PCNT Unit
To install a PCNT unit, there is a configuration structure that needs to be given in advance: :cpp:type:`pcnt_unit_config_t`:
- :cpp:member:`pcnt_unit_config_t::low_limit` and :cpp:member:`pcnt_unit_config_t::high_limit` specify the range for the internal hardware counter. The counter will reset to zero automatically when it crosses either the high or low limit.
- :cpp:member:`pcnt_unit_config_t::accum_count` sets whether to create an internal accumulator for the counter. This is helpful when you want to extend the counter's width, which by default is 16 bit at most, defined in the hardware. See also :ref:`pcnt-compensate-overflow-loss` for how to use this feature to compensate the overflow loss.
- :cpp:member:`pcnt_unit_config_t::intr_priority` sets the priority of the interrupt. If it is set to ``0``, the driver will allocate an interrupt with a default priority. Otherwise, the driver will use the given priority.
.. list::
- :cpp:member:`pcnt_unit_config_t::low_limit` and :cpp:member:`pcnt_unit_config_t::high_limit` specify the range for the internal hardware counter. The counter will reset to zero automatically when it crosses either the high or low limit.
- :cpp:member:`pcnt_unit_config_t::accum_count` sets whether to create an internal accumulator for the counter. This is helpful when you want to extend the counter's width, which by default is 16 bit at most, defined in the hardware. See also :ref:`pcnt-compensate-overflow-loss` for how to use this feature to compensate the overflow loss.
:SOC_PCNT_SUPPORT_STEP_NOTIFY: - :cpp:member:`pcnt_unit_config_t::en_step_notify_up` Configure whether to enable watch step to count in the positive direction.
:SOC_PCNT_SUPPORT_STEP_NOTIFY: - :cpp:member:`pcnt_unit_config_t::en_step_notify_down` Configure whether to enable watch step to count in the negative direction.
- :cpp:member:`pcnt_unit_config_t::intr_priority` sets the priority of the interrupt. If it is set to ``0``, the driver will allocate an interrupt with a default priority. Otherwise, the driver will use the given priority.
.. note::
@@ -141,7 +146,36 @@ It is recommended to remove the unused watch point by :cpp:func:`pcnt_unit_remov
Due to the hardware limitation, after adding a watch point, you should call :cpp:func:`pcnt_unit_clear_count` to make it take effect.
.. _pcnt-register-event-callbacks:
.. only:: SOC_PCNT_SUPPORT_STEP_NOTIFY
.. _pcnt-step-notify:
Watch Step
^^^^^^^^^^^
PCNT unit can be configured to watch a specific value increment (can be positive or negative) that you are interested in. The function of watching value increment is also called **Watch Step**. To install watch step requires enabling :cpp:member:`pcnt_unit_config_t::en_step_notify_up` or :cpp:member:`pcnt_unit_config_t::en_step_notify_down`. The step interval itself can not exceed the range set in :cpp:type:`pcnt_unit_config_t` by :cpp:member:`pcnt_unit_config_t::low_limit` and :cpp:member:`pcnt_unit_config_t::high_limit`.When the counter increment reaches step interval, a watch event will be triggered and notify you by interrupt if any watch event callback has ever registered in :cpp:func:`pcnt_unit_register_event_callbacks`. See :ref:`pcnt-register-event-callbacks` for how to register event callbacks.
The watch step can be added and removed by :cpp:func:`pcnt_unit_add_watch_step` and :cpp:func:`pcnt_unit_remove_watch_step`. You can not add multiple watch step, otherwise it will return error :c:macro:`ESP_ERR_INVALID_STATE`
It is recommended to remove the unused watch step by :cpp:func:`pcnt_unit_remove_watch_step` to recycle the watch step resources.
.. note::
When a watch step and a watch point are triggered at the same time (i.e. at the same absolute point), the callback function only gets called by once.
The step interval must be a divisor of :cpp:member:`pcnt_unit_config_t::low_limit` or :cpp:member:`pcnt_unit_config_t::high_limit`.
.. code:: c
// add positive direction watch step with 100 step intervals
ESP_ERROR_CHECK(pcnt_unit_add_watch_step(pcnt_unit, 100));
.. _pcnt-register-event-callbacks:
.. only:: not SOC_PCNT_SUPPORT_STEP_NOTIFY
.. _pcnt-register-event-callbacks:
Register Event Callbacks
^^^^^^^^^^^^^^^^^^^^^^^^
@@ -152,10 +186,10 @@ When PCNT unit reaches any enabled watch point, specific event will be generated
You can save their own context to :cpp:func:`pcnt_unit_register_event_callbacks` as well, via the parameter ``user_ctx``. This user data will be directly passed to the callback functions.
In the callback function, the driver will fill in the event data of specific event. For example, the watch point event data is declared as :cpp:type:`pcnt_watch_event_data_t`:
In the callback function, the driver will fill in the event data of specific event. For example, the watch point event or watch step event data is declared as :cpp:type:`pcnt_watch_event_data_t`:
- :cpp:member:`pcnt_watch_event_data_t::watch_point_value` saves the watch point value that triggers the event.
- :cpp:member:`pcnt_watch_event_data_t::zero_cross_mode` saves how the PCNT unit crosses the zero point in the latest time. The possible zero cross modes are listed in the :cpp:type:`pcnt_unit_zero_cross_mode_t`. Usually different zero cross mode means different **counting direction** and **counting step size**.
- :cpp:member:`pcnt_watch_event_data_t::watch_point_value` saves the count value when the event triggered.
- :cpp:member:`pcnt_watch_event_data_t::zero_cross_mode` saves how the PCNT unit crosses the zero point in the latest time. The possible zero cross modes are listed in the :cpp:type:`pcnt_unit_zero_cross_mode_t`. Usually different zero cross mode means different **counting direction** and **counting step size**.
Registering callback function results in lazy installation of interrupt service, thus this function should only be called before the unit is enabled by :cpp:func:`pcnt_unit_enable`. Otherwise, it can return :c:macro:`ESP_ERR_INVALID_STATE` error.
@@ -278,8 +312,11 @@ Compensate Overflow Loss
The internal hardware counter will be cleared to zero automatically when it reaches high or low limit. If you want to compensate for that count loss and extend the counter's bit-width, you can:
.. list::
1. Enable :cpp:member:`pcnt_unit_config_t::accum_count` when installing the PCNT unit.
2. Add the high/low limit as the :ref:`pcnt-watch-points`.
:SOC_PCNT_SUPPORT_STEP_NOTIFY: 2. Add the high/low limit as the :ref:`pcnt-watch-points` or add watch step as the :ref:`pcnt-step-notify`.
:not SOC_PCNT_SUPPORT_STEP_NOTIFY: 2. Add the high/low limit as the :ref:`pcnt-watch-points`.
3. Now, the returned count value from the :cpp:func:`pcnt_unit_get_count` function not only reflects the hardware's count value, but also accumulates the high/low overflow loss to it.
.. note::