examples: add esp_timer example

This commit is contained in:
Ivan Grokhotkov 2018-06-01 12:36:34 +02:00
parent d7d559a489
commit 2f5cd89833
7 changed files with 296 additions and 0 deletions

View file

@ -43,6 +43,11 @@ 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
Application Example
-------------------
The following example illustrates usage of ``esp_timer`` APIs: :example:`system/esp_timer`.
API Reference
-------------

View file

@ -0,0 +1,9 @@
#
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
# project subdirectory.
#
PROJECT_NAME := esp_timer_example
include $(IDF_PATH)/make/project.mk

View file

@ -0,0 +1,79 @@
# High resolution timer (`esp_timer`) example
This example illustrates usage of `esp_timer` APIs to create one-shot and periodic software timers.
`esp_timer` APIs allow application to create multiple timers using single hardware timer, and hides complexity associated with managing multiple timers, invoking callbacks, accounting for APB frequency changes (if dynamic frequency scaling is enabled), and maintaining correct time after light sleep.
`esp_timer` API also provides `esp_timer_get_time` function which returns time since boot, in microseconds. This can be useful for fine-grained timing in tasks and ISRs.
## Example flow explained
1. Example starts and timers are created.
```
I (265) example: Starting timers, time since boot: 2479 us
```
2. These two repeating lines are the output of `esp_timer_dump` function. There is one line for each of the timers created. This function can be useful for debugging purposes. Note that such debugging information is available because the example sets `CONFIG_ESP_TIMER_PROFILING` option in sdkconfig. Without this option, less information will be available. See documentation of `esp_timer_dump` in ESP-IDF programming guide for more details.
```
timer period next time times times callback
name to fire started fired run time (us)
------------------------------------------------------------------------------------
periodic 500000 502455 1 0 0
one-shot 0 5002469 1 0 0
```
3. Periodic timer keeps running at 500ms period:
```
I (765) example: Periodic timer called, time since boot: 502506 us
I (1265) example: Periodic timer called, time since boot: 1002478 us
I (1765) example: Periodic timer called, time since boot: 1502478 us
I (2265) example: Periodic timer called, time since boot: 2002478 us
periodic 500000 2502455 1 4 511
one-shot 0 5002469 1 0 0
I (2765) example: Periodic timer called, time since boot: 2502478 us
I (3265) example: Periodic timer called, time since boot: 3002478 us
I (3765) example: Periodic timer called, time since boot: 3502478 us
I (4265) example: Periodic timer called, time since boot: 4002478 us
periodic 500000 4502455 1 8 971
one-shot 0 5002469 1 0 0
I (4765) example: Periodic timer called, time since boot: 4502478 us
I (5265) example: Periodic timer called, time since boot: 5002476 us
```
4. One-shot timer runs, and changes the period of the periodic timer. Now the periodic timer runs with 1 second period:
```
I (5265) example: One-shot timer called, time since boot: 5002586 us
I (5265) example: Restarted periodic timer with 1s period, time since boot: 5005475 us
I (6265) example: Periodic timer called, time since boot: 6005492 us
periodic 1000000 7005469 2 11 1316
one-shot 0 0 1 1 11474
I (7265) example: Periodic timer called, time since boot: 7005492 us
I (8265) example: Periodic timer called, time since boot: 8005492 us
periodic 1000000 9005469 2 13 1550
one-shot 0 0 1 1 11474
I (9265) example: Periodic timer called, time since boot: 9005492 us
I (10265) example: Periodic timer called, time since boot: 10005492 us
```
5. To illustrate that timekeeping continues correctly after light sleep, the example enters light sleep for 0.5s. This sleep does not impact timer period, and the timer is executed 1 second after the previous iteration. Note that the timers can not execute during light sleep, since the CPU is not running at that time. Such timers would execute immediately after light sleep, and then continue running with their normal period.
```
I (10275) example: Entering light sleep for 0.5s, time since boot: 10011559 us
I (10275) example: Woke up from light sleep, time since boot: 10512007 us
I (10765) example: Periodic timer called, time since boot: 11005492 us
I (11765) example: Periodic timer called, time since boot: 12005492 us
```
6. Finally, timers are deleted.
```
I (12275) example: Stopped and deleted timers
```
---
See the README.md file in the upper level 'examples' directory for more information about examples.

