wordpress 数据库查询数据库,沈阳seo排名收费,广东深圳区号,融媒体建设网站怎么搞STM32F1HAL库FreeTOTS学习17——事件标志组 1. 事件标志组1.1 事件标志组的的引入1.2 事件标志组简介1.3 事件标志组与队列、信号量的区别 2. 事件标志组下相关API函数2. 1 xEventGroupCreate()2. 2 xEventGroupCreateStatic()2. 3 vEventGroupDelete()2. 4 xEventGroupWaitBit… STM32F1HAL库FreeTOTS学习17——事件标志组 1. 事件标志组1.1 事件标志组的的引入1.2 事件标志组简介1.3 事件标志组与队列、信号量的区别 2. 事件标志组下相关API函数2. 1 xEventGroupCreate()2. 2 xEventGroupCreateStatic()2. 3 vEventGroupDelete()2. 4 xEventGroupWaitBits()2. 5 xEventGroupSetBits()2. 7 xEventGroupSetBitsFromISR()2. 8 xEventGroupClearBits()2. 9 xEventGroupClearBitsFromISR()2. 10 xEventGroupGetBits()2. 11 xEventGroupGetBitsFromISR()2. 12 xEventGroupSync() 3. 事件标志组操作实验3.1. 实验内容3.2 代码实现3.2 实验结果 上期我们介绍了队列集这一期我们来开始学习事件标志组 1. 事件标志组
1.1 事件标志组的的引入
前面我们在介绍信号量的时候有提到过信号量是为了解决任务与任务之间的同步问题而引入的但是对于任务之间的多个事件同步使用信号量也会比较麻烦为了实现任务之间多个事件的同步方便统一管理我们引入事件标志组。
1.2 事件标志组简介
在事件标志组中每一个位都可以用来表征一个事件的标志这样的一个位叫做事件标志位而事件标志组就是事件标志位的集合。当有个标志位被置1了表示某个事件已经发送反正则未发生。事件标志组包含了一个 EventBits_t 类型的变量实际上就是一个整数EventBits_t 类型变量的具体定义如下 typedef TickType_t EventBits_t;#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可以见的EventBits_t 的变量在我们这里是32位变量但实际上我们能够使用的只有0~23位高八位不可用即一个事件组最大可以存储24个事件标志 在实际使用中事件标志组支持同时等待、设置(置位)多个标志位 事件标志组的置位、等待、清除标志位、获取标志位信息等操作支持在任务和中断中使用。
1.3 事件标志组与队列、信号量的区别
队列和信号量在事件发生时只会唤醒一个任务是消耗型的资源队列中的数据被读走就没有了信号量被获取之后就减少需要再次写入队列或者释放消息给信号量。 事件标志组事件发生时会唤醒所有符合条件的任务被唤醒的任务有两个选择可以让事件标志位保持不变也可以清除事件标志。
2. 事件标志组下相关API函数
FreeRTOS 提供了事件标志组的一些相关操作函数如下表所示
函数描述xEventGroupCreate()使用动态方式创建事件标志组xEventGroupCreateStstic()使用静态方式创建事件标志组vEventGroupDelete()删除事件标志组xEventGroupWaitBits()等待事件标志位xEventGroupSetBits()设置事件标志位列xEventGroupSetBitsFromISR()在中断中设置事件标志位xEventGroupClearBits()清零事件标志位xEventGroupClearBitsFromISR()在中断中清零事件标志位xEventGroupGetBits()获取事件组中各事件标志位的值xEventGroupGetBitsFromISR()在中断中获取事件组中各事件标志位的值xEventGroupSync()设置事件标志位并等待事件标志位
2. 1 xEventGroupCreate()
此函数用于动态方式创建事件标志组该函数在 event_groups.c 文件中有定义函数的原型如下所示
/*** brief xEventGroupCreate动态方式创建事件标志组* param void* retval 返回值为NULL表示创建失败其他值表示为创建事件标志组的句柄*/
EventGroupHandle_t xEventGroupCreate(void);2. 2 xEventGroupCreateStatic()
此函数用于动态方式创建事件标志组该函数在 event_groups.c 文件中有定义函数的原型如下所示
/*** brief xEventGroupCreateStatic静态方式创建事件标志组* param pxEventGroupBuffer: 指向StaticEventGroup_t 变量类型的指针用来存放创建完成的事件标志组* retval 返回值为NULL表示创建失败其他值表示为创建事件标志组的句柄*/
EventGroupHandle_t xEventGroupCreateStatic( StaticEventGroup_t *pxEventGroupBuffer );2. 3 vEventGroupDelete()
此函数用于删除事件标志组该函数在 event_groups.c 文件中有定义函数的原型如下所示
/*** brief vEventGroupDelete删除事件标志组* param xEventGroup待删除的事件标志组句柄* retval void*/
void vEventGroupDelete(EventGroupHandle_t xEventGroup);2. 4 xEventGroupWaitBits()
此函数用于等待事件标志组中的某一个或多个标志位该函数在 event_groups.c 文件中有定义函数的原型如下所示
/*** brief xEventGroupWaitBits等待事件标志组中的某一个或多个标志位* param xEventGroup等待的事件标志组句柄* param uxBitsToWaitFor等待的事件标志位可以使用逻辑或等待多个事件标志位* param xClearOnExit等待成功后是否清除对应标志位pdTRUE清除pdFALSE不清除* param xWaitForAllBits等待事件标志位中的一个还是所有pdTRUE等待所有pdFLASE等待一个* param xTicksToWait等待阻塞时间* retval void*/
EventBits_t xEventGroupWaitBits( EventGroupHandle_t xEventGroup,const EventBits_t uxBitsToWaitFor,const BaseType_t xClearOnExit,const BaseType_t xWaitForAllBits,TickType_t xTicksToWait)2. 5 xEventGroupSetBits()
此函数用于设置(置位)事件标志位该函数在 event_groups.c 文件中有定义函数的原型如下所示
/*** brief xEventGroupSetBits设置(置位)事件标志位* param xEventGroup待设置的事件标志组句柄* param uxBitsToSet 需要设置的事件标志位可以通过逻辑或的方式同时设置多个事件标志位* retval 事件标志组值可以表征事件标志组中的事件标志位的设置(置位)情况。*/
EventBits_t xEventGroupSetBits( EventGroupHandle_t xEventGroup,const EventBits_t uxBitsToSet );2. 7 xEventGroupSetBitsFromISR()
此函数用于在中断中设置(置位)事件标志位该函数在 event_groups.c 文件中有定义函数的原型如下所示
/*** brief xEventGroupSetBitsFromISR在中断中设置(置位)事件标志位* param xEventGroup待设置的事件标志组句柄* param uxBitsToSet 需要设置的事件标志位可以通过逻辑或的方式同时设置多个事件标志位* param pxHigherPriorityTaskWoken 是否需要进行任务切换如果为pdTRUE表示需要进行任务切换为pdFALSE则不需要* retval 如果消息已发送到 RTOS 守护进程任务则返回 pdPASS否则返回 pdFAIL。 如果定时器服务队列已满则返回 pdFAIL。*/
BaseType_t xEventGroupSetBitsFromISR( EventGroupHandle_t xEventGroup,const EventBits_t uxBitsToSet,BaseType_t *pxHigherPriorityTaskWoken );【注】在事件组中设置位将自动解除 所有等待位的任务的阻塞状态。这个操作是不确定的因为有可能同时存在多个任务解除阻塞FreeRTOS不允许在中断中出现这种操作因此xEventGroupSetBitsFromISR()会向RTOS 守护进程任务发送一条消息 从而在守护进程任务(也叫做定时器服务任务)的上下文中执行设置操作其中使用的是调度器锁 而非临界区。
总结一句话就是xEventGroupSetBitsFromISR()函数中的标志位置位操作会被推迟到 RTOS 守护进程任务中进行。
2. 8 xEventGroupClearBits()
此函数用于在任务中清零事件标志位该函数在 event_groups.c 文件中有定义函数的原型如下所示
/*** brief xEventGroupClearBits在任务中清除事件标志位* param xEventGroup需要清除标志位的事件标志组句柄* param uxBitsToClear 需要清除的事件标志位可以通过逻辑或的方式同时清除多个事件标志位* retval 清除指定位之前的事件组的值。*/
EventBits_t xEventGroupClearBits(EventGroupHandle_t xEventGroup,const EventBits_t uxBitsToClear );2. 9 xEventGroupClearBitsFromISR()
此函数用于在中断中清零事件标志位该函数在 event_groups.c 文件中有定义函数的原型如下所示
/*** brief xEventGroupClearBitsFromISR在中断中清除事件标志位* param xEventGroup需要清除标志位的事件标志组句柄* param uxBitsToClear 需要清除的事件标志位可以通过逻辑或的方式同时清除多个事件标志位* retval 如果返回pdPASS表示操作成功延迟到RTOS守护进程任务否则表示定时器命令队列已满事件标志位清零失败。*/
BaseType_t xEventGroupClearBitsFromISR(EventGroupHandle_t xEventGroup,const EventBits_t uxBitsToClear );2. 10 xEventGroupGetBits()
此函数用于获取事件标志组的值可以表征事件标志组内成员的置位情况该函数在 event_groups.c 文件中有定义函数的原型如下所示
/*** brief xEventGroupGetBits获取事件标志组的值* param xEventGroup需要查询的事件标志组句柄* retval 事件标志组的值*/
EventBits_t xEventGroupGetBits( EventGroupHandle_t xEventGroup );2. 11 xEventGroupGetBitsFromISR()
此函数用于中断中获取事件标志组的值可以表征事件标志组内成员的置位情况该函数在 event_groups.c 文件中有定义函数的原型如下所示
/*** brief xEventGroupGetBitsFromISR获取事件标志组的值* param xEventGroup需要查询的事件标志组句柄* retval 事件标志组的值*/
EventBits_t xEventGroupGetBitsFromISR( EventGroupHandle_t xEventGroup );2. 12 xEventGroupSync()
此函数用于设置事件标志组中的位并且等待同一事件标志组的标志位常用于同步多个任务(通常称为任务集合)其中每个任务必须等待其他任务到达同步点后才能继续且不能在中断中使用此函数。
举个栗子来说我们在打王者荣耀进入游戏之前需要先匹配队友如何等待所有队友点击“确认”才可以进入游戏如果中途有玩家未点击确认则无法进入游戏。
在这个例子里面自己点击“确认”是自己将标志位置位但此时需要等待其他队友点击“确认”是等待同一事件标志组的其他标志位。这个就是多个任务之间的消息同步。同时每个队友(任务)都需要等待其他队友(任务)点击确认(所有人都到达同步点)才能继续。
上述是我自己对于此函数的理解下面我们来看一下函数原型
/*** brief xEventGroupSync设置事件标志组中的位并且等待同一事件标志组的标志位常用于同步多个任务* param xEventGroup待设置和等待位的事件标志组句柄* param uxBitsToSet在确定uxBitsToWait参数指定的所有位是否都已设置可能还要等待之前要在事件组中设置的一个或多个位。* param uxBitsToWaitFor指定要在事件组中等待的一个或多个位的按位值。* param xTicksToWait 等待 uxBitsToWaitFor 参数值指定的所有位被设置的最长时间以滴答为单位 。* retval 如果等待事件标志位成功返回等待到的事件标志位如果等待事件标志位失败返回事件组中的事件标志位*/
EventBits_t xEventGroupSync( EventGroupHandle_t xEventGroup,const EventBits_t uxBitsToSet,const EventBits_t uxBitsToWaitFor,TickType_t xTicksToWait );3. 事件标志组操作实验
3.1. 实验内容
在STM32F103RCT6上运行FreeRTOS通过按键控制完成对应的事件标志位操作具体要求如下
定义一个事件标志位定义任务1按下按键0按键0对应的事件标志位置1LED指示灯亮起按下按键1按键1对应的事件标志位置1LED指示灯亮起。定义任务2等待事件标志组中按键0和按键1对于的标志位当两者都被置1时串口打印相关信息并且关闭LED指示灯。
3.2 代码实现
由于本期内容涉及到按键扫描会用到 STM32框架之按键扫描新思路 这里不做过多介绍。我们直接来看代码
freertos_demo.c
#include freertos_demo.h
#include gpio.h
#include queue.h //需要包含队列和任务相关的头文件
#include key.h //包含按键相关头文件/*FreeRTOS*********************************************************************************************//******************************************************************************************************/
/*FreeRTOS配置*//* TASK1 任务 配置* 包括: 任务句柄 任务优先级 堆栈大小 */
#define TASK1_PRIO 1 /* 任务优先级 */
#define TASK1_STK_SIZE 128 /* 任务堆栈大小 */
TaskHandle_t Task1Task_Handler; /* 任务句柄 */
void task1(void *pvParameters); /*任务函数*//* TASK2 任务 配置* 包括: 任务句柄 任务优先级 堆栈大小 */
#define TASK2_PRIO 2 /* 任务优先级 */
#define TASK2_STK_SIZE 128 /* 任务堆栈大小 */
TaskHandle_t Task2Task_Handler; /* 任务句柄 */
void task2(void *pvParameters); /*任务函数*//** 事件标志组配置*/
EventGroupHandle_t EventGroup_Handler; /* 事件标志组句柄 *//******************************************************************************************************//*** brief FreeRTOS例程入口函数* param 无* retval 无*/
void freertos_demo(void)
{taskENTER_CRITICAL(); /* 进入临界区关闭中断此时停止任务调度*//* 创建事件标志组 */EventGroup_Handler xEventGroupCreate();if(EventGroup_Handler NULL){printf(事件标志组创建失败!!!\r\n);}else{printf(事件标志组创建成功!!!\r\n);}/* 创建任务1 */xTaskCreate((TaskFunction_t )task1,(const char* )task1,(uint16_t )TASK1_STK_SIZE,(void* )NULL,(UBaseType_t )TASK1_PRIO,(TaskHandle_t* )Task1Task_Handler);/* 创建任务2 */xTaskCreate((TaskFunction_t )task2,(const char* )task2,(uint16_t )TASK2_STK_SIZE,(void* )NULL,(UBaseType_t )TASK2_PRIO,(TaskHandle_t* )Task2Task_Handler);taskEXIT_CRITICAL(); /* 退出临界区重新开启中断开启任务调度 */vTaskStartScheduler(); //开启任务调度
}/**
* brief task1:用于按键扫描按键0或1按下自动置位事件标志组并开启相应的LED指示* param pvParameters : 传入参数(未用到)* retval 无*/
void task1(void *pvParameters)
{while(1){Key_One_Scan(Key_Name_Key0,Key0_Up_Task,Key0_Down_Task);Key_One_Scan(Key_Name_Key1,Key1_Up_Task,Key1_Down_Task);vTaskDelay(10);}
}
/**
* brief task2:当按键1和0的事件标志组位都被置1打印相关信息并关闭相应的LED指示* param pvParameters : 传入参数(未用到)* retval 无*/
void task2(void *pvParameters)
{ EventBits_t eventBits;while(1){ /* 等待按键0和1的事件标志位 */eventBits xEventGroupWaitBits(EventGroup_Handler,Key0_EventBit|Key1_EventBit,pdTRUE,pdTRUE,portMAX_DELAY);/* 打印相关信息 */printf(等待到的事件标志为%#x\r\n,eventBits);/* 关闭LED指示 */HAL_GPIO_WritePin(LED0_GPIO_Port,LED0_Pin,LED_OFF);HAL_GPIO_WritePin(LED1_GPIO_Port,LED1_Pin,LED_OFF);vTaskDelay(50);}}
key.c
/* USER CODE BEGIN 2 */#include key.h
#include freertos_demo.h
#include usart.h
#include event_groups.h //包含事件标志组头文件
#include gpio.hvoid Key0_Down_Task(void)
{/* 设置事件标志位 */HAL_GPIO_WritePin(LED0_GPIO_Port,LED0_Pin,LED_ON);/* 开启LED指示 */xEventGroupSetBits(EventGroup_Handler,Key0_EventBit);}
void Key0_Up_Task(void)
{}
void Key1_Down_Task(void)
{/* 设置事件标志位 */HAL_GPIO_WritePin(LED1_GPIO_Port,LED1_Pin,LED_ON);/* 开启LED指示 */xEventGroupSetBits(EventGroup_Handler,Key1_EventBit);}
void Key1_Up_Task(void)
{}
void Key2_Down_Task(void)
{}
void Key2_Up_Task(void)
{}
void WKUP_Down_Task(void)
{}
void WWKUP_Up_Task(void)
{}void Key_One_Scan(uint8_t KeyName ,void(*OnKeyOneUp)(void), void(*OnKeyOneDown)(void))
{static uint8_t Key_Val[Key_Name_Max]; //按键值的存放位置static uint8_t Key_Flag[Key_Name_Max]; //KEY0~2为0时表示按下为1表示松开WKUP反之Key_Val[KeyName] Key_Val[KeyName] 1; //每次扫描完将上一次扫描的结果左移保存switch(KeyName){case Key_Name_Key0: Key_Val[KeyName] Key_Val[KeyName] | (HAL_GPIO_ReadPin(KEY0_GPIO_Port, KEY0_Pin)); //读取Key0按键值break;case Key_Name_Key1: Key_Val[KeyName] Key_Val[KeyName] | (HAL_GPIO_ReadPin(KEY1_GPIO_Port, KEY1_Pin)); //读取Key1按键值break;case Key_Name_Key2: Key_Val[KeyName] Key_Val[KeyName] | (HAL_GPIO_ReadPin(KEY2_GPIO_Port, KEY2_Pin)); //读取Key2按键值break;
// case Key_Name_WKUP: Key_Val[KeyName] Key_Val[KeyName] | (HAL_GPIO_ReadPin(WKUP_GPIO_Port, WKUP_Pin)); //读取WKUP按键值
// break; default:break;}
// if(KeyName Key_Name_WKUP) //WKUP的电路图与其他按键不同所以需要特殊处理
// {
// //WKUP特殊情况
// //当按键标志为1(松开)是判断是否按下WKUP按下时为0xff
// if(Key_Val[KeyName] 0xff Key_Flag[KeyName] 1)
// {
// (*OnKeyOneDown)();
// Key_Flag[KeyName] 0;
// }
// //当按键标志位为0按下判断按键是否松开,WKUP松开时为0x00
// if(Key_Val[KeyName] 0x00 Key_Flag[KeyName] 0)
// {
// (*OnKeyOneUp)();
// Key_Flag[KeyName] 1;
// }
// }
// else //Key0~2按键逻辑判断
// {//Key0~2常规判断//当按键标志为1(松开)是判断是否按下if(Key_Val[KeyName] 0x00 Key_Flag[KeyName] 1){(*OnKeyOneDown)();Key_Flag[KeyName] 0;}//当按键标志位为0按下if(Key_Val[KeyName] 0xff Key_Flag[KeyName] 0){(*OnKeyOneUp)();Key_Flag[KeyName] 1;} }//}
/* USER CODE END 2 */
3.2 实验结果 以上就是本期的所有内容创造不易点个关注再走呗。