当前位置: 首页 > news >正文

FreeRTOS任务同步与通信--事件标志组

FreeRTOS任务同步--事件标志组

  • Chapter1 FreeRTOS任务同步--事件标志组
    • 事件标志组相关API函数
    • 一、事件标志组简介
      • 1.1事件位(事件标志)
      • 1.2事件组
      • 1.3事件标志组和事件位的数据类型
    • 二、创建事件标志组
      • 2.1函数 xEventGroupCreate()
      • 2.2函数xEventGroupCreateStatic()
    • 三、设置事件位
      • 3.1函数 xEventGroupClearBits()
      • 3.2函数xEventGroupClearBitsFromISR()
      • 3.3函数 xEventGroupSetBits()
      • 3.4函数xEventGroupSetBitsFromISR()
    • 四、获取事件标志组值
      • 4.1函数xEventGroupGetBits()
      • 4.2函数xEventGroupGetBitsFromISR()
    • 五、等待指定的事件位
    • 六、事件标志组实验
      • 6.1实验要求
      • 6.2实验代码
      • 6.3实验要求2
      • 6.4实验代码
      • 6.5 场景:多个传感器数据采集任务,主任务需要在全部采集完成后处理数据。
    • 七、注意事项与最佳实践


Chapter1 FreeRTOS任务同步–事件标志组

原文链接:https://blog.csdn.net/qq_27928443/article/details/131275520

在这里插入图片描述

事件标志组相关API函数

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

一、事件标志组简介

1.1事件位(事件标志)

事件位用于表明某个事件是否发生,事件位通常用作事件标志,比如下面的几个例子:
当收到一条消息并且把这条消息处理掉以后就可以将某个位(标志)置1,当队列中没有消息需要处理的时候就可以将这个位(标志)置0。
当把队列中的消息通过网络发送输出以后就可以将某个位(标志)置1,当没有数据需要从网络发送出去的话就将这个位(标志)置0。
现在需要向网络中发送一个心跳信息,将某个位(标志)置1。现在不需要项网络中发送心跳信息,这个位(标志)置0。

1.2事件组

一个事件组就是一组的事件位,事件组中的事件位通过位编号来访问,同样,以上面列出的三个例子为例:
事件标志组bit0 表示队列中的消息是否处理掉。
事件标志组bit1 表示是否有信息需要从网络中发送出去。
事件标志组bit2 表示现在是否需要向网路发送心跳信息。

1.3事件标志组和事件位的数据类型

事件标志组的数据类型为 EventBits_t,当configUSE_16_BIT_TICKS为1的时候,事件标志组可以存储8个事件位,当configUSE_16_BIT_TICKS为0的时候,事件标志组存储24个事件位。
事件标志组中所有事件位都存储在一个无符号的EventBits_t类型的变量中,EventBits_t在event_groups.h中有如下定义:

typedef TickType_t EventBits_t;

数据类型TickType_t在文件portmacro.h中有如下定义:

#if( configUSE_16_BIT_TICKS == 1 )typedef uint16_t TickType_t;#define portMAX_DELAY ( TickType_t ) 0xffff
#elsetypedef uint32_t TickType_t;#define portMAX_DELAY ( TickType_t ) 0xffffffffUL/* 32-bit tick type on a 32-bit architecture, so reads of the tick count donot need to be guarded with a critical section. */#define portTICK_TYPE_IS_ATOMIC 1
#endif

可以看出当 configUSE_16_BIT_TICKS 为0的时候,TickType_t是个32位的数据类型,因此EventBits_t也是个32位的数据类型。EventBits_t类型的变量可以存储24个事件位,另外的那高8位有其他用。事件位0存放在这个变量的bit0上,变量的bit1就是事件位1,以此类推。对于STM32来说,一个事件标志组最多可以存储24个事件位,如下图:
在这里插入图片描述

二、创建事件标志组

FreeRTOS提供了两个用于创建事件标志组的函数:
在这里插入图片描述

2.1函数 xEventGroupCreate()

