Pārlūkot izejas kodu

Merge branch 'staging/esp_timer_restart' into 'master'

esp_timer: add a function to restart timers

See merge request espressif/esp-idf!20386
Omar Chebib 3 gadi atpakaļ
vecāks
revīzija
009d15e59d

+ 2 - 2
components/esp_pm/linker.lf

@@ -47,10 +47,10 @@ entries:
 archive: libesp_timer.a
 archive: libesp_timer.a
 entries:
 entries:
     if PM_SLP_IRAM_OPT = y:
     if PM_SLP_IRAM_OPT = y:
-        # esp_timer_feed is called from task_wdt_timer_feed, so put it
+        # esp_timer_restart is called from task_wdt_timer_feed, so put it
         # in IRAM if task_wdt_timer_feed itself is in IRAM.
         # in IRAM if task_wdt_timer_feed itself is in IRAM.
         if ESP_TASK_WDT_USE_ESP_TIMER = y:
         if ESP_TASK_WDT_USE_ESP_TIMER = y:
-            esp_timer:esp_timer_feed (noflash)
+            esp_timer:esp_timer_restart (noflash)
         if ESP_TIMER_IMPL_TG0_LAC = y:
         if ESP_TIMER_IMPL_TG0_LAC = y:
             esp_timer_impl_lac:esp_timer_impl_lock (noflash)
             esp_timer_impl_lac:esp_timer_impl_lock (noflash)
             esp_timer_impl_lac:esp_timer_impl_unlock (noflash)
             esp_timer_impl_lac:esp_timer_impl_unlock (noflash)

+ 2 - 7
components/esp_system/task_wdt/task_wdt_impl_esp_timer.c

@@ -17,12 +17,6 @@
 #include "esp_timer.h"
 #include "esp_timer.h"
 #include "esp_private/esp_task_wdt_impl.h"
 #include "esp_private/esp_task_wdt_impl.h"
 
 
-/**
- * Private API provided by esp_timer component to feed a timer without
- * the need of disabling it, removing it and inserting it manually.
- */
-esp_err_t esp_timer_feed(esp_timer_handle_t timer);
-
 /**
 /**
  * Context for the software implementation of the Task WatchDog Timer.
  * Context for the software implementation of the Task WatchDog Timer.
  * This will be passed as a parameter to public functions below. */
  * This will be passed as a parameter to public functions below. */
@@ -108,7 +102,8 @@ esp_err_t esp_task_wdt_impl_timer_feed(twdt_ctx_t obj)
     }
     }
 
 
     if (ret == ESP_OK) {
     if (ret == ESP_OK) {
-        ret = esp_timer_feed(ctx->sw_timer);
+        /* Feed the periodic timer by restarting it, specifying the same period */
+        ret = esp_timer_restart(ctx->sw_timer, ctx->period_ms * 1000);
     }
     }
 
 
     return ret;
     return ret;

+ 16 - 0
components/esp_timer/include/esp_timer.h

@@ -164,6 +164,22 @@ esp_err_t esp_timer_start_once(esp_timer_handle_t timer, uint64_t timeout_us);
  */
  */
 esp_err_t esp_timer_start_periodic(esp_timer_handle_t timer, uint64_t period);
 esp_err_t esp_timer_start_periodic(esp_timer_handle_t timer, uint64_t period);
 
 
+/**
+ * @brief Restart a currently running timer
+ *
+ * If the given timer is a one-shot timer, the timer is restarted immediately and will timeout once in `timeout_us` microseconds.
+ * If the given timer is a periodic timer, the timer is restarted immediately with a new period of `timeout_us` microseconds.
+ *
+ * @param timer timer Handle created using esp_timer_create
+ * @param timeout_us Timeout, in microseconds relative to the current time.
+ *                   In case of a periodic timer, also represents the new period.
+ * @return
+ *      - ESP_OK on success
+ *      - ESP_ERR_INVALID_ARG if the handle is invalid
+ *      - ESP_ERR_INVALID_STATE if the timer is not running
+ */
+esp_err_t esp_timer_restart(esp_timer_handle_t timer, uint64_t timeout_us);
+
 /**
 /**
  * @brief Stop the timer
  * @brief Stop the timer
  *
  *

+ 15 - 13
components/esp_timer/src/esp_timer.c

@@ -143,11 +143,7 @@ esp_err_t esp_timer_create(const esp_timer_create_args_t* args,
     return ESP_OK;
     return ESP_OK;
 }
 }
 
 
-/**
- * Function to feed a timer. It is not part of the public header
- * file on purpose as it shall only be used by the Task WDT component.
- */
-esp_err_t esp_timer_feed(esp_timer_handle_t timer)
+esp_err_t esp_timer_restart(esp_timer_handle_t timer, uint64_t timeout_us)
 {
 {
     esp_err_t ret = ESP_OK;
     esp_err_t ret = ESP_OK;
 
 
@@ -155,7 +151,7 @@ esp_err_t esp_timer_feed(esp_timer_handle_t timer)
         return ESP_ERR_INVALID_ARG;
         return ESP_ERR_INVALID_ARG;
     }
     }
 
 
