代码
#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到底干什么用的也没说,还得靠自己深入翻定义,麻。
不过攻克难题后,成就感也油然而生