此函数用于创建一个时间标志组,锁需要的内存通过动态内存管理方法分配。由于内部处理的原因,事件标志组可用的bit数取决于configUSE_16_BIT_TICKS,当configUSE_16_BIT_TICKS为1的时候,事件标志组有8个可用的位(bit0-bit7),当configUSE_16_BIT_TICKS为0的时候,时间标志组有24个可用的位(bit0~bit23)。EventBits_t类型的变量用来存储事件标志组中的各个事件位,函数原型如下:

EventGroupHandle_t xEventGroupCreate( void )

参数:

无。

返回值:

NULL:事件标志组创建失败。

其他值:创建成功的事件标志组句柄。

2.2函数xEventGroupCreateStatic()

此函数用于创建一个事件标志组,所需要的内存需要用于自行分配,此函数原型如下:

EventGroupHandle_t xEventGroupCreateStatic( StaticEventGroup_t *pxEventGroupBuffer )

参数:

pxEventGroupBuffer:参数指向一个StaticEventGroup_t类型的变量,用来保存时间组结构体。

返回值:

NULL:事件标志组创建失败。

其他值:创建成功的事件标志组句柄。

三、设置事件位

FreeRTOS提供了4个函数用来设置事件标志组中事件位(标志),事件位的设置包括清零和置1两种操作:
在这里插入图片描述

3.1函数 xEventGroupClearBits()

将事件标志组中指定事件位清零,此函数只能用在任务中,不能用在中断服务函数中,中断服务函数中有其他的API函数。函数原型如下:

EventBits_t xEventGroupClearBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToClear )

参数:

xEventGroup:要操作的事件标志组的句柄。

uxBitsToClear:要清零的事件位,比如要清除bit3的话就设置为0x08。可以同时清除多个bit,如设置0x09的话就是同时清除bit3和bit0。

返回值:

任何值:将指定事件位清零之前的事件组值。

3.2函数xEventGroupClearBitsFromISR()

此函数为xEventGroupClearBits()的中断级版本,也是将指定的事件位(标志)清零。此函数用在中断服务函数中,函数原型如下:

BaseType_t xEventGroupClearBitsFromISR( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet ) 

参数:

xEventGroup:要操作的事件标志组的句柄。

uxBitsToClear:要清零的事件位,比如要清除bit3的话就设置为0x08。可以同时清除多个bit,如设置0x09的话就是同时清除bit3和bit0。

返回值:

pdPASS:事件位清零成功。

pdFALSE:事件位清零失败。

3.3函数 xEventGroupSetBits()

设置指定的事件位为1,此函数只能用在任务中,不能用于中断服务函数。此函数原型如下:

EventBits_t xEventGroupSetBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet )

参数:

xEventGroup:要操作的事件标志组句柄。

uxBitsToSet:指定要置1的事件位,比如要将bit3 置1的话就设置为0x08。可以同时将多个bit置1,如设置为0x09的话就是同时将bit3和bit0置1。

返回值:

任何值:在将指定事件位置1后的事件组值。

3.4函数xEventGroupSetBitsFromISR()

此函数也用于将指定事件位置1,此函数是xEventGroupSetBits()的中断版本,用在中断服务函数中,函数原型如下:

BaseType_t xEventGroupSetBitsFromISR( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet, BaseType_t *pxHigherPriorityTaskWoken )

参数:

xEventGroup:要操作的事件标志组的句柄。

uxBitsToClear:指定要置1的事件位,比如要将bit3置1的话就设置0x08。可以同时将多个bit置1,如设置为0x09的话就是同时将bit3和bit0置1。

pxHigherPriorityTaskWoken:标记退出此函数以后是否进行任务切换。这个变量的值,函数会自动设置,用户不用进行设置,用户只需要提供一个变量来保存这个值就行了。当此值为pdTRUE的时候在退出中断服务函数之前一定要进行一次任务切换。

返回值:

pdPASS:事件位置1成功。

pdFALSE:事件位置1失败。

四、获取事件标志组值

我们可以通过FreeRTOS提供的API函数来查询事件标志组值。FreeRTOS一共提供了两个这样的API函数。
在这里插入图片描述