View file

@ -0,0 +1,97 @@
from __future__ import print_function
import re
import os
import sys
# this is a test case write with tiny-test-fw.
# to run test cases outside tiny-test-fw,
# we need to set environment variable `TEST_FW_PATH`,
# then get and insert `TEST_FW_PATH` to sys path before import FW module
test_fw_path = os.getenv('TEST_FW_PATH')
if test_fw_path and test_fw_path not in sys.path:
sys.path.insert(0, test_fw_path)
import TinyFW
import IDF
STARTING_TIMERS_REGEX = re.compile(r'Started timers, time since boot: (\d+) us')
# name, period, next_alarm, times_started, times_fired, cb_exec_time
TIMER_DUMP_LINE_REGEX = re.compile(r'([\w-]+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)')
PERIODIC_TIMER_REGEX = re.compile(r'Periodic timer called, time since boot: (\d+) us')
LIGHT_SLEEP_ENTER_REGEX = re.compile(r'Entering light sleep for 0\.5s, time since boot: (\d+) us')
LIGHT_SLEEP_EXIT_REGEX = re.compile(r'Woke up from light sleep, time since boot: (\d+) us')
ONE_SHOT_REGEX = re.compile(r'One\-shot timer called, time since boot: (\d+) us')
RESTART_REGEX = re.compile(r'Restarted periodic timer with 1s period, time since boot: (\d+) us')
STOP_REGEX = re.compile(r'Stopped and deleted timers')
INITIAL_TIMER_PERIOD = 500000
FINAL_TIMER_PERIOD = 1000000
LIGHT_SLEEP_TIME = 500000
ONE_SHOT_TIMER_PERIOD = 5000000
@IDF.idf_example_test(env_tag='Example_WIFI')
def test_examples_system_esp_timer(env, extra_data):
dut = env.get_dut('esp_timer_example', 'examples/system/esp_timer')
# start test
dut.start_app()
groups = dut.expect(STARTING_TIMERS_REGEX, timeout=30)
start_time = int(groups[0])
print('Start time: {} us'.format(start_time))
groups = dut.expect(TIMER_DUMP_LINE_REGEX, timeout=2)
assert(groups[0] == 'periodic' and int(groups[1]) == INITIAL_TIMER_PERIOD)
groups = dut.expect(TIMER_DUMP_LINE_REGEX, timeout=2)
assert(groups[0] == 'one-shot' and int(groups[1]) == 0)
for i in range(0, 5):
groups = dut.expect(PERIODIC_TIMER_REGEX, timeout=2)
cur_time = int(groups[0])
diff = start_time + (i + 1) * INITIAL_TIMER_PERIOD - cur_time
print('Callback #{}, time: {} us, diff: {} us'.format(i, cur_time, diff))
assert(abs(diff) < 100)
groups = dut.expect(ONE_SHOT_REGEX, timeout=3)
one_shot_timer_time = int(groups[0])
diff = start_time + ONE_SHOT_TIMER_PERIOD - one_shot_timer_time
print('One-shot timer, time: {} us, diff: {}'.format(one_shot_timer_time, diff))
assert(abs(diff) < 200)
groups = dut.expect(RESTART_REGEX, timeout=3)
start_time = int(groups[0])
print('Timer restarted, time: {} us'.format(start_time))
for i in range(0, 5):
groups = dut.expect(PERIODIC_TIMER_REGEX, timeout=2)
cur_time = int(groups[0])
diff = start_time + (i + 1) * FINAL_TIMER_PERIOD - cur_time
print('Callback #{}, time: {} us, diff: {} us'.format(i, cur_time, diff))
assert(abs(diff) < 100)
groups = dut.expect(LIGHT_SLEEP_ENTER_REGEX, timeout=2)
sleep_enter_time = int(groups[0])
groups = dut.expect(LIGHT_SLEEP_EXIT_REGEX, timeout=2)
sleep_exit_time = int(groups[0])
sleep_time = sleep_exit_time - sleep_enter_time
print('Enter sleep: {}, exit sleep: {}, slept: {}'.format(
sleep_enter_time, sleep_exit_time, sleep_time))
assert(abs(sleep_time - LIGHT_SLEEP_TIME) < 1000)
for i in range(5, 7):
groups = dut.expect(PERIODIC_TIMER_REGEX, timeout=2)
cur_time = int(groups[0])
diff = abs(start_time + (i + 1) * FINAL_TIMER_PERIOD - cur_time)
print('Callback #{}, time: {} us, diff: {} us'.format(i, cur_time, diff))
assert(diff < 100)
dut.expect(STOP_REGEX, timeout=2)
if __name__ == '__main__':
test_examples_system_esp_timer()

