esp32-freeRTOS

事件组

FreeRTOS 中的事件组(Event Groups)是用于任务间同步的一种机制。在 ESP32 的 FreeRTOS 中,事件组可以用来实现任务间的事件通知。下面是关于如何应用事件组的详细解析和示例:

事件组的创建和应用

1. 创建事件组

使用 xEventGroupCreate 函数创建一个事件组。这个函数返回一个句柄,用于后续的事件组操作。

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

// 创建事件组句柄
EventGroupHandle_t xEventGroup;

void app_main() {
    // 创建事件组
    xEventGroup = xEventGroupCreate();
    if (xEventGroup == NULL) {
        // 处理创建失败的情况
        printf("Event Group creation failed\n");
    } else {
        printf("Event Group created successfully\n");
    }
}
2. 设置和清除事件标志

使用 xEventGroupSetBitsxEventGroupClearBits 函数设置或清除事件组中的某些事件标志位。

// 任务1:设置事件标志位
void vTask1(void *pvParameters) {
    // 设置事件标志位0x01
    xEventGroupSetBits(xEventGroup, BIT0);
    vTaskDelete(NULL);
}

// 任务2:清除事件标志位
void vTask2(void *pvParameters) {
    // 清除事件标志位0x01
    xEventGroupClearBits(xEventGroup, BIT0);
    vTaskDelete(NULL);
}
3. 等待事件标志

使用 xEventGroupWaitBits 函数等待特定的事件标志位被设置。可以指定等待条件和超时时间。

#define BIT0 (1 << 0)
#define BIT1 (1 << 1)

void vTask3(void *pvParameters) {
    // 等待事件标志位BIT0或BIT1
    EventBits_t uxBits = xEventGroupWaitBits(
        xEventGroup,     // 事件组句柄
        BIT0 | BIT1,     // 等待的事件位
        pdTRUE,          // 清除已设置的事件位
        pdFALSE,         // 等待任一事件位
        portMAX_DELAY    // 无限等待
    );

    if (uxBits & BIT0) {
        // 处理事件BIT0被设置
        printf("Event BIT0 was set\n");
    }
    if (uxBits & BIT1) {
        // 处理事件BIT1被设置
        printf("Event BIT1 was set\n");
    }

    vTaskDelete(NULL);
}

综合示例

下面是一个完整的示例,演示如何在 ESP32 的 FreeRTOS 中创建和使用事件组。

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

// 定义事件标志位
#define BIT0 (1 << 0)
#define BIT1 (1 << 1)

// 创建事件组句柄
EventGroupHandle_t xEventGroup;

// 任务1:设置事件标志位BIT0
void vTask1(void *pvParameters) {
    // 设置事件标志位0x01
    xEventGroupSetBits(xEventGroup, BIT0);
    vTaskDelete(NULL);
}

// 任务2:设置事件标志位BIT1
void vTask2(void *pvParameters) {
    // 设置事件标志位0x02
    xEventGroupSetBits(xEventGroup, BIT1);
    vTaskDelete(NULL);
}

// 任务3:等待事件标志位BIT0或BIT1
void vTask3(void *pvParameters) {
    // 等待事件标志位BIT0或BIT1
    EventBits_t uxBits = xEventGroupWaitBits(
        xEventGroup,     // 事件组句柄
        BIT0 | BIT1,     // 等待的事件位
        pdTRUE,          // 清除已设置的事件位
        pdFALSE,         // 等待任一事件位
        portMAX_DELAY    // 无限等待
    );

    if (uxBits & BIT0) {
        // 处理事件BIT0被设置
        printf("Event BIT0 was set\n");
    }
    if (uxBits & BIT1) {
        // 处理事件BIT1被设置
        printf("Event BIT1 was set\n");
    }

    vTaskDelete(NULL);
}

void app_main() {
    // 创建事件组
    xEventGroup = xEventGroupCreate();
    if (xEventGroup == NULL) {
        // 处理创建失败的情况
        printf("Event Group creation failed\n");
        return;
    }

    // 创建任务
    xTaskCreate(vTask1, "Task 1", 2048, NULL, 1, NULL);
    xTaskCreate(vTask2, "Task 2", 2048, NULL, 1, NULL);
    xTaskCreate(vTask3, "Task 3", 2048, NULL, 1, NULL);
}

