当前位置: 代码迷 >> 综合 >> 从0到1学习FreeRTOS:FreeRTOS 内核应用开发:(十八)事件 NO.2 事件函数接口讲解
  详细解决方案

从0到1学习FreeRTOS:FreeRTOS 内核应用开发:(十八)事件 NO.2 事件函数接口讲解

热度:90   发布时间:2023-12-16 14:10:08.0

从0到1学习FreeRTOS:FreeRTOS 内核应用开发:(十八)事件 NO.2 事件函数接口讲解

目录

一、事件创建函数 xEventGroupCreate()

二、事件删除函数 vEventGroupDelete()

三、事件组置位函数 xEventGroupSetBits()(任务)

四、事件组置位函数 xEventGroupSetBitsFromISR()(中断)

五、等待事件函数 xEventGroupWaitBits()

六、xEventGroupClearBits()与 xEventGroupClearBitsFromISR()


一、事件创建函数 xEventGroupCreate()

① xEventGroupCreate()用于创建一个事件组,并返回对应的句柄。 要想使用该函数必须在头文件 FreeRTOSConfig.h 定义宏 configSUPPORT_DYNAMIC_ALLOCATION 为 1(在FreeRTOS.h 中默认定义为 1) 且需要把 FreeRTOS/source/event_groups.c 这个 C 文件添加到工程中。
② FreeRTOS 给我们提供了一个创建事件的函数 xEventGroupCreate(),当创建一个事件时, 系统会首先给我们分配事件控制块的内存空间,然后对该事件控制块进行基本的初始化,创建成功返回事件句柄;创建失败返回 NULL。

static EventGroupHandle_t Event_Handle =NULL;
/* 创建 Event_Handle */
Event_Handle = xEventGroupCreate();
if (NULL != Event_Handle)
printf("Event_Handle 事件创建成功!\r\n");
else
/* 创建失败,应为内存空间不足 */

二、事件删除函数 vEventGroupDelete()

       vEventGroupDelete()用于删除事件传入要删除事件的句柄,在删除的时候,如果有任务阻塞在这个事件上,那么就要把事件从等待事件列表中移

       当事件组被删除之后,阻塞在该事件组上的任务都会被解锁,并向等待事件的任务返回事件组的值为0

       当系统不再使用事件对象时,可以通过删除事件对象控制块来释放系统资源。

       该函数不允许在中断里面使用。 当事件组被删除之后,阻塞在该事件组上的任务都会被解锁,并向等待事件的任务返回事件组的值为 0。

static EventGroupHandle_t Event_Handle =NULL;
/* 创建 Event_Handle */
Event_Handle = xEventGroupCreate();
if (NULL != Event_Handle)
{
printf("Event_Handle 事件创建成功!\r\n");
/* 创建成功,可以删除 */
xEventGroupCreate(Event_Handle);
} else
/* 创建失败,应为内存空间不足 */

三、事件组置位函数 xEventGroupSetBits()(任务)

       xEventGroupSetBits()用于置位事件组中指定的位, 当位被置位之后,阻塞在该位上的任务将会被解锁。 使用该函数接口时,通过参数指定的事件标志来设定事件的标志位,然后遍历等待在事件对象上的事件等待列表,判断是否有任务的事件激活要求与当前事件对象标志值匹配,如果有,则唤醒该任务。简单来说,就是设置我们自己定义的事件标志位为 1,并且看看有没有任务在等待这个事件,有的话就唤醒它。

函数原型

EventBits_t xEventGroupSetBits( EventGroupHandle_t xEventGroup,

 

                                                          const EventBits_t uxBitsToSet );

功能

置位事件组中指定的位。

参数

xEventGroup

事件句柄。

uxBitsToSet

指定事件中的事件标志位。如设置uxBitsToSet0x08则只置位位3

 

如果设置uxBitsToSet0x09则位3和位0都需要被置位。

返回值

返回调用xEventGroupSetBits() 时事件组中的值。

       xEventGroupSetBits()用于置位事件组中指定的位,当位被置位之后,阻塞在该位上的任务将会被解锁。使用该函数接口时,通过参数指定的事件标志来设定事件的标志位,然后遍历等待在事件对象上的事件等待列表,判断是否有任务的事件激活要求与当前事件对象标志值匹配,如果有,则唤醒该任务。

       为了便于理解,一般操作我们都是用宏定义来实现    #define  EVENT  (0x01 << x)    “<< x”表示写入事件集合的bit x。

