FreeRTOS操作系统学习


前言

在前面我们已经学习了信号量,我们知道信号量可以用来做任务同步,但是这种任务同步有一个缺点,就是只能在两个任务之间进行同步,所以为了解决这个问题,FreeRTOS提供了事件标志组,使得多任务之间进行同步。
作用:
1.用来做任务与任务之间的同步即一个任务发生了就触发了另一个任务的运行
2.用来任务与多个任务之间的同步,即多个任务都已经发生完了才会触发一个任务运行

一、事件标志组简介

1、事件位(事件标志)
事件位用来表明某个事件是否发生,事件位通常用作事件标志,比如下面的几个例子:
● 当收到一条消息并且把这条消息处理掉以后就可以将某个位(标志)置 1,当队列中没有消息需要处理的时候就可以将这个位(标志)置 0。
● 当把队列中的消息通过网络发送输出以后就可以将某个位(标志)置 1,当没有数据需要从网络发送出去的话就将这个位(标志)置 0。
● 现在需要向网络中发送一个心跳信息,将某个位(标志)置 1。现在不需要向网络中发送心跳信息,这个位(标志)置 0。

2、事件组
一个事件组就是一组的事件位,事件组中的事件位通过位编号来访问,同样,以上面列出的三个例子为例:
● 事件标志组的 bit0 表示队列中的消息是否处理掉。
● 事件标志组的 bit1 表示是否有消息需要从网络中发送出去。
● 事件标志组的 bit2 表示现在是否需要向网络发送心跳信息。

3、事件标志组和事件位的数据类型
事件标志组的数据类型为 EventGroupHandle_t,当 configUSE_16_BIT_TICKS 为 1 的时候事件标志组可以存储 8 个事件位,当 configUSE_16_BIT_TICKS 为 0 的时候事件标志组存储 24个事件位。

bit0置一采集到的就是2的零次方,bit1置一就是2的一次方

二、事件标志组API函数

1.创建事件标志组

在这里插入图片描述
1、函数 xEventGroupCreate()
此函数用于创建一个事件标志组,所需要的内存通过动态内存管理方法分配。由于内部处理的原因,事件标志组可用的 bit 数取决于 configUSE_16_BIT_TICKS , 当configUSE_16_BIT_TICKS1 为 1 的 时 候 事 件 标 志 组 有 8 个 可 用 的 位 (bit0~bit7) , 当configUSE_16_BIT_TICKS 为 0 的时候事件标志组有 24 个可用的位(bit0~bit23)。

EventGroupHandle_t xEventGroupCreate( void )

返回值:
NULL: 事件标志组创建失败。
其他值: 创建成功的事件标志组句柄。

2.设置事件位(清零或置一)

1.清零

在这里插入图片描述
1、函数 xEventGroupClearBits()
将事件标志组中的指定事件位清零,此函数只能用在任务中,不能用在中断服务函数中!

EventBits_t xEventGroupClearBits( EventGroupHandle_t xEventGroup, 
const EventBits_t uxBitsToClear );

参数:
xEventGroup: 要操作的事件标志组的句柄。
uxBitsToClear: 要清零的事件位,比如要清除 bit3 的话就设置为 0X08。可以同时清除多个bit,如设置为 0X09 的话就是同时清除 bit3 和 bit0。

返回值:
任何值: 将指定事件位清零之前的事件组值。

2、函数 xEventGroupClearBitsFromISR()
此函数为函数 xEventGroupClearBits()的中断级版本,也是将指定的事件位(标志)清零。

BaseType_t xEventGroupClearBitsFromISR( EventGroupHandle_t xEventGroup, 
const EventBits_t uxBitsToSet );

参数:
xEventGroup: 要操作的事件标志组的句柄。
uxBitsToClear: 要清零的事件位,比如要清除 bit3 的话就设置为 0X08。可以同时清除多个bit,如设置为 0X09 的话就是同时清除 bit3 和 bit0。

返回值:
pdPASS: 事件位清零成功。
pdFALSE: 事件位清零失败。

2.置一

在这里插入图片描述
1、函数 xEventGroupSetBits()
设置指定的事件位为 1,此函数只能用在任务中,不能用于中断服务函数

EventBits_t xEventGroupSetBits( EventGroupHandle_t xEventGroup, 
 const EventBits_t uxBitsToSet );

