ESP32 入門到精通 – 使用 xTimer


簡介

ESP32 是由 Espressif Systems 開發的一款低功耗 Wi-Fi 和藍牙雙模系統級芯片,而 FreeRTOS 是一個開源的實時操作系統內核,適用於微控制器。當這兩者結合在一起使用時,ESP32 可以利用 FreeRTOS 來實現多任務調度,並提高應用的實時性能。

xTimer 是 FreeRTOS 中一個強大的計時器工具,用於在 ESP32 上實現精確的延遲和定時操作。

xTimer 功能

xTimer 是一個靈活且高效的方式來處理軟體計時器。軟體計時器允許你設定一個計時器,該計時器可以在指定時間後執行一個回調函數。這對於需要週期性任務或延遲操作的應用非常有用。

如何使用 xTimer

1. 創建和初始化計時器。
2. 設置計時器參數。
3. 啟動計時器。
4. 停止計時器。

xTimer 常用函數

1. xTimerCreate : 創建一個軟件計時器。
2. xTimerStart : 啟動計時器。
3. xTimerStop : 停止計時器。
4. xTimerChangePeriod : 更改計時器的週期。
5. xTimerReset : 重置計時器,將計時器的計時重新開始。
6. xTimerDelete : 刪除計時器。
7. xTimerGetPeriod : 獲取計時器的週期。
8. xTimerGetExpiryTime : 獲取計時器下一次到期的時間。
9. xTimerGetTimerDaemonTaskHandle : 獲取計時器守護任務的句柄。
10. pvTimerGetTimerID : 獲取計時器的ID。
11. vTimerSetTimerID : 設置計時器的ID。
12. xTimerPendFunctionCall : 執行一個函數,該函數將在計時器守護任務的上下文中運行。

安裝 VSCode 和 ESP-IDF VSCode 擴展

確保你已經安裝和配置好了 ESP-IDF 開發環境。您也可以參考 ESP32 入門到精通 – 在 VSCode 安裝 ESP-IDF 插件 這篇文章。

新建 ESP32 專案

利用在 VSCode 的 IDF 插件來新建一個 ESP32 專案可參考 ESP32 入門到精通 – 如何用 VSCode 創建 ESP32 專案

一次完整成現 xTimer 功能範例

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/timers.h"
#include "esp_log.h"

#define TAG "xtimer_example"

// Counter to track the number of timer callbacks
static int callback_count = 0;
static const int stop_after_count = 4;

// Callback function
void timer_callback(TimerHandle_t xTimer) {
    callback_count++;
    ESP_LOGI(TAG, "Timer expired! Callback count: %d", callback_count);

    // Stop the timer after 4 callbacks
    if (callback_count >= stop_after_count) {
        if (xTimerStop(xTimer, 0) != pdPASS) {
            ESP_LOGE(TAG, "Timer stop failed!");
        } else {
            ESP_LOGI(TAG, "Timer stopped after %d callbacks", stop_after_count);
        }
    }
}

// Function to be executed in the timer daemon task context
void pendingFunction(void *pvParameter1, uint32_t ulParameter2) {
    ESP_LOGI(TAG, "Pending function called with param1: %p, param2: %d", pvParameter1, ulParameter2);
}

void app_main() {
    // Create a software timer
    TimerHandle_t xTimer = xTimerCreate(
        "Timer",                  // Timer name
        pdMS_TO_TICKS(1000),      // Timer period in ticks (milliseconds to ticks)
        pdTRUE,                   // Auto-reload flag
        (void *) 0,               // Timer ID
        timer_callback            // Callback function
    );

    if (xTimer == NULL) {
        ESP_LOGE(TAG, "Timer creation failed!");
        return;
    }

    // Start the timer
    if (xTimerStart(xTimer, 0) != pdPASS) {
        ESP_LOGE(TAG, "Timer start failed!");
    }

    // Change the timer period to 2000 ticks (assuming 1 tick = 1 millisecond)
    if (xTimerChangePeriod(xTimer, pdMS_TO_TICKS(2000), 0) != pdPASS) {
        ESP_LOGE(TAG, "Timer change period failed!");
    }

    // Get the timer period
    TickType_t period = xTimerGetPeriod(xTimer);
    ESP_LOGI(TAG, "Timer period: %d ticks", period);

    // Get the timer expiry time
    TickType_t expiryTime = xTimerGetExpiryTime(xTimer);
    ESP_LOGI(TAG, "Timer expiry time: %d ticks", expiryTime);

    // Set and get the timer ID
    void *newTimerID = (void *)111;
    vTimerSetTimerID(xTimer, newTimerID);
    void *timerID = pvTimerGetTimerID(xTimer);
    ESP_LOGI(TAG, "Timer ID: %p", timerID);

    // Call a function in the timer daemon task context
    if (xTimerPendFunctionCall(pendingFunction, (void *)0xFFF, 111, 0) != pdPASS) {
        ESP_LOGE(TAG, "Pending function call failed!");
    }

    // Get the timer daemon task handle
    TaskHandle_t timerDaemonTaskHandle = xTimerGetTimerDaemonTaskHandle();
    ESP_LOGI(TAG, "Timer daemon task handle: %p", timerDaemonTaskHandle);
}

編譯和燒錄

在 VSCode 中找到 "ESP32-IDF : Build, Flash and Monitor" ICON 執行和燒錄。

查看結果

我們可以查看在終端機的列印結果,如下...
I (306) xtimer_example: Timer period: 100 ticks
I (316) xtimer_example: Timer expiry time: 1073407732 ticks
I (316) xtimer_example: Timer ID: 0x6f
I (326) xtimer_example: Timer daemon task handle: 0x3ffb6b48
I (326) xtimer_example: Pending function called with param1: 0xfff, param2: 111
I (2326) xtimer_example: Timer expired! Callback count: 1
I (4326) xtimer_example: Timer expired! Callback count: 2
I (6326) xtimer_example: Timer expired! Callback count: 3
I (8326) xtimer_example: Timer expired! Callback count: 4
I (8326) xtimer_example: Timer stopped after 4 callbacks

結論

ESP32 的 FreeRTOS 計時器的精度取決於系統的滴答頻率,通常為 1ms。確保在創建和刪除計時器時正確管理內存,以避免內存泄漏。回調函數應該執行快速且非阻塞的操作,避免長時間運行,以免影響系統其他任務的調度。