diff --git a/examples/peripherals/timer_group/README.md b/examples/peripherals/timer_group/README.md index 7fb754c47a..1da4ef74d3 100644 --- a/examples/peripherals/timer_group/README.md +++ b/examples/peripherals/timer_group/README.md @@ -19,40 +19,27 @@ See the [ESP-IDF Getting Started Guide](https://idf.espressif.com/) for all the ## Example Output ``` -Timer Group with auto reload -Group[0], timer[0] alarm event -------- EVENT TIME -------- -Counter: 0x0000000000000008 -Time : 0.00000160 s --------- TASK TIME -------- -Counter: 0x0000000000004ed8 -Time : 0.00403680 s -Timer Group without auto reload -Group[1], timer[0] alarm event -------- EVENT TIME -------- -Counter: 0x00000000017d7848 -Time : 5.00000160 s --------- TASK TIME -------- -Counter: 0x00000000017dcb32 -Time : 5.00424680 s -Timer Group with auto reload -Group[0], timer[0] alarm event -------- EVENT TIME -------- -Counter: 0x0000000000000008 -Time : 0.00000160 s --------- TASK TIME -------- -Counter: 0x0000000000004dd4 -Time : 0.00398480 s +I (0) cpu_start: Starting scheduler on APP CPU. +I (325) example: Init timer with auto-reload +I (835) example: Timer auto reloaded, count value in ISR: 3 +I (1335) example: Timer auto reloaded, count value in ISR: 3 +I (1835) example: Timer auto reloaded, count value in ISR: 3 +I (2335) example: Timer auto reloaded, count value in ISR: 3 +I (2335) example: Init timer without auto-reload +I (2835) example: Timer alarmed at 500003 +I (3335) example: Timer alarmed at 1000003 +I (3835) example: Timer alarmed at 1500003 +I (4335) example: Timer alarmed at 2000003 ``` ## Functionality Overview -* Two timers are configured -* Each timer is set with some sample alarm interval -* On reaching the interval value each timer will generate an alarm -* One of the timers is configured to automatically reload it's counter value on the alarm -* The other timer is configured to keep incrementing and is reloaded by the application each time the alarm happens -* Alarms trigger subsequent interrupts, that is tracked with messages printed on the terminal: +* Configure one timer with auto-reload enabled, alarm period set to 0.5s +* On reaching the interval value the timer will generate an alarm +* The timer will reload with initial count value on alarm, by hardware +* Reconfigure the timer with auto-reload disabled, initial alarm value set to 0.5s +* The timer keeps incrementing and in the alarm callback, the software reconfigures its alarm value by increasing 0.5s +* The main task will print the count value that captured in the alarm callback ## Troubleshooting diff --git a/examples/peripherals/timer_group/example_test.py b/examples/peripherals/timer_group/example_test.py deleted file mode 100644 index f7fc541ab9..0000000000 --- a/examples/peripherals/timer_group/example_test.py +++ /dev/null @@ -1,42 +0,0 @@ -#!/usr/bin/env python -# -# SPDX-FileCopyrightText: 2019-2021 Espressif Systems (Shanghai) CO LTD -# -# SPDX-License-Identifier: CC0-1.0 -# - -from __future__ import unicode_literals - -import re -from typing import Any - -import ttfw_idf - - -@ttfw_idf.idf_example_test(env_tag='Example_GENERIC', target=['esp32', 'esp32s2', 'esp32c3']) -def test_examples_timergroup(env, extra_data): # type: (Any, Any) -> None - dut = env.get_dut('timer_group', 'examples/peripherals/timer_group') - dut.start_app() - - # check auto reload function - with_auto_reload = dut.expect(re.compile(r'Timer Group (\S+) auto reload'), timeout=30)[0] - assert with_auto_reload == 'with' - select_groups = dut.expect(re.compile(r'Group\[(\d)\], timer\[(\d)\] alarm event')) - timer_group_num = int(select_groups[0]) - timer_instance_num = int(select_groups[1]) - assert timer_group_num == 0 and timer_instance_num == 0 - dut.expect('EVENT TIME') - counter_value = dut.expect(re.compile(r'Counter:\s+(0x\d+)'))[0] - counter_value = int(counter_value, 16) - print('counter value at auto reload event: ', counter_value) - assert counter_value < 20 - - # check timer interval - dut.expect('Timer Group without auto reload', timeout=5) - dut.expect('EVENT TIME') - event_time0 = dut.expect(re.compile(r'Time\s+:\s+(\d+\.\d+)\s+s'))[0] - print('event0={}'.format(event_time0)) - - -if __name__ == '__main__': - test_examples_timergroup() diff --git a/examples/peripherals/timer_group/main/timer_group_example_main.c b/examples/peripherals/timer_group/main/timer_group_example_main.c index fbc23c144f..5b90ab190e 100644 --- a/examples/peripherals/timer_group/main/timer_group_example_main.c +++ b/examples/peripherals/timer_group/main/timer_group_example_main.c @@ -9,131 +9,119 @@ #include "freertos/task.h" #include "freertos/queue.h" #include "driver/timer.h" +#include "esp_log.h" -#define TIMER_DIVIDER (16) // Hardware timer clock divider -#define TIMER_SCALE (TIMER_BASE_CLK / TIMER_DIVIDER) // convert counter value to seconds +#define TIMER_RESOLUTION_HZ 1000000 // 1MHz resolution +#define TIMER_ALARM_PERIOD_S 0.5 // Alarm period 0.5s -typedef struct { - int timer_group; - int timer_idx; - int alarm_interval; - bool auto_reload; -} example_timer_info_t; +static const char *TAG = "example"; /** * @brief A sample structure to pass events from the timer ISR to task - * */ typedef struct { - example_timer_info_t info; - uint64_t timer_counter_value; + uint64_t timer_count_value; } example_timer_event_t; -static xQueueHandle s_timer_queue; - -/* - * A simple helper function to print the raw timer counter value - * and the counter value converted to seconds +/** + * @brief Timer user data, will be pass to timer alarm callback */ -static void inline print_timer_counter(uint64_t counter_value) -{ - printf("Counter: 0x%08x%08x\r\n", (uint32_t) (counter_value >> 32), - (uint32_t) (counter_value)); - printf("Time : %.8f s\r\n", (double) counter_value / TIMER_SCALE); -} +typedef struct { + xQueueHandle user_queue; + int timer_group; + int timer_idx; + int alarm_value; + bool auto_reload; +} example_timer_user_data_t; static bool IRAM_ATTR timer_group_isr_callback(void *args) { BaseType_t high_task_awoken = pdFALSE; - example_timer_info_t *info = (example_timer_info_t *) args; - - uint64_t timer_counter_value = timer_group_get_counter_value_in_isr(info->timer_group, info->timer_idx); - - /* Prepare basic event data that will be then sent back to task */ + example_timer_user_data_t *user_data = (example_timer_user_data_t *) args; + // fetch current count value + uint64_t timer_count_value = timer_group_get_counter_value_in_isr(user_data->timer_group, user_data->timer_idx); example_timer_event_t evt = { - .info.timer_group = info->timer_group, - .info.timer_idx = info->timer_idx, - .info.auto_reload = info->auto_reload, - .info.alarm_interval = info->alarm_interval, - .timer_counter_value = timer_counter_value + .timer_count_value = timer_count_value, }; - if (!info->auto_reload) { - timer_counter_value += info->alarm_interval * TIMER_SCALE; - timer_group_set_alarm_value_in_isr(info->timer_group, info->timer_idx, timer_counter_value); + // set new alarm value if necessary + if (!user_data->auto_reload) { + user_data->alarm_value += TIMER_ALARM_PERIOD_S * TIMER_RESOLUTION_HZ; + timer_group_set_alarm_value_in_isr(user_data->timer_group, user_data->timer_idx, user_data->alarm_value); } - /* Now just send the event data back to the main program task */ - xQueueSendFromISR(s_timer_queue, &evt, &high_task_awoken); + // Send the event data back to the main program task + xQueueSendFromISR(user_data->user_queue, &evt, &high_task_awoken); - return high_task_awoken == pdTRUE; // return whether we need to yield at the end of ISR + return high_task_awoken == pdTRUE; // return whether a task switch is needed } -/** - * @brief Initialize selected timer of timer group - * - * @param group Timer Group number, index from 0 - * @param timer timer ID, index from 0 - * @param auto_reload whether auto-reload on alarm event - * @param timer_interval_sec interval of alarm - */ -static void example_tg_timer_init(int group, int timer, bool auto_reload, int timer_interval_sec) +static void example_tg_timer_init(example_timer_user_data_t *user_data) { - /* Select and initialize basic parameters of the timer */ + int group = user_data->timer_group; + int timer = user_data->timer_idx; + timer_config_t config = { - .divider = TIMER_DIVIDER, + .clk_src = TIMER_SRC_CLK_APB, + .divider = APB_CLK_FREQ / TIMER_RESOLUTION_HZ, .counter_dir = TIMER_COUNT_UP, .counter_en = TIMER_PAUSE, .alarm_en = TIMER_ALARM_EN, - .auto_reload = auto_reload, - }; // default clock source is APB - timer_init(group, timer, &config); + .auto_reload = user_data->auto_reload, + }; + ESP_ERROR_CHECK(timer_init(group, timer, &config)); - /* Timer's counter will initially start from value below. - Also, if auto_reload is set, this value will be automatically reload on alarm */ - timer_set_counter_value(group, timer, 0); + // For the timer counter to a initial value + ESP_ERROR_CHECK(timer_set_counter_value(group, timer, 0)); + // Set alarm value and enable alarm interrupt + ESP_ERROR_CHECK(timer_set_alarm_value(group, timer, user_data->alarm_value)); + ESP_ERROR_CHECK(timer_enable_intr(group, timer)); + // Hook interrupt callback + ESP_ERROR_CHECK(timer_isr_callback_add(group, timer, timer_group_isr_callback, user_data, 0)); + // Start timer + ESP_ERROR_CHECK(timer_start(group, timer)); +} - /* Configure the alarm value and the interrupt on alarm. */ - timer_set_alarm_value(group, timer, timer_interval_sec * TIMER_SCALE); - timer_enable_intr(group, timer); - - example_timer_info_t *timer_info = calloc(1, sizeof(example_timer_info_t)); - timer_info->timer_group = group; - timer_info->timer_idx = timer; - timer_info->auto_reload = auto_reload; - timer_info->alarm_interval = timer_interval_sec; - timer_isr_callback_add(group, timer, timer_group_isr_callback, timer_info, 0); - - timer_start(group, timer); +static void example_tg_timer_deinit(int group, int timer) +{ + ESP_ERROR_CHECK(timer_isr_callback_remove(group, timer)); + ESP_ERROR_CHECK(timer_deinit(group, timer)); } void app_main(void) { - s_timer_queue = xQueueCreate(10, sizeof(example_timer_event_t)); + example_timer_user_data_t *user_data = calloc(1, sizeof(example_timer_user_data_t)); + assert(user_data); + user_data->user_queue = xQueueCreate(10, sizeof(example_timer_event_t)); + assert(user_data->user_queue); + user_data->timer_group = 0; + user_data->timer_idx = 0; + user_data->alarm_value = TIMER_ALARM_PERIOD_S * TIMER_RESOLUTION_HZ; - example_tg_timer_init(TIMER_GROUP_0, TIMER_0, true, 3); - example_tg_timer_init(TIMER_GROUP_1, TIMER_0, false, 5); - while (1) { - example_timer_event_t evt; - xQueueReceive(s_timer_queue, &evt, portMAX_DELAY); + ESP_LOGI(TAG, "Init timer with auto-reload"); + user_data->auto_reload = true; + example_tg_timer_init(user_data); - /* Print information that the timer reported an event */ - if (evt.info.auto_reload) { - printf("Timer Group with auto reload\n"); - } else { - printf("Timer Group without auto reload\n"); - } - printf("Group[%d], timer[%d] alarm event\n", evt.info.timer_group, evt.info.timer_idx); - - /* Print the timer values passed by event */ - printf("------- EVENT TIME --------\n"); - print_timer_counter(evt.timer_counter_value); - - /* Print the timer values as visible by this task */ - printf("-------- TASK TIME --------\n"); - uint64_t task_counter_value; - timer_get_counter_value(evt.info.timer_group, evt.info.timer_idx, &task_counter_value); - print_timer_counter(task_counter_value); + example_timer_event_t evt; + uint32_t test_count = 4; + while (test_count--) { + xQueueReceive(user_data->user_queue, &evt, portMAX_DELAY); + ESP_LOGI(TAG, "Timer auto reloaded, count value in ISR: %llu", evt.timer_count_value); } + example_tg_timer_deinit(user_data->timer_group, user_data->timer_idx); + + ESP_LOGI(TAG, "Init timer without auto-reload"); + user_data->auto_reload = false; + example_tg_timer_init(user_data); + + test_count = 4; + while (test_count--) { + xQueueReceive(user_data->user_queue, &evt, portMAX_DELAY); + ESP_LOGI(TAG, "Timer alarmed at %llu", evt.timer_count_value); + } + example_tg_timer_deinit(user_data->timer_group, user_data->timer_idx); + + vQueueDelete(user_data->user_queue); + free(user_data); } diff --git a/examples/peripherals/timer_group/pytest_timer_group.py b/examples/peripherals/timer_group/pytest_timer_group.py new file mode 100644 index 0000000000..b715ec81ea --- /dev/null +++ b/examples/peripherals/timer_group/pytest_timer_group.py @@ -0,0 +1,24 @@ +# SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: CC0-1.0 + +import pytest +from pytest_embedded.dut import Dut + + +@pytest.mark.esp32 +@pytest.mark.esp32s2 +@pytest.mark.esp32s3 +@pytest.mark.esp32c3 +@pytest.mark.generic +def test_timer_group_example(dut: Dut): # type: ignore + dut.expect(r'Init timer with auto-reload', timeout=5) + res = dut.expect(r'Timer auto reloaded, count value in ISR: (\d+)', timeout=5) + reloaded_count = res.group(1).decode('utf8') + assert 0 <= int(reloaded_count) < 10 + + alarm_increase_step = 500000 + dut.expect(r'Init timer without auto-reload') + for i in range(1, 5): + res = dut.expect(r'Timer alarmed at (\d+)', timeout=3) + alarm_count = res.group(1).decode('utf8') + assert (i * alarm_increase_step - 10) < int(alarm_count) < (i * alarm_increase_step + 10)