esp-idf 之事件循环(Event Loop)例程详解

本文是将Default Event Loop:esp-idf/examples/system/esp_event/default_event_loop at release/v3.3 · espressif/esp-idf 例程内的README和代码注释翻译一下,便于各位看官理解并理顺思路。

README

事件循环库Event Loop Library (‘esp_event’) 默认事件循环例程Default Event Loop Example

本例程是事件循环库的一个示例。为了保持简单,示例仅限于使用默认事件循环。
默认事件循环是系统自带的事件循环,在一般情况下已经够用。
如果需要,用户也可以参考 user_event_loops 例程创建用户自定义的事件循环。

例程的内容如下:

事件的声明与定义 (Declaring and Defining Events)

本例程展示了如何在头文件中声明event base和event ID,并在源文件中进行定义。
在头文件中使用了 ESP_EVENT_DECLARE_BASE这个宏来声明 event base, 而event IDs 在一个枚举enum中进行声明(参考esp-idf/event_source.h at release/v3.3 · espressif/esp-idf)。
。在源文件 main.c 中使用了 ESP_EVENT_DEFINE_BASE这个宏来定义event base.

创建默认事件循环 (Creating the Default Event Loop)

本例程中使用了esp_event_loop_create_default这个API来创建默认事件循环。

向默认事件循环发送事件 (Posting Events to the Default Event Loop)

简单说,就是向默认事件循环发送事件相当于事件的handler依次执行队列中的命令。 使用API esp_event_post来向默认事件循环发送事件。例程中也展示了如何同时传递事件数据。

Handler的注册与注销 (Handler Registration/Unregistration)

本例程使用 esp_event_handler_register注册了三个handler:
(1) 特定事件
(2) 特定event base的任意事件
(3) 任意事件
使用 esp_event_handler_unregister来注销handler。注销handler之后,即便事件此前已注册的事件再次被发送时,也不会再执行了。

例程解析 (Example Flow Explained)

以例程的LOG输出来解释:

I (276) default_event_loop: setting up
I (276) default_event_loop: starting event sources
I (276) default_event_loop: TIMER_EVENTS:TIMER_EVENT_STARTED: posting to default loop
I (276) default_event_loop: TASK_EVENTS:TASK_ITERATION_EVENT: posting to default loop, 1 out of 5
I (296) default_event_loop: TIMER_EVENTS:TIMER_EVENT_STARTED: timer_started_handler
I (306) default_event_loop: TIMER_EVENTS:TIMER_EVENT_STARTED: timer_any_handler
I (316) default_event_loop: TIMER_EVENTS:TIMER_EVENT_STARTED: all_event_handler
I (326) default_event_loop: TASK_EVENTS:TASK_ITERATION_EVENT: task_iteration_handler, executed 1 times
I (336) default_event_loop: TASK_EVENTS:TASK_ITERATION_EVENT: all_event_handler
I (806) default_event_loop: TASK_EVENTS:TASK_ITERATION_EVENT: posting to default loop, 2 out of 5
I (806) default_event_loop: TASK_EVENTS:TASK_ITERATION_EVENT: task_iteration_handler, executed 2 times
I (806) default_event_loop: TASK_EVENTS:TASK_ITERATION_EVENT: all_event_handler
I (1276) default_event_loop: TIMER_EVENTS:TIMER_EVENT_EXPIRY: posting to default loop
I (1276) default_event_loop: TIMER_EVENTS:TIMER_EVENT_EXPIRY: timer_expiry_handler, executed 1 out of 3 times
I (1286) default_event_loop: TIMER_EVENTS:TIMER_EVENT_EXPIRY: timer_any_handler
I (1296) default_event_loop: TIMER_EVENTS:TIMER_EVENT_EXPIRY: all_event_handler
I (1306) default_event_loop: TASK_EVENTS:TASK_ITERATION_EVENT: posting to default loop, 3 out of 5
I (1316) default_event_loop: TASK_EVENTS:TASK_ITERATION_EVENT: unregistering task_iteration_handler
I (1316) default_event_loop: TASK_EVENTS:TASK_ITERATION_EVENT: task_iteration_handler, executed 3 times
I (1336) default_event_loop: TASK_EVENTS:TASK_ITERATION_EVENT: all_event_handler
I (1846) default_event_loop: TASK_EVENTS:TASK_ITERATION_EVENT: posting to default loop, 4 out of 5
I (1846) default_event_loop: TASK_EVENTS:TASK_ITERATION_EVENT: all_event_handler
I (2276) default_event_loop: TIMER_EVENTS:TIMER_EVENT_EXPIRY: posting to default loop
I (2276) default_event_loop: TIMER_EVENTS:TIMER_EVENT_EXPIRY: timer_expiry_handler, executed 2 out of 3 times
I (2286) default_event_loop: TIMER_EVENTS:TIMER_EVENT_EXPIRY: timer_any_handler
I (2296) default_event_loop: TIMER_EVENTS:TIMER_EVENT_EXPIRY: all_event_handler
I (2346) default_event_loop: TASK_EVENTS:TASK_ITERATION_EVENT: posting to default loop, 5 out of 5
I (2346) default_event_loop: TASK_EVENTS:TASK_ITERATION_EVENT: all_event_handler
I (3276) default_event_loop: TIMER_EVENTS:TIMER_EVENT_EXPIRY: posting to default loop
I (3276) default_event_loop: TIMER_EVENTS:TIMER_EVENT_STOPPED: posting to default loop
I (3286) default_event_loop: TIMER_EVENTS:TIMER_EVENT_EXPIRY: timer_expiry_handler, executed 3 out of 3 times
I (3296) default_event_loop: TIMER_EVENTS:TIMER_EVENT_EXPIRY: timer_any_handler
I (3306) default_event_loop: TIMER_EVENTS:TIMER_EVENT_EXPIRY: all_event_handler
I (3316) default_event_loop: TIMER_EVENTS:TIMER_EVENT_STOPPED: timer_stopped_handler
I (3326) default_event_loop: TIMER_EVENTS:TIMER_EVENT_STOPPED: deleted timer event source
I (3326) default_event_loop: TIMER_EVENTS:TIMER_EVENT_STOPPED: timer_any_handler
I (3336) default_event_loop: TIMER_EVENTS:TIMER_EVENT_STOPPED: all_event_handler
I (3346) default_event_loop: TASK_EVENTS:TASK_ITERATION_EVENT: deleting task event source