参数:
xEventGroup: 要操作的事件标志组的句柄。
uxBitsToClear: 指定要置 1 的事件位,比如要将 bit3 值 1 的话就设置为 0X08。可以同时将多个 bit 置 1,如设置为 0X09 的话就是同时将 bit3 和 bit0 置 1。

返回值:
任何值: 在将指定事件位置 1 后的事件组值

2、函数 xEventGroupSetBitsFromISR()
此函数也用于将指定的事件位置 1,此函数是 xEventGroupSetBits()的中断版本

BaseType_t xEventGroupSetBitsFromISR( EventGroupHandle_t xEventGroup, 
const EventBits_t uxBitsToSet, 
BaseType_t * pxHigherPriorityTaskWoken );

参数:
xEventGroup: 要操作的事件标志组的句柄。
uxBitsToClear: 指定要置 1 的事件位,比如要将 bit3 值 1 的话就设置为 0X08。可以同时将
多个 bit 置 1,如设置为 0X09 的话就是同时将 bit3 和 bit0 置 1。
pxHigherPriorityTaskWoken:标记退出此函数以后是否进行任务切换,这个变量的值函数会自动设置的,用户不用进行设置,用户只需要提供一个变量来保存这个值就行了。当此值为 pdTRUE 的时候在退出中断服务函数之前一定要进行一次任务切换。

返回值:
pdPASS: 事件位置 1 成功。
pdFALSE: 事件位置 1 失败。

3、获取事件标志组值

在这里插入图片描述
1、函数 xEventGroupGetBits()
此函数用于获取当前事件标志组的值,也就是各个事件位的值。此函数用在任务中,不能
用在中断服务函数中。

EventBits_t xEventGroupGetBits( EventGroupHandle_t xEventGroup )

参数:
xEventGroup: 要获取的事件标志组的句柄。

返回值:
任何值:当前事件标志组的值。

2、函数 xEventGroupGetBitsFromISR()
获取当前事件标志组的值,此函数是 xEventGroupGetBits()的中断版本

EventBits_t xEventGroupGetBitsFromISR( EventGroupHandle_t xEventGroup )

参数:
xEventGroup: 要获取的事件标志组的句柄。
返回值:
任何值: 当前事件标志组的值。

4、等待指定的事件位

某个任务可能需要与多个事件进行同步,那么这个任务就需要等待并判断多个事件位(标 志),使用函数 xEventGroupWaitBits()可以完成这个功能。调用函数以后如果任务要等待的事件位还没有准备好(置 1 或清零)的话任务就会进入阻塞态,直到阻塞时间到达或者所等待的事件位准备好。

EventBits_t xEventGroupWaitBits( EventGroupHandle_t xEventGroup,
const EventBits_t uxBitsToWaitFor,
const BaseType_t xClearOnExit,
const BaseType_t xWaitForAllBits,
const TickType_t xTicksToWait );

参数:
xEventGroup: 指定要等待的事件标志组。

uxBitsToWaitFord:指定要等待的事件位,比如要等待bit0和(或)bit2的时候此参数就是0X05,如果要等待 bit0 和(或)bit1 和(或)bit2 的时候此参数就是 0X07,以此类推。

xClearOnExit: 此参数要是为pdTRUE的话,那么在退出此函数之前由参数uxBitsToWaitFor所设置的这些事件位就会清零。如果设置位 pdFALSE 的话这些事件位就不会改变。

xWaitForAllBits: 此参数如果设置为 pdTRUE 的话,当 uxBitsToWaitFor 所设置的这些事件位都置 1,或者指定的阻塞时间到的时候函数 xEventGroupWaitBits()才会返回。当此函数为 pdFALSE 的话,只要 uxBitsToWaitFor 所设置的这些事件 位 其 中 的 任 意 一 个 置 1 , 或 者 指 定 的 阻 塞 时 间 到 的 话 函 数xEventGroupWaitBits()就会返回。

xTicksToWait: 设置阻塞时间,单位为节拍数。

返回值:
任何值: 返回当所等待的事件位置 1 以后的事件标志组的值,或者阻塞时间到。根据这个值我们就知道哪些事件位置 1 了。如果函数因为阻塞时间到而返回的话那么这个返回值就不代表任何的含义。

三、事件标志组实验