4.1函数xEventGroupGetBits()

此函数用于获取当前事件标志组的值,也就是各个事件位的值。此函数用在任务中,不能用在中断服务函数中。此函数是个宏,真正执行的事函数xEventGroupClearBits(),函数原型如下:

EventBits_t xEventGroupGetBits( EventGroupHandle_t xEventGroup )

参数:

xEventGroup:要获取的事件标志组的句柄。

返回值:

任何值:当前时间标志组的值。

4.2函数xEventGroupGetBitsFromISR()

获取当前事件标志组的值,此函数是xEventGroupGetBits()的中断版本,函数原型如下:

EventBits_t xEventGroupGetBitsFromISR( EventGroupHandle_t xEventGroup )

五、等待指定的事件位

某个任务可能需要与多个事件进行同步,那么这个任务就需要等待并判断多个事件位(标志),使用函数 xEventGroupWaitBits()可以完成这个功能。调用函数以后如果任务要等待的事件位还没有准备好(置1或清零)的话任务就会进入阻塞态,直到阻塞时间达到或者所等待的事件位准备好。函数原型如下:

EventBits_t xEventGroupWaitBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToWaitFor, const BaseType_t xClearOnExit, const BaseType_t xWaitForAllBits, TickType_t xTicksToWait )

参数:

xEventGroup:指定要等待的事件标志组。

uxBitsToWaitFor:指定要等待的事件位,比如要等待bit0和(或)bit2的时候,此参数就是0x05,如果要等待bit0和(或)bit1和(或)bit2的时候此函数就是0x07,以此类推。

xClearOnExit:此参数要是pdTRUE的话,那么在退出此函数之前有由参数uxBitsToWaitFor所设置的这些事件位就会清零。如果设置为pdFALSE的话这些事件位就不会改变。

xWaitForAllBits:此参数如果设置为pdTRUE的话,当uxBitsToWaitFor所设置的这些事件位都置1,或者指定的阻塞时间到的时候函数xEventGroupWaitBits()才会返回。当此参数为pdFALSE的话,只要uxBitsToWaitFor所设置的这些事件位其中的任意一个置1,或者指定的阻塞时间到的话函数xEventGroupWaitBits()就会返回。

xTicksToWait:设置阻塞时间,单位为节拍数。

返回值:

任何值:返回当所等待的事件位置1以后的事件标志组的值,或者阻塞时间到。根据这个值我们就知道哪些事件位置1了。如果函数因为阻塞时间到而返回的话,那么这个返回值就不代表任何的含义。

六、事件标志组实验

6.1实验要求

创建事件标志组、将相应的事件位置1、等待相应事件位置1的操作。
实验设置三个任务:start_task、eventsetbit_task、eventgroup_task。
start_task:用来创建其他两个任务,创建时间标志组。
eventsetbit_task:通过不同按键值,将事件标志组中相应事件位置1。
eventgroup_task:等待事件标志组中的事件位,当这些事件位都置1,执行相应的处理。
EventGroupHandler:创建的事件标志组句柄。使用事件标志组的事件位:bit0 bit1 bit2

6.2实验代码

任务分配:

//任务优先级
#define START_TASK_PRIO        1
//任务堆栈大小    
#define START_STK_SIZE         128  
//任务句柄
TaskHandle_t StartTask_Handler;
//任务函数
void start_task(void *pvParameters);//任务优先级
#define EVENTSETBIT_TASK_PRIO        2
//任务堆栈大小    
#define EVENTSETBIT_STK_SIZE         50  
//任务句柄
TaskHandle_t EventSetbitTask_Handler;
//任务函数
void eventsetbit_task(void *pvParameters);//任务优先级
#define EVENTGROUP_TASK_PRIO        3
//任务堆栈大小    
#define EVENTGROUP_STK_SIZE         50  
//任务句柄
TaskHandle_t EventGroupTask_Handler;
//任务函数
void eventgroup_task(void *pvParameters);EventGroupHandle_t EventGroupHandle;    // 事件标志组句柄// 定义事件位
#define BIT_0    (1<<0)
#define BIT_1    (1<<1)
#define BIT_2    (1<<2)