设置 (Setting)

本例程使用了两个事件源:一个循环定时器和一个包含循环的任务。定时器在如下情况会产生事件:

  1. 定时器启动
  2. 定时器间隔时间到
  3. 定时器停止
    当任务每次循环时,也会产生事件。

除了如上事件对应的handler之外,还有一个handler,在任意事件发生的时候都会执行。
例程设定了定时器的重复执行次数和任务迭代次数。当定时器循环指定次数的时候,定时器会被停止。当任务迭代达到指定次数时,任务会被删除。对于任务循环来说,还有另外一个设定:即当迭代达到某个次数的时候,对应的handler被注销。

分步解析 (Step-by-Step Explanation)

如下输出内容即为关键点:
a.

I (297)  default_event_loop: setting up

此时默认事件循环已经被创建,同时各个事件handler已经被注册。

b.

I (276) default_event_loop: starting event sources
I (276) default_event_loop: TIMER_EVENTS:TIMER_EVENT_STARTED: posting to default loop
I (276) default_event_loop: TASK_EVENTS:TASK_ITERATION_EVENT: posting to default loop, 1 out of 5

两个事件源已经启动,并向默认事件循环发送事件。

c.

I (296) default_event_loop: TIMER_EVENTS:TIMER_EVENT_STARTED: timer_started_handler
I (306) default_event_loop: TIMER_EVENTS:TIMER_EVENT_STARTED: timer_any_handler
I (316) default_event_loop: TIMER_EVENTS:TIMER_EVENT_STARTED: all_event_handler
I (326) default_event_loop: TASK_EVENTS:TASK_ITERATION_EVENT: task_iteration_handler, executed 1 times
I (336) default_event_loop: TASK_EVENTS:TASK_ITERATION_EVENT: all_event_handler

(b) 中发送的事件对应handler已被执行,另外
timer_started_handlertask_iteration_handler, timer_any_handlerall_event_handler 都被执行。

任意定时器事件均会触发timer_any_handler的执行。可以看到定时器间隔时间到和定时器停止的时候也执行了timer_any_handler

任意事件均会触发all_event_handler的执行。所以在 TIMER_EVENTS:TIMER_EVENT_STARTEDTASK_EVENTS:TASK_ITERATION_EVENT 两个事件发生的时候可以看到它在执行。

对于定时器和任务事件,esp_event确保了他们是按照handler注册的顺序执行的。
如下输出也是如此,事件的发送和handler的执行都完全依照上述注册的顺序。

d.

...
I (1316) default_event_loop: TASK_EVENTS:TASK_ITERATION_EVENT: unregistering task_iteration_handler

此时 task_iteration_handler 已注销, 因此当 TASK_EVENTS:TASK_ITERATION_EVENT发送时已不再执行。

I (1867) default_event_loop: TASK_EVENTS:TASK_ITERATION_EVENT: posting to default loop, 4 out of 5
I (1867) default_event_loop: TASK_EVENTS:TASK_ITERATION_EVENT: all_event_handler

...

