FreeRTOS的学习系列文章目录
FreeRTOS的学习(一)——STM32上的移植问题
FreeRTOS的学习(二)——任务优先级问题
FreeRTOS的学习(三)——中断机制
FreeRTOS的学习(四)——列表
FreeRTOS的学习(五)——系统延时
FreeRTOS的学习(六)——系统时钟
FreeRTOS的学习(七)——1.队列概念
FreeRTOS的学习(七)——2.队列入队源码分析
FreeRTOS的学习(七)——3.队列出队源码分析
FreeRTOS的学习(八)——1.二值信号量
FreeRTOS的学习(八)——2.计数型信号量
FreeRTOS的学习(八)——3.优先级翻转问题
FreeRTOS的学习(八)——4.互斥信号量
FreeRTOS的学习(九)——软件定时器
FreeRTOS的学习(十)——事件标志组
FreeRTOS的学习(十一)——任务通知
目录
前言
FreeRTOS可以使用信号量来同步任务,但是信号量的方法只能与单个的事件或任务进行同步。有时候某个任务可能会需要与多个事件或任务进行同步,此时信号量就无能为力了。FreeRTOS 为此提供了一个可选的解决方法,那就是事件标志组。
1 事件标志组简介
1.1 事件位
事件位用来表明某个事件是否发生,事件位通常用作事件标志,比如下面几个例子:
- 当收到一条消息并且把这条消息处理掉以后就可以将某个位(标志)置1,当队列中没有消息需要处理的时候就可以将这个位(标志)置 0。
- 当把队列中的消息通过网络发送输出以后就可以将某个位(标志)置1,当没有数据需要从网络发送出去的话就将这个位(标志)置 0。
- 现在需要向网络中发送一个心跳信息,将某个位(标志)置 1。现在不需要向网络中发送心跳信息,这个位(标志)置 0。
1.2 事件组
一个事件组就是一组的事件位,事件组中的事件位通过位编号来访问,同样,以上面列出的三个例子为例:
- 事件标志组的 bit0 表示队列中的消息是否处理掉。
- 事件标志组的 bit1 表示是否有消息需要从网络中发送出去。
- 事件标志组的 bit2 表示现在是否需要向网络发送心跳信息。
1.3 事件标志组和事件位的数据类型
事件标志组实际是一个结构体,其类型为EventGroupHandle_t。
当configUSE_16_BIT_TICKS(之前说明的是作为系统节拍计数值的数据类型)为1时,eventEVENT_BITS_CONTROL_BYTES为0xff00U,由于该变量的高8位用于其他功能,所以只有低8位是可以用于事件标志组的标志位的;反之,为0时,eventEVENT_BITS_CONTROL_BYTES为0xff000000UL,依旧高8位不能用,故此时只有低24位可以用于事件位。
- 事件标志组的 bit0 表示队列中的消息是否处理掉。
- 事件标志组的 bit1 表示是否有消息需要从网络中发送出去。
- 事件标志组的 bit2 表示现在是否需要向网络发送心跳信息。
2 事件标志组的配置
2.1 事件标志组的创建
FreeRTOS提供了两个用于创建事件标志组的函数。
- xEventGroupCreate()
使用动态方法创建事件标志组。- xEventGroupCreateStatic()
使用静态方法创建事件标志组
2.2 事件位的设置
FreeRTOS 提供了 4 个函数用来设置事件标志组中事件位(标志),事件位(标志)的设置包括清 0 和置 1 两种操作:
- xEventGroupClearBits()
将指定的事件位清零,用在任务中。- xEventGroupClearBitsFromISR()
将指定的事件位清零,用在中断服务函数中- xEventGroupSetBits()
将指定的事件位置 1,用在任务中。- xEventGroupSetBitsFromISR()
将指定的事件位置 1,用在中断服务函数中。
2.2.1 事件位的清零
事件位的清零就是将事件标志组中的指定事件位清零,由任务级和中断级两种函数(FreeRTOS常规操作了,所以一般只讲任务级)
任务函数原型如下:
EventBits_t xEventGroupClearBits( EventGroupHandle_t xEventGroup,
const EventBits_t uxBitsToClear )
只要给定事件标志组的句柄以及想要消除的标志位,如消除第0位和第3位,即输入0x09(默认低8位有效)。
另外值得注意的是,xEventGroupClearBits函数会返回未处理前的事件标志组。
对于普通任务,如果想要获取事件标志组,可以调用xEventGroupGetBits,不过这个函数实际就是调用xEventGroupClearBits,只不过不对事件标志位做任何清零操作。
对于中断任务中,如果想要获取事件标志组,可以调用xEventGroupGetBitsFromISR函数。
2.2.2 事件位的置一
事件位的置一就是将事件标志组中的指定事件位设置为1,同样分为任务级和中断级两种函数。
任务级函数原型如下:
EventBits_t xEventGroupSetBits( EventGroupHandle_t xEventGroup,
const EventBits_t uxBitsToSet )
只要给定事件标志组的句柄以及想要置一的标志位,如置一第0位和第3位,同样输入0x09即可(默认低8位有效)。
设置标志位的函数中明显比较复杂,这是由于在设置标志位时需要处理一些等待标志位为 1 后才能从阻塞态解除的某些任务,这种情况就需要获取xTasksWaitingForBits(等待置位的任务列表)的头和尾,然后遍历,判断是否有需要从阻塞态中解除的任务,并且最后需要调用vTaskRemoveFromUnorderedEventList函数将解除后的任务从事件列表中移除。
值得说明的是,某些任务在等到事件标志位置一后需要清除该标志位,因此,在xEventGroupSetBits函数的最后会有这些语句:
if( xMatchFound != pdFALSE )
{
if( ( uxControlBits & eventCLEAR_EVENTS_ON_EXIT_BIT ) != ( EventBits_t ) 0 )
{
uxBitsToClear |= uxBitsWaitedFor;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
vTaskRemoveFromUnorderedEventList( pxListItem, pxEventBits->uxEventBits | eventUNBLOCKED_DUE_TO_BIT_SET );
}
以及
pxEventBits->uxEventBits &= ~uxBitsToClear;
与此同时,置位的函数也会返回事件标志组,但是由于上述的原因以及函数的复杂度,该方式并不适用。
2.2.3 等待指定的事件位
某个任务可能需要与多个事件进行同步,那么这个任务就需要等待并判断多个事件位(标志),使用函数 xEventGroupWaitBits可以完成这个功能。调用函数以后如果任务要等待的事件位还没有准备好(置 1 或清零)的话任务就会进入阻塞态,直到阻塞时间到达或者所等待的事件位准备好。
函数原型:
EventBits_t xEventGroupWaitBits( EventGroupHandle_t xEventGroup,
const EventBits_t uxBitsToWaitFor,
const BaseType_t xClearOnExit,
const BaseType_t xWaitForAllBits,
TickType_t xTicksToWait )
其中部分参数释义如下:
- uxBitsToWaitFor
指定要等待的事件位,比如要等待bit0和(或)bit2的时候此参数就是0X05,如果要等待 bit0 和(或)bit1 和(或)bit2 的时候此参数就是 0X07,以此类推。- xClearOnExit
此参数要是为pdTRUE的话,那么在退出此函数之前由参数uxBitsToWaitFor所设置的这些事件位就会清零。如果设置位 pdFALSE 的话这些事件位就不会改变。- xWaitForAllBits
此参数如果设置为 pdTRUE 的话,当 uxBitsToWaitFor 所设置的这些事件位都置 1,或者指定的阻塞时间到的时候函数xEventGroupWaitBits()才会返回。当此函数为 pdFALSE 的话,只要uxBitsToWaitFor 所设置的这些事件位其中的任意一个置 1 ,或者指定的阻塞时间到的话函数xEventGroupWaitBits()就会返回。- 返回值
返回当所等待的事件位置 1 以后的事件标志组的值,或者阻塞时间到。根据这个值我们就知道哪些事件位置 1 了。如果函数因为阻塞时间到而返回的话那么这个返回值就不代表任何的含义。