View file

@ -0,0 +1,5 @@
#
# "main" pseudo-component makefile.
#
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)

View file

@ -0,0 +1,100 @@
/* esp_timer (high resolution timer) example
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include "esp_timer.h"
#include "esp_log.h"
#include "esp_sleep.h"
#include "sdkconfig.h"
static void periodic_timer_callback(void* arg);
static void oneshot_timer_callback(void* arg);
static const char* TAG = "example";
void app_main()
{
/* Create two timers:
* 1. a periodic timer which will run every 0.5s, and print a message
* 2. a one-shot timer which will fire after 5s, and re-start periodic
* timer with period of 1s.
*/
const esp_timer_create_args_t periodic_timer_args = {
.callback = &periodic_timer_callback,
/* name is optional, but may help identify the timer when debugging */
.name = "periodic"
};
esp_timer_handle_t periodic_timer;
ESP_ERROR_CHECK(esp_timer_create(&periodic_timer_args, &periodic_timer));
/* The timer has been created but is not running yet */
const esp_timer_create_args_t oneshot_timer_args = {
.callback = &oneshot_timer_callback,
/* argument specified here will be passed to timer callback function */
.arg = (void*) periodic_timer,
.name = "one-shot"
};
esp_timer_handle_t oneshot_timer;
ESP_ERROR_CHECK(esp_timer_create(&oneshot_timer_args, &oneshot_timer));
/* Start the timers */
ESP_ERROR_CHECK(esp_timer_start_periodic(periodic_timer, 500000));
ESP_ERROR_CHECK(esp_timer_start_once(oneshot_timer, 5000000));
ESP_LOGI(TAG, "Started timers, time since boot: %lld us", esp_timer_get_time());
/* Print debugging information about timers to console every 2 seconds */
for (int i = 0; i < 5; ++i) {
ESP_ERROR_CHECK(esp_timer_dump(stdout));
usleep(2000000);
}
/* Timekeeping continues in light sleep, and timers are scheduled
* correctly after light sleep.
*/
ESP_LOGI(TAG, "Entering light sleep for 0.5s, time since boot: %lld us",
esp_timer_get_time());
ESP_ERROR_CHECK(esp_sleep_enable_timer_wakeup(500000));
esp_light_sleep_start();
ESP_LOGI(TAG, "Woke up from light sleep, time since boot: %lld us",
esp_timer_get_time());
/* Let the timer run for a little bit more */
usleep(2000000);
/* Clean up and finish the example */
ESP_ERROR_CHECK(esp_timer_stop(periodic_timer));
ESP_ERROR_CHECK(esp_timer_delete(periodic_timer));
ESP_ERROR_CHECK(esp_timer_delete(oneshot_timer));
ESP_LOGI(TAG, "Stopped and deleted timers");
}
static void periodic_timer_callback(void* arg)
{
int64_t time_since_boot = esp_timer_get_time();
ESP_LOGI(TAG, "Periodic timer called, time since boot: %lld us", time_since_boot);
}
static void oneshot_timer_callback(void* arg)
{
int64_t time_since_boot = esp_timer_get_time();
ESP_LOGI(TAG, "One-shot timer called, time since boot: %lld us", time_since_boot);
esp_timer_handle_t periodic_timer_handle = (esp_timer_handle_t) arg;
/* To start the timer which is running, need to stop it first */
ESP_ERROR_CHECK(esp_timer_stop(periodic_timer_handle));
ESP_ERROR_CHECK(esp_timer_start_periodic(periodic_timer_handle, 1000000));
time_since_boot = esp_timer_get_time();
ESP_LOGI(TAG, "Restarted periodic timer with 1s period, time since boot: %lld us",
time_since_boot);
}

View file

@ -0,0 +1 @@
CONFIG_ESP_TIMER_PROFILING=y