#define KEY1_EVENT (0x01 << 0)//设置事件掩码的位 0
#define KEY2_EVENT (0x01 << 1)//设置事件掩码的位 1
static EventGroupHandle_t Event_Handle =NULL;
/* 创建 Event_Handle */
Event_Handle = xEventGroupCreate();
if (NULL != Event_Handle)
printf("Event_Handle 事件创建成功!\r\n");
static void KEY_Task(void* parameter)
{
/* 任务都是一个无限循环,不能返回 */
while (1) {
//如果 KEY1 被按下
if ( Key_Scan(KEY1_GPIO_PORT,KEY1_GPIO_PIN) == KEY_ON ) {
printf ( "KEY1 被按下\n" );
/* 触发一个事件 1 */
xEventGroupSetBits(Event_Handle,KEY1_EVENT);
}
//如果 KEY2 被按下
if ( Key_Scan(KEY2_GPIO_PORT,KEY2_GPIO_PIN) == KEY_ON ) {
printf ( "KEY2 被按下\n" );
/* 触发一个事件 2 */
xEventGroupSetBits(Event_Handle,KEY2_EVENT);
}
vTaskDelay(20); //每 20ms 扫描一次
}
}

四、事件组置位函数 xEventGroupSetBitsFromISR()(中断)

       xEventGroupSetBitsFromISR()是 xEventGroupSetBits()的中断版本,用于置位事件组中指定的位。置位事件组中的标志位是一个不确定的操作,因为阻塞在事件组的标志位上的任务的个数是不确定的。 FreeRTOS 是不允许不确定的操作在中断和临界段中发生的, 所以xEventGroupSetBitsFromISR()给 FreeRTOS 的守护任务发送一个消息,让置位事件组的操作在守护任务里面完成,守护任务是基于调度锁而非临界段的机制来实现的。
       其实 xEventGroupSetBitsFromISR()函数真正调用的也是 xEventGroupSetBits()函数,只不过是在守护任务中进行调用的,所以它实际上执行的上下文环境依旧是在任务中。

       要想使用该函数,必须把configUSE_TIMERS 和INCLUDE_xTimerPendFunctionCall 这些宏在 FreeRTOSConfig.h 中都定义为 1,并且把FreeRTOS/source/event_groups.c 这个 C 文件添加到工程中编译。

函数原型

BaseType_t xEventGroupSetBitsFromISR( EventGroupHandle_t xEventGroup,

 

                                                                        const EventBits_t uxBitsToSet,

 

                                                                        BaseType_t *pxHigherPriorityTaskWoken);

功能

置位事件组中指定的位,在中断函数中使用。

参数

xEventGroup

事件句柄。

uxBitsToSet

指定事件组中的哪些位需要置位。如设置uxBitsToSet0x08则只置位位3,如果设置

 

uxBitsToSet0x09则位3和位0都需要被置位。

pxHigherPriorityTaskWoken

pxHigherPriorityTaskWoken在使用之前必须初始化成pdFALSE

 

调用xEventGroupSetBitsFromISR()会给守护任务发送一个消息,如果守护任务

 

优先级高于当前被中断的任务的优先级的话(一般情况下都需要将守护任务的

 

级设置为所有任务中最高优先级),pxHigherPriorityTaskWoken会被置

 

pdTRUE,然后在中断退出前执行一次上下文切换。

返回值

消息成功发送给守护任务之后则返回pdTRUE,否则返回pdFAIL。如果定时器服务队列满了将返回pdFAIL

#define BIT_0 ( 1 << 0 )
#define BIT_4 ( 1 << 4 )
/* 假定事件组已经被创建 */
EventGroupHandle_t xEventGroup;
/* 中断 ISR */
void anInterruptHandler( void )
{
BaseType_t xHigherPriorityTaskWoken, xResult;
/* xHigherPriorityTaskWoken 在使用之前必须先初始化为 pdFALSE */
xHigherPriorityTaskWoken = pdFALSE;
/* 置位事件组 xEventGroup 的的 Bit0 和 Bit4 */
xResult = xEventGroupSetBitsFromISR(
xEventGroup,
BIT_0 | BIT_4,
&xHigherPriorityTaskWoken );
/* 信息是否发送成功 */
if ( xResult != pdFAIL ) {
/* 如果 xHigherPriorityTaskWoken 的值为 pdTRUE
则进行一次上下文切换*/
portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
}
}

五、等待事件函数 xEventGroupWaitBits()

       FreeRTOS提供了一个等待指定事件的函数——xEventGroupWaitBits(),通过这个函数,任务可以知道事件标志组中的哪些位,有什么事件发生了,然后通过 “逻辑与”、“逻辑或”等操作对感兴趣的事件进行获取,并且这个函数实现了等待超时机制,当且仅当任务等待的事件发生时,任务才能获取到事件信息

       注意:如果等待到对应的事件,就会返回对应的事件标志位,由用户判断再做处理,因为在事件超时的时候也会返回一个不能确定的事件值,所以需要判断任务所等待的事件是否真的发生。

       EventGroupWaitBits()用于获取事件组中的一个或多个事件发生标志,当要读取的事件标志位没有被置位时任务将进入阻塞等待状态 。 要想使用该函数必须把FreeRTOS/source/event_groups.c 这个 C 文件添加到工程中。 

      简单分析处理过程:当用户调用这个函数接口时,系统首先根据用户指定参数和接收选项来判断它要等待的事件是否发生,如果已经发生,则根据参数 xClearOnExit 来决定是否清除事件的相应标志位,并且返回事件标志位的值,但是这个值并不是一个稳定的值,所以在等待到对应事件的时候,还需我们判断事件是否与任务需要的一致 如果事件没有发生,则把任务添加到事件等待列表中, 把任务感兴趣的事件标志值和等待选项填用列表项的值来表示,直到事件发生或等待时间超时

