代码
#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);
vTaskDelay(10);
}
}
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);
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) {
loop_to_post_to = loop_with_task;
} else {
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));
vTaskDelay(pdMS_TO_TICKS(TASK_PERIOD));
}
vTaskDelay(pdMS_TO_TICKS(TASK_PERIOD));
ESP_LOGI(TAG, "deleting task event source");
vTaskDelete(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_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
};
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));
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");
xTaskCreate(task_event_source, "task_event_source", 2048, NULL, uxTaskPriorityGet(NULL), NULL);
ESP_LOGI(TAG, "starting application 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_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
};
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));
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");
xTaskCreate(task_event_source, "task_event_source", 2048, NULL, uxTaskPriorityGet(NULL), NULL);
ESP_LOGI(TAG, "starting application 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_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
};
参数依次是: 消息队列长度 任务名 优先级 分配的栈空间大小 要分配给哪个内核运行的内核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));
第二步:注册事件处理函数 注册事件处理函数之后,当发生事件时,就会执行相应的处理函数
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)
{
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);
}
当触发事件时,事件循环的句柄被传入该处理函数,进而用来判断到底是哪个事件循环触发的。
第三步:创建两个任务(进程):事件源任务与用户自定任务
xTaskCreate(task_event_source, "task_event_source", 2048, NULL, uxTaskPriorityGet(NULL), NULL);
ESP_LOGI(TAG, "starting application 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) {
loop_to_post_to = loop_with_task;
} else {
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));
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);
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到底干什么用的也没说,还得靠自己深入翻定义,麻。
不过攻克难题后,成就感也油然而生
|