基于VScode的ESP32开发学习(五):用户自定事件循环详解,dedicated task

代码

#include "esp_log.h"

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

#include "event_source.h"
#include "esp_event_base.h"

static const char* TAG = "user_event_loops";

//事件循环
esp_event_loop_handle_t loop_with_task;         //循环带任务
esp_event_loop_handle_t loop_without_task;      //循环不带任务

static void application_task(void* args)        //应用任务
{
    while(1) {
        ESP_LOGI(TAG, "application_task: running application task");
        esp_event_loop_run(loop_without_task, 100);                 //投送事件给事件循环,100个时间片
        vTaskDelay(10);
    }
}

/* Event source task related definitions */
//任务类型事件源的定义
ESP_EVENT_DEFINE_BASE(TASK_EVENTS);

//TaskHandle_t g_task;            //任务句柄

static void task_iteration_handler(void* handler_args, esp_event_base_t base, int32_t id, void* event_data)         //任务迭代处理函数
{
    // Two types of data can be passed in to the event handler: the handler specific data and the event-specific data.
    //
    // The handler specific data (handler_args) is a pointer to the original data, therefore, the user should ensure that
    // the memory location it points to is still valid when the handler executes.
    //
    // The event-specific data (event_data) is a pointer to a deep copy of the original data, and is managed automatically.
    //有两种特定的参数可以被传入事件处理函数:handler_args与event_data
    //handler_args是一个指向传入的参数原地址的指针,所以当该函数运行时,要保证源参数依旧有效
    //event_data是一个指向原参数副本的指针,副本(copy)过程是系统自动完成的
    int iteration = *((int*) event_data);

    char* loop;

    if (handler_args == loop_with_task) {
        loop = "loop_with_task";
    } else {
        loop = "loop_without_task";
    }

    ESP_LOGI(TAG, "handling %s:%s from %s, iteration %d", base, "TASK_ITERATION_EVENT", loop, iteration);
}

static void task_event_source(void* args)               //向任务发送事件的事件源函数
{
    for(int iteration = 1; iteration <= TASK_ITERATIONS_COUNT; iteration++) {           //迭代次数判断
        esp_event_loop_handle_t loop_to_post_to;

        if (iteration % 2 == 0) {
            // if even, post to the event loop with dedicated task
            //如果是偶数次,则用专用函数发送到事件循环
            loop_to_post_to = loop_with_task;
        } else {
            // if odd, post to the event loop without a dedicated task
            //如果奇数次,不带任务发送到事件循环
            loop_to_post_to = loop_without_task;
        }

        ESP_LOGI(TAG, "posting %s:%s to %s, iteration %d out of %d", TASK_EVENTS, "TASK_ITERATION_EVENT",
                loop_to_post_to == loop_with_task ? "loop_with_task" : "loop_without_task",
                iteration, TASK_ITERATIONS_COUNT);

        ESP_ERROR_CHECK(esp_event_post_to(loop_to_post_to, TASK_EVENTS, TASK_ITERATION_EVENT, &iteration, sizeof(iteration), portMAX_DELAY));           //发送事件到事件循环loop_to_post_to

        vTaskDelay(pdMS_TO_TICKS(TASK_PERIOD));
    }

    vTaskDelay(pdMS_TO_TICKS(TASK_PERIOD));

    ESP_LOGI(TAG, "deleting task event source");

    vTaskDelete(NULL);
}

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

    esp_event_loop_args_t loop_with_task_args = {           //配置事件循环
        .queue_size = 5,                                    //队列长度
        .task_name = "loop_task", // task will be created           //任务名
        .task_priority = uxTaskPriorityGet(NULL),           //优先级
        .task_stack_size = 2048,                            //分配栈大小
        .task_core_id = tskNO_AFFINITY
    };

    esp_event_loop_args_t loop_without_task_args = {        //配置不带任务的循环
        .queue_size = 5,
        .task_name = NULL // no task will be created
    };

    // 创建事件循环
    ESP_ERROR_CHECK(esp_event_loop_create(&loop_with_task_args, &loop_with_task));
    ESP_ERROR_CHECK(esp_event_loop_create(&loop_without_task_args, &loop_without_task));

    // Register the handler for task iteration event. Notice that the same handler is used for handling event on different loops.
    // The loop handle is provided as an argument in order for this example to display the loop the handler is being run on.
    //注册任务迭代事件的句柄,注意:同一个句柄也可被用于在不同循环中处理事件
    //这里事件循环的句柄同时还被当作了参数,用以区别/判断对应事件循环处理函数的运行
    ESP_ERROR_CHECK(esp_event_handler_instance_register_with(loop_with_task, TASK_EVENTS, TASK_ITERATION_EVENT, task_iteration_handler, loop_with_task, NULL));
    ESP_ERROR_CHECK(esp_event_handler_instance_register_with(loop_without_task, TASK_EVENTS, TASK_ITERATION_EVENT, task_iteration_handler, loop_without_task, NULL));

    ESP_LOGI(TAG, "starting event source");

    // Create the event source task with the same priority as the current task
    //创建与当下任务有相同优先级的事件源任务
    xTaskCreate(task_event_source, "task_event_source", 2048, NULL, uxTaskPriorityGet(NULL), NULL);

    ESP_LOGI(TAG, "starting application task");
    // Create the application task with the same priority as the current task
    //创建与当下任务有相同优先级的应用任务
    xTaskCreate(application_task, "application_task", 2048, NULL, uxTaskPriorityGet(NULL), NULL);
}