-    if (!is_initialized() || !timer_armed(timer) || timer->period == 0 ) {
+    if (!is_initialized() || !timer_armed(timer)) {
         return ESP_ERR_INVALID_STATE;
         return ESP_ERR_INVALID_STATE;
     }
     }
 
 
@@ -164,10 +160,6 @@ esp_err_t esp_timer_feed(esp_timer_handle_t timer)
 
 
     const int64_t now = esp_timer_impl_get_time();
     const int64_t now = esp_timer_impl_get_time();
     const uint64_t period = timer->period;
     const uint64_t period = timer->period;
-    /* Currently we are guaranteed that the remaining delay between now and the timer's
-     * alarm is less than the period, so the following won't cause the alarm to be
-     * triggered earlier than before feeding occurs. */
-    const uint64_t alarm = now + period;
 
 
     /* We need to remove the timer to the list of timers and reinsert it at
     /* We need to remove the timer to the list of timers and reinsert it at
      * the right position. In fact, the timers are sorted by their alarm value
      * the right position. In fact, the timers are sorted by their alarm value
@@ -175,9 +167,19 @@ esp_err_t esp_timer_feed(esp_timer_handle_t timer)
     ret = timer_remove(timer);
     ret = timer_remove(timer);
 
 
     if (ret == ESP_OK) {
     if (ret == ESP_OK) {
-        /* Remove function got rid of the alarm and period fields, restore them */
-        timer->alarm = alarm;
-        timer->period = period;
+        /* Two cases here:
+         * - if the alarm was a periodic one, i.e. `period` is not 0, the given timeout_us becomes the new period
+         * - if the alarm was a one-shot one, i.e. `period` is 0, it remains non-periodic. */
+        if (period != 0) {
+            /* Remove function got rid of the alarm and period fields, restore them */
+            const uint64_t new_period = MAX(timeout_us, esp_timer_impl_get_min_period_us());
+            timer->alarm = now + new_period;
+            timer->period = new_period;
+        } else {
+            /* The new one-shot alarm shall be triggered timeout_us after the current time */
+            timer->alarm = now + timeout_us;
+            timer->period = 0;
+        }
         ret = timer_insert(timer, false);
         ret = timer_insert(timer, false);
     }
     }
 
 

+ 60 - 20
components/esp_timer/test/test_esp_timer.c

@@ -866,48 +866,88 @@ TEST_CASE("Test a latency between a call of callback and real event", "[esp_time
     TEST_ESP_OK(esp_timer_delete(periodic_timer));
     TEST_ESP_OK(esp_timer_delete(periodic_timer));
 }
 }
 
 
-static void test_periodic_timer_feed(void* timer1_fed)
+static void test_timer_triggered(void* timer1_trig)
 {
 {
-    *((int*) timer1_fed) = 1;
+    int* timer = (int *)timer1_trig;
+    *timer = *timer + 1;
 }
 }
 
 