main() 函数:

int main(void)
{NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//设置系统中断优先级分组4     delay_init();                        //延时函数初始化      uart_init(115200);                    //初始化串口LED_Init();                              //初始化LEDKEY_Init();                            // 初始化按键//创建开始任务xTaskCreate((TaskFunction_t )start_task,            //任务函数(const char*    )"start_task",          //任务名称(uint16_t       )START_STK_SIZE,        //任务堆栈大小(void*          )NULL,                  //传递给任务函数的参数(UBaseType_t    )START_TASK_PRIO,       //任务优先级(TaskHandle_t*  )&StartTask_Handler);   //任务句柄              vTaskStartScheduler();          //开启任务调度
}

任务函数:

//开始任务任务函数
void start_task(void *pvParameters)
{taskENTER_CRITICAL();           //进入临界区EventGroupHandle =  xEventGroupCreate();            // 创建事件标志组if(EventGroupHandle == NULL){printf("EventGroup Create Failed!\r\n");}//创建eventsetbit任务xTaskCreate((TaskFunction_t )eventsetbit_task,         (const char*    )"eventsetbit_task",       (uint16_t       )EVENTSETBIT_STK_SIZE, (void*          )NULL,                (UBaseType_t    )EVENTSETBIT_TASK_PRIO,    (TaskHandle_t*  )&EventSetbitTask_Handler);   //创建eventgroup任务xTaskCreate((TaskFunction_t )eventgroup_task,     (const char*    )"eventgroup_task",   (uint16_t       )EVENTGROUP_STK_SIZE, (void*          )NULL,(UBaseType_t    )EVENTGROUP_TASK_PRIO,(TaskHandle_t*  )&EventGroupTask_Handler);         vTaskDelete(StartTask_Handler); //删除开始任务taskEXIT_CRITICAL();            //退出临界区
}//eventsetbit_task任务函数 
void eventsetbit_task(void *pvParameters)
{u8 key = 0;while(1){key = KEY_Scan(0);switch(key){case KEY1_PRES:if(EventGroupHandle != NULL){xEventGroupSetBits(EventGroupHandle,BIT_0);    // 设置事件标志组 BIT_0 置1}break;case KEY2_PRES:if(EventGroupHandle != NULL){xEventGroupSetBits(EventGroupHandle,BIT_1);    // 设置事件标志组 BIT_0 置1}break;}vTaskDelay(10);}
}   //eventgroup_task任务函数
void eventgroup_task(void *pvParameters)
{EventBits_t EventBitsVal = 0;while(1){if(EventGroupHandle != NULL){EventBitsVal = xEventGroupWaitBits(EventGroupHandle,        // 要等待的事件标志组(BIT_0|BIT_1),            // 等待的事件位pdTRUE,                    // 清零pdFALSE,                // 只要事件位其一得到就退出portMAX_DELAY );        // 死等printf("EventBitsVal = %#x\r\n",EventBitsVal);}else{vTaskDelay(10);}}
}

6.3实验要求2

创建一个事件标志组和两个任务( task1 和 task2),task1 检测按键,如果检测到 KEY1 和 KEY2 都按过,则执行 task2 。

6.4实验代码

在代码中创建一个事件标志组

EventGroupHandle_t eventgroup_handle; // 事件标志组句柄eventgroup_handle = xEventGroupCreate(); // 创建一个事件标志组

task1 检测按键,如果检测到 KEY1 和 KEY2 都按过,则执行 task2

freertos.c

void StartTaskKey1(void const * argument)
{for(;;){if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET){osDelay(20);if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET){xEventGroupSetBits(eventgroup_handle, 0x01);}while (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET);}if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1) == GPIO_PIN_RESET){osDelay(20);if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1) == GPIO_PIN_RESET){xEventGroupSetBits(eventgroup_handle, 0x02);}while (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1) == GPIO_PIN_RESET);}osDelay(10);}
}void StartTaskKey2(void const * argument)
{for(;;){event_bit = xEventGroupWaitBits(eventgroup_handle,0x01|0x02,pdTRUE,pdTRUE,portMAX_DELAY);printf("返回值:%#x,按键都按下,任务2可以执行了\r\n",event_bit); // %#x是带格式输出, 效果为在输出前加0x. osDelay(1);}
}

打开串口助手看运行结果
在这里插入图片描述

6.5 场景:多个传感器数据采集任务,主任务需要在全部采集完成后处理数据。

步骤:
创建事件组;
每个采集任务完成后设置对应事件位;
主任务等待所有位置位后继续执行。
示例代码:

#define SENSOR1_DONE_BIT  (1 << 0)
#define SENSOR2_DONE_BIT  (1 << 1)EventGroupHandle_t xSyncEvent;void vSensor1Task(void *pvParameters) {// 模拟采集vTaskDelay(pdMS_TO_TICKS(100));xEventGroupSetBits(xSyncEvent, SENSOR1_DONE_BIT);vTaskDelete(NULL);
}void vSensor2Task(void *pvParameters) {vTaskDelay(pdMS_TO_TICKS(200));xEventGroupSetBits(xSyncEvent, SENSOR2_DONE_BIT);vTaskDelete(NULL);
}void vMainTask(void *pvParameters) {xSyncEvent = xEventGroupCreate();xTaskCreate(vSensor1Task, "Sensor1", 128, NULL, 1, NULL);xTaskCreate(vSensor2Task, "Sensor2", 128, NULL, 1, NULL);// 等待两个任务完成xEventGroupWaitBits(xSyncEvent,SENSOR1_DONE_BIT | SENSOR2_DONE_BIT,pdTRUE,pdTRUE,portMAX_DELAY);printf("所有传感器数据采集完成,开始处理\n");vEventGroupDelete(xSyncEvent);vTaskDelete(NULL);
}

