doc: API reference for GPTimer

This commit is contained in:
morris 2022-01-02 16:14:57 +08:00
parent 7000f5d85a
commit e36f47a153
11 changed files with 317 additions and 228 deletions

View File

@ -106,7 +106,7 @@ INPUT = \
$(PROJECT_PATH)/components/driver/include/driver/spi_slave.h \
$(PROJECT_PATH)/components/driver/include/driver/spi_slave_hd.h \
$(PROJECT_PATH)/components/hal/include/hal/timer_types.h \
$(PROJECT_PATH)/components/driver/include/driver/timer.h \
$(PROJECT_PATH)/components/driver/include/driver/gptimer.h \
$(PROJECT_PATH)/components/hal/include/hal/touch_sensor_types.h \
$(PROJECT_PATH)/components/driver/include/driver/touch_sensor_common.h \
$(PROJECT_PATH)/components/hal/include/hal/twai_types.h \

View File

@ -0,0 +1,278 @@
General Purpose Timer (GPTimer)
===============================
Introduction
------------
A general purpose timer is a hardware timer with high resolution and flexible alarm action. The behavior when the internal counter of a timer reaches a specific target value is called timer alarm. When a timer alarms, a user registered per-timer callback would be called.
Typically, a general purpose timer can be used in scenarios like:
- Free running as a wall clock, fetching a high-resolution time stamp at any time and any places
- Generate period alarms, trigger events periodically
- Generate one-shot alarm, respond in target time
Functional Overview
-------------------
The following sections of this document cover the typical steps to install and operate a timer:
- `Resource Allocation <#resource-allocation>`__ - covers which parameters should be set up to get a timer handle and how to recycle the resources when GPTimer finishes working.
- `Set and Get count value <#set-and-get-count-value>`__ - covers how to force the timer counting from a start point and how to get the count value at anytime.
- `Start and Stop timer <#start-and-stop-timer>`__ - covers which parameters should be set up to start the timer with specific alarm event behavior.
- `Power Management <#power-management>`__ - describes how different source clock selections can affect power consumption.
- `IRAM safe <#iram-safe>`__ - describes tips on how to make the timer interrupt and IO control functions work better along with a disabled cache.
Resource Allocation
^^^^^^^^^^^^^^^^^^^
Different ESP chip might have different number of independent timer groups, and within each group, there could also be several independent timers. Refer to the datasheet to find out how many hardware timers exist (usually described in the "General Purpose Timer" chapter).
From driver's point of view, a GPTimer instance is represented by :cpp:type:`gptimer_handle_t`. The driver behind will manage all available hardware resources in a pool, so that users don't need to care about which timer and which group it belongs to.
To install a timer instance, there's a configuration structure that needs to be given in advance: :cpp:type:`gptimer_config_t`:
- :cpp:member:`clk_src` selects the source clock for the timer. The available clocks are listed in :cpp:type:`gptimer_clock_source_t`, [1]_ you can only pick one of them. For the effect on power consumption of different clock source, please refer to `Power management <#power-management>`__ section.
- :cpp:member:`direction` sets the counting direction of the timer, supported directions are listed in :cpp:type:`gptimer_count_direction_t`, you can only pick one of them.
- :cpp:member:`resolution_hz` sets the resolution of the internal counter. Each count step is equivalent to **1 / resolution_hz** seconds.
- Optional :cpp:member:`intr_shared` sets whether or not mark the timer interrupt source as a shared one. For the pros/cons of a shared interrupt, you can refer to :doc:`Interrupt Handling <../../api-reference/system/intr_alloc>`.
With all the above configurations set in the structure, the structure can be passed to :cpp:func:`gptimer_new_timer` which will instantiate the timer instance and return a handle of the timer.
The function can fail due to various errors such as insufficient memory, invalid arguments, etc. Specifically, when there are no more free timers (i.e. all hardware resources have been used up), then :cpp:member:`ESP_ERR_NOT_FOUND` will be returned. The total number of available timers is represented by the :c:macro:`SOC_TIMER_GROUP_TOTAL_TIMERS` and its value will depend on the ESP chip.
If a previously created GPTimer instance is no longer required, you should recycle the timer by calling :cpp:func:`gptimer_del_timer`. This will allow the underlying HW timer to be used for other purposes. Before deleting a GPTimer handle, you should stop it by :cpp:func:`gptimer_stop` in advance or make sure it has not started yet by :cpp:func:`gptimer_start`.
Creating a GPTimer Handle with Resolution of 1MHz
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. code:: c
gptimer_handle_t gptimer = NULL;
gptimer_config_t timer_config = {
.clk_src = GPTIMER_CLK_SRC_APB,
.direction = GPTIMER_COUNT_UP,
.resolution_hz = 1 * 1000 * 1000, // 1MHz, 1 tick = 1us
};
ESP_ERROR_CHECK(gptimer_new_timer(&timer_config, &gptimer));
Set and Get Count Value
^^^^^^^^^^^^^^^^^^^^^^^
When the GPTimer is created, the internal counter will be reset to zero by default. The counter value can be updated asynchronously by :cpp:func:`gptimer_set_raw_count`. The maximum count value is dependent on the hardware timer's bit-width, which is also reflected by the SOC macro :c:macro:`SOC_TIMER_GROUP_COUNTER_BIT_WIDTH`. When updating the raw count of an active timer, the timer will immediately start counting from the new value.
Count value can be retrieved by :cpp:func:`gptimer_get_raw_count`, at anytime.
Set Up Alarm Action
^^^^^^^^^^^^^^^^^^^
Most of the use cases of GPTimer should set up the alarm action before starting the timer, except for the simple wall-clock scenario, where a free running timer is enough. To set up the alarm action, one should configure several members of :cpp:type:`gptimer_alarm_config_t` based on how he takes use of the alarm event:
- :cpp:member:`alarm_count` sets the target count value that will trigger the alarm event. You should also take the counting direction into consideration when setting the alarm value.
Specially, :cpp:member:`alarm_count` and :cpp:member:`reload_count` can't be set to the same value when :cpp:member:`auto_reload_on_alarm` is true, as keeping reload with a target alarm count is meaningless.
- :cpp:member:`reload_count` sets the count value to be reloaded when the alarm event happens. This configuration only takes effect when :cpp:member:`auto_reload_on_alarm` is set to true.
- :cpp:member:`auto_reload_on_alarm` flag sets whether to enable the auto-reload feature. If enabled, the hardware timer will reload the value of :cpp:member:`reload_count` into counter immediately when alarm event happens.
To make the alarm configurations take effect, one should call :cpp:func:`gptimer_set_alarm_action`. Especially, if :cpp:type:`gptimer_alarm_config_t` is set to ``NULL``, the alarm function will be disabled.
.. note::
* If an alarm value is set and the timer has already crossed this value, the alarm will be triggered immediately.
Register Event Callbacks
^^^^^^^^^^^^^^^^^^^^^^^^
After the timer starts up, it can generate specific event (e.g. the "Alarm Event") dynamically. If you have some function that should be called when event happens, you should hook your function to the interrupt service routine by calling :cpp:func:`gptimer_register_event_callbacks`. All supported event callbacks are listed in the :cpp:type:`gptimer_event_callbacks_t`:
- :cpp:member:`on_alarm` sets callback function for alarm event. As this function is called within the ISR context, user must ensure that the function doesn't attempt to block (e.g., by making sure that only FreeRTOS APIs with ``ISR`` suffix are called from within the function). The function prototype is declared in :cpp:type:`gptimer_alarm_cb_t`.
One can save his own context to :cpp:func:`gptimer_register_event_callbacks` as well, via the parameter ``user_data``. The user data will be directly passed to the callback functions.
Start and Stop Timer
^^^^^^^^^^^^^^^^^^^^
To start a timer means to enable its internal counter, it can only be achieved by calling :cpp:func:`gptimer_start`. The timer can be stopped at any time (even in the interrupt context) by :cpp:func:`gptimer_stop`. One thing should be kept in mind, calling of :cpp:func:`gptimer_start` should have the same times of calling :cpp:func:`gptimer_stop` before you delete the timer, otherwise the driver might be put in an undetermined state. For example, the timer might keep a Power Management lock, which in return increase the power consumption. Also see `Power management <#power-management>`__ section.
Start Timer As a Wall Clock
~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. code:: c
ESP_ERROR_CHECK(gptimer_start(gptimer));
// Retrieve timestamp at anytime
uint64_t count;
ESP_ERROR_CHECK(gptimer_get_raw_count(gptimer, &count));
Trigger Period Events
~~~~~~~~~~~~~~~~~~~~~
.. code:: c
typedef struct {
uint64_t event_count;
} example_queue_element_t;
static bool example_timer_on_alarm_cb(gptimer_handle_t timer, const gptimer_alarm_event_data_t *edata, void *user_ctx)
{
BaseType_t high_task_awoken = pdFALSE;
QueueHandle_t queue = (QueueHandle_t)user_ctx;
// Retrieve count value from event data
example_queue_element_t ele = {
.event_count = edata->count_value
};
// Optional: send the event data to other task by OS queue
// Don't introduce complex logics in callbacks.
// Suggest dealing with event data in the main loop, instead of in this callback.
xQueueSendFromISR(queue, &ele, &high_task_awoken);
// return whether we need to yield at the end of ISR
return high_task_awoken == pdTRUE;
}
gptimer_alarm_config_t alarm_config = {
.reload_count = 0, // counter will reload with 0 on alarm event
.alarm_count = 1000000, // period = 1s @resolution 1MHz
.flags.auto_reload_on_alarm = true, // enable auto-reload
};
ESP_ERROR_CHECK(gptimer_set_alarm_action(gptimer, &alarm_config));
gptimer_event_callbacks_t cbs = {
.on_alarm = example_timer_on_alarm_cb, // register user callback
};
ESP_ERROR_CHECK(gptimer_register_event_callbacks(gptimer, &cbs, queue));
ESP_ERROR_CHECK(gptimer_start(gptimer));
Trigger One-Shot Event
~~~~~~~~~~~~~~~~~~~~~~
.. code:: c
typedef struct {
uint64_t event_count;
} example_queue_element_t;
static bool example_timer_on_alarm_cb(gptimer_handle_t timer, const gptimer_alarm_event_data_t *edata, void *user_ctx)
{
BaseType_t high_task_awoken = pdFALSE;
QueueHandle_t queue = (QueueHandle_t)user_ctx;
// Stop timer the sooner the better
gptimer_stop(timer);
// Retrieve count value from event data
example_queue_element_t ele = {
.event_count = edata->count_value
};
// Optional: send the event data to other task by OS queue
xQueueSendFromISR(queue, &ele, &high_task_awoken);
// return whether we need to yield at the end of ISR
return high_task_awoken == pdTRUE;
}
gptimer_alarm_config_t alarm_config = {
.alarm_count = 1 * 1000 * 1000, // alarm target = 1s @resolution 1MHz
};
ESP_ERROR_CHECK(gptimer_set_alarm_action(gptimer, &alarm_config));
gptimer_event_callbacks_t cbs = {
.on_alarm = example_timer_on_alarm_cb, // register user callback
};
ESP_ERROR_CHECK(gptimer_register_event_callbacks(gptimer, &cbs, queue));
ESP_ERROR_CHECK(gptimer_start(gptimer));
Dynamic Alarm Update
~~~~~~~~~~~~~~~~~~~~
Alarm value can be updated dynamically inside the ISR handler callback, by changing the :cpp:member:`alarm_value` of :cpp:type:`gptimer_alarm_event_data_t`. Then the alarm value will be updated after the callback function returns.
.. code:: c
typedef struct {
uint64_t event_count;
} example_queue_element_t;
static bool example_timer_on_alarm_cb(gptimer_handle_t timer, const gptimer_alarm_event_data_t *edata, void *user_ctx)
{
BaseType_t high_task_awoken = pdFALSE;
QueueHandle_t queue = (QueueHandle_t)user_data;
// Retrieve count value from event data
example_queue_element_t ele = {
.event_count = edata->count_value
};
// Optional: send the event data to other task by OS queue
xQueueSendFromISR(queue, &ele, &high_task_awoken);
// reconfigure alarm value
gptimer_alarm_config_t alarm_config = {
.alarm_count = edata->alarm_value + 1000000, // alarm in next 1s
};
gptimer_set_alarm_action(timer, &alarm_config);
// return whether we need to yield at the end of ISR
return high_task_awoken == pdTRUE;
}
gptimer_alarm_config_t alarm_config = {
.alarm_count = 1000000, // initial alarm target = 1s @resolution 1MHz
};
ESP_ERROR_CHECK(gptimer_set_alarm_action(gptimer, &alarm_config));
gptimer_event_callbacks_t cbs = {
.on_alarm = example_timer_on_alarm_cb, // register user callback
};
ESP_ERROR_CHECK(gptimer_register_event_callbacks(gptimer, &cbs, queue));
ESP_ERROR_CHECK(gptimer_start(gptimer, &alarm_config));
Power Management
^^^^^^^^^^^^^^^^
When power management is enabled (i.e. ``CONFIG_PM_ENABLE`` is on), the system will adjust the APB frequency before going into light sleep, thus potentially changing the period of a GPTimer's counting step and leading to inaccurate time keeping.
However, the driver can prevent the system from changing APB frequency by acquiring a power management lock of type :c:member:`ESP_PM_APB_FREQ_MAX`. Whenever the driver creates a GPTimer instance that has selected :c:member:`GPTIMER_CLK_SRC_APB` as its clock source, the driver will guarantee that the power management lock is acquired when the timer is started by :cpp:func:`gptimer_start`. Likewise, the driver releases the lock when :cpp:func:`gptimer_stop` is called for that timer. This requires that the :cpp:func:`gptimer_start` and :cpp:func:`gptimer_stop` should appear in pairs.
IRAM Safe
^^^^^^^^^
By default, the GPTimer interrupt will be deferred when the Cache is disabled for reasons like writing/erasing Flash. Thus the alarm interrupt will not get executed in time, which is not expected in a real-time application.
There's a Kconfig option :ref:`CONFIG_GPTIMER_ISR_IRAM_SAFE` that will:
1. Enable the interrupt being serviced even when cache is disabled
2. Place all functions that used by the ISR into IRAM [2]_
3. Place driver object into DRAM (in case it's linked to PSRAM by accident)
This will allow the interrupt to run while the cache is disabled but will come at the cost of increased IRAM consumption.
There's another Kconfig option :ref:`CONFIG_GPTIMER_CTRL_FUNC_IN_IRAM` that can put commonly used IO control functions into IRAM as well. So that these functions can also be executable when the cache is disabled. These IO control functions are as follows:
- :cpp:func:`gptimer_start`
- :cpp:func:`gptimer_stop`
- :cpp:func:`gptimer_get_raw_count`
- :cpp:func:`gptimer_set_raw_count`
- :cpp:func:`gptimer_set_alarm_action`
Application Examples
--------------------
Typical use cases of GPTimer are listed in the example: :example:`peripherals/timer_group/gptimer`.
API Reference
-------------
.. include-build-file:: inc/gptimer.inc
.. include-build-file:: inc/timer_types.inc
.. [1]
Some ESP chip might only support a sub-set of the clocks, if an unsupported clock source is specified, you will get a runtime error during timer installation.
.. [2]
:cpp:member:`on_alarm` callback and the functions invoked by itself should also be placed in IRAM, users need to take care of them by themselves.

View File

@ -8,8 +8,8 @@ Peripherals API
adc
:SOC_DAC_SUPPORTED: dac
timer
gpio
gptimer
:SOC_DEDICATED_GPIO_SUPPORTED: dedic_gpio
:SOC_HMAC_SUPPORTED: hmac
:SOC_DIG_SIGN_SUPPORTED: ds

View File

@ -224,7 +224,7 @@ Application Example
MCPWM example are located under: :example:`peripherals/mcpwm`:
* Control of BLDC (brushless DC) motor with hall sensor feedback - :example:`peripherals/mcpwm/mcpwm_bldc_hall_control`
* Brushed DC motor control - :example:`peripherals/mcpwm/mcpwm_brushed_dc_control`
* Brushed DC motor control - :example:`peripherals/mcpwm/mcpwm_bdc_speed_control`
* Servo motor control - :example:`peripherals/mcpwm/mcpwm_servo_control`
* HC-SR04 sensor with capture - :example:`peripherals/mcpwm/mcpwm_capture_hc_sr04`

View File

@ -1,112 +0,0 @@
General Purpose Timer
=====================
:link_to_translation:`zh_CN:[中文]`
{IDF_TARGET_TIMER_COUNTER_BIT_WIDTH:default="54", esp32="64", esp32s2="64"}
{IDF_TARGET_TIMER_GROUPS:default="two", esp8684="one"}
{IDF_TARGET_TIMERS_PER_GROUP:default="two", esp32c3="one", esp8684="one"}
{IDF_TARGET_TIMERS_TOTAL:default="four", esp32c3="two", esp8684="one"}
Introduction
------------
The {IDF_TARGET_NAME} chip contains {IDF_TARGET_TIMER_GROUPS} hardware timer group(s). Each group has {IDF_TARGET_TIMERS_PER_GROUP} general-purpose hardware timer(s). They are all {IDF_TARGET_TIMER_COUNTER_BIT_WIDTH}-bit generic timers based on 16-bit pre-scalers and {IDF_TARGET_TIMER_COUNTER_BIT_WIDTH}-bit up / down counters which are capable of being auto-reloaded.
Functional Overview
-------------------
The following sections of this document cover the typical steps to configure and operate a timer:
* :ref:`timer-api-timer-initialization` - covers which parameters should be set up to get the timer working, and also what specific functionality is provided depending on the timer configuration.
* :ref:`timer-api-timer-control` - describes how to read a timer's value, pause or start a timer, and change how it operates.
* :ref:`timer-api-alarms` - shows how to set and use alarms.
* :ref:`timer-api-interrupts`- explains how to use interrupt callbacks.
.. _timer-api-timer-initialization:
Timer Initialization
^^^^^^^^^^^^^^^^^^^^
The {IDF_TARGET_TIMER_GROUPS} {IDF_TARGET_NAME} timer group(s), with {IDF_TARGET_TIMERS_PER_GROUP} timer(s) in each, provide the total of {IDF_TARGET_TIMERS_TOTAL} individual timers for use. An {IDF_TARGET_NAME} timer group should be identified using :cpp:type:`timer_group_t`. An individual timer in a group should be identified with :cpp:type:`timer_idx_t`.
First of all, the timer should be initialized by calling the function :cpp:func:`timer_init` and passing a structure :cpp:type:`timer_config_t` to it to define how the timer should operate. In particular, the following timer parameters can be set:
.. list::
:not esp32: - **Clock Source**: Select the clock source, which together with the **Divider** define the resolution of the working timer.
- **Divider**: Sets how quickly the timer's counter is "ticking". The setting :cpp:member:`divider` is used as a divisor of the clock source that by default is APB_CLK running at 80 MHz. For more information of APB_CLK frequency, please check *{IDF_TARGET_NAME} Technical Reference Manual* > *Reset and Clock* [`PDF <{IDF_TARGET_TRM_EN_URL}#resclk>`__] chapter for more details.
- **Mode**: Sets if the counter should be incrementing or decrementing. It can be defined using :cpp:member:`counter_dir` by selecting one of the values from :cpp:type:`timer_count_dir_t`.
- **Counter Enable**: If the counter is enabled, it will start incrementing / decrementing immediately after calling :cpp:func:`timer_init`. You can change the behavior with :cpp:member:`counter_en` by selecting one of the values from :cpp:type:`timer_start_t`.
- **Alarm Enable**: Can be set using :cpp:member:`alarm_en`.
- **Auto Reload**: Sets if the counter should :cpp:member:`auto_reload` the initial counter value on the timer's alarm or continue incrementing or decrementing.
To get the current values of the timer's settings, use the function :cpp:func:`timer_get_config`.
.. _timer-api-timer-control:
Timer Control
^^^^^^^^^^^^^
Once the timer is enabled, its counter starts running. To enable the timer, call the function :cpp:func:`timer_init` with :cpp:member:`counter_en` set to ``true``, or call :cpp:func:`timer_start`. You can specify the timer's initial counter value by calling :cpp:func:`timer_set_counter_value`. To check the timer's current value, call :cpp:func:`timer_get_counter_value` or :cpp:func:`timer_get_counter_time_sec`.
To pause the timer at any time, call :cpp:func:`timer_pause`. To resume it, call :cpp:func:`timer_start`.
To reconfigure the timer, you can call :cpp:func:`timer_init`. This function is described in Section :ref:`timer-api-timer-initialization`.
You can also reconfigure the timer by using dedicated functions to change individual settings:
============= =================================== ==========================================================================
Setting Dedicated Function Description
============= =================================== ==========================================================================
Divider :cpp:func:`timer_set_divider` Change the rate of ticking. To avoid unpredictable results, the timer should be paused when changing the divider. If the timer is running, :cpp:func:`timer_set_divider` pauses it, change the setting, and start the timer again.
Mode :cpp:func:`timer_set_counter_mode` Set if the counter should be incrementing or decrementing
Auto Reload :cpp:func:`timer_set_auto_reload` Set if the initial counter value should be reloaded on the timer's alarm
============= =================================== ==========================================================================
.. _timer-api-alarms:
Alarms
^^^^^^
To set an alarm, call the function :cpp:func:`timer_set_alarm_value` and then enable the alarm using :cpp:func:`timer_set_alarm`. The alarm can also be enabled during the timer initialization stage, when :cpp:func:`timer_init` is called.
After the alarm is enabled, and the timer reaches the alarm value, the following two actions can occur depending on the configuration:
* An interrupt will be triggered if previously configured. See Section :ref:`timer-api-interrupts` on how to configure interrupts.
* When :cpp:member:`auto_reload` is enabled, the timer's counter will automatically be reloaded to start counting again from a previously configured value. This value should be set in advance with :cpp:func:`timer_set_counter_value`.
.. note::
* If an alarm value is set and the timer has already reached this value, the alarm is triggered immediately.
* Once triggered, the alarm is disabled automatically and needs to be re-enabled to trigger again.
To check the specified alarm value, call :cpp:func:`timer_get_alarm_value`.
.. _timer-api-interrupts:
Interrupts
^^^^^^^^^^
Registration of an interrupt callback for a specific timer can be done by calling :cpp:func:`timer_isr_callback_add` and passing in the group ID, timer ID, callback handler and user data. The callback handler will be invoked in ISR context, so user shouldn't put any blocking API in the callback function.
The benefit of using interrupt callback instead of precessing interrupt from scratch is, you don't have to deal with interrupt status check and clean stuffs, they are all addressed before the callback got run in driver's default interrupt handler.
For more information on how to use interrupts, please see the application example below.
Application Example
-------------------
The {IDF_TARGET_TIMER_COUNTER_BIT_WIDTH}-bit hardware timer example: :example:`peripherals/timer_group`.
API Reference
-------------
.. include-build-file:: inc/timer.inc
.. include-build-file:: inc/timer_types.inc

View File

@ -103,6 +103,7 @@ The following peripherals work normally even when the APB frequency is changing:
- **UART**: if REF_TICK is used as a clock source. See `use_ref_tick` member of :cpp:class:`uart_config_t`.
- **LEDC**: if REF_TICK is used as a clock source. See :cpp:func:`ledc_timer_config` function.
- **RMT**: if REF_TICK or XTAL is used as a clock source. See `flags` member of :cpp:class:`rmt_config_t` and macro `RMT_CHANNEL_FLAGS_AWARE_DFS`.
- **GPTimer**: if APB is used as the clock source. See :c:member:`clk_src` member of :c:type:`gptimer_config_t`.
Currently, the following peripheral drivers are aware of DFS and will use the ``ESP_PM_APB_FREQ_MAX`` lock for the duration of the transaction:
@ -128,7 +129,7 @@ The following peripheral drivers are not aware of DFS yet. Applications need to
- PCNT
- Sigma-delta
- Timer group
- The legacy timer group driver (note, the new :doc:`GPTimer <../peripherals/gptimer>` will hold the ``ESP_PM_APB_FREQ_MAX`` lock while the timer is working, if the clock source is set to APB)
:SOC_MCPWM_SUPPORTED: - MCPWM
API Reference

View File

@ -26,3 +26,30 @@ GPIO
----
The previous Kconfig option `RTCIO_SUPPORT_RTC_GPIO_DESC` has been removed, thus the ``rtc_gpio_desc`` array is unavailable. Please use ``rtc_io_desc`` array instead.
Timer Group Driver
------------------
Timer Group driver has been redesigned into :doc:`GPTimer <../api-reference/peripherals/gptimer>`, which aims to unify and simplify the usage of general purpose timer.
Although it's recommended to use the the new driver APIs, the legacy driver is till available in the previous include path ``driver/timer.h``. However, by default, including ``driver/timer.h`` will bring a build warning like "legacy timer group driver is deprecated, please migrate to driver/gptimer.h". The warning can be suppressed by the Kconfig option :ref:`CONFIG_GPTIMER_SUPPRESS_DEPRECATE_WARN`.
The major breaking changes in concept and usage are listed as follows:
Breaking Changes in Concepts
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- ``timer_group_t`` and ``timer_idx_t`` which used to identify the hardware timer are removed from user's code. In the new driver, a timer is represented by :cpp:type:`gptimer_handle_t`.
- Definition of timer source clock is moved to :cpp:type:`gptimer_clock_source_t`, the previous ``timer_src_clk_t`` is not used.
- Definition of timer count direction is moved to :cpp:type:`gptimer_count_direction_t`, the previous ``timer_count_dir_t`` is not used.
- Only level interrupt is supported, ``timer_intr_t`` and ``timer_intr_mode_t`` are not used.
- Auto-reload is enabled by set the :cpp:member:`auto_reload_on_alarm` flag. ``timer_autoreload_t`` is not used.
Breaking Changes in Usage
~~~~~~~~~~~~~~~~~~~~~~~~~
- Timer initialization is done by creating a timer instance from :cpp:func:`gptimer_new_timer`. Basic configurations like clock source, resolution and direction should be set in :cpp:type:`gptimer_config_t`. Note that, alarm event specific configurations are not needed during the driver install stage.
- Alarm event is configured by :cpp:func:`gptimer_set_alarm_action`, with parameters set in the :cpp:type:`gptimer_alarm_config_t`.
- Setting and getting count value are done by :cpp:func:`gptimer_get_raw_count` and :cpp:func:`gptimer_set_raw_count`. The driver doesn't help convert the raw value into UTC time-stamp. Instead, the conversion should be done form user's side as the timer resolution is also known to the user.
- The driver will install the interrupt service as well if :cpp:member:`on_alarm` is set to a valid callback function. In the callback, user doesn't have to deal with the low level registers (like "clear interrupt status", "re-enable alarm event" and so on). So functions like ``timer_group_get_intr_status_in_isr`` and ``timer_group_get_auto_reload_in_isr`` are not used anymore.
- To update the alarm configurations when alarm event happens, one can call :cpp:func:`gptimer_set_alarm_action` in the interrupt callback, then the alarm will be re-enabled again.
- Alarm will always be re-enabled by the driver if :cpp:member:`auto_reload_on_alarm` is set to true.

View File

@ -46,3 +46,8 @@ SOC dependency
- Public API headers who are listed in the Doxyfiles won't expose unstable and unnecessary soc header files like ``soc/soc.h``, ``soc/rtc.h``. That means, the user has to explicitly include them in their code if these "missing" header files are still wanted.
- Kconfig option ``LEGACY_INCLUDE_COMMON_HEADERS`` is also removed.
APP Trace
---------
One of the timestamp sources has changed from the legacy timer group driver to the new :doc:`GPTimer <../api-reference/peripherals/gptimer>`. Kconfig choices like ``APPTRACE_SV_TS_SOURCE_TIMER00`` has been changed to ``APPTRACE_SV_TS_SOURCE_GPTIMER``. User doesn't need to choose the group and timer ID any more.

View File

@ -0,0 +1 @@
.. include:: ../../../en/api-reference/peripherals/gptimer.rst

View File

@ -8,8 +8,8 @@
adc
:SOC_DAC_SUPPORTED: dac
timer
gpio
gptimer
:SOC_DEDICATED_GPIO_SUPPORTED: dedic_gpio
:SOC_HMAC_SUPPORTED: hmac
:SOC_DIG_SIGN_SUPPORTED: ds

View File

@ -1,111 +0,0 @@
通用定时器
==========
:link_to_translation:`en:[English]`
{IDF_TARGET_TIMER_COUNTER_BIT_WIDTH:default="54", esp32="64", esp32s2="64"}
{IDF_TARGET_TIMERS_PER_GROUP:default="2", esp32c3="1"}
{IDF_TARGET_TIMERS_TOTAL:default="4", esp32c3="2"}
简介
----
{IDF_TARGET_NAME} 芯片提供两组硬件定时器。每组包含 {IDF_TARGET_TIMERS_PER_GROUP} 个通用硬件定时器。这些 {IDF_TARGET_TIMER_COUNTER_BIT_WIDTH} 位通用定时器均基于 16 位预分频器和 {IDF_TARGET_TIMER_COUNTER_BIT_WIDTH} 位可自动重新加载向上/向下计数器。
功能概述
--------
下文介绍了配置和操作定时器的常规步骤:
* :ref:`timer-api-timer-initialization` - 启动定时器前应设置的参数,以及每个设置提供的具体功能。
* :ref:`timer-api-timer-control` - 如何读取定时器的值,如何暂停/启动定时器以及如何改变定时器的操作方式。
* :ref:`timer-api-alarms` - 如何设置和使用警报。
* :ref:`timer-api-interrupts`- 如何使用中断提供的回调函数。
.. _timer-api-timer-initialization:
定时器初始化
^^^^^^^^^^^^
两个 {IDF_TARGET_NAME} 定时器组中,每组都有 {IDF_TARGET_TIMERS_PER_GROUP} 个定时器,两组共有 {IDF_TARGET_TIMERS_TOTAL} 个定时器供使用。可使用 :cpp:type:`timer_group_t` 查看 {IDF_TARGET_NAME} 定时器组的类型,使用 :cpp:type:`timer_idx_t` 查看每组中的个体定时器类型。
首先调用 :cpp:func:`timer_init` 函数,并将 :cpp:type:`timer_config_t` 结构体传递给此函数,用于定义定时器的工作方式,实现定时器初始化。特别注意设置以下定时器参数:
.. list::
:not esp32: - **时钟源**: 选择时钟源,与时钟分频器一起决定了定时器的分辨率。
- **分频器**: 设置定时器中计数器计数的速度,:cpp:member:`divider` 的设置将用作输入时钟源的除数。默认的时钟源是 APB_CLK (一般是 80 MHz)。更多有关 APB_CLK 时钟频率信息,请查看 *{IDF_TARGET_NAME} 技术参考手册* > *复位和时钟* [`PDF <{IDF_TARGET_TRM_CN_URL}#resclk>`__] 章节。
- **模式**: 设置计数器是递增还是递减。可通过从 :cpp:type:`timer_count_dir_t` 中选取一个值,然后使用 :cpp:member:`counter_dir` 来选择模式。
- **计数器使能**: 如果计数器已使能,在调用 :cpp:func:`timer_init` 后计数器将立即开始递增/递减。您可通过从 :cpp:type:`timer_start_t` 中选取一个值,然后使用 :cpp:member:`counter_en` 改变此行为。
- **报警使能**: 可使用 :cpp:member:`alarm_en` 设置。
- **自动重载**: 设置计数器是否应该在定时器警报上使用 :cpp:member:`auto_reload` 自动重载首个计数值,还是继续递增或递减。
要获取定时器设置的当前值,请使用函数 :cpp:func:`timer_get_config`
.. _timer-api-timer-control:
定时器控制
^^^^^^^^^^^^^
定时器使能后便开始计数。要使能定时器,可首先设置 :cpp:member:`counter_en```true``,然后调用函数 :cpp:func:`timer_init`,或者直接调用函数 :cpp:func:`timer_start`。您可通过调用函数 :cpp:func:`timer_set_counter_value` 来指定定时器的首个计数值。要检查定时器的当前值,调用函数 :cpp:func:`timer_get_counter_value` 或 :cpp:func:`timer_get_counter_time_sec`
可通过调用函数 :cpp:func:`timer_pause` 随时暂停定时器。若要再次启动它,可调用函数 :cpp:func:`timer_start`
要重新配置定时器,可调用函数 :cpp:func:`timer_init`,该函数详细介绍见 :ref:`timer-api-timer-initialization`
除此之外,还可通过使用专有函数更改个别设置来重新配置定时器:
============= =================================== ==========================================================================
设置 专有函数 描述
============= =================================== ==========================================================================
分频器 :cpp:func:`timer_set_divider` 更改计数频率。为避免发生不可预测情况,更改分频器时应暂停定时器。如果定时器正在运行,则使用 :cpp:func:`timer_set_divider` 将其暂停并更改设置,然后重启定时器。
模式 :cpp:func:`timer_set_counter_mode` 设置计数器应递增还是递减
自动重载 :cpp:func:`timer_set_auto_reload` 设置是否应在定时器警报上重载首个计数值
============= =================================== ==========================================================================
.. _timer-api-alarms:
警报
^^^^^^
要设置警报,先调用函数 :cpp:func:`timer_set_alarm_value`,然后使用 :cpp:func:`timer_set_alarm` 使能警报。当调用函数 :cpp:func:`timer_init` 时,也可以在定时器初始化阶段使能警报。
警报已使能且定时器达到警报值后,根据配置,可能会出现以下两种行为:
* 如果先前已配置,此时将触发中断。有关如何配置中断,请参见 :ref:`timer-api-interrupts`
* 如 :cpp:member:`auto_reload` 已使能,定时器的计数器将重新加载,从先前配置好的值开始再次计数。应使用函数 :cpp:func:`timer_set_counter_value` 预先设置该值。
.. note::
* 如果已设置警报值且定时器已超过该值,则将立即触发警报。
* 一旦触发后,警报将自动关闭,需要重新使能以再次触发。
要检查某特定的警报值,调用函数 :cpp:func:`timer_get_alarm_value`
.. _timer-api-interrupts:
处理中断事务
^^^^^^^^^^^^
通过调用 :cpp:func:`timer_isr_callback_add` 函数并向该函数传递组 ID、定时器 ID、回调处理程序以及用户数据可以给某个定时器注册一个中断回调函数。回调处理程序会在 ISR 上下文中调用,因此用户不能在回调函数中放置任何会阻塞 CPU 的 API。
相较于从头编写中断处理程序,使用中断回调函数的好处是,用户无需检测和处理中断的状态位,这些操作会由驱动中默认的中断处理程序替我们完成。
有关如何使用中断回调函数,请参考如下应用示例。
应用示例
--------
{IDF_TARGET_TIMER_COUNTER_BIT_WIDTH} 位通用硬件定时器示例::example:`peripherals/timer_group`
API 参考
--------
.. include-build-file:: inc/timer.inc
.. include-build-file:: inc/timer_types.inc