Merge branch 'bugfix/esp_timer_profiling' into 'master'

esp_timer: fix for deletion of timer in a callback, add docs

See merge request !1607
This commit is contained in:
Ivan Grokhotkov 2017-11-29 12:32:38 +08:00
commit 6228d0c409
8 changed files with 113 additions and 7 deletions

View file

@ -762,7 +762,6 @@ config NO_BLOBS
config ESP_TIMER_PROFILING
bool "Enable esp_timer profiling features"
depends on MAKING_ESP_TIMER_A_PUBLIC_API
default n
help
If enabled, esp_timer_dump will dump information such as number of times

View file

@ -75,12 +75,14 @@ static LIST_HEAD(esp_timer_list, esp_timer) s_timers =
// all the timers
static LIST_HEAD(esp_inactive_timer_list, esp_timer) s_inactive_timers =
LIST_HEAD_INITIALIZER(s_timers);
// used to keep track of the timer when executing the callback
static esp_timer_handle_t s_timer_in_callback;
#endif
// task used to dispatch timer callbacks
static TaskHandle_t s_timer_task;
// counting semaphore used to notify the timer task from ISR
static SemaphoreHandle_t s_timer_semaphore;
// lock protecting s_timers and s_inactive_timers
// lock protecting s_timers, s_inactive_timers, s_timer_in_callback
static portMUX_TYPE s_timer_lock = portMUX_INITIALIZER_UNLOCKED;
@ -149,6 +151,9 @@ esp_err_t esp_timer_delete(esp_timer_handle_t timer)
return ESP_ERR_INVALID_STATE;
}
#if WITH_PROFILING
if (timer == s_timer_in_callback) {
s_timer_in_callback = NULL;
}
timer_remove_inactive(timer);
#endif
if (timer == NULL) {
@ -266,14 +271,21 @@ static void timer_process_alarm(esp_timer_dispatch_t dispatch_method)
}
#if WITH_PROFILING
uint64_t callback_start = now;
s_timer_in_callback = it;
#endif
timer_list_unlock();
(*it->callback)(it->arg);
timer_list_lock();
now = esp_timer_impl_get_time();
#if WITH_PROFILING
it->times_triggered++;
it->total_callback_run_time += now - callback_start;
/* The callback might have deleted the timer.
* If this happens, esp_timer_delete will set s_timer_in_callback
* to NULL.
*/
if (s_timer_in_callback) {
s_timer_in_callback->times_triggered++;
s_timer_in_callback->total_callback_run_time += now - callback_start;
}
#endif
it = LIST_FIRST(&s_timers);
}

View file

@ -29,11 +29,11 @@
* use RTOS notification mechanisms (queues, semaphores, event groups, etc.) to
* pass information to other tasks.
*
* <to be implemented> It should be possible to request the callback to be called
* To be implemented: it should be possible to request the callback to be called
* directly from the ISR. This reduces the latency, but has potential impact on
* all other callbacks which need to be dispatched. This option should only be
* used for simple callback functions, which do not take longer than a few
* microseconds to run. </to be implemented>
* microseconds to run.
*
* Implementation note: on the ESP32, esp_timer APIs use the "legacy" FRC2
* timer. Timer callbacks are called from a task running on the PRO CPU.

View file

@ -4,11 +4,16 @@
#include <sys/time.h>
#include "unity.h"
#include "esp_timer.h"
#include "esp_heap_caps.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "test_utils.h"
#ifdef CONFIG_ESP_TIMER_PROFILING
#define WITH_PROFILING 1
#endif
TEST_CASE("esp_timer orders timers correctly", "[esp_timer]")
{
@ -379,3 +384,37 @@ TEST_CASE("Can dump esp_timer stats", "[esp_timer]")
{
esp_timer_dump(stdout);
}
TEST_CASE("Can delete timer from callback", "[esp_timer]")
{
typedef struct {
SemaphoreHandle_t notify_from_timer_cb;
esp_timer_handle_t timer;
} test_arg_t;
void timer_func(void* varg)
{
test_arg_t arg = *(test_arg_t*) varg;
esp_timer_delete(arg.timer);
printf("Timer %p is deleted\n", arg.timer);
xSemaphoreGive(arg.notify_from_timer_cb);
}
test_arg_t args = {
.notify_from_timer_cb = xSemaphoreCreateBinary(),
};
esp_timer_create_args_t timer_args = {
.callback = &timer_func,
.arg = &args,
.name = "self_deleter"
};
esp_timer_create(&timer_args, &args.timer);
esp_timer_start_once(args.timer, 10000);
TEST_ASSERT_TRUE(xSemaphoreTake(args.notify_from_timer_cb, 1000 / portTICK_PERIOD_MS));
printf("Checking heap at %p\n", args.timer);
TEST_ASSERT_TRUE(heap_caps_check_integrity_addr((intptr_t) args.timer, true));
vSemaphoreDelete(args.notify_from_timer_cb);
}

View file

@ -151,7 +151,9 @@ INPUT = \
../components/app_trace/include/esp_app_trace.h \
### Power management
../components/esp32/include/esp_pm.h \
../components/esp32/include/esp32/pm.h
../components/esp32/include/esp32/pm.h \
### esp_timer, High Resolution Timer
../components/esp32/include/esp_timer.h
## Get warnings for functions that have no documentation for their parameters or return value

View file

@ -0,0 +1,52 @@
High Resolution Timer
=====================
Overview
--------
Although FreeRTOS provides software timers, these timers have a few limitations:
- Mmaximum resolution is equal to RTOS tick period
- Timer callbacks are dispatched from a low-priority task
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.
``esp_timer`` set of APIs provide such facility. Internally, ``esp_timer`` uses a 32-bit hardware timer (FRC1, "legacy" timer). ``esp_timer`` provides one-shot and periodic timers, microsecond time resolution, and 64-bit range.
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.
.. note: Provisions are made to dispatch some simple callbacks directly from the interrupt handler, if needed. However this option is not implemented at the moment.
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.
- 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`.
The timer can be started in one-shot mode or in periodic mode.
- To start the timer in one-shot mode, call :cpp:func:`esp_timer_start_once`, passing the time interval after which the callback should be called. When the callback gets called, the timer is considered to be stopped.
- To start the timer in periodic mode, call :cpp:func:`esp_timer_start_periodic`, passing the period with which the callback should be called. The timer keeps running until :cpp:func:`esp_timer_stop` is called.
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.
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`:
- Start from zero after the chip wakes up from deep sleep
- Do not have timezone or DST adjustments applied
API Reference
-------------
.. include:: /_build/inc/esp_timer.inc

View file

@ -10,6 +10,7 @@ System API
Watchdogs <wdts>
Hooks <hooks>
Inter-Processor Call <ipc>
High Resolution Timer <esp_timer>
Over The Air Updates (OTA) <ota>
Sleep Modes <sleep_modes>
Power Management <power_management>

View file

@ -24,3 +24,4 @@ CONFIG_FREERTOS_QUEUE_REGISTRY_SIZE=7
CONFIG_STACK_CHECK_STRONG=y
CONFIG_STACK_CHECK=y
CONFIG_SUPPORT_STATIC_ALLOCATION=y
CONFIG_ESP_TIMER_PROFILING=y