-/**
- * Feed function is not part of the esp_timer header file: it's a public in the sense that it is not static,
- * but it is only meant to be used in IDF components.
- */
-esp_err_t esp_timer_feed(esp_timer_handle_t timer);
-
-TEST_CASE("periodic esp_timer can be fed", "[esp_timer]")
+TEST_CASE("periodic esp_timer can be restarted", "[esp_timer]")
 {
 {
     const int delay_ms = 100;
     const int delay_ms = 100;
-    int timer_fed = 0;
+    int timer_trig = 0;
     esp_timer_handle_t timer1;
     esp_timer_handle_t timer1;
     esp_timer_create_args_t create_args = {
     esp_timer_create_args_t create_args = {
-            .callback = &test_periodic_timer_feed,
-            .arg = &timer_fed,
+            .callback = &test_timer_triggered,
+            .arg = &timer_trig,
             .name = "timer1",
             .name = "timer1",
     };
     };
     TEST_ESP_OK(esp_timer_create(&create_args, &timer1));
     TEST_ESP_OK(esp_timer_create(&create_args, &timer1));
     TEST_ESP_OK(esp_timer_start_periodic(timer1, delay_ms * 1000));
     TEST_ESP_OK(esp_timer_start_periodic(timer1, delay_ms * 1000));
-    /* Sleep for delay_ms/2 and feed the timer */
+    /* Sleep for delay_ms/2 and restart the timer */
     vTaskDelay((delay_ms / 2) * portTICK_PERIOD_MS);
     vTaskDelay((delay_ms / 2) * portTICK_PERIOD_MS);
     /* Check that the alarm was not triggered */
     /* Check that the alarm was not triggered */
-    TEST_ASSERT_EQUAL(0, timer_fed);
+    TEST_ASSERT_EQUAL(0, timer_trig);
     /* Reaching this point, the timer will be triggered in delay_ms/2.
     /* Reaching this point, the timer will be triggered in delay_ms/2.
-     * Let's feed the timer now. */
-    TEST_ESP_OK(esp_timer_feed(timer1));
+     * Let's restart the timer now with the same period. */
+    TEST_ESP_OK(esp_timer_restart(timer1, delay_ms * 1000));
     /* Sleep for a bit more than delay_ms/2 */
     /* Sleep for a bit more than delay_ms/2 */
     vTaskDelay(((delay_ms / 2) + 1) * portTICK_PERIOD_MS);
     vTaskDelay(((delay_ms / 2) + 1) * portTICK_PERIOD_MS);
-    /* If the alarm was triggered, feed didn't work */
-    TEST_ASSERT_EQUAL(0, timer_fed);
+    /* If the alarm was triggered, restart didn't work */
+    TEST_ASSERT_EQUAL(0, timer_trig);
     /* Else, wait for another delay_ms/2, which should trigger the alarm */
     /* Else, wait for another delay_ms/2, which should trigger the alarm */
-    vTaskDelay(((delay_ms / 2) + 1) * portTICK_PERIOD_MS);
-    TEST_ASSERT_EQUAL(1, timer_fed);
+    vTaskDelay(((delay_ms / 2) + 2) * portTICK_PERIOD_MS);
+    TEST_ASSERT_EQUAL(1, timer_trig);
+    /* Now wait for another delay_ms to make sure the timer is still periodic */
+    timer_trig = 0;
+    vTaskDelay((delay_ms * portTICK_PERIOD_MS) + 1);
+    /* Make sure the timer was triggered */
+    TEST_ASSERT_EQUAL(1, timer_trig);
+    /* Reduce the period of the timer to delay/2 */
+    timer_trig = 0;
+    TEST_ESP_OK(esp_timer_restart(timer1, delay_ms / 2 * 1000));
+    vTaskDelay((delay_ms * portTICK_PERIOD_MS) + 1);
+    /* Check that the alarm was triggered twice */
+    TEST_ASSERT_EQUAL(2, timer_trig);
 
 
     TEST_ESP_OK( esp_timer_stop(timer1) );
     TEST_ESP_OK( esp_timer_stop(timer1) );
     TEST_ESP_OK( esp_timer_delete(timer1) );
     TEST_ESP_OK( esp_timer_delete(timer1) );
 }
 }
 
 
+TEST_CASE("one-shot esp_timer can be restarted", "[esp_timer]")
+{
+    const int delay_ms = 100;
+    int timer_trig = 0;
+    esp_timer_handle_t timer1;
+    esp_timer_create_args_t create_args = {
+            .callback = &test_timer_triggered,
+            .arg = &timer_trig,
+            .name = "timer1",
+    };
+    TEST_ESP_OK(esp_timer_create(&create_args, &timer1));
+    TEST_ESP_OK(esp_timer_start_once(timer1, delay_ms * 1000));
+    vTaskDelay((delay_ms / 2) * portTICK_PERIOD_MS);
+    /* Check that the alarm was not triggered */
+    TEST_ASSERT_EQUAL(0, timer_trig);
+    /* Reaching this point, the timer will be triggered in delay_ms/2.
+     * Let's restart the timer now with the same timeout. */
+    TEST_ESP_OK(esp_timer_restart(timer1, delay_ms * 1000));
+    vTaskDelay(((delay_ms / 2) + 1) * portTICK_PERIOD_MS);
+    /* If the alarm was triggered, restart didn't work */
+    TEST_ASSERT_EQUAL(0, timer_trig);
+    /* Else, wait for another delay_ms/2, which should trigger the alarm */
+    vTaskDelay(((delay_ms / 2) + 2) * portTICK_PERIOD_MS);
+    TEST_ASSERT_EQUAL(1, timer_trig);
+    /* Make sure the timer is NOT periodic, wait for another delay and make sure
+     * our callback was not called */
+    timer_trig = 0;
+    vTaskDelay(delay_ms * 2 * portTICK_PERIOD_MS);
+    /* Make sure the timer was triggered */
+    TEST_ASSERT_EQUAL(0, timer_trig);
+
+    TEST_ESP_OK( esp_timer_delete(timer1) );
+}
+
 
 
 #ifdef CONFIG_ESP_TIMER_SUPPORTS_ISR_DISPATCH_METHOD
 #ifdef CONFIG_ESP_TIMER_SUPPORTS_ISR_DISPATCH_METHOD
 static int64_t old_time[2];
 static int64_t old_time[2];