本实验设计四个任务:start_task、eventsetbit_task、eventgroup_task 和 eventquery_task 这四
个任务的任务功能如下:
start_task:用来创建其他三个任务和事件标志组。
eventsetbit_task:读取按键值,根据不同的按键值将事件标志组中相应的事件位置 1,用来模拟事件的发生。
eventgroup_task:同时等待事件标志组中的多个事件位,当这些事件位都置 1 的话就执行相应的处理,例程中是刷新 LCD 指定区域的背景色。
eventquery_task:查询事件组的值,也就是各个事件位的值。获取到事件组值以后就将其显示到 LCD 上,并且也通过串口打印出来。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

/ÉèÖÃʼþλµÄÈÎÎñ
void eventsetbit_task(void *pvParameters)
{
	u8 key;
	while(1)
	{
		if(EventGroupHandler!=NULL)
		{
			key=KEY_Scan(0);
			switch(key)
			{
				case KEY0_PRES:
					xEventGroupSetBits(EventGroupHandler,EVENTBIT_0);//ÉèÖÃʼþλ    bit0 ÖÃÒ»
					break;
				
				case KEY1_PRES:
					xEventGroupSetBits(EventGroupHandler,EVENTBIT_1);//ÉèÖÃʼþλ     bit1 ÖÃÒ»
					break;
				case WKUP_PRES:
					xEventGroupSetBits(EventGroupHandler,EVENTBIT_2); // ÉèÖÃʼþλ   bit2     ÖÃÒ»
					break;	
			}
		}
        vTaskDelay(10); //ÑÓʱ10ms£¬Ò²¾ÍÊÇ10¸öʱÖÓ½ÚÅÄ
	}
}

//ʼþ±êÖ¾×é´¦ÀíÈÎÎñ
void eventgroup_task(void *pvParameters)
{
	u8 num;
	EventBits_t EventValue;
	while(1)
	{

		if(EventGroupHandler!=NULL)   //ÅжÏʼþ±êÖ¾×éÊÇ·ñ´´½¨³É¹¦
		{
			//µÈ´ýʼþ×éÖеÄÏàӦʼþλ
			EventValue=xEventGroupWaitBits((EventGroupHandle_t	)EventGroupHandler,		   //·µ»ØµÄÊÇʼþ±êÖ¾×éµÄÖµ
										   (EventBits_t			)EVENTBIT_ALL,
										   (BaseType_t			)pdTRUE,				
										   (BaseType_t			)pdTRUE,   //Èý¸ö¶¼ÖÃÒ»²Å·µ»Ø  Ò²¾ÍÊÇ3¸ö¶¼ÖÃÒ»²ÅÖ´ÐÐÏÂÃæµÄ³ÌÐò
								           (TickType_t			)portMAX_DELAY);	
			printf("ʼþ±êÖ¾×éµÄÖµ:%d\r\n",EventValue);
			LCD_ShowxNum(174,110,EventValue,1,16,0);
			
			num++;
			LED1=!LED1;	
			LCD_Fill(6,131,233,313,lcd_discolor[num%14]);
		}
		else
		{
			vTaskDelay(10); //ÑÓʱ10ms£¬Ò²¾ÍÊÇ10¸öʱÖÓ½ÚÅÄ
		}
	}
}

//ʼþ²éѯÈÎÎñ
void eventquery_task(void *pvParameters)
{	
	u8 num=0;
	EventBits_t NewValue,LastValue;
	while(1)
	{
		if(EventGroupHandler!=NULL)
		{
			NewValue=xEventGroupGetBits(EventGroupHandler);	//»ñȡʼþ×éµÄ
			if(NewValue!=LastValue)
			{
				LastValue=NewValue;
				printf("ʼþ±êÖ¾×éµÄÖµ:%d\r\n",NewValue);
				LCD_ShowxNum(174,110,NewValue,1,16,0);
			}
		}
		num++;
		if(num==10) 	//ÿ500msLED0ÉÁ˸һ´Î
		{
			num=0;
			LED0=!LED0;	
		}
		vTaskDelay(50); //ÑÓʱ50ms£¬Ò²¾ÍÊÇ50¸öʱÖÓ½ÚÅÄ
	}
}


总结

用于任务可与多个事件或任务进行同步,只能使用事件标志组(或者任务通知模拟事件标志组),此时信号量就无能为力了。
例如:迅雷需要下载一部10集的连续剧,下载完成之后才执行“观看”动作。每一集电影下载时一个独立的事件,比如第3集下载完了,登记到事件组,不关心其它任务是否完成。等10集都登记完成过后,进入“观看”事件。

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