static void LED_Task(void* parameter)
{
EventBits_t r_event; /* 定义一个事件接收变量 */
/* 任务都是一个无限循环,不能返回 */
while (1) {
/****************************************************************
* 等待接收事件标志
*
* 如果 xClearOnExit 设置为 pdTRUE,那么在 xEventGroupWaitBits()返回之前,
* 如果满足等待条件(如果函数返回的原因不是超时),那么在事件组中设置
* 的 uxBitsToWaitFor 中的任何位都将被清除。
* 如果 xClearOnExit 设置为 pdFALSE,
* 则在调用 xEventGroupWaitBits()时,不会更改事件组中设置的位。
*
* xWaitForAllBits 如果 xWaitForAllBits 设置为 pdTRUE,则当 uxBitsToWaitFor 中
* 的所有位都设置或指定的块时间到期时, xEventGroupWaitBits()才返回。
* 如果 xWaitForAllBits 设置为 pdFALSE,则当设置 uxBitsToWaitFor 中设置的任何
* 一个位置 1 或指定的块时间到期时, xEventGroupWaitBits()都会返回。
* 阻塞时间由 xTicksToWait 参数指定。
*********************************************************/
r_event = xEventGroupWaitBits(Event_Handle, /* 事件对象句柄 */
KEY1_EVENT|KEY2_EVENT,/* 接收任务感兴趣的事件 */
pdTRUE, /* 退出时清除事件位 */
pdTRUE, /* 满足感兴趣的所有事件 */
portMAX_DELAY);/* 指定超时事件,一直等 */
if ((r_event & (KEY1_EVENT|KEY2_EVENT)) == (KEY1_EVENT|KEY2_EVENT)) {
/* 如果接收完成并且正确 */
printf ( "KEY1 与 KEY2 都按下\n");
LED1_TOGGLE; //LED1 反转
} else
printf ( "事件错误! \n");
}
}

六、xEventGroupClearBits()与 xEventGroupClearBitsFromISR()

        xEventGroupClearBits()xEventGroupClearBitsFromISR()都是用于清除事件组指定的位,如果在获取事件的时候没有将对应的标志位清除,那么就需要用这个函数来进行显式清除

       具有中断保护功能的xEventGroupClearBitsFromISR() ,中断清除事件标志位的操作在守护任务(也叫定时器服务任务)里面完成 。 守护进程的优先级由 FreeRTOSConfig.h 中的宏configTIMER_TASK_PRIORITY 来定义 。 要想使用该函数必须把FreeRTOS/source/event_groups.c 这个 C 文件添加到工程中。

函数原型

EventBits_t xEventGroupClearBits( EventGroupHandle_t xEventGroup,

 

                                                             const EventBits_t uxBitsToClear );

 

BaseType_t xEventGroupClearBitsFromISR( EventGroupHandle_t xEventGroup,

 

                                                                            const EventBits_t uxBitsToClear );

功能

清除事件组中指定的位。

参数

xEventGroup

事件句柄。

uxBitsToClear

指定事件组中的哪个位需要清除。如设置uxBitsToSet0x08则只清除位3如果

 

设置uxBitsToSet0x09则位3和位0都需要被清除。

返回值

事件在还没有清除指定位之前的值。

xEventGroupClearBits()函数使用实例

#define BIT_0 ( 1 << 0 )
#define BIT_4 ( 1 << 4 )
void aFunction( EventGroupHandle_t xEventGroup )
{
EventBits_t uxBits;
/* 清楚事件组的 bit 0 and bit 4 */
uxBits = xEventGroupClearBits(
xEventGroup,
BIT_0 | BIT_4 );
if ( ( uxBits & ( BIT_0 | BIT_4 ) ) == ( BIT_0 | BIT_4 ) ) {
/* 在调用 xEventGroupClearBits()之前 bit0 和 bit4 都置位
但是现在是被清除了*/
} else if ( ( uxBits & BIT_0 ) != 0 ) {
/* 在调用 xEventGroupClearBits()之前 bit0 已经置位
但是现在是被清除了*/
} else if ( ( uxBits & BIT_4 ) != 0 ) {
/* 在调用 xEventGroupClearBits()之前 bit4 已经置位
但是现在是被清除了*/
} else {
/* 在调用 xEventGroupClearBits()之前 bit0 和 bit4 都没被置位 */
}
}

 

  相关解决方案