/* * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ #include #include "sdkconfig.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "unity.h" #include "driver/gptimer.h" #include "soc/soc_caps.h" #include "esp_attr.h" #if CONFIG_GPTIMER_ISR_IRAM_SAFE #define TEST_ALARM_CALLBACK_ATTR IRAM_ATTR #else #define TEST_ALARM_CALLBACK_ATTR #endif // CONFIG_GPTIMER_ISR_IRAM_SAFE void test_app_include_gptimer(void) { } TEST_CASE("gptimer_set_get_raw_count", "[gptimer]") { gptimer_config_t config = { .clk_src = GPTIMER_CLK_SRC_APB, .direction = GPTIMER_COUNT_UP, .resolution_hz = 1 * 1000 * 1000, }; gptimer_handle_t timers[SOC_TIMER_GROUP_TOTAL_TIMERS]; for (int i = 0; i < SOC_TIMER_GROUP_TOTAL_TIMERS; i++) { TEST_ESP_OK(gptimer_new_timer(&config, &timers[i])); } TEST_ASSERT_EQUAL(ESP_ERR_NOT_FOUND, gptimer_new_timer(&config, &timers[0])); unsigned long long get_value = 0; printf("check gptimer initial count value\r\n"); for (int i = 0; i < SOC_TIMER_GROUP_TOTAL_TIMERS; i++) { TEST_ESP_OK(gptimer_get_raw_count(timers[i], &get_value)); TEST_ASSERT_EQUAL(0, get_value); } unsigned long long set_values[] = {100, 500, 666}; for (size_t j = 0; j < sizeof(set_values) / sizeof(set_values[0]); j++) { for (int i = 0; i < SOC_TIMER_GROUP_TOTAL_TIMERS; i++) { printf("set raw count to %llu for gptimer %d\r\n", set_values[j], i); TEST_ESP_OK(gptimer_set_raw_count(timers[i], set_values[j])); } vTaskDelay(pdMS_TO_TICKS(10)); for (int i = 0; i < SOC_TIMER_GROUP_TOTAL_TIMERS; i++) { TEST_ESP_OK(gptimer_get_raw_count(timers[i], &get_value)); printf("get raw count of gptimer %d: %llu\r\n", i, get_value); TEST_ASSERT_EQUAL(set_values[j], get_value); } } for (int i = 0; i < SOC_TIMER_GROUP_TOTAL_TIMERS; i++) { TEST_ESP_OK(gptimer_del_timer(timers[i])); } } TEST_CASE("gptimer_wallclock_with_various_clock_sources", "[gptimer]") { gptimer_clock_source_t test_clk_srcs[] = { GPTIMER_CLK_SRC_APB, #if SOC_TIMER_GROUP_SUPPORT_XTAL GPTIMER_CLK_SRC_XTAL, #endif // SOC_TIMER_GROUP_SUPPORT_XTAL }; // test with various clock sources for (size_t i = 0; i < sizeof(test_clk_srcs) / sizeof(test_clk_srcs[0]); i++) { gptimer_config_t timer_config = { .clk_src = test_clk_srcs[i], .direction = GPTIMER_COUNT_UP, .resolution_hz = 1 * 1000 * 1000, }; gptimer_handle_t timers[SOC_TIMER_GROUP_TOTAL_TIMERS]; for (int i = 0; i < SOC_TIMER_GROUP_TOTAL_TIMERS; i++) { TEST_ESP_OK(gptimer_new_timer(&timer_config, &timers[i])); } printf("start timers\r\n"); for (int i = 0; i < SOC_TIMER_GROUP_TOTAL_TIMERS; i++) { TEST_ESP_OK(gptimer_start(timers[i])); } vTaskDelay(pdMS_TO_TICKS(20)); // 20ms = 20_000 ticks unsigned long long value = 0; for (int i = 0; i < SOC_TIMER_GROUP_TOTAL_TIMERS; i++) { TEST_ESP_OK(gptimer_get_raw_count(timers[i], &value)); TEST_ASSERT_UINT_WITHIN(1000, 20000, value); } printf("stop timers\r\n"); for (int i = 0; i < SOC_TIMER_GROUP_TOTAL_TIMERS; i++) { TEST_ESP_OK(gptimer_stop(timers[i])); } printf("check whether timers have stopped\r\n"); vTaskDelay(pdMS_TO_TICKS(20)); for (int i = 0; i < SOC_TIMER_GROUP_TOTAL_TIMERS; i++) { TEST_ESP_OK(gptimer_get_raw_count(timers[i], &value)); printf("get raw count of gptimer %d: %llu\r\n", i, value); TEST_ASSERT_UINT_WITHIN(1000, 20000, value); } printf("restart timers\r\n"); for (int i = 0; i < SOC_TIMER_GROUP_TOTAL_TIMERS; i++) { TEST_ESP_OK(gptimer_start(timers[i])); } vTaskDelay(pdMS_TO_TICKS(20)); printf("stop timers again\r\n"); for (int i = 0; i < SOC_TIMER_GROUP_TOTAL_TIMERS; i++) { TEST_ESP_OK(gptimer_stop(timers[i])); } printf("check whether timers have stopped\r\n"); vTaskDelay(pdMS_TO_TICKS(20)); for (int i = 0; i < SOC_TIMER_GROUP_TOTAL_TIMERS; i++) { TEST_ESP_OK(gptimer_get_raw_count(timers[i], &value)); printf("get raw count of gptimer %d: %llu\r\n", i, value); TEST_ASSERT_UINT_WITHIN(2000, 40000, value); } for (int i = 0; i < SOC_TIMER_GROUP_TOTAL_TIMERS; i++) { TEST_ESP_OK(gptimer_del_timer(timers[i])); } } } TEST_ALARM_CALLBACK_ATTR static bool test_gptimer_alarm_stop_callback(gptimer_handle_t timer, const gptimer_alarm_event_data_t *edata, void *user_data) { xTaskHandle task_handle = (xTaskHandle)user_data; BaseType_t high_task_wakeup; gptimer_stop(timer); esp_rom_printf("count=%lld @alarm\n", edata->count_value); vTaskNotifyGiveFromISR(task_handle, &high_task_wakeup); return high_task_wakeup == pdTRUE; } TEST_CASE("gptimer_stop_on_alarm", "[gptimer]") { xTaskHandle task_handle = xTaskGetCurrentTaskHandle(); gptimer_config_t timer_config = { .resolution_hz = 1 * 1000 * 1000, .clk_src = GPTIMER_CLK_SRC_APB, .direction = GPTIMER_COUNT_UP, }; gptimer_handle_t timers[SOC_TIMER_GROUP_TOTAL_TIMERS]; for (int i = 0; i < SOC_TIMER_GROUP_TOTAL_TIMERS; i++) { TEST_ESP_OK(gptimer_new_timer(&timer_config, &timers[i])); } gptimer_event_callbacks_t cbs = { .on_alarm = test_gptimer_alarm_stop_callback, }; gptimer_alarm_config_t alarm_config = {}; printf("start timers\r\n"); for (int i = 0; i < SOC_TIMER_GROUP_TOTAL_TIMERS; i++) { alarm_config.alarm_count = 100000 * (i + 1); TEST_ESP_OK(gptimer_set_alarm_action(timers[i], &alarm_config)); TEST_ESP_OK(gptimer_register_event_callbacks(timers[i], &cbs, task_handle)); TEST_ESP_OK(gptimer_start(timers[i])); printf("alarm value for gptimer %d: %llu\r\n", i, alarm_config.alarm_count); } for (int i = 0; i < SOC_TIMER_GROUP_TOTAL_TIMERS; i++) { TEST_ASSERT_NOT_EQUAL(0, ulTaskNotifyTake(pdFALSE, pdMS_TO_TICKS(1000))); } printf("check whether the timers have stopped in the ISR\r\n"); vTaskDelay(pdMS_TO_TICKS(20)); unsigned long long value = 0; for (int i = 0; i < SOC_TIMER_GROUP_TOTAL_TIMERS; i++) { TEST_ESP_OK(gptimer_get_raw_count(timers[i], &value)); printf("get raw count of gptimer %d: %llu\r\n", i, value); TEST_ASSERT_UINT_WITHIN(40, 100000 * (i + 1), value); } printf("restart timers\r\n"); for (int i = 0; i < SOC_TIMER_GROUP_TOTAL_TIMERS; i++) { alarm_config.alarm_count = 100000 * (i + 1); // reset counter value to zero TEST_ESP_OK(gptimer_set_raw_count(timers[i], 0)); TEST_ESP_OK(gptimer_start(timers[i])); } for (int i = 0; i < SOC_TIMER_GROUP_TOTAL_TIMERS; i++) { TEST_ASSERT_NOT_EQUAL(0, ulTaskNotifyTake(pdFALSE, pdMS_TO_TICKS(1000))); } printf("check whether the timers have stopped in the ISR\r\n"); vTaskDelay(pdMS_TO_TICKS(20)); for (int i = 0; i < SOC_TIMER_GROUP_TOTAL_TIMERS; i++) { TEST_ESP_OK(gptimer_get_raw_count(timers[i], &value)); printf("get raw count of gptimer %d: %llu\r\n", i, value); TEST_ASSERT_UINT_WITHIN(40, 100000 * (i + 1), value); } for (int i = 0; i < SOC_TIMER_GROUP_TOTAL_TIMERS; i++) { TEST_ESP_OK(gptimer_del_timer(timers[i])); } } TEST_ALARM_CALLBACK_ATTR static bool test_gptimer_alarm_reload_callback(gptimer_handle_t timer, const gptimer_alarm_event_data_t *edata, void *user_data) { xTaskHandle task_handle = (xTaskHandle)user_data; BaseType_t high_task_wakeup; esp_rom_printf("alarm isr count=%llu\r\n", edata->count_value); // check if the count value has been reloaded TEST_ASSERT_UINT_WITHIN(20, 100, edata->count_value); vTaskNotifyGiveFromISR(task_handle, &high_task_wakeup); return high_task_wakeup == pdTRUE; } TEST_CASE("gptimer_auto_reload_on_alarm", "[gptimer]") { xTaskHandle task_handle = xTaskGetCurrentTaskHandle(); gptimer_config_t timer_config = { .resolution_hz = 1 * 1000 * 1000, .clk_src = GPTIMER_CLK_SRC_APB, .direction = GPTIMER_COUNT_UP, }; gptimer_handle_t timers[SOC_TIMER_GROUP_TOTAL_TIMERS]; for (int i = 0; i < SOC_TIMER_GROUP_TOTAL_TIMERS; i++) { TEST_ESP_OK(gptimer_new_timer(&timer_config, &timers[i])); } gptimer_event_callbacks_t cbs = { .on_alarm = test_gptimer_alarm_reload_callback, }; gptimer_alarm_config_t alarm_config = { .reload_count = 100, .alarm_count = 100000, .flags.auto_reload_on_alarm = true, }; printf("start timers\r\n"); for (int i = 0; i < SOC_TIMER_GROUP_TOTAL_TIMERS; i++) { TEST_ESP_OK(gptimer_set_alarm_action(timers[i], &alarm_config)); TEST_ESP_OK(gptimer_register_event_callbacks(timers[i], &cbs, task_handle)); TEST_ESP_OK(gptimer_start(timers[i])); TEST_ASSERT_NOT_EQUAL(0, ulTaskNotifyTake(pdFALSE, pdMS_TO_TICKS(1000))); TEST_ASSERT_NOT_EQUAL(0, ulTaskNotifyTake(pdFALSE, pdMS_TO_TICKS(1000))); TEST_ASSERT_NOT_EQUAL(0, ulTaskNotifyTake(pdFALSE, pdMS_TO_TICKS(1000))); // delete should fail if timer is not stopped TEST_ASSERT_EQUAL(ESP_ERR_INVALID_STATE, gptimer_del_timer(timers[i])); TEST_ESP_OK(gptimer_stop(timers[i])); } for (int i = 0; i < SOC_TIMER_GROUP_TOTAL_TIMERS; i++) { TEST_ESP_OK(gptimer_del_timer(timers[i])); } } TEST_ALARM_CALLBACK_ATTR static bool test_gptimer_alarm_normal_callback(gptimer_handle_t timer, const gptimer_alarm_event_data_t *edata, void *user_data) { xTaskHandle task_handle = (xTaskHandle)user_data; BaseType_t high_task_wakeup; esp_rom_printf("alarm isr count=%llu\r\n", edata->count_value); // check the count value at alarm event vTaskNotifyGiveFromISR(task_handle, &high_task_wakeup); return high_task_wakeup == pdTRUE; } TEST_CASE("gptimer_one_shot_alarm", "[gptimer]") { xTaskHandle task_handle = xTaskGetCurrentTaskHandle(); gptimer_config_t timer_config = { .resolution_hz = 1 * 1000 * 1000, .clk_src = GPTIMER_CLK_SRC_APB, .direction = GPTIMER_COUNT_UP, }; gptimer_handle_t timers[SOC_TIMER_GROUP_TOTAL_TIMERS]; for (int i = 0; i < SOC_TIMER_GROUP_TOTAL_TIMERS; i++) { TEST_ESP_OK(gptimer_new_timer(&timer_config, &timers[i])); } gptimer_event_callbacks_t cbs = { .on_alarm = test_gptimer_alarm_normal_callback, }; gptimer_alarm_config_t alarm_config = { .reload_count = 0, .alarm_count = 100000, // 100ms }; printf("start timers\r\n"); for (int i = 0; i < SOC_TIMER_GROUP_TOTAL_TIMERS; i++) { TEST_ESP_OK(gptimer_set_alarm_action(timers[i], &alarm_config)); TEST_ESP_OK(gptimer_register_event_callbacks(timers[i], &cbs, task_handle)); TEST_ESP_OK(gptimer_start(timers[i])); TEST_ASSERT_NOT_EQUAL(0, ulTaskNotifyTake(pdFALSE, pdMS_TO_TICKS(1000))); // no alarm event should trigger again, as auto-reload is not enabled and alarm value hasn't changed in the isr TEST_ASSERT_EQUAL(0, ulTaskNotifyTake(pdFALSE, pdMS_TO_TICKS(1000))); // the alarm is stopped, but the counter should still work uint64_t value = 0; TEST_ESP_OK(gptimer_get_raw_count(timers[i], &value)); TEST_ASSERT_UINT_WITHIN(1000, 1100000, value); // 1100000 = 100ms alarm + 1s delay TEST_ESP_OK(gptimer_stop(timers[i])); } printf("restart timers\r\n"); for (int i = 0; i < SOC_TIMER_GROUP_TOTAL_TIMERS; i++) { TEST_ESP_OK(gptimer_start(timers[i])); // alarm should be triggered immediately as the counter value has across the target alarm value already TEST_ASSERT_NOT_EQUAL(0, ulTaskNotifyTake(pdFALSE, 0)); TEST_ESP_OK(gptimer_stop(timers[i])); } for (int i = 0; i < SOC_TIMER_GROUP_TOTAL_TIMERS; i++) { TEST_ESP_OK(gptimer_del_timer(timers[i])); } } TEST_ALARM_CALLBACK_ATTR static bool test_gptimer_alarm_update_callback(gptimer_handle_t timer, const gptimer_alarm_event_data_t *edata, void *user_data) { xTaskHandle task_handle = (xTaskHandle)user_data; BaseType_t high_task_wakeup; esp_rom_printf("alarm isr count=%llu\r\n", edata->count_value); gptimer_alarm_config_t alarm_config = { .alarm_count = edata->count_value + 100000, // alarm in next 100ms again }; gptimer_set_alarm_action(timer, &alarm_config); vTaskNotifyGiveFromISR(task_handle, &high_task_wakeup); return high_task_wakeup == pdTRUE; } TEST_CASE("gptimer_update_alarm_dynamically", "[gptimer]") { xTaskHandle task_handle = xTaskGetCurrentTaskHandle(); gptimer_config_t timer_config = { .resolution_hz = 1 * 1000 * 1000, .clk_src = GPTIMER_CLK_SRC_APB, .direction = GPTIMER_COUNT_UP, }; gptimer_handle_t timers[SOC_TIMER_GROUP_TOTAL_TIMERS]; for (int i = 0; i < SOC_TIMER_GROUP_TOTAL_TIMERS; i++) { TEST_ESP_OK(gptimer_new_timer(&timer_config, &timers[i])); } gptimer_event_callbacks_t cbs = { .on_alarm = test_gptimer_alarm_update_callback, }; gptimer_alarm_config_t alarm_config = { .alarm_count = 100000, // initial alarm count, 100ms }; printf("start timers\r\n"); for (int i = 0; i < SOC_TIMER_GROUP_TOTAL_TIMERS; i++) { TEST_ESP_OK(gptimer_set_alarm_action(timers[i], &alarm_config)); TEST_ESP_OK(gptimer_register_event_callbacks(timers[i], &cbs, task_handle)); TEST_ESP_OK(gptimer_start(timers[i])); // check the alarm event for multiple times TEST_ASSERT_NOT_EQUAL(0, ulTaskNotifyTake(pdFALSE, pdMS_TO_TICKS(500))); TEST_ASSERT_NOT_EQUAL(0, ulTaskNotifyTake(pdFALSE, pdMS_TO_TICKS(500))); TEST_ASSERT_NOT_EQUAL(0, ulTaskNotifyTake(pdFALSE, pdMS_TO_TICKS(500))); TEST_ESP_OK(gptimer_stop(timers[i])); // check there won't be more interrupts triggered than expected TEST_ASSERT_EQUAL(0, ulTaskNotifyTake(pdFALSE, pdMS_TO_TICKS(500))); } printf("restart timers\r\n"); for (int i = 0; i < SOC_TIMER_GROUP_TOTAL_TIMERS; i++) { TEST_ESP_OK(gptimer_start(timers[i])); // check the alarm event for multiple times TEST_ASSERT_NOT_EQUAL(0, ulTaskNotifyTake(pdFALSE, pdMS_TO_TICKS(500))); TEST_ASSERT_NOT_EQUAL(0, ulTaskNotifyTake(pdFALSE, pdMS_TO_TICKS(500))); TEST_ASSERT_NOT_EQUAL(0, ulTaskNotifyTake(pdFALSE, pdMS_TO_TICKS(500))); TEST_ESP_OK(gptimer_stop(timers[i])); // check there won't be more interrupts triggered than expected TEST_ASSERT_EQUAL(0, ulTaskNotifyTake(pdFALSE, pdMS_TO_TICKS(500))); } for (int i = 0; i < SOC_TIMER_GROUP_TOTAL_TIMERS; i++) { TEST_ESP_OK(gptimer_del_timer(timers[i])); } } TEST_ALARM_CALLBACK_ATTR static bool test_gptimer_count_down_reload_alarm_callback(gptimer_handle_t timer, const gptimer_alarm_event_data_t *edata, void *user_data) { xTaskHandle task_handle = (xTaskHandle)user_data; BaseType_t high_task_wakeup; esp_rom_printf("alarm isr count=%llu\r\n", edata->count_value); // check if the count value has been reloaded TEST_ASSERT_UINT_WITHIN(20, 200000, edata->count_value); vTaskNotifyGiveFromISR(task_handle, &high_task_wakeup); return high_task_wakeup == pdTRUE; } TEST_CASE("gptimer_count_down_reload", "[gptimer]") { xTaskHandle task_handle = xTaskGetCurrentTaskHandle(); gptimer_config_t timer_config = { .resolution_hz = 1 * 1000 * 1000, .clk_src = GPTIMER_CLK_SRC_APB, .direction = GPTIMER_COUNT_DOWN, }; gptimer_handle_t timers[SOC_TIMER_GROUP_TOTAL_TIMERS]; for (int i = 0; i < SOC_TIMER_GROUP_TOTAL_TIMERS; i++) { TEST_ESP_OK(gptimer_new_timer(&timer_config, &timers[i])); TEST_ESP_OK(gptimer_set_raw_count(timers[i], 200000)); } gptimer_event_callbacks_t cbs = { .on_alarm = test_gptimer_count_down_reload_alarm_callback, }; gptimer_alarm_config_t alarm_config = { .reload_count = 200000, // 200ms .alarm_count = 0, .flags.auto_reload_on_alarm = true, }; for (int i = 0; i < SOC_TIMER_GROUP_TOTAL_TIMERS; i++) { TEST_ESP_OK(gptimer_set_alarm_action(timers[i], &alarm_config)); TEST_ESP_OK(gptimer_register_event_callbacks(timers[i], &cbs, task_handle)); TEST_ESP_OK(gptimer_start(timers[i])); // check twice, as it's a period event TEST_ASSERT_NOT_EQUAL(0, ulTaskNotifyTake(pdFALSE, pdMS_TO_TICKS(1000))); TEST_ASSERT_NOT_EQUAL(0, ulTaskNotifyTake(pdFALSE, pdMS_TO_TICKS(1000))); TEST_ESP_OK(gptimer_stop(timers[i])); } printf("restart gptimer with previous configuration\r\n"); for (int i = 0; i < SOC_TIMER_GROUP_TOTAL_TIMERS; i++) { TEST_ESP_OK(gptimer_start(timers[i])); // check twice, as it's a period event TEST_ASSERT_NOT_EQUAL(0, ulTaskNotifyTake(pdFALSE, pdMS_TO_TICKS(1000))); TEST_ASSERT_NOT_EQUAL(0, ulTaskNotifyTake(pdFALSE, pdMS_TO_TICKS(1000))); TEST_ESP_OK(gptimer_stop(timers[i])); } for (int i = 0; i < SOC_TIMER_GROUP_TOTAL_TIMERS; i++) { TEST_ESP_OK(gptimer_del_timer(timers[i])); } } TEST_ALARM_CALLBACK_ATTR static bool test_gptimer_overflow_reload_callback(gptimer_handle_t timer, const gptimer_alarm_event_data_t *edata, void *user_data) { xTaskHandle task_handle = (xTaskHandle)user_data; BaseType_t high_task_wakeup; // Note: esp_rom_printf can't print value with 64 bit length, so the following print result is meaningless, but as an incidator for test that the alarm has fired esp_rom_printf("alarm isr count=%llu\r\n", edata->count_value); vTaskNotifyGiveFromISR(task_handle, &high_task_wakeup); return high_task_wakeup == pdTRUE; } TEST_CASE("gptimer_overflow", "[gptimer]") { xTaskHandle task_handle = xTaskGetCurrentTaskHandle(); gptimer_config_t timer_config = { .resolution_hz = 1 * 1000 * 1000, .clk_src = GPTIMER_CLK_SRC_APB, .direction = GPTIMER_COUNT_UP, }; gptimer_handle_t timers[SOC_TIMER_GROUP_TOTAL_TIMERS]; for (int i = 0; i < SOC_TIMER_GROUP_TOTAL_TIMERS; i++) { TEST_ESP_OK(gptimer_new_timer(&timer_config, &timers[i])); } #if SOC_TIMER_GROUP_COUNTER_BIT_WIDTH == 64 uint64_t reload_at = UINT64_MAX - 100000; #else uint64_t reload_at = (1ULL << SOC_TIMER_GROUP_COUNTER_BIT_WIDTH) - 100000; #endif gptimer_event_callbacks_t cbs = { .on_alarm = test_gptimer_overflow_reload_callback, }; gptimer_alarm_config_t alarm_config = { .reload_count = reload_at, .alarm_count = 100000, // 100ms .flags.auto_reload_on_alarm = true, }; // The counter should start from [COUNTER_MAX-100000] and overflows to [0] and continue, then reached to alarm value [100000], reloaded to [COUNTER_MAX-100000] automatically // thus the period should be 200ms printf("start timers\r\n"); for (int i = 0; i < SOC_TIMER_GROUP_TOTAL_TIMERS; i++) { TEST_ESP_OK(gptimer_set_alarm_action(timers[i], &alarm_config)); TEST_ESP_OK(gptimer_register_event_callbacks(timers[i], &cbs, task_handle)); // we start from the reload value TEST_ESP_OK(gptimer_set_raw_count(timers[i], reload_at)); TEST_ESP_OK(gptimer_start(timers[i])); TEST_ASSERT_NOT_EQUAL(0, ulTaskNotifyTake(pdFALSE, pdMS_TO_TICKS(400))); TEST_ESP_OK(gptimer_stop(timers[i])); } for (int i = 0; i < SOC_TIMER_GROUP_TOTAL_TIMERS; i++) { TEST_ESP_OK(gptimer_del_timer(timers[i])); } }