- 创建或获取一个事件组句柄。
- 使用xEvetnGroupSync函数等待指定的事件位。可以设置需要等待的事件位、需要设置的事件位和等待超时时间。
- 根据返回值判断等待是否成功,根据已发生的事件位执行相应的操作。
注意,xEvetnGroupSync函数是一个阻塞函数,即在等待期间会阻塞当前任务的执行。如果有其他任务在等待相同的事件组,则它们可能会被唤醒以执行后续操作。因此,在使用xEvetnGroupSync函数时需要谨慎设计,以避免出现死锁或优先级反转等问题。
2.3 功能实现
STM32CubeMX功能配置
GPIO略
根据接口说明 ,事件标志组中断中需要开启守护任务
按键中断及事件标志组创建
//freertos.c
//...略
#include "event_groups.h"
EventGroupHandle_t KeyEventGroup; //全局变量句柄
void MX_FREERTOS_Init(void) {
//创建
KeyEventGroup = xEventGroupCreate();
if(KeyEventGroup == NULL){
printf("KeyEventGroup Create Error\r\n");
}
//...略
}
按键检测任务和蜂鸣器报警任务
//gpio.c
#include "event_groups.h"
//...略
/*
#define KEY3_EVENT_BIT (1<<0)
#define KEY4_EVENT_BIT (1<<1)
#define KEY5_EVENT_BIT (1<<2)
#define KEY6_EVENT_BIT (1<<3)
typedef enum
{
KEY_DOWN,
KEY_UP,
KEY_RESET
}teKeyStatus;
*/
teKeyStatus KeyStatus;
extern EventGroupHandle_t KeyEventGroup;
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin){
if(Key3_Pin == GPIO_Pin)
{
if(HAL_GPIO_ReadPin(Key3_GPIO_Port,Key3_Pin) == GPIO_PIN_RESET)
{
HAL_Delay(10);
if(HAL_GPIO_ReadPin(Key3_GPIO_Port,Key3_Pin) == GPIO_PIN_RESET)
{
//设置事件
xEventGroupSetBitsFromISR(KeyEventGroup,KEY3_EVENT_BIT,NULL);
}
}
}
if(Key4_Pin == GPIO_Pin)
{
if(HAL_GPIO_ReadPin(Key4_GPIO_Port,Key4_Pin) == GPIO_PIN_RESET)
{
HAL_Delay(10);
if(HAL_GPIO_ReadPin(Key4_GPIO_Port,Key4_Pin) == GPIO_PIN_RESET)
{
//设置事件
xEventGroupSetBitsFromISR(KeyEventGroup,KEY4_EVENT_BIT,NULL);
}
}
}
if(Key5_Pin == GPIO_Pin)
{
if(HAL_GPIO_ReadPin(Key5_GPIO_Port,Key5_Pin) == GPIO_PIN_RESET)
{
HAL_Delay(10);
if(HAL_GPIO_ReadPin(Key5_GPIO_Port,Key5_Pin) == GPIO_PIN_RESET)
{
//设置事件
xEventGroupSetBitsFromISR(KeyEventGroup,KEY5_EVENT_BIT,NULL);
}
}
}
if(Key6_Pin == GPIO_Pin)
{
if(HAL_GPIO_ReadPin(Key6_GPIO_Port,Key6_Pin) == GPIO_PIN_RESET)
{
HAL_Delay(10);
if(HAL_GPIO_ReadPin(Key6_GPIO_Port,Key6_Pin) == GPIO_PIN_RESET)
{
//设置事件
xEventGroupSetBitsFromISR(KeyEventGroup,KEY6_EVENT_BIT,NULL);
}
}
}
}
//freertos.c
void Delay_Task(void const * argument)
{
/* USER CODE BEGIN Delay_Task */
EventBits_t KeyEventBits;
/* Infinite loop */
for(;;)
{
KeyEventBits = xEventGroupWaitBits(KeyEventGroup,
KEY3_EVENT_BIT|KEY4_EVENT_BIT|KEY5_EVENT_BIT|KEY6_EVENT_BIT,
pdFALSE,
pdFALSE,
portMAX_DELAY);
printf("Key is Down Key Event Bit is %x\r\n",KeyEventBits);
osDelay(10);
}
}
void High_Task(void const * argument)
{
EventBits_t KeyEventBits;
for(;;)
{
KeyEventBits = xEventGroupWaitBits(KeyEventGroup,
KEY3_EVENT_BIT|KEY4_EVENT_BIT|KEY5_EVENT_BIT|KEY6_EVENT_BIT,
pdTRUE,
pdTRUE,
portMAX_DELAY);
if(KeyEventBits == (KEY3_EVENT_BIT|KEY4_EVENT_BIT|KEY5_EVENT_BIT|KEY6_EVENT_BIT))
{
printf("Buzzer is Toggle\r\n");
HAL_GPIO_TogglePin(Buzzer_GPIO_Port,Buzzer_Pin);
}
osDelay(10);
}
}
3 事件标志组原理
3.1 事件标志组控制块
#define eventCLEAR_EVENTS_ON_EXIT_BIT 0x01000000UL //表示退出是否清楚已经触发的标志位 25bit表示
#define eventUNBLOCKED_DUE_TO_BIT_SET 0x02000000UL //解除阻塞是否,已经设置标志位 26位
#define eventWAIT_FOR_ALL_BITS 0x04000000UL //是与逻辑还是或逻辑
#define eventEVENT_BITS_CONTROL_BYTES 0xff000000UL //用于分解出,事件标志组位使用
typedef struct xEventGroupDefinition
{
//事件标志组
EventBits_t uxEventBits;
//任务等待的列表
List_t xTasksWaitingForBits; /*< List of tasks waiting for a bit to be set. */
} EventGroup_t;
3.2 事件标志组获取标志位
全局变量一定要保护,所以要进入临界段
#define xEventGroupGetBits( xEventGroup ) xEventGroupClearBits( xEventGroup, 0 )
/*
参数:
1、控制块/句柄
2、要清除位
返回值:
事件标志位
*/ 1<<0
EventBits_t xEventGroupClearBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToClear )
{
EventGroup_t *pxEventBits = ( EventGroup_t * ) xEventGroup;
EventBits_t uxReturn;
/*
1、进入临界段
2、获取当前事件标志位
3、清除要设置的事件标志位
4、退出临界段
5、返回事件标志组值
*/
taskENTER_CRITICAL();
{
/* The value returned is the event group value prior to the bits being
cleared. */
uxReturn = pxEventBits->uxEventBits;
/* Clear the bits. */
pxEventBits->uxEventBits &= ~uxBitsToClear;
}
taskEXIT_CRITICAL();
return uxReturn;
}
/*
参数:
1、事件控制块
返回值:
事件标志位
*/
EventBits_t xEventGroupGetBitsFromISR( EventGroupHandle_t xEventGroup )
{
UBaseType_t uxSavedInterruptStatus;
EventGroup_t *pxEventBits = ( EventGroup_t * ) xEventGroup;
EventBits_t uxReturn;
//禁止中断 带返回值
uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR();
{
//获取事件标志位
uxReturn = pxEventBits->uxEventBits;
}
//恢复中断,在进入禁止之前的状态
portCLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedInterruptStatus );
return uxReturn;
}
3.3 等待事件标志触发
复位列表项:表示事件信息
/*
参数:
1、事件控制块
2、要等待出发的标志位
3、退出是否要清除
4、与逻辑还是或逻辑
5、阻塞等待时间
返回值:
当前事件标志位
*/
EventBits_t xEventGroupWaitBits( EventGroupHandle_t xEventGroup,
const EventBits_t uxBitsToWaitFor,
const BaseType_t xClearOnExit,
const BaseType_t xWaitForAllBits,
TickType_t xTicksToWait )
{
EventGroup_t *pxEventBits = ( EventGroup_t * ) xEventGroup;
EventBits_t uxReturn, uxControlBits = 0;
BaseType_t xWaitConditionMet, xAlreadyYielded;
BaseType_t xTimeoutOccurred = pdFALSE;
//挂起调度器
vTaskSuspendAll();
{
//获取当前的事件标志位
const EventBits_t uxCurrentEventBits = pxEventBits->uxEventBits;
/* 检查是否触发
参数:
1、当前的事件标志位
2、要等待触发的事件标志位
3、触发逻辑???
返回值:
pdFALSE pdTRUE
*/
xWaitConditionMet = prvTestWaitCondition( uxCurrentEventBits, uxBitsToWaitFor, xWaitForAllBits );
if( xWaitConditionMet != pdFALSE )
{
/* 已经触发 */
uxReturn = uxCurrentEventBits;
xTicksToWait = ( TickType_t ) 0;
/* 清楚已经触发的标志 */
if( xClearOnExit != pdFALSE )
{
pxEventBits->uxEventBits &= ~uxBitsToWaitFor;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
else if( xTicksToWait == ( TickType_t ) 0 )
{
/* 不需要超时,直接返回标志位. */
uxReturn = uxCurrentEventBits;
}
else
{
/* 事件没有触发,并且需要超时*/
if( xClearOnExit != pdFALSE )
{
//uxControlBits = 0x01000000UL;
uxControlBits |= eventCLEAR_EVENTS_ON_EXIT_BIT;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
if( xWaitForAllBits != pdFALSE )
{
//uxControlBits = 0x05000000UL;
uxControlBits |= eventWAIT_FOR_ALL_BITS;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
/* 把任务添加到事件列表中
参数:
1、列表的地址
2、传入列表项值
3、任务阻塞时间
*/
vTaskPlaceOnUnorderedEventList( &( pxEventBits->xTasksWaitingForBits ), ( uxBitsToWaitFor | uxControlBits ), xTicksToWait );
uxReturn = 0;
traceEVENT_GROUP_WAIT_BITS_BLOCK( xEventGroup, uxBitsToWaitFor );
}
}
//恢复调度器
xAlreadyYielded = xTaskResumeAll();
//再次判断是否需要超时
if( xTicksToWait != ( TickType_t ) 0 )
{
if( xAlreadyYielded == pdFALSE )
{
//进行上下文切换 ->pendSV
portYIELD_WITHIN_API();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
/*
任务已经恢复
1、复位列表项中的值 复位为任务有优先级
*/
uxReturn = uxTaskResetEventItemValue();
//是不是通过事件置位解除的任务
if( ( uxReturn & eventUNBLOCKED_DUE_TO_BIT_SET ) == ( EventBits_t ) 0 )
{
//进入临界段
taskENTER_CRITICAL();
{
/* 获取当前事件位. */
uxReturn = pxEventBits->uxEventBits;
/* 再此判断是否已经置位 */
if( prvTestWaitCondition( uxReturn, uxBitsToWaitFor, xWaitForAllBits ) != pdFALSE )
{
//如果需要清除,清除触发后的标志位
if( xClearOnExit != pdFALSE )
{
pxEventBits->uxEventBits &= ~uxBitsToWaitFor;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
taskEXIT_CRITICAL();
xTimeoutOccurred = pdFALSE;
}
else
{
/* The task unblocked because the bits were set. */
}
/* 返回当前事件标志位. */
uxReturn &= ~eventEVENT_BITS_CONTROL_BYTES;
}
traceEVENT_GROUP_WAIT_BITS_END( xEventGroup, uxBitsToWaitFor, xTimeoutOccurred );
return uxReturn;
}
3.4 事件标志组设置标志位
/*
参数:
1、事件控制块
2、要设置的事件位
返回值:
1、当前事件标志位
*/
EventBits_t xEventGroupSetBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet )
{
ListItem_t *pxListItem, *pxNext;
ListItem_t const *pxListEnd;
List_t *pxList;
EventBits_t uxBitsToClear = 0, uxBitsWaitedFor, uxControlBits;
EventGroup_t *pxEventBits = ( EventGroup_t * ) xEventGroup;
BaseType_t xMatchFound = pdFALSE;
//获取事件列表头
pxList = &( pxEventBits->xTasksWaitingForBits );
//获取列表尾节点
pxListEnd = listGET_END_MARKER( pxList ); /*lint !e826 !e740 The mini list structure is used as the list end to save RAM. This is checked and valid. */
//挂起调度器
vTaskSuspendAll();
{
//获取头节点
pxListItem = listGET_HEAD_ENTRY( pxList );
/* 设置事件标志位 */
pxEventBits->uxEventBits |= uxBitsToSet;
/* 循环遍历整个列表项,直到列表头节点等于尾节点(指针) */
while( pxListItem != pxListEnd )
{
//获取下个列表项
pxNext = listGET_NEXT( pxListItem );
//获取当前列表项的值
uxBitsWaitedFor = listGET_LIST_ITEM_VALUE( pxListItem );
//标记,是否找到需要处理的节点
xMatchFound = pdFALSE;
/* 拆分*/
uxControlBits = uxBitsWaitedFor & eventEVENT_BITS_CONTROL_BYTES;
## 最后
**自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。**
**深知大多数Java工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。**
**因此收集整理了一份《2024年嵌入式&物联网开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。**
![img](https://img-blog.csdnimg.cn/img_convert/a93d9d968415ac07a1f8180b049c9812.png)
![img](https://img-blog.csdnimg.cn/img_convert/4b0cbec2d34a4e1638669c8a80f9fb47.jpeg)
![img](https://img-blog.csdnimg.cn/img_convert/15d2980b14cf0cfb668a653560080d02.png)
![img](https://img-blog.csdnimg.cn/img_convert/50a07ff31ff3d6ff8e7b3a27c52347d5.png)
![img](https://img-blog.csdnimg.cn/img_convert/87d7547a57e426b9251ca164fdfb5bec.png)
![img](https://img-blog.csdnimg.cn/img_convert/616b1f5eef40eea1d36153bbcb84cc75.png)
![](https://img-blog.csdnimg.cn/img_convert/43b4750b70659097885140ef841e0fab.png)
**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上嵌入式&物联网开发知识点,真正体系化!**
[**如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!**](https://bbs.csdn.net/topics/618654289)
**由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新**!!
图片转存中...(img-XtOGMuli-1715575331713)]
[外链图片转存中...(img-5nJLbkqX-1715575331714)]
[外链图片转存中...(img-kPebdDxe-1715575331714)]
[外链图片转存中...(img-ehF3Kttn-1715575331715)]
[外链图片转存中...(img-OXXBa4hV-1715575331715)]
[外链图片转存中...(img-MvBLKFyC-1715575331716)]
[外链图片转存中...(img-eQLH1pPx-1715575331716)]
**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上嵌入式&物联网开发知识点,真正体系化!**
[**如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!**](https://bbs.csdn.net/topics/618654289)
**由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新**!!