diff --git a/docs/en/api-reference/system/esp_timer.rst b/docs/en/api-reference/system/esp_timer.rst index ec1b079aa1..bf35447f34 100644 --- a/docs/en/api-reference/system/esp_timer.rst +++ b/docs/en/api-reference/system/esp_timer.rst @@ -1,6 +1,8 @@ High Resolution Timer (ESP Timer) ================================= +:link_to_translation:`zh_CN:[中文]` + {IDF_TARGET_HR_TIMER:default = "SYSTIMER", esp32 = "LAC timer"} {IDF_TARGET_HR_TIMER_Resolution:default = "Not updated", esp32 = "64", esp32s2 = "64", esp32c3 = "52", esp32s3 = "52", esp32c2 = "52", esp32c6 = "52"} @@ -9,38 +11,38 @@ High Resolution Timer (ESP Timer) Overview -------- -Although FreeRTOS provides software timers, these timers have a few limitations: +Although FreeRTOS provides software timers, FreeRTOS software timers have a few limitations: -- Maximum resolution is equal to RTOS tick period -- Timer callbacks are dispatched from a low-priority task +- Maximum resolution is equal to the RTOS tick period +- Timer callbacks are dispatched from a low-priority timer service (i.e., daemon) task. This task can be preempted by other tasks, leading to decreased precision and accuracy. -Hardware timers are free from both of the limitations, but often they are less convenient to use. For example, application components may need timer events to fire at certain times in the future, but the hardware timer only contains one "compare" value used for interrupt generation. This means that some facility needs to be built on top of the hardware timer to manage the list of pending events can dispatch the callbacks for these events as corresponding hardware interrupts happen. +Although hardware timers are not subject to the limitations mentioned, they may not be as user-friendly. For instance, application components may require timer events to be triggered at specific future times, but hardware timers typically have only one "compare" value for interrupt generation. This necessitates the creation of an additional system on top of the hardware timer to keep track of pending events and ensure that callbacks are executed when the corresponding hardware interrupts occur. -An interrupt level of the handler depends on the :ref:`CONFIG_ESP_TIMER_INTERRUPT_LEVEL` option. It allows to set this: 1, 2 or 3 level (by default 1). Raising the level, the interrupt handler can reduce the timer processing delay. +The hardware timer interrupt's priority is configured via the :ref:`CONFIG_ESP_TIMER_INTERRUPT_LEVEL` option (possible priorities being 1, 2, or 3). Raising the timer interrupt's priority can reduce the timer processing delay caused by interrupt latency. ``esp_timer`` set of APIs provides one-shot and periodic timers, microsecond time resolution, and {IDF_TARGET_HR_TIMER_Resolution}-bit range. -Internally, ``esp_timer`` uses a {IDF_TARGET_HR_TIMER_Resolution}-bit hardware timer, where the implementation depends on the target. {IDF_TARGET_HR_TIMER} is used for {IDF_TARGET_NAME}. +Internally, ``esp_timer`` uses a {IDF_TARGET_HR_TIMER_Resolution}-bit hardware timer. The exact hardware timer implementation used will depend on the target, where {IDF_TARGET_HR_TIMER} is used for {IDF_TARGET_NAME}. Timer callbacks can be dispatched by two methods: -- ``ESP_TIMER_TASK`` +- ``ESP_TIMER_TASK``. - ``ESP_TIMER_ISR``. Available only if :ref:`CONFIG_ESP_TIMER_SUPPORTS_ISR_DISPATCH_METHOD` is enabled (by default disabled). -``ESP_TIMER_TASK``. Timer callbacks are dispatched from a high-priority ``esp_timer`` task. Because all the callbacks are dispatched from the same task, it is recommended to only do the minimal possible amount of work from the callback itself, posting an event to a lower priority task using a queue instead. +``ESP_TIMER_TASK``. Timer callbacks are dispatched from a high-priority ``esp_timer`` task. Because all the callbacks are dispatched from the same task, it is recommended to only do the minimal possible amount of work from the callback itself, posting an event to a lower-priority task using a queue instead. -If other tasks with priority higher than ``esp_timer`` are running, callback dispatching will be delayed until ``esp_timer`` task has a chance to run. For example, this will happen if an SPI Flash operation is in progress. +If other tasks with a priority higher than ``esp_timer`` are running, callback dispatching will be delayed until the ``esp_timer`` task has a chance to run. For example, this will happen if an SPI Flash operation is in progress. ``ESP_TIMER_ISR``. Timer callbacks are dispatched directly from the timer interrupt handler. This method is useful for some simple callbacks which aim for lower latency. -Creating and starting a timer, and dispatching the callback takes some time. Therefore, there is a lower limit to the timeout value of one-shot ``esp_timer``. If :cpp:func:`esp_timer_start_once` is called with a timeout value less than 20us, the callback will be dispatched only after approximately 20us. +Creating and starting a timer, and dispatching the callback takes some time. Therefore, there is a lower limit to the timeout value of one-shot ``esp_timer``. If :cpp:func:`esp_timer_start_once` is called with a timeout value of less than 20 us, the callback will be dispatched only after approximately 20 us. -Periodic ``esp_timer`` also imposes a 50us restriction on the minimal timer period. Periodic software timers with period of less than 50us are not practical since they would consume most of the CPU time. Consider using dedicated hardware peripherals or DMA features if you find that a timer with small period is required. +Periodic ``esp_timer`` also imposes a 50 us restriction on the minimal timer period. Periodic software timers with a period of less than 50 us are not practical since they would consume most of the CPU time. Consider using dedicated hardware peripherals or DMA features if you find that a timer with a small period is required. Using ``esp_timer`` APIs ------------------------ -Single timer is represented by :cpp:type:`esp_timer_handle_t` type. Timer has a callback function associated with it. This callback function is called from the ``esp_timer`` task each time the timer elapses. +A single timer is represented by :cpp:type:`esp_timer_handle_t` type. Each timer has a callback function associated with it. This callback function is called from the ``esp_timer`` task each time the timer elapses. - To create a timer, call :cpp:func:`esp_timer_create`. - To delete the timer when it is no longer needed, call :cpp:func:`esp_timer_delete`. @@ -53,66 +55,50 @@ The timer can be started in one-shot mode or in periodic mode. Note that the timer must not be running when :cpp:func:`esp_timer_start_once` or :cpp:func:`esp_timer_start_periodic` is called. To restart a running timer, call :cpp:func:`esp_timer_stop` first, then call one of the start functions. -Callback functions +Callback Functions ------------------ -.. note:: Keep the callback functions as short as possible otherwise it will affect all timers. +.. note:: Keep the callback functions as short as possible. Otherwise, it will affect all timers. -Timer callbacks which are processed by ``ESP_TIMER_ISR`` method should not call the context switch call - ``portYIELD_FROM_ISR()``, instead of this you should use the :cpp:func:`esp_timer_isr_dispatch_need_yield` function. -The context switch will be done after all ISR dispatch timers have been processed, if required by the system. +Timer callbacks that are processed by the ``ESP_TIMER_ISR`` method should not call the context switch call - ``portYIELD_FROM_ISR()``. Instead, use the :cpp:func:`esp_timer_isr_dispatch_need_yield` function. The context switch will be done after all ISR dispatch timers have been processed if required by the system. .. only:: SOC_ETM_SUPPORTED and SOC_SYSTIMER_SUPPORT_ETM ETM Event --------- - The esp_timer is constructed based on a hardware timer called *systimer*, which is able to generate the alarm event and interact with the :doc:`ETM ` module. You can call :cpp:func:`esp_timer_new_etm_alarm_event` to get the corresponding ETM event handle. + The ``esp_timer`` is constructed based on a hardware timer called *systimer*, which is able to generate the alarm event and interact with the :doc:`ETM ` module. You can call :cpp:func:`esp_timer_new_etm_alarm_event` to get the corresponding ETM event handle. To know more about how to connect the event to an ETM channel, please refer to the :doc:`ETM ` documentation. -esp_timer during light sleep +``esp_timer`` During Light-sleep -------------------------------- -During light sleep, the esp_timer counter stops and no callback functions are called. -Instead, the time is counted by the RTC counter. Upon waking up, the system gets the difference -between the counters and calls a function that advances the esp_timer counter. -Since the counter has been advanced, the system starts calling callbacks that were not called during sleep. -The number of callbacks depends on the duration of the sleep and the period of the timers. It can lead to overflow of some queues. -This only applies to periodic timers, one-shot timers will be called once. +During Light-sleep, the ``esp_timer`` counter stops and no callback functions are called. Instead, the time is counted by the RTC counter. Upon waking up, the system gets the difference between the counters and calls a function that advances the ``esp_timer`` counter. Since the counter has been advanced, the system starts calling callbacks that were not called during sleep. The number of callbacks depends on the duration of the sleep and the period of the timers. It can lead to the overflow of some queues. This only applies to periodic timers, since one-shot timers will be called once. -This behavior can be changed by calling :cpp:func:`esp_timer_stop` before sleeping. -In some cases, this can be inconvenient, and instead of the stop function, -you can use the `skip_unhandled_events` option during :cpp:func:`esp_timer_create`. -When the `skip_unhandled_events` is true, if a periodic timer expires one or more times during light sleep -then only one callback is called on wake. +This behavior can be changed by calling :cpp:func:`esp_timer_stop` before sleeping. In some cases, this can be inconvenient, and instead of the stop function, you can use the ``skip_unhandled_events`` option during :cpp:func:`esp_timer_create`. When the ``skip_unhandled_events`` is true, if a periodic timer expires one or more times during Light-sleep, then only one callback is called on wake. -Using the `skip_unhandled_events` option with `automatic light sleep` (see :doc:`Power Management APIs `) helps to reduce the power consumption of the system when it is in light sleep. The duration of light sleep is also determined by esp_timers. Timers with `skip_unhandled_events` option will not wake up the system. +Using the ``skip_unhandled_events`` option with automatic Light-sleep (see :doc:`Power Management APIs `) helps to reduce the power consumption of the system when it is in Light-sleep. The duration of Light-sleep is also in part determined by the next event occurs. Timers with ``skip_unhandled_events`` option will not wake up the system. -Handling callbacks +Handling Callbacks ------------------ -esp_timer is designed to achieve a high-resolution low latency timer and the ability to handle delayed events. -If the timer is late then the callback will be called as soon as possible, it will not be lost. -In the worst case, when the timer has not been processed for more than one period (for periodic timers), -the callbacks will be called one after the other without waiting for the set period. -This can be bad for some applications, and the `skip_unhandled_events` option was introduced to eliminate this behavior. -If `skip_unhandled_events` is set then a periodic timer that has expired multiple times without being able to call -the callback will still result in only one callback event once processing is possible. +``esp_timer`` is designed to achieve a high-resolution and low-latency timer with the ability to handle delayed events. If the timer is late, then the callback will be called as soon as possible, and it will not be lost. In the worst case, when the timer has not been processed for more than one period (for periodic timers), the callbacks will be called one after the other without waiting for the set period. This can be bad for some applications, and the ``skip_unhandled_events`` option is introduced to eliminate this behavior. If ``skip_unhandled_events`` is set, then a periodic timer that has expired multiple times without being able to call the callback will still result in only one callback event once processing is possible. Obtaining Current Time ---------------------- ``esp_timer`` also provides a convenience function to obtain the time passed since start-up, with microsecond precision: :cpp:func:`esp_timer_get_time`. This function returns the number of microseconds since ``esp_timer`` was initialized, which usually happens shortly before ``app_main`` function is called. -Unlike `gettimeofday` function, values returned by :cpp:func:`esp_timer_get_time`: +Unlike ``gettimeofday`` function, values returned by :cpp:func:`esp_timer_get_time`: -- Start from zero after the chip wakes up from deep sleep +- Start from zero after the chip wakes up from Deep-sleep - Do not have timezone or DST adjustments applied Application Example ------------------- -The following example illustrates usage of ``esp_timer`` APIs: :example:`system/esp_timer`. +The following example illustrates the usage of ``esp_timer`` APIs: :example:`system/esp_timer`. API Reference diff --git a/docs/zh_CN/api-reference/system/esp_timer.rst b/docs/zh_CN/api-reference/system/esp_timer.rst index 2f78425259..305e2285dc 100644 --- a/docs/zh_CN/api-reference/system/esp_timer.rst +++ b/docs/zh_CN/api-reference/system/esp_timer.rst @@ -1 +1,109 @@ -.. include:: ../../../en/api-reference/system/esp_timer.rst \ No newline at end of file +高分辨率定时器(ESP 定时器) +================================= + +:link_to_translation:`en:[English]` + +{IDF_TARGET_HR_TIMER:default = "SYSTIMER", esp32 = "LAC 定时器"} + +{IDF_TARGET_HR_TIMER_Resolution:default = "Not updated", esp32 = "64", esp32s2 = "64", esp32c3 = "52", esp32s3 = "52", esp32c2 = "52", esp32c6 = "52"} + + +概述 +-------- + +尽管 FreeRTOS 提供软件定时器,但 FreeRTOS 软件定时器存在一定局限性: + +- 最大分辨率等于 RTOS 滴答周期 +- 定时器回调函数从低优先级的定时器服务(即守护进程)任务中分发。该任务可能会被其他任务抢占,导致精度和准确性下降。 + +硬件定时器虽不受上述限制,但使用不便。例如,应用组件可能需要在特定的未来时间触发计时器事件,但硬件定时器通常只有一个“compare(比较)”值用于中断生成。为提升使用的便利性,应在硬件定时器的基础上构建某种机制来管理待处理事件列表,确保在相应的硬件中断发生时调度回调。 + +配置 :ref:`CONFIG_ESP_TIMER_INTERRUPT_LEVEL` 选项,设置硬件定时器中断的优先级(可设置为 1、2 或 3 级),提高定时器中断的优先级可以减少由中断延迟引起的定时器处理延迟。 + +``esp_timer`` API 集支持单次定时器和周期定时器、微秒级的时间分辨率、以及 {IDF_TARGET_HR_TIMER_Resolution} 位范围。 + +``esp_timer`` 内部使用 {IDF_TARGET_HR_TIMER_Resolution} 位硬件定时器,具体硬件实现取决于芯片型号,如 {IDF_TARGET_NAME} 使用的是 {IDF_TARGET_HR_TIMER}。 + +定时器回调可通过以下两种方式调度: + +- ``ESP_TIMER_TASK``。 +- ``ESP_TIMER_ISR``。仅当 :ref:`CONFIG_ESP_TIMER_SUPPORTS_ISR_DISPATCH_METHOD` 被启用时可用(默认为禁用)。 + +使用 ``ESP_TIMER_TASK`` 这一途径时,定时器回调函数是从高优先级的 ``esp_timer`` 任务中调度的。由于所有回调函数都是从同一个任务中调度,因此建议在回调函数本身中仅执行最小化的工作,如使用队列向低优先级任务发布事件。 + +如果有优先级高于 ``esp_timer`` 的其他任务正在运行,则回调调度将延迟,直至 ``esp_timer`` 能够运行。例如,执行 SPI flash 操作时便会发生此类情况。 + +使用 ``ESP_TIMER_ISR`` 这一途径时,定时器回调由定时器中断处理程序直接调度。对旨在降低延迟的简单回调,建议使用此途径。 + +创建、启动定时器并调度回调需要一些时间。因此,单次 ``esp_timer`` 的超时值存在最小限制。若调用 :cpp:func:`esp_timer_start_once` 时设置的超时值小于 20 us,回调函数仍会在大约 20 微秒后被调度。 + +周期 ``esp_timer`` 将最小周期限制为 50 us。周期小于 50 us 的软件定时器会占用大部分 CPU 时间,因此它们并不实用。如需使用小周期定时器,请考虑使用专用硬件外设或 DMA 功能。 + +使用 ``esp_timer`` API +------------------------ + +单个定时器由 :cpp:type:`esp_timer_handle_t` 类型表示。每个定时器都有与之关联的回调函数,定时器超时时便会从 ``esp_timer`` 任务中调用此函数。 + +- 要创建定时器,请调用函数 :cpp:func:`esp_timer_create`。 +- 要删除定时器,请调用函数 :cpp:func:`esp_timer_delete`。 + +定时器可以以单次模式和周期模式启动。 + +- 要以单次模式启动定时器,请调用函数 :cpp:func:`esp_timer_start_once`,传递应在多久后调用回调的时间间隔。调用回调时,定时器被视为停止工作。 + +- 要以周期模式启动定时器,请调用函数 :cpp:func:`esp_timer_start_periodic`,传递应调用回调的周期。计时器持续运行,直到调用函数 :cpp:func:`esp_timer_stop`。 + +注意,当调用函数 :cpp:func:`esp_timer_start_once` 或 :cpp:func:`esp_timer_start_periodic` 时,应确保定时器处于非运行状态。因此,要重启运行中的定时器,需首先调用函数 :cpp:func:`esp_timer_stop` 停止定时器,然后调用 :cpp:func:`esp_timer_start_once` 或 :cpp:func:`esp_timer_start_periodic` 来启动定时器。 + +回调函数 +------------------ + +.. note:: 回调函数应尽可能简略,避免影响所有定时器。 + +若定时器回调由 ``ESP_TIMER_ISR`` 方式处理,则该回调不应调用切换上下文的 ``portYIELD_FROM_ISR()``,而应调用函数 :cpp:func:`esp_timer_isr_dispatch_need_yield`。如果系统有此需求,上下文切换将在所有 ISR 调度定时器处理完毕后进行。 + +.. only:: SOC_ETM_SUPPORTED and SOC_SYSTIMER_SUPPORT_ETM + + ETM 事件 + --------- + + ``esp_timer`` 的构建基于 *systimer* 硬件定时器,能够产生报警事件并与 :doc:`ETM ` 模块交互。调用函数 :cpp:func:`esp_timer_new_etm_alarm_event` 以获取相应的 ETM 事件句柄。 + + 如需了解如何将 ETM 事件连接到相应 ETM 通道,请参阅 :doc:`ETM `。 + +Light-sleep 模式下的 ``esp_timer`` +----------------------------------- + +在 Light-sleep 期间, ``esp_timer`` 计数器停止工作,并且不调用回调函数,而是由 RTC 计数器负责计算时间。唤醒后,系统得到两个计数器间的差值,并调用函数推进 ``esp_timer`` 计数器计数。计数器计数被推进后,系统开始调用 Light-sleep 期间未被调用的回调。回调数量取决于 Light-sleep 模式持续时长和定时器周期,这可能会导致某些队列溢出。以上情况仅适用于周期性定时器,单次定时器只会被调用一次。 + +通过在 Light-sleep 模式前调用函数 :cpp:func:`esp_timer_stop` 可以改变上述行为。但在某些情况下这可能并不方便。比起使用停止函数,在 :cpp:func:`esp_timer_create` 中使用 ``skip_unhandled_events`` 选项将更加便利。 当 ``skip_unhandled_events`` 为真时,如果一个周期性定时器在 Light-sleep 期间超时一次或多次,那么在唤醒时只有一个回调会被调用。 + +使用带有自动 Light-sleep 的 ``skip_unhandled_events`` 选项(请参阅 :doc:`电源管理 `),有助于在系统处于 Light-sleep 状态时降低功耗。 Light-sleep 的持续时间也在一定程度上由下一个事件发生的时间确定。具有 ``skip_unhandled_events``` 选项的定时器不会唤醒系统。 + +处理回调 +------------------ + +设计 ``esp_timer`` 是为了使定时器实现高分辨率和低延迟,并具备处理延迟事件的能力。如果定时器延迟,回调将被尽快调用,不会丢失。在最糟的情况下,周期性定时器可能超过一个周期还没有被处理,此时回调将被陆续调用,而不会等待设定的周期。这会给某些应用带来负面影响,为避免此类情况发生,特引入 ``skip_unhandled_events`` 选项。设置该选项后,即使一个周期性定时器多次超时且无法调用回调,该定时器在恢复处理能力后,仍将只产生一个回调事件。 + +获取当前时间 +---------------------- + +``esp_timer`` 还提供了一个便捷函数 :cpp:func:`esp_timer_get_time` 以获取自启动以来经过的时间,可精确到微秒。这个函数通常会在 ``app_main`` 函数即将被调用前,返回自 ``esp_timer`` 启动以来的微秒数。 + +不同于 ``gettimeofday`` 函数,:cpp:func:`esp_timer_get_time` 返回的值: + +- 芯片从 Deep-sleep 中唤醒后,从零开始 +- 没有应用时区或 DST 调整 + +应用示例 +------------------- + +``esp_timer`` API 的详细用法可参阅 :example:`system/esp_timer`。 + + +API 参考 +------------- + +.. include-build-file:: inc/esp_timer.inc + +