系列文章目录
知不足而奋进 望远山而前行
目录
系列文章目录
文章目录
前言
目标
内容
概念
事件标志位
开发流程
功能介绍
创建事件组
触发事件
等待事件触发
同步
清理事件
案例
总结
前言
在嵌入式系统开发中,任务之间的同步和通信是至关重要的。FreeRTOS作为一款开源的实时操作系统,提供了多种机制来实现任务间的有效协调。其中,事件组(Event Group)机制是一种强大的工具,允许任务等待和检测多个事件的状态,并在事件发生时进行及时的通知。通过使用事件组,开发人员可以实现复杂的任务调度和协同工作,提高系统的效率和可靠性。
本文将深入探讨FreeRTOS中事件组的基本概念、功能特性以及具体的使用方法。我们将详细介绍如何创建和管理事件组,以及如何利用事件组进行任务间的同步和通信。通过实际案例和代码示例,读者将能够全面理解如何在实际项目中应用事件组,从而更好地优化和管理嵌入式系统中的任务流程。
目标
- 理解什么是事件组
- 理解事件组标志位
- 掌握事件组开发流程
内容
概念
在FreeRTOS中,事件组(Event Group)是一种用于任务间同步和通信的机制。事件组允许任务等待和检测多个事件的状态,并在事件发生时进行通知。
事件组由一组标志位(或事件位)组成,每个标志位代表一个特定的事件。任务可以等待某些标志位被置位或清除,也可以设置或清除标志位。
以下是事件组的一些关键概念:
- 事件组句柄(Event Group Handle):
事件组句柄是用于标识和操作事件组的变量。它可以在创建事件组时由FreeRTOS分配,也可以通过函数返回。
- 标志位(Event Bit):
标志位是事件组中的单个位,它表示一个特定的事件。每个标志位可以被置位或清除,以表示事件发生或未发生。
标志位通常用二进制位来表示,如第0位、第1位等。可以使用位操作函数进行置位和清除操作。
- 等待事件(Waiting Events):
任务可以通过等待事件组中的一个或多个标志位来暂停执行,直到这些标志位满足特定的条件。
等待事件可以通过函数如xEventGroupWaitBits()、xEventGroupSync()等实现。
- 通知事件(Signaling Events):
任务可以通过设置或清除事件组中的标志位来通知其他任务事件的发生。
通知事件可以通过函数如xEventGroupSetBits()、xEventGroupClearBits()等实现。
通过使用事件组,任务可以进行有效的同步和通信,实现复杂的任务调度和协调。任务可以等待多个事件同时发生,也可以通过设置或清除标志位来触发其他任务的执行。
请注意,事件组是FreeRTOS中的一个特性,具体的使用方法和函数可能因不同的FreeRTOS版本而略有差异。建议参考相关的FreeRTOS文档和参考资料以获取更详细和准确的信息。
事件标志位
- 事件标志为一个32位的数字。也就是采用
uint32_t
来记录事件状态。 - 32位的状态分为高8位和低24位。
- 高8位位系统级事件和状态,由操作系统自行管理,无需开发人员关系。
- 低24位表示24种事件,每1个bit位表示一种事件,由开发人员控制这些事件位。
- 低24位中,每一位的取值是0或者1,0表示当前位对应的事件没有触发,1表示当前位的事件触发了。
开发流程
- 创建事件组
- 开启一个任务,用来等待事件触发
- 开启一个任务,用来改变事件触发
事件组标志位像一个订阅中心一样,一方可以订阅事件的触发,一方可以改变事件的状态。
功能介绍
功能 | 描述 |
xEventGroupCreate | 创建事件组 |
xEventGroupSetBits | 将事件状态改为触发 |
xEventGroupWaitBits | 等待事件触发 |
创建事件组
EventGroupHandle_t xEventGroupCreate();
返回值为事件组的句柄。
触发事件
将事件状态改为触发
EventBits_t xEventGroupSetBits( EventGroupHandle_t xEventGroup,
const EventBits_t uxBitsToSet )
参数说明:
EventGroupHandle_t xEventGroup
:事件组的句柄。const EventBits_t uxBitsToSet
:事件标志位。
返回值:
当前触发的事件有哪些。
等待事件触发
EventBits_t xEventGroupWaitBits( EventGroupHandle_t xEventGroup,
const EventBits_t uxBitsToWaitFor,
const BaseType_t xClearOnExit,
const BaseType_t xWaitForAllBits,
TickType_t xTicksToWait )
参数说明:
EventGroupHandle_t xEventGroup
:事件组的句柄。const EventBits_t uxBitsToWaitFor
:等待事件组的哪个标志位。可以是多个标志位。例如:
-
- 一个标志位(索引为1):0x02
- 两个标志位(索引为1和3):0x0A(0x08 + 0x02)
const BaseType_t xClearOnExit
:等待事件触发后,是否清除这个事件,如果清除,其他的订阅者将不会收到,不清除,就会收到。
-
pdTRUE
表示清除pdFALSE
表示不清除。
const BaseType_t xWaitForAllBits
:指定是否等待所有的标志位都被设置。
-
pdTRUE
表示等待所有标志位都被设置。(都为1)pdFALSE
表示只要有任何一个标志位被设置就可以继续执行任务。(任意一个为1)
TickType_t xTicksToWait
:等待标志位被设置的超时时间,以 FreeRTOS 的 Tick 单位表示。
-
- 可以使用
pdMS_TO_TICKS
宏将毫秒转换为 Tick 值。 - 如果设置为
portMAX_DELAY
,则表示无限等待,直到标志位被设置。
- 可以使用
返回值表示已设置的标志位。
同步
EventBits_t xEventGroupSync( EventGroupHandle_t xEventGroup,
const EventBits_t uxBitsToSet,
const EventBits_t uxBitsToWaitFor,
TickType_t xTicksToWait )
参数说明:
EventGroupHandle_t xEventGroup
:事件组句柄const EventBits_t uxBitsToSet
:要设置的标志位const EventBits_t uxBitsToWaitFor
:要等待的事件标志位TickType_t xTicksToWait
:等待的超时时间
清理事件
案例
开启三个任务,等待事件发生。通过按键触发事件发生。观察效果。
#include "gd32f4xx.h"
#include "systick.h"
#include <stdio.h>
#include "main.h"
#include "FreeRTOS.h"
#include "task.h"
#include "event_groups.h"
#include "Usart0.h"
#define EVENT_BIT(N) (1 << N)
#define EVENT_BIT_0 EVENT_BIT(0)
#define EVENT_BIT_1 EVENT_BIT(1)
#define EVENT_BIT_2 EVENT_BIT(2)
#define EVENT_BIT_3 EVENT_BIT(3)
#define EVENT_BIT_4 EVENT_BIT(4)
#define EVENT_BIT_5 EVENT_BIT(5)
#define EVENT_BIT_6 EVENT_BIT(6)
#define EVENT_BIT_7 EVENT_BIT(7)
#define EVENT_BIT_8 EVENT_BIT(8)
#define EVENT_BIT_9 EVENT_BIT(9)
#define EVENT_BIT_10 EVENT_BIT(10)
#define EVENT_BIT_11 EVENT_BIT(11)
#define EVENT_BIT_12 EVENT_BIT(12)
#define EVENT_BIT_13 EVENT_BIT(13)
#define EVENT_BIT_14 EVENT_BIT(14)
#define EVENT_BIT_15 EVENT_BIT(15)
#define EVENT_BIT_16 EVENT_BIT(16)
#define EVENT_BIT_17 EVENT_BIT(17)
#define EVENT_BIT_18 EVENT_BIT(18)
#define EVENT_BIT_19 EVENT_BIT(19)
#define EVENT_BIT_20 EVENT_BIT(20)
#define EVENT_BIT_21 EVENT_BIT(21)
#define EVENT_BIT_22 EVENT_BIT(22)
#define EVENT_BIT_23 EVENT_BIT(23)
TaskHandle_t task_handler;
TaskHandle_t task_key_handler;
TaskHandle_t task1_handler;
TaskHandle_t task2_handler;
TaskHandle_t task3_handler;
EventGroupHandle_t event_group;
void task1(void *pvParameters) {
EventBits_t bits;
while(1) {
bits = xEventGroupWaitBits(event_group, EVENT_BIT_0, pdTRUE, pdFALSE, portMAX_DELAY);
printf("task1: %d\r\n", bits);
vTaskDelay(1000);
}
vTaskDelete(NULL);
}
void task2(void *pvParameters) {
EventBits_t bits;
while(1) {
bits = xEventGroupWaitBits(event_group, EVENT_BIT_0, pdTRUE, pdTRUE, portMAX_DELAY);
printf("task2: %d\r\n", bits);
vTaskDelay(2000);
}
vTaskDelete(NULL);
}
void task3(void *pvParameters) {
EventBits_t bits = 0;
while(1) {
bits = xEventGroupWaitBits(event_group, EVENT_BIT_0, pdTRUE, pdTRUE, portMAX_DELAY);
printf("task3: %d\r\n", bits);
vTaskDelay(3000);
}
vTaskDelete(NULL);
}
void task_key(void *pvParameters) {
FlagStatus pre_state = RESET;
while(1) {
FlagStatus state = gpio_input_bit_get(GPIOA, GPIO_PIN_0);
if(SET == state && pre_state == RESET) {
// 当前高电平, 上一次为低电平,按下
pre_state = state;
printf("set bit \r\n");
xEventGroupSetBits(event_group, EVENT_BIT_0);
} else if(RESET == state && pre_state == SET) {
// 当前高电平, 上一次为低电平,抬起
pre_state = state;
}
vTaskDelay(20);
}
}
void start_task(void *pvParameters) {
GPIO_config();
Usart0_init();
event_group = xEventGroupCreate();
if(event_group != NULL) {
printf("group success\r\n");
}
taskENTER_CRITICAL();
xTaskCreate(task_key, "task_key", 64, NULL, 0, &task_key_handler);
xTaskCreate(task1, "task1", 64, NULL, 4, &task1_handler);
xTaskCreate(task2, "task2", 64, NULL, 3, &task2_handler);
xTaskCreate(task3, "task3", 64, NULL, 2, &task3_handler);
vTaskDelete(task_handler);
taskEXIT_CRITICAL();
}
void Usart0_recv(uint8_t *data, uint32_t len)
{
// printf("recv: %s\n", data);
xEventGroupSetBitsFromISR(event_group, EVENT_BIT_0, NULL);
}
static void GPIO_config() {
// 时钟初始化
rcu_periph_clock_enable(RCU_GPIOA);
// 配置GPIO模式
gpio_mode_set(GPIOA, GPIO_MODE_INPUT, GPIO_PUPD_PULLDOWN, GPIO_PIN_0);
}
int main(void)
{
nvic_priority_group_set(NVIC_PRIGROUP_PRE4_SUB0);
xTaskCreate(start_task, "start_task", 128, NULL, 0, &task_handler);
vTaskStartScheduler();
while(1) {}
}
总结
事件组作为FreeRTOS中的重要特性,为嵌入式系统开发带来了灵活而强大的任务协调机制。通过本文的学习,我们深入了解了事件组的核心概念,包括事件组句柄、标志位的设置和等待、以及任务间的同步与通信方式。我们学习了如何创建事件组、如何设置和等待特定的事件标志位,以及如何通过事件组实现任务的有效协作。
在实际应用中,事件组可以帮助开发人员实现高效的任务调度和资源管理,提升系统的可靠性和响应速度。通过合理利用事件组的功能,开发人员能够更好地优化系统性能,适应不同的应用场景需求,从而实现更为复杂和可靠的嵌入式软件设计。
通过持续学习和实践,我们可以进一步探索和应用事件组的高级特性,为嵌入式系统开发注入更多创新和效率。希望本文能为读者提供实用且深入的指导,帮助他们在实际项目中充分发挥事件组的优势,构建出更加稳健和高效的嵌入式应用程序。