关键点总结

  • 事件组创建:使用 xEventGroupCreate 创建事件组。
  • 设置事件标志:使用 xEventGroupSetBits 设置特定位。
  • 清除事件标志:使用 xEventGroupClearBits 清除特定位。
  • 等待事件标志:使用 xEventGroupWaitBits 等待特定位被设置,支持多种等待条件。

事件组在任务同步和事件通知方面非常有用,通过灵活设置和等待不同的事件标志,可以实现复杂的任务间协作和通信。



总结

我说一下我对esp32-freeRTOS 事件组的理解,就是我们如果想要实现任务间的同步,比如1个任务完成了,通知多个任务,就会用到事件组,具体操作就是:1、创建事件组 2、设置,清除某些事件组中的bit, 3、在任务重等待实践组被设置或者清除,然后通知该事件,执行相应的回调函数。


ESP32-FreeRTOS中的事件组确实是用来实现任务间同步的一种机制,特别适用于一个任务完成后通知多个任务的情况。以下是对您理解的具体步骤的详细解释和确认:

1. 创建事件组

这是事件组应用的第一步,使用 xEventGroupCreate 函数来创建一个事件组。

2. 设置和清除事件组中的 bit

任务可以使用 xEventGroupSetBits 来设置事件组中的特定位,表示某个事件发生。同样,可以使用 xEventGroupClearBits 来清除特定位,表示某个事件结束或状态恢复。

3. 等待事件组中的 bit 被设置或清除

任务可以使用 xEventGroupWaitBits 函数来等待特定位被设置或清除。等待条件满足后,任务会继续执行,通常会在这个点执行相应的回调函数或处理逻辑。

示例总结

您描述的步骤可以通过以下示例代码进一步说明:

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

// 定义事件标志位
#define BIT0 (1 << 0)
#define BIT1 (1 << 1)

// 创建事件组句柄
EventGroupHandle_t xEventGroup;

// 任务1:设置事件标志位BIT0
void vTask1(void *pvParameters) {
    // 模拟任务1完成了一些工作
    vTaskDelay(pdMS_TO_TICKS(1000)); // 模拟延迟
    // 设置事件标志位0x01
    xEventGroupSetBits(xEventGroup, BIT0);
    vTaskDelete(NULL);
}

// 任务2:等待事件标志位BIT0
void vTask2(void *pvParameters) {
    // 等待事件标志位BIT0
    EventBits_t uxBits = xEventGroupWaitBits(
        xEventGroup,     // 事件组句柄
        BIT0,            // 等待的事件位
        pdTRUE,          // 清除已设置的事件位
        pdFALSE,         // 等待任一事件位
        portMAX_DELAY    // 无限等待
    );

    if (uxBits & BIT0) {
        // 处理事件BIT0被设置
        printf("Task 2: Event BIT0 was set\n");
    }
    vTaskDelete(NULL);
}

// 任务3:等待事件标志位BIT0
void vTask3(void *pvParameters) {
    // 等待事件标志位BIT0
    EventBits_t uxBits = xEventGroupWaitBits(
        xEventGroup,     // 事件组句柄
        BIT0,            // 等待的事件位
        pdTRUE,          // 清除已设置的事件位
        pdFALSE,         // 等待任一事件位
        portMAX_DELAY    // 无限等待
    );

    if (uxBits & BIT0) {
        // 处理事件BIT0被设置
        printf("Task 3: Event BIT0 was set\n");
    }
    vTaskDelete(NULL);
}

void app_main() {
    // 创建事件组
    xEventGroup = xEventGroupCreate();
    if (xEventGroup == NULL) {
        // 处理创建失败的情况
        printf("Event Group creation failed\n");
        return;
    }

    // 创建任务
    xTaskCreate(vTask1, "Task 1", 2048, NULL, 1, NULL);
    xTaskCreate(vTask2, "Task 2", 2048, NULL, 1, NULL);
    xTaskCreate(vTask3, "Task 3", 2048, NULL, 1, NULL);
}

关键点总结

  • 创建事件组:用 xEventGroupCreate 创建一个事件组。
  • 设置事件标志:任务完成某些操作后,用 xEventGroupSetBits 设置事件组中的特定位。
  • 等待事件标志:其他任务用 xEventGroupWaitBits 等待特定位被设置,接收到事件后执行相应的操作。
  • 清除事件标志:可以通过等待函数中的参数或显式调用 xEventGroupClearBits 来清除特定位。

您对ESP32-FreeRTOS事件组的理解非常准确,以上示例代码进一步说明了事件组的使用方法。通过这些步骤,可以有效实现任务间的同步和事件通知。