I (1846) default_event_loop: TASK_EVENTS:TASK_ITERATION_EVENT: posting to default loop, 4 out of 5
I (1846) default_event_loop: TASK_EVENTS:TASK_ITERATION_EVENT: all_event_handle

任务迭代事件继续发送,但是只有 all_event_handler 被执行。

e.

...
I (3276) default_event_loop: TIMER_EVENTS:TIMER_EVENT_EXPIRY: posting to default loop
I (3276) default_event_loop: TIMER_EVENTS:TIMER_EVENT_STOPPED: posting to default loop
I (3286) default_event_loop: TIMER_EVENTS:TIMER_EVENT_EXPIRY: timer_expiry_handler, executed 3 out of 3 times
I (3296) default_event_loop: TIMER_EVENTS:TIMER_EVENT_EXPIRY: timer_any_handler
I (3306) default_event_loop: TIMER_EVENTS:TIMER_EVENT_EXPIRY: all_event_handler
I (3316) default_event_loop: TIMER_EVENTS:TIMER_EVENT_STOPPED: timer_stopped_handler
I (3326) default_event_loop: TIMER_EVENTS:TIMER_EVENT_STOPPED: deleted timer event source

When the periodic timer expiry limit is reached, the event TIMER_EVENTS:TIMER_EVENT_STOPPED is posted to the loop. The periodic timer is consequently deleted in the handler timer_stopped_handler.
当定时器循环达到指定次数的时候,TIMER_EVENTS:TIMER_EVENT_STOPPED被发送到事件循环,然后在timer_stopped_handler中定时器被删除。

...
I (3346) default_event_loop: TASK_EVENTS:TASK_ITERATION_EVENT: deleting task event source
...

此时发送迭代事件的任务也被删除。例程执行结束。

代码:

/* esp_event (event loop library) basic 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 "esp_log.h"

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

#include "event_source.h"

static const char* TAG = "default_event_loop";

static char* get_id_string(esp_event_base_t base, int32_t id) {
    char* event = "";
    if (base == TIMER_EVENTS) {
        switch(id) {
            case TIMER_EVENT_STARTED:
            event = "TIMER_EVENT_STARTED";
            break;
            case TIMER_EVENT_EXPIRY:
            event = "TIMER_EVENT_EXPIRY";
            break;
            case TIMER_EVENT_STOPPED:
            event = "TIMER_EVENT_STOPPED";
            break;
        }
    } else {
        event = "TASK_ITERATION_EVENT";
    }
    return event;
}

/* 循环定时器相关的event base定义 */
ESP_EVENT_DEFINE_BASE(TIMER_EVENTS);

esp_timer_handle_t TIMER;


//定时器回调,回调中向默认事件循环发送事件
static void timer_callback(void* arg)
{
    ESP_LOGI(TAG, "%s:%s: posting to default loop", TIMER_EVENTS, get_id_string(TIMER_EVENTS, TIMER_EVENT_EXPIRY));
    ESP_ERROR_CHECK(esp_event_post(TIMER_EVENTS, TIMER_EVENT_EXPIRY, NULL, 0, portMAX_DELAY));
}

// 定时器启动事件的Handler 
static void timer_started_handler(void* handler_args, esp_event_base_t base, int32_t id, void* event_data)
{
    ESP_LOGI(TAG, "%s:%s: timer_started_handler", base, get_id_string(base, id));
}


//定时器循环事件handler,每次发送定时到达事件,同时此回调记录定时器执行次数,达到指定次数的时候会停止定时器并发送定时器停止事件。
static void timer_expiry_handler(void* handler_args, esp_event_base_t base, int32_t id, void* event_data)
{
    static int count = 0;

    count++;

    if (count >= TIMER_EXPIRIES_COUNT) {
        // 停止定时器
        ESP_ERROR_CHECK(esp_timer_stop(TIMER));

        ESP_LOGI(TAG, "%s:%s: posting to default loop", base, get_id_string(base, TIMER_EVENT_STOPPED));

        // 发送定时器停止事件
        ESP_ERROR_CHECK(esp_event_post(TIMER_EVENTS, TIMER_EVENT_STOPPED, NULL, 0, portMAX_DELAY));
    }

    ESP_LOGI(TAG, "%s:%s: timer_expiry_handler, executed %d out of %d times", base, get_id_string(base, id), count, TIMER_EXPIRIES_COUNT);
}


//任意定时器事件handler,在定时器开启、时间到、停止时均会执行
static void timer_any_handler(void* handler_args, esp_event_base_t base, int32_t id, void* event_data)
{
    ESP_LOGI(TAG, "%s:%s: timer_any_handler", base, get_id_string(base, id));
}