运行效果

在这里插入图片描述

详解

依旧是从主函数开始看

void app_main(void)
{
    ESP_LOGI(TAG, "setting up");

    esp_event_loop_args_t loop_with_task_args = {           //配置事件循环
        .queue_size = 5,                                    //队列长度
        .task_name = "loop_task", // task will be created           //任务名
        .task_priority = uxTaskPriorityGet(NULL),           //优先级
        .task_stack_size = 2048,                            //分配栈大小
        .task_core_id = tskNO_AFFINITY
    };

    esp_event_loop_args_t loop_without_task_args = {        //配置不带任务的循环
        .queue_size = 5,
        .task_name = NULL // no task will be created
    };

    // 创建事件循环
    ESP_ERROR_CHECK(esp_event_loop_create(&loop_with_task_args, &loop_with_task));
    ESP_ERROR_CHECK(esp_event_loop_create(&loop_without_task_args, &loop_without_task));

    // Register the handler for task iteration event. Notice that the same handler is used for handling event on different loops.
    // The loop handle is provided as an argument in order for this example to display the loop the handler is being run on.
    //注册任务迭代事件的句柄,注意:同一个句柄也可被用于在不同循环中处理事件
    //这里事件循环的句柄同时还被当作了参数,用以区别/判断对应事件循环处理函数的运行
    ESP_ERROR_CHECK(esp_event_handler_instance_register_with(loop_with_task, TASK_EVENTS, TASK_ITERATION_EVENT, task_iteration_handler, loop_with_task, NULL));
    ESP_ERROR_CHECK(esp_event_handler_instance_register_with(loop_without_task, TASK_EVENTS, TASK_ITERATION_EVENT, task_iteration_handler, loop_without_task, NULL));

    ESP_LOGI(TAG, "starting event source");

    // Create the event source task with the same priority as the current task
    //创建与当下任务有相同优先级的事件源任务
    xTaskCreate(task_event_source, "task_event_source", 2048, NULL, uxTaskPriorityGet(NULL), NULL);

    ESP_LOGI(TAG, "starting application task");
    // Create the application task with the same priority as the current task
    //创建与当下任务有相同优先级的应用任务
    xTaskCreate(application_task, "application_task", 2048, NULL, uxTaskPriorityGet(NULL), NULL);
}

第一步:配置要创建事件循环,一个带专属任务,一个不带,创建事件循环

    esp_event_loop_args_t loop_with_task_args = {           //配置事件循环
        .queue_size = 5,                                    //队列长度
        .task_name = "loop_task", // task will be created           //任务名
        .task_priority = uxTaskPriorityGet(NULL),           //优先级
        .task_stack_size = 2048,                            //分配栈大小
        .task_core_id = tskNO_AFFINITY
    };

    esp_event_loop_args_t loop_without_task_args = {        //配置不带任务的循环
        .queue_size = 5,
        .task_name = NULL // no task will be created
    };