七、注意事项与最佳实践

在这里插入图片描述

http://www.dtcms.com/a/449184.html

相关文章:

  • Excel基础知识 - 导图笔记
  • Flink 执行模式在 STREAMING 与 BATCH 之间做出正确选择
  • 杭州网站制作平台公司医院网站建设存在问题
  • Python中*args与**kwargs用法解析
  • 【大模型】多智能体架构详解:Context 数据流与工作流编排的艺术
  • 描述逻辑(Description Logic)对自然语言处理深层语义分析的影响与启示
  • python爬虫(三) ---- 分页抓取数据
  • 探索大语言模型(LLM):大模型微调方式全解析
  • 【学习笔记03】C++STL标准模板库核心技术详解
  • 做网站有什么关于财务的问题网络设计工作
  • P9751 [CSP-J 2023] 旅游巴士
  • 宠物用品网站开发背景网站推广设计
  • MySql复习及面试题学习
  • .NET周刊【9月第2期 2025-09-14】
  • 秦皇岛企业网站建设wordpress 悬浮音乐
  • 日语学习-日语知识点小记-进阶-JLPT-N1阶段应用练习(6):语法 +考え方19+2022年7月N1
  • 【Linux指南】gdb进阶技巧:断点高级玩法与变量跟踪实战
  • 跨平台游戏引擎 Axmol-2.9.0 发布
  • 金融 - neo4j、Graph Data Science 安装
  • c 可以做网站吗梧州seo排名
  • LuaC API知识点汇总
  • mysql学习--DCL
  • 开源 C++ QT QML 开发(七)自定义控件--仪表盘
  • 论坛开源网站源码网站建设实验总结报告
  • Ansible实战:VMware下K8s自动化部署指南
  • Ansible(三)—— 使用Ansible自动化部署LNMP环境实战指南
  • 【深度学习新浪潮】有没有可能设计出一种统一架构,可以同时处理图像理解的各种下游任务?
  • 介绍一下什么是RabbitMQ的发送者可靠性?
  • 网站后台管理页面模板北京企业建网站定制价格
  • AI编辑器(二) ---调用模型的fim功能