互斥量

城哥,xSemaphoreCreateMutex 是 FreeRTOS 中用于创建互斥量(Mutex)的函数,互斥量是一种用于任务间同步和资源保护的工具。下面是对 xSemaphoreCreateMutex 及其在 ESP32-FreeRTOS 中应用的详细解析:

函数原型

SemaphoreHandle_t xSemaphoreCreateMutex( void );

功能描述

  • 创建互斥量xSemaphoreCreateMutex 用于创建一个互斥量。互斥量是一种特殊的二值信号量,设计用于保护共享资源,以确保同一时刻只有一个任务能够访问该资源。
  • 返回值:该函数返回一个 SemaphoreHandle_t 类型的句柄,如果创建成功,返回互斥量的句柄;如果创建失败,返回 NULL。

互斥量的特点

  • 任务间独占访问:互斥量确保同一时间只有一个任务能够“持有”互斥量,从而独占访问共享资源。
  • 优先级继承机制:FreeRTOS 的互斥量实现了优先级继承机制,可以防止优先级反转问题。在一个低优先级任务持有互斥量而高优先级任务等待该互斥量时,低优先级任务会临时提升到高优先级,以减少高优先级任务的等待时间。

使用步骤

1. 创建互斥量
SemaphoreHandle_t xMutex;

void app_main() {
    xMutex = xSemaphoreCreateMutex();
    if (xMutex == NULL) {
        // 处理创建失败的情况
        printf("Mutex creation failed\n");
    } else {
        printf("Mutex created successfully\n");
    }
}
2. 获取互斥量

任务在访问共享资源前需要先获取互斥量,可以使用 xSemaphoreTake 函数。

if (xSemaphoreTake(xMutex, portMAX_DELAY) == pdTRUE) {
    // 成功获取互斥量,安全访问共享资源
    printf("Mutex acquired\n");
    // ... 访问共享资源
}
3. 释放互斥量

任务在访问完共享资源后需要释放互斥量,可以使用 xSemaphoreGive 函数。

// 访问完共享资源,释放互斥量
xSemaphoreGive(xMutex);
printf("Mutex released\n");

综合示例

下面是一个完整的示例,演示如何在 ESP32 的 FreeRTOS 中创建和使用互斥量来保护共享资源。

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

// 定义互斥量句柄
SemaphoreHandle_t xMutex;

// 共享资源
int sharedResource = 0;

// 任务1:访问共享资源
void vTask1(void *pvParameters) {
    while (1) {
        // 获取互斥量
        if (xSemaphoreTake(xMutex, portMAX_DELAY) == pdTRUE) {
            // 访问共享资源
            sharedResource++;
            printf("Task 1: Shared resource value: %d\n", sharedResource);
            // 释放互斥量
            xSemaphoreGive(xMutex);
        }
        vTaskDelay(pdMS_TO_TICKS(1000)); // 模拟任务周期
    }
}

// 任务2:访问共享资源
void vTask2(void *pvParameters) {
    while (1) {
        // 获取互斥量
        if (xSemaphoreTake(xMutex, portMAX_DELAY) == pdTRUE) {
            // 访问共享资源
            sharedResource++;
            printf("Task 2: Shared resource value: %d\n", sharedResource);
            // 释放互斥量
            xSemaphoreGive(xMutex);
        }
        vTaskDelay(pdMS_TO_TICKS(1000)); // 模拟任务周期
    }
}

void app_main() {
    // 创建互斥量
    xMutex = xSemaphoreCreateMutex();
    if (xMutex == NULL) {
        // 处理创建失败的情况
        printf("Mutex creation failed\n");
        return;
    }

    // 创建任务
    xTaskCreate(vTask1, "Task 1", 2048, NULL, 1, NULL);
    xTaskCreate(vTask2, "Task 2", 2048, NULL, 1, NULL);
}

关键点总结

  • 互斥量创建:使用 xSemaphoreCreateMutex 创建一个互斥量,确保共享资源的独占访问。
  • 获取互斥量:任务在访问共享资源前使用 xSemaphoreTake 获取互斥量,成功获取后才能安全访问资源。
  • 释放互斥量:任务在访问完共享资源后使用 xSemaphoreGive 释放互斥量,使其他任务能够访问资源。
  • 优先级继承:互斥量具备优先级继承机制,防止优先级反转,提高系统的实时性。