在这里插入图片描述
参数依次是:
消息队列长度
任务名
优先级
分配的栈空间大小
要分配给哪个内核运行的内核id

配置完成后,创建循环

    // 创建事件循环
    ESP_ERROR_CHECK(esp_event_loop_create(&loop_with_task_args, &loop_with_task));
    ESP_ERROR_CHECK(esp_event_loop_create(&loop_without_task_args, &loop_without_task));

第二步:注册事件处理函数
注册事件处理函数之后,当发生事件时,就会执行相应的处理函数

    // Register the handler for task iteration event. Notice that the same handler is used for handling event on different loops.
    // The loop handle is provided as an argument in order for this example to display the loop the handler is being run on.
    //注册任务迭代事件的句柄,注意:同一个句柄也可被用于在不同循环中处理事件
    //这里事件循环的句柄同时还被当作了参数,用以区别/判断对应事件循环处理函数的运行
    ESP_ERROR_CHECK(esp_event_handler_instance_register_with(loop_with_task, TASK_EVENTS, TASK_ITERATION_EVENT, task_iteration_handler, loop_with_task, NULL));
    ESP_ERROR_CHECK(esp_event_handler_instance_register_with(loop_without_task, TASK_EVENTS, TASK_ITERATION_EVENT, task_iteration_handler, loop_without_task, NULL));

查看定义
在这里插入图片描述
参数依次:
事件循环的句柄
事件类型
具体事件id
事件处理函数
要传入事件处理函数的参数
事件处理函数的实例化对象

这里事件循环的句柄同时还被当作参数传给了事件处理函数之中,用以区别/判断。详见下

任务迭代处理函数task_iteration_handler(void* handler_args, esp_event_base_t base, int32_t id, void* event_data)

static void task_iteration_handler(void* handler_args, esp_event_base_t base, int32_t id, void* event_data)         //任务迭代处理函数
{
    // Two types of data can be passed in to the event handler: the handler specific data and the event-specific data.
    //
    // The handler specific data (handler_args) is a pointer to the original data, therefore, the user should ensure that
    // the memory location it points to is still valid when the handler executes.
    //
    // The event-specific data (event_data) is a pointer to a deep copy of the original data, and is managed automatically.
    //有两种特定的参数可以被传入事件处理函数:handler_args与event_data
    //handler_args是一个指向传入的参数原地址的指针,所以当该函数运行时,要保证源参数依旧有效
    //event_data是一个指向原参数副本的指针,副本(copy)过程是系统自动完成的
    int iteration = *((int*) event_data);

    char* loop;

    if (handler_args == loop_with_task) {
        loop = "loop_with_task";
    } else {
        loop = "loop_without_task";
    }

    ESP_LOGI(TAG, "handling %s:%s from %s, iteration %d", base, "TASK_ITERATION_EVENT", loop, iteration);
}

当触发事件时,事件循环的句柄被传入该处理函数,进而用来判断到底是哪个事件循环触发的。

第三步:创建两个任务(进程):事件源任务与用户自定任务

    // Create the event source task with the same priority as the current task
    //创建与当下任务有相同优先级的事件源任务
    xTaskCreate(task_event_source, "task_event_source", 2048, NULL, uxTaskPriorityGet(NULL), NULL);

    ESP_LOGI(TAG, "starting application task");
    // Create the application task with the same priority as the current task
    //创建与当下任务有相同优先级的应用任务
    xTaskCreate(application_task, "application_task", 2048, NULL, uxTaskPriorityGet(NULL), NULL);

事件源任务:task_event_source

周期发送事件给事件循环,奇数次发给带任务的事件循环,偶数次则发给不带任务的事件循环,进而触发相关事件处理函数

