FreeRTOS学习笔记 & 事件标志组
- 事件标志组 (Event Groups)
- 事件标志组概述
- 事件标志组特性
- 事件应用场景
- 事件运作机制
- 事件标志组 API 函数
- xEventGroupCreate() //动态方式创建一个事件组 (推荐动态方式创建)
- xEventGroupCreateStatic //静态方式创建一个事件组
- vEventGroupDelete() //删除指定的事件组
- xEventGroupSetBits() //事件组设置位(标志)(任务中使用)
- xEventGroupSetBitsFromISR() //事件组设置位(标志)(中断中使用)
- xEventGroupWaitBits() //读取 RTOS 事件组中的位(标志)(任务中使用)
- xEventGroupClearBits() //清除 RTOS 事件组中的位(标志)(任务中使用)
- xEventGroupClearBitsFromISR() //清除 RTOS 事件组中的位(标志)(中断中使用)
- xEventGroupGetBits() //查询 RTOS 事件组中的所有位(标志)(任务中使用)
- xEventGroupGetBitsFromISR() //查询 RTOS 事件组中的所有位(标志)(中断中使用)
- xEventGroupSync() 事件组同步
事件标志组 (Event Groups)
事件标志组概述
- 事件通讯是一种任务之间的通讯机制,实现任务之间信息同步
- 事件通讯只能实现事件类型数据通讯,不提供数据传输功能(与消息队列不同)
- 事件通讯可以等待单个或多个事件的发生,来唤醒阻塞任务进行处理
事件标志组特性
- 事件组存储在一个 EventBits_t 类型的变量中,该变量在事件组结构体中定义
- 如果宏configUSE_16_BIT_TICKS 定义为 1,那么变量 uxEventBits 就是 16 位的,其中有 8 个位用来存储事件组
- 而如果宏 configUSE_16_BIT_TICKS 定义为 0,那么变量 uxEventBits 就是 32 位的,其中有 24 个位用来存储事件组
- 每一位代表一个事件,任务通过“逻辑与”或“逻辑或”与一个或多个事件建立关联,形成一个事件组
- 事件无排队性,即多次向任务设置同一事件(如果任务还未来得及读走),等效于只设置一次
- 允许多个任务对同一事件进行读写操作
- 支持事件等待超时机制
事件应用场景
FreeRTOS 的事件用于事件类型的通讯,无数据传输,也就是说,我们可以用事件来做标志位,判断某些事件是否发生了,然后根据结果做处理
事件运作机制
- 接收事件时,可以根据感兴趣的事件类型接收事件的单个或者多个事件类型
- 设置事件时,对指定事件写入指定的事件类型,设置事件集合的对应事件位为 1,可以一次同时写多个事件类型,设置事件成功可能会触发任务调度
- 清除事件时,根据入参数事件句柄和待清除的事件类型,对事件对应位进行清 0 操作
任务 1 对事件 3 或事件 5 感兴趣(逻辑或),当发生其中的某一个事件都会被唤醒,并且执行相应操作。
而任务 2 对事件 3 与事件 5 感兴趣(逻辑与),当且仅当事件 3 与事件 5 都发生的时候,任务 2 才会被唤醒,如果只
有一个其中一个事件发生,那么任务还是会继续等待事件发生。
如果在接收事件函数中设置了清除事件位 xClearOnExit,那么当任务唤醒后将把事件 3 和事件5 的事件标志清零,否则事件标志将依然存在
事件标志组 API 函数
xEventGroupCreate() //动态方式创建一个事件组 (推荐动态方式创建)
xEventGroupCreateStatic //静态方式创建一个事件组
要使 xEventGroupCreate() 此 RTOS API 函数可用:
configSUPPORT_DYNAMIC_ALLOCATION 必须在 FreeRTOSConfig.h 中设置为 1
RTOS 源文件 FreeRTOS/source/event_groups.c 必须包含在工程中。
要使 xEventGroupCreateStatic () 此 RTOS API 函数可用:
configSUPPORT_STATIC_ALLOCATION必须在 FreeRTOSConfig.h 中设置为 1
RTOS 源文件 FreeRTOS/source/event_groups.c 必须包含在工程中。
EventGroupHandle_t xEventGroupCreate( void );//函数声明
/* 创建事件组句柄. */
EventGroupHandle_t xCreatedEventGroup;
//用法示例:
void Task_Event_Group(void const * argument)
{
/* 调用创建事件组API,创建成功返回事件组句柄,失败返回NULL. */
xCreatedEventGroup = xEventGroupCreate();
/* 判断变量值,确认事件组是否创建成功? */
if( xCreatedEventGroup == NULL )
{
/* 事件组创建失败执行内部代码. */
}
else
{
/* 事件组创建成功执行内部代码. */
}
}
vEventGroupDelete() //删除指定的事件组
Note : 无法从中断调用此函数
当事件组被删除之后,阻塞在该事件组上的任务都会被解锁,并向等待事件的任务返回事件组的值为 0
void vEventGroupDelete( EventGroupHandle_t xEventGroup );
/* 创建事件组句柄. */
EventGroupHandle_t xCreatedEventGroup;
//用法示例:
void Task_Event_Group(void const * argument)
{
/* 调用创建事件组API,创建成功返回事件组句柄,失败返回NULL. */
xCreatedEventGroup = xEventGroupCreate();
/* 判断变量值,确认事件组是否创建成功? */
if( xCreatedEventGroup == NULL )
{
/* 事件组创建失败执行内部代码. */
}
else
{
/* 事件组创建成功执行内部代码. */
vEventGroupDelete( xCreatedEventGroup );//调用事件组删除API,传入事件组句柄
}
}
xEventGroupSetBits() //事件组设置位(标志)(任务中使用)
xEventGroupSetBitsFromISR() //事件组设置位(标志)(中断中使用)
要使用 xEventGroupSetBitsFromISR() 此RTOS API需配置3个宏定义
configUSE_TRACE_FACILITY 1
INCLUDE_xTimerPendFunctionCall 1
configUSE_TIMERS 1
EventGroupHandle_t xEventGroupCreate( void );//函数声明
EventBits_t xEventGroupSetBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet );//函数声明
xEventGroupSetBitsFromISR( xEventGroup, uxBitsToSet, pxHigherPriorityTaskWoken );//函数声明
/* 创建事件组句柄. */
EventGroupHandle_t xCreatedEventGroup;
//用法示例:
#define BIT_0 ( 1 << 0 )
#define BIT_1 ( 1 << 1 )
void Task_Event_Group(void const * argument)//任务函数
{
/* 接收置位函数返回值. */
EventBits_t uxBits;
/* 调用创建事件组API,创建成功返回事件组句柄,失败返回NULL. */
xCreatedEventGroup = xEventGroupCreate();
/* 判断变量值,确认事件组是否创建成功? */
if( xCreatedEventGroup == NULL )
{
/* 事件组创建失败执行内部代码. */
}
else
{
/* 事件组创建成功执行内部代码. */
uxBits = xEventGroupSetBits( xCreatedEventGroup , BIT_0 );//给指定事件组BIT0位置位
}
}
void IRQ_Interrupt_Handle(void)//中断服务函数
{
/* 接函数返回值. */
BaseType_t xHigherPriorityTaskWoken, xResult;
/* xHigherPriorityTaskWoken 初始化为 pdFALSE. */
xHigherPriorityTaskWoken = pdFALSE;
xResult = xEventGroupSetBitsFromISR( xCreatedEventGroup , BIT_1, &xHigherPriorityTaskWoken);
if( xResult != pdFAIL )
{
/* 如果xHigherPriorityTaskWoken现在设置为pdTRUE,那么上下文应要求切换. */
portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
}
}
xEventGroupWaitBits() //读取 RTOS 事件组中的位(标志)(任务中使用)
函数参数介绍:
xEventGroup
已通过 xEventGroupCreate() 创建好事件组句柄。
uxBitsToWaitFor
指定事件组中要测试的一个或多个事件位的位值
xClearOnExit
如果 xClearOnExit 设置为 pdTRUE, 那么当调用 xEventGroupWaitBits() 返回时,事件组中设置的位会清除。
如果 xClearOnExit 设置为 pdFALSE, 那么当调用 xEventGroupWaitBits() 返回时,事件组中设置的位不会改变。
xWaitForAllBits
设置为 pdTRUE, 所有位均已在事件组中设置好,或指定的阻塞时间已过期,则 xEventGroupWaitBits() 会返回相应值。
设置为 pdFALSE,任何位已在事件组中设置好, 或指定的阻塞时间已过期,则 xEventGroupWaitBits() 会返回相应值。
xTicksToWait
阻塞时间。
Note : 无法从中断调用此函数。
EventBits_t xEventGroupWaitBits(
const EventGroupHandle_t xEventGroup,
const EventBits_t uxBitsToWaitFor,
const BaseType_t xClearOnExit,
const BaseType_t xWaitForAllBits,
TickType_t xTicksToWait );
EventGroupHandle_t xEventGroupCreate( void );//函数声明
/* 创建事件组句柄. */
EventGroupHandle_t xCreatedEventGroup;
//用法示例:
#define BIT_0 ( 1 << 0 )
#define BIT_1 ( 1 << 1 )
void Task_Event_Group(void const * argument)//任务函数
{
/* 接收置位函数返回值. */
EventBits_t uxBits;
/* 调用创建事件组API,创建成功返回事件组句柄,失败返回NULL. */
xCreatedEventGroup = xEventGroupCreate();
/* 判断变量值,确认事件组是否创建成功? */
if( xCreatedEventGroup == NULL )
{
/* 事件组创建失败执行内部代码. */
}
else
{
/* 事件组创建成功执行内部代码. */
uxBits = xEventGroupSetBits( xCreatedEventGroup , BIT_0 );//给指定事件组BIT0位置位
}
}
void aFunction( void )
{
/* 接收置位函数返回值. */
EventBits_t uxBits;
/* . */
uxBits = xEventGroupWaitBits(
xCreatedEventGroup , /* 事件组句柄. */
BIT_0 | BIT_1, /* 填入要等待读取的标志位. */
pdTRUE, /* 读取对应位后清除位. */
pdTRUE, /* 读取位都置位才唤醒该函数. */
portMAX_DELAY ); /* 最大阻塞时间. */
if( ( uxBits & ( BIT_0 | BIT_1 ) ) == ( BIT_0 | BIT_1 ) )
{
/* xEventGroupWaitBits() returned BIT_0,BIT_1成为set. */
}
else
{
/* xEventGroupWaitBits() returned 没有BIT_0或BIT_1成为set. */
}
}
xEventGroupClearBits() //清除 RTOS 事件组中的位(标志)(任务中使用)
EventBits_t xEventGroupClearBits(
EventGroupHandle_t xEventGroup,
const EventBits_t uxBitsToClear );
参数:
xEventGroup 要在其中清除位的事件组。 此事件组 必须已通过 调用 xEventGroupCreate() 事先创建。
uxBitsToClear 表示要在事件组中清除一个或多个位的 按位值。 例如,将 uxBitsToClear 设置为 0x08 以仅清除位 3。 将 uxBitsToClear 设置为 0x09 以清除位 3 和位 0。
Returns:
清除指定位之前的事件组的值。
用法示例:
#define BIT_0 ( 1 << 0 )
#define BIT_4 ( 1 << 4 )
void aFunction( EventGroupHandle_t xEventGroup )
{
EventBits_t uxBits;
/* Clear bit 0 and bit 4 in xEventGroup. */
uxBits = xEventGroupClearBits(
xEventGroup, /* The event group being updated. */
BIT_0 | BIT_4 );/* The bits being cleared. */
if( ( uxBits & ( BIT_0 | BIT_4 ) ) == ( BIT_0 | BIT_4 ) )
{
/* Both bit 0 and bit 4 were set before xEventGroupClearBits()
was called. Both will now be clear (not set). */
}
else if( ( uxBits & BIT_0 ) != 0 )
{
/* Bit 0 was set before xEventGroupClearBits() was called. It will
now be clear. */
}
else if( ( uxBits & BIT_4 ) != 0 )
{
/* Bit 4 was set before xEventGroupClearBits() was called. It will
now be clear. */
}
else
{
/* Neither bit 0 nor bit 4 were set in the first place. */
}
}
xEventGroupClearBitsFromISR() //清除 RTOS 事件组中的位(标志)(中断中使用)
EventBits_t xEventGroupClearBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToClear );//函数声明
xEventGroupClearBitsFromISR( xEventGroup, uxBitsToClear );//函数声明
/* 创建事件组句柄. */
EventGroupHandle_t xCreatedEventGroup;
//用法示例:
#define BIT_0 ( 1 << 0 )
#define BIT_1 ( 1 << 1 )
void Task_Event_Group(void const * argument)//任务函数
{
/* 接收置位函数返回值. */
EventBits_t uxBits;
/* 调用创建事件组API,创建成功返回事件组句柄,失败返回NULL. */
xCreatedEventGroup = xEventGroupCreate();
/* 判断变量值,确认事件组是否创建成功? */
if( xCreatedEventGroup == NULL )
{
/* 事件组创建失败执行内部代码. */
}
else
{
/* 事件组创建成功执行内部代码. */
uxBits = xEventGroupClearBits( xCreatedEventGroup , BIT_0 );//清除事件组BIT0位
}
if( uxBits == BIT_0 )
{
/* 标志位清除成功. */
}
}
void IRQ_Interrupt_Handle( void )//中断服务函数
{
BaseType_t xSuccess;
/* 清除BIT0. */
xSuccess = xEventGroupClearBitsFromISR(
xCreatedEventGroup , /* 事件组句柄. */
BIT_0 ); /* 清除位. */
if( xSuccess == pdPASS )
{
/* 清除成功. */
}
else
{
/* 清除失败. */
}
}
xEventGroupGetBits() //查询 RTOS 事件组中的所有位(标志)(任务中使用)
EventBits_t xEventGroupGetBits( EventGroupHandle_t xEventGroup );
参数:
xEventGroup 正在查询的事件组。 该必须已通过 xEventGroupCreate()创建的。
Returns:
调用xEventGroupGetBits()时,事件组中的 事件组中事件位的值
xEventGroupGetBitsFromISR() //查询 RTOS 事件组中的所有位(标志)(中断中使用)
xEventGroupGetBits( xEventGroup );//函数声明
EventBits_t xEventGroupGetBitsFromISR( EventGroupHandle_t xEventGroup );//函数声明
/* 创建事件组句柄. */
EventGroupHandle_t xCreatedEventGroup;
//用法示例:
#define BIT_0 ( 1 << 0 )
#define BIT_1 ( 1 << 1 )
void Task_Event_Group(void const * argument)//任务函数
{
/* 接收置位函数返回值. */
EventBits_t uxBits;
/* 调用创建事件组API,创建成功返回事件组句柄,失败返回NULL. */
xCreatedEventGroup = xEventGroupCreate();
/* 判断变量值,确认事件组是否创建成功? */
if( xCreatedEventGroup == NULL )
{
/* 事件组创建失败执行内部代码. */
}
else
{
/* 事件组创建成功执行内部代码. */
uxBits = xEventGroupSetBits( xCreatedEventGroup , BIT_0 );//清除事件组BIT0位
}
if( uxBits == BIT_0 )
{
/* 标志位置位成功. */
uxBits = xEventGroupGetBits( xCreatedEventGroup );//查询事件组
printf("xEventGroupGetBits --> %#X \r\n",uxBits );
}
}
void IRQ_Interrupt_Handle( void )//中断服务函数
{
/* 接收置位函数返回值. */
EventBits_t uxBits;
uxBits= xEventGroupGetBitsFromISR( xCreatedEventGroup );//中断查询事件组
printf("xEventGroupGetBits --> %#X \r\n",uxBits );
}
xEventGroupSync() 事件组同步
此功能通常 用于同步多个任务(通常称为任务集合),其中每个 任务必须等待其他任务到达同步点 才能继续
EventBits_t xEventGroupSync( EventGroupHandle_t xEventGroup,
const EventBits_t uxBitsToSet,
const EventBits_t uxBitsToWaitFor,
TickType_t xTicksToWait );
参数:
xEventGroup 事件组句柄。 必须 必须已通过 通过调用 xEventGroupCreate() 创建。
uxBitsToSet 在等待uxBitsToWait参数指定的所有位设置完成之前, 要设置事件组中的一个或多个位。 例如,将uxBitsToSet设置为0x04 即可设置事件组中的位2。
uxBitsToWaitFor 指定事件组中要测试的一个或多个事件位 的按位值。 例如,如果要等待 位0和位2被设置,则将uxBitsToWaitFor设置为0x05。 如果要等待 位0、位1和位2被设置,则将uxBitsToWaitFor设置为0x07 等等。
xTicksToWait 等待uxBitsToWaitFor参数值指定的 所有位设置完成的最长时间 (以“ticks”表示)。
Returns:
等待置位时或阻塞到期时 事件组的值。 测试返回值 以便了解设置了哪些位。
如果xEventGroupSync()由于超时而返回, 则不会设置所有等待位。
如果 xEventGroupSync()因其所等待的所有位都被设置而返回, 那么返回值是自动清除任何位之前的 事件组值
用法示例:
/* Bits used by the three tasks. */
#define TASK_0_BIT ( 1 << 0 )
#define TASK_1_BIT ( 1 << 1 )
#define TASK_2_BIT ( 1 << 2 )
#define ALL_SYNC_BITS ( TASK_0_BIT | TASK_1_BIT | TASK_2_BIT )
/* Use an event group to synchronise three tasks. It is assumed this event
group has already been created elsewhere. */
EventGroupHandle_t xEventBits;
void vTask0( void *pvParameters )
{
EventBits_t uxReturn;
TickType_t xTicksToWait = 100 / portTICK_PERIOD_MS;
for( ;; )
{
/* Perform task functionality here. */
. . .
/* Set bit 0 in the event group to note this task has reached the
sync point. The other two tasks will set the other two bits defined
by ALL_SYNC_BITS. All three tasks have reached the synchronisation
point when all the ALL_SYNC_BITS are set. Wait a maximum of 100ms
for this to happen. */
uxReturn = xEventGroupSync( xEventBits,
TASK_0_BIT,
ALL_SYNC_BITS,
xTicksToWait );
if( ( uxReturn & ALL_SYNC_BITS ) == ALL_SYNC_BITS )
{
/* All three tasks reached the synchronisation point before the call
to xEventGroupSync() timed out. */
}
}
}
void vTask1( void *pvParameters )
{
for( ;; )
{
/* Perform task functionality here. */
. . .
/* Set bit 1 in the event group to note this task has reached the
synchronisation point. The other two tasks will set the other two
bits defined by ALL_SYNC_BITS. All three tasks have reached the
synchronisation point when all the ALL_SYNC_BITS are set. Wait
indefinitely for this to happen. */
xEventGroupSync( xEventBits, TASK_1_BIT, ALL_SYNC_BITS, portMAX_DELAY );
/* xEventGroupSync() was called with an indefinite block time, so
this task will only reach here if the syncrhonisation was made by all
three tasks, so there is no need to test the return value. */
}
}
void vTask2( void *pvParameters )
{
for( ;; )
{
/* Perform task functionality here. */
. . .
/* Set bit 2 in the event group to note this task has reached the
synchronisation point. The other two tasks will set the other two
bits defined by ALL_SYNC_BITS. All three tasks have reached the
synchronisation point when all the ALL_SYNC_BITS are set. Wait
indefinitely for this to happen. */
xEventGroupSync( xEventBits, TASK_2_BIT, ALL_SYNC_BITS, portMAX_DELAY );
/* xEventGroupSync() was called with an indefinite block time, so
this task will only reach here if the syncrhonisation was made by all
three tasks, so there is no need to test the return value. */
}
}