//定时器停止事件handler。定时器停止之后即可安全删除定时器。
static void timer_stopped_handler(void* handler_args, esp_event_base_t base, int32_t id, void* event_data)
{
    ESP_LOGI(TAG, "%s:%s: timer_stopped_handler", base, get_id_string(base, id));

    // 删除定时器
    esp_timer_delete(TIMER);

    ESP_LOGI(TAG, "%s:%s: deleted timer event source", base, get_id_string(base, id));
}

/* 任务相关的event base定义 */
ESP_EVENT_DEFINE_BASE(TASK_EVENTS)

static void task_iteration_handler(void* handler_args, esp_event_base_t base, int32_t id, void* event_data)
{
    int iteration = *((int*) event_data);
    ESP_LOGI(TAG, "%s:%s: task_iteration_handler, executed %d times", base, get_id_string(base, id), iteration);
}

static void task_event_source(void* args)
{
    for(int iteration = 1; iteration <= TASK_ITERATIONS_COUNT; iteration++) {

        ESP_LOGI(TAG, "%s:%s: posting to default loop, %d out of %d", TASK_EVENTS,
                get_id_string(TASK_EVENTS, TASK_ITERATION_EVENT), iteration, TASK_ITERATIONS_COUNT);


     //发送任务迭代事件,迭代次数同时被传递给handler。请注意传递的数据是原数据的深拷贝。
        ESP_ERROR_CHECK(esp_event_post(TASK_EVENTS, TASK_ITERATION_EVENT, &iteration, sizeof(iteration), portMAX_DELAY));

        if (iteration == TASK_ITERATIONS_UNREGISTER){
            ESP_LOGI(TAG, "%s:%s: unregistering task_iteration_handler", TASK_EVENTS, get_id_string(TASK_EVENTS, TASK_ITERATION_EVENT));
            ESP_ERROR_CHECK(esp_event_handler_unregister(TASK_EVENTS, TASK_ITERATION_EVENT, task_iteration_handler));
        }

        vTaskDelay(pdMS_TO_TICKS(TASK_PERIOD));
    }

    vTaskDelay(pdMS_TO_TICKS(TASK_PERIOD));

    ESP_LOGI(TAG, "%s:%s: deleting task event source", TASK_EVENTS, get_id_string(TASK_EVENTS, TASK_ITERATION_EVENT));

    vTaskDelete(NULL);
}

/* 所有时间的handler*/
static void all_event_handler(void* handler_args, esp_event_base_t base, int32_t id, void* event_data)
{
    ESP_LOGI(TAG, "%s:%s: all_event_handler", base, get_id_string(base, id));
}

/* Example main */
void app_main(void)
{
    ESP_LOGI(TAG, "setting up");

    // Create the default event loop
    ESP_ERROR_CHECK(esp_event_loop_create_default());

    // 注册特定定时器事件handler
    ESP_ERROR_CHECK(esp_event_handler_register(TIMER_EVENTS, TIMER_EVENT_STARTED, timer_started_handler, NULL));
    ESP_ERROR_CHECK(esp_event_handler_register(TIMER_EVENTS, TIMER_EVENT_EXPIRY, timer_expiry_handler, NULL));
    ESP_ERROR_CHECK(esp_event_handler_register(TIMER_EVENTS, TIMER_EVENT_STOPPED, timer_stopped_handler, NULL));

    //注册所有定时器事件的handler,包括定时器开启、时间到和停止事件
    ESP_ERROR_CHECK(esp_event_handler_register(TIMER_EVENTS, ESP_EVENT_ANY_ID, timer_any_handler, NULL));

    // 注册迭代任务handler
    ESP_ERROR_CHECK(esp_event_handler_register(TASK_EVENTS, TASK_ITERATION_EVENT, task_iteration_handler, NULL));

    
//注册所有事件的handler,定时器事件和任务迭代事件都会触发它的执行
ESP_ERROR_CHECK(esp_event_handler_register(ESP_EVENT_ANY_BASE, ESP_EVENT_ANY_ID, all_event_handler, NULL));

    // 创建并启动定时器事件源
    esp_timer_create_args_t timer_args = {
        .callback = &timer_callback,
    };

    ESP_ERROR_CHECK(esp_timer_create(&timer_args, &TIMER));

    ESP_LOGI(TAG, "starting event sources");

     // 创建任务事件源
    xTaskCreate(task_event_source, "task_event_source", 2048, NULL, uxTaskPriorityGet(NULL), NULL);

    ESP_ERROR_CHECK(esp_timer_start_periodic(TIMER, TIMER_PERIOD));

    // 发送定时器启动事件
    ESP_LOGI(TAG, "%s:%s: posting to default loop", TIMER_EVENTS, get_id_string(TIMER_EVENTS, TIMER_EVENT_STARTED));
    ESP_ERROR_CHECK(esp_event_post(TIMER_EVENTS, TIMER_EVENT_STARTED, NULL, 0, portMAX_DELAY));
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章