static void task_event_source(void* args)               //向任务发送事件的事件源函数
{
    for(int iteration = 1; iteration <= TASK_ITERATIONS_COUNT; iteration++) {           //迭代次数判断
        esp_event_loop_handle_t loop_to_post_to;

        if (iteration % 2 == 0) {
            // if even, post to the event loop with dedicated task
            //如果是偶数次,则用专用函数发送到事件循环
            loop_to_post_to = loop_with_task;
        } else {
            // if odd, post to the event loop without a dedicated task
            //如果奇数次,不带任务发送到事件循环
            loop_to_post_to = loop_without_task;
        }

        ESP_LOGI(TAG, "posting %s:%s to %s, iteration %d out of %d", TASK_EVENTS, "TASK_ITERATION_EVENT",
                loop_to_post_to == loop_with_task ? "loop_with_task" : "loop_without_task",
                iteration, TASK_ITERATIONS_COUNT);

        ESP_ERROR_CHECK(esp_event_post_to(loop_to_post_to, TASK_EVENTS, TASK_ITERATION_EVENT, &iteration, sizeof(iteration), portMAX_DELAY));           //发送事件到事件循环loop_to_post_to

        vTaskDelay(pdMS_TO_TICKS(TASK_PERIOD));
    }

    vTaskDelay(pdMS_TO_TICKS(TASK_PERIOD));

    ESP_LOGI(TAG, "deleting task event source");

    vTaskDelete(NULL);
}

用户自定任务(应用任务):application_task
负责开启不带任务的事件循环

static void application_task(void* args)        //应用任务
{
    while(1) {
        ESP_LOGI(TAG, "application_task: running application task");
        esp_event_loop_run(loop_without_task, 100);                 //投送事件给事件循环,100个时间片
        vTaskDelay(10);
    }
}

关于esp_event_loop_run
在这里插入图片描述
此函数用于在没有专用任务的情况下将事件发送到循环
参数:要运行的事件循环的句柄,运行时间

分析运行结果

主函数开始执行,初始化,事件源、用户进程开始运行
在这里插入图片描述
2.
事件被事件源进程投送给了loop_without_task
注意:此时事件已被投送给了loop_without_task,但loop_without_task整个循环并没有收到,等到应用进程执行esp_event_loop_run(loop_without_task, 100);之后,loop_without_task才得以接收到事件,进而触发对应处理函数task_iteration_handler
在这里插入图片描述
我们观察接下来的输出信息
在这里插入图片描述
第七次投送事件可以很好的证实这一点,也就明白了应用进程的作用

那么,这种情况又怎么回事儿呢?
在这里插入图片描述
很简单,esp_event_loop_run(loop_without_task, 100);,loop_without_task会持续开启100个时间片的时间,而第五次投送时还没到期。

后言

我本以为esp_event_loop_run这玩意儿是开启事件循环的,结果发现并不是,我就对为什么loop_without_task需要这函数手动开启才能接收到事件,而loop_with_task不需要这就能自行接受到事件感到非常困惑。

最后问题定位到这:配置事件循环,一个带了专属任务,一个没带
???最初我看到这,什么是专属任务?干啥的?一脸懵
在这里插入图片描述
跳转esp_event_loop_create定义,然后找到了这个,专属任务就是在这创建的
在这里插入图片描述
哟,就这个xTaskCreatePinnedToCore,那他第一个参数呢,那个处理函数esp_event_loop_run_task的作用是什么?继续跳转
在这里插入图片描述
好小子,可逮到你了
在这里插入图片描述
那么配置的专属任务的作用也就一清二楚了:负责发送事件给事件循环
loop_without_task一开始就没有配置专属任务,所以需要我们手动给发送下事件,这就是例程中应用任务所干的事儿。

最后总结:事件的发送,能否成功到达,需要满足两个条件:
1.事件源先调用esp_event_post_to
2.专属任务(dedicated task)再调用esp_event_loop_run
这样子,一个 事件才能成功到达并触发相关事件处理函数

官方例程是英文注释,一句dedicated task就带过去了,这个dedicated task到底干什么用的也没说,还得靠自己深入翻定义,麻。

不过攻克难题后,成就感也油然而生

  • 5
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值