通过这些步骤,您可以在 ESP32-FreeRTOS 中有效地使用互斥量来保护共享资源,确保任务间的同步和资源的独占访问。

队列

城哥,以下是对 xQueueSendFromISR 函数的详细解析和应用示例。这个函数是FreeRTOS中的一个重要函数,用于在中断服务例程(ISR)中向队列发送数据。

函数定义

BaseType_t xQueueSendFromISR(
    QueueHandle_t xQueue,
    const void *pvItemToQueue,
    BaseType_t *pxHigherPriorityTaskWoken
);

参数解析

  1. xQueue:

    • 队列句柄,指定要向其中发送数据的队列。
  2. pvItemToQueue:

    • 指向要发送到队列中的数据的指针。数据的大小应与创建队列时指定的项目大小相同。
  3. pxHigherPriorityTaskWoken:

    • 一个指向BaseType_t类型变量的指针。如果在中断退出时需要执行一个优先级更高的任务,这个变量将被设置为 pdTRUE

返回值

  • pdPASS:

    • 如果数据成功发送到队列,返回 pdPASS
  • errQUEUE_FULL:

    • 如果队列已满,数据无法发送时,返回 errQUEUE_FULL

示例代码

以下是一个应用示例,展示如何在中断服务例程中使用 xQueueSendFromISR 函数。

#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "driver/gpio.h"

#define BTN_PIN GPIO_NUM_0 // 按键引脚

static QueueHandle_t key_evt_queue;

// 中断服务例程
static void IRAM_ATTR btn_isr_handler(void *arg)
{
    uint32_t gpio_num = (uint32_t) arg;
    BaseType_t xHigherPriorityTaskWoken = pdFALSE;
    
    // 将GPIO引脚号发送到队列
    xQueueSendFromISR(key_evt_queue, &gpio_num, &xHigherPriorityTaskWoken);

    // 如果从ISR中唤醒了一个优先级更高的任务,进行任务切换
    if (xHigherPriorityTaskWoken) {
        portYIELD_FROM_ISR();
    }
}

// 处理按键事件的任务
static void key_read_task(void *arg)
{
    uint32_t io_num;
    while (1) {
        // 等待队列中的按键事件
        if (xQueueReceive(key_evt_queue, &io_num, portMAX_DELAY)) {
            printf("GPIO[%d] intr, val: %d\n", io_num, gpio_get_level(io_num));
        }
    }
}

// 初始化按键和队列
void btn_init()
{
    gpio_config_t io_conf;
    
    // 配置GPIO中断
    io_conf.intr_type = GPIO_INTR_NEGEDGE; // 下降沿触发中断
    io_conf.pin_bit_mask = (1ULL << BTN_PIN);
    io_conf.mode = GPIO_MODE_INPUT;
    io_conf.pull_up_en = 1;
    gpio_config(&io_conf);

    // 创建队列
    key_evt_queue = xQueueCreate(10, sizeof(uint32_t));

    // 安装GPIO中断服务
    gpio_install_isr_service(0);
    gpio_isr_handler_add(BTN_PIN, btn_isr_handler, (void*) BTN_PIN);

    // 创建按键事件处理任务
    xTaskCreate(key_read_task, "key_read_task", 2048, NULL, 10, NULL);
}

void app_main()
{
    btn_init();
}

解析

  1. 中断服务例程(ISR):

    • btn_isr_handler 是一个GPIO中断处理函数,当按键引脚发生中断时触发。
    • 使用 xQueueSendFromISR 将中断事件(按键引脚号)发送到队列 key_evt_queue
    • portYIELD_FROM_ISR 用于在需要时进行任务切换。
  2. 按键事件处理任务:

    • key_read_task 是一个FreeRTOS任务,从队列中接收按键事件并处理。
    • 使用 xQueueReceive 从队列中读取数据(按键引脚号),并打印中断引脚及其电平值。
  3. 按键和队列初始化:

    • btn_init 函数配置按键引脚,创建队列,安装GPIO中断服务,并创建按键事件处理任务。

结论

  • xQueueSendFromISR 函数允许在中断服务例程中向队列发送数据,从而在中断和任务之间传递信息。
  • 通过使用 xQueueSendFromISR 可以实现高效的中断处理,确保中断处理程序快速返回,并将更多的处理工作交给任务来完成。
  • 需要特别注意在ISR中使用合适的FreeRTOS函数,以避免违反实时操作系统的限制和规则。

如果有更多的问题或需要进一步的解释,请告诉我。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值