freeRTOS事件标志组(1-16)

事件标志组定义

事件标志位:用一个为来表示事件是否发生

事件标志组是事件标志位的集合可以简单的理解事件标志组,就是一个整数

事件标志组的特点:

  • 1:它的每一个位表示一个事件(高8位不算)

  • 2:每一个事件的含义,由用户自己决定,如:bit0表示按键是否按下,bit表示是否接收到消息......(这些位的值为1:表示事件发生了,值为0:表示事件未发生)

  • 3:任意任务或中断都可以读写这些位

  • 4:可以等待某一位成立,或者诸多位同事成立

事件标志组简介

一个事件组就包含了一个EventBits_t 数据类型的变量,变量类型EventBits_t的定义如下所示。

typdef TickType_t EventBits_t ;

#if (configUSE_16_BIT_TICKS == 1)
        
     typedef uint16_t TickType_t;          // 1 表示的是无符号16位的数据类型

#else

     typedef uint32_t TickType_t;         //  0 表示的是无符号32位的数据类型   

#endif

#define configUSE_16_BIT_TICKS   0

虽然使用了32位无符号的数据类型变量来存储事件标志,但其中的高8位用作存储事件标志组的控制信息,低24位用作存储事件标志,所以说一个事件组最多可以存储24个事件标志!

事件标志组与队列,信号量的区别?

功能唤醒对象事件清除
队列,信号量事件发生时,只能唤醒一个任务是消耗型资源,队列的数据被读走就没有了,信号量被获取后就减少了
事件标志组事件发生时会唤醒所有符合条件的任务,可以理解为广播的作用被唤醒的任务有两个选择,可以让事件保留不动,也可以清除事件

事件标志组API函数

函数描述

xEventGroupCreate()

使用动态方式创建事件标志组
xEventGroupCreateStatic()使用静态方式创建事件标志组
xEventGroupClearBits()清零事件标志位
xEventGroupClearBitsFromISR()在中断中清零事件标志位
xEventGroupSetBits()设置事件标志位
xEventGroupSetBitsFromISR()在中断中设置事件标志位
xEventGroupWaitBits()等待事件标志位
xEventGroupSync()设置事件标志位并等待事件标志位

创建标志组

使用动态的方式创建事件标志组

使用事件组之前,要先创建,得到一个句柄;

使用事件组时,要使用句柄来表明使用哪个事件组。

有两种创建方法:动态分配内存、静态分配内存。

函数原型如下:

EventGroupHandle_t xEventGroupCreate ( void ) ;
返回值描述
NULL事件标志组创建失败
其他值事件标志组创建成功,返回句柄

创建示例:

清除事件标志位API函数

EventBits_t xEventGroupClearBits(

                            EventGroupHandle_t xEventGroup,

                            const EventBits_t  uxBitsToClear

);

EventBits_t xEventGroupClearBits
(
              EventGroupHandle_t xEventGroup,
              const EventBits_t  uxBitsToClear
);
形参描述
xEventGroup待操作的事件标志组句柄
uxBitsToSet待清零的事件标志位
返回值描述
整数清零事件标志位之前事件组中事件标志位的值

设置事件标志位API函数

EventBits_t xEventGroupSetBits
(
       EventGroupHandle_t xEventGroup, 
       const EventBits_t  uxBitsToSet
);
形参描述
xEventGroup待操作的事件标志组句柄
uxBitsToSet待设置的事件标志位
返回值描述
整数事件组中的事件标志位值

可以设置事件组的某个位、某些位,使用的函数有2个:

  • 在任务中使用 xEventGroupSetBits()

  • 在ISR中使用 xEventGroupSetBitsFromISR()

有一个或多个任务在等待事件,如果这些事件符合这些任务的期望,那么任务还会被唤醒。函数原型如下:

注:

值得注意的是,ISR中的函数,比如队列函数 xQueueSendToBackFromISR、信号量函数xSemaphoreGiveFromISR,它们会唤醒某个任务,最多只会唤醒1个任务。

但是设置事件组时,有可能导致多个任务被唤醒,这会带来很大的不确定性。

所以xEventGroupSetBitsFromISR函数不是直接去设置事件组,而是给一个FreeRTOS后台任务(daemon task)发送队列数据,由这个任务来设置事件组。

如果后台任务的优先级比当前被中断的任务优先级高, xEventGroupSetBitsFromISR会设置 *pxHigherPriorityTaskWoken为pdTRUE。

如果daemon task成功地把队列数据发送给了后台任务,那么 xEventGroupSetBitsFromISR的返回值就是pdPASS。

等待事件标志位的API函数

形参描述
xEventGroup等待事件标志组句柄
uxBitsToWaitFor等待事件标志位,可以用逻辑或等待多个事件标志位
xClearOnExit

成功等待事件标志位后,清除事件组中对应的事件标志位,

pdTRUE:  清除uxBitsToWaitFor指定位

pdFALSE: 不清除

xWaitForAllBits

等待uxBitsToWaitFor中的所有事件标志位(逻辑与)

pdTRUE:等待的位,全部为1

pdFALSE:等待的位,某个为1

xTicksToWait等待的阻塞时间
返回值描述
等待事件标志位值等待事件标志位成功,返回等待到的事件标志位
其他值等待事件标志位失败,返回事件组中的事件标志位

特点:可以等待某一位也可以等待多位,等到期望事件后,还可以清除某些位

同步函数

同步点

EventBits_t xEventGroupSysc(
    EventGroupHandle_t   xEventGroup,
    const EventBits_t    uxBitsToSet,
    const EventBits_t    uxBitsToWaitFor,
    TickType_t           xTickToWait
)

形参描述
xEventGroup等待事件标志所在事件组
uxBitsToSet达到同步点后,要设置的事件标志
uxBitsToWaitFor等待事件标志
xTickToWait等待阻塞时间

 同步点函数形参与返回值

返回值描述
等待的事件标志位值等待事件标志位成功,返回等待到的事件标志位
其他值等待事件标志位失败,返回事件组中的事件标志位

例子:

Task 1 : 做饭

Task 2 : 做菜

Task1做好自己的事情之后, 需要等到菜也做好后,大家再一起吃饭


事件组的使用:

等待事件

本节程序:20_freertos_example_event_group

  • 创建3个任务

  • 任务1:累加10000000次,然后设置事件bit 0

  • 任务2:累减5000000次,然后设置事件bit 1

  • 任务3:等待

    • 事件0和事件1

    • 事件0或事件1

step1:使用动态创建任务的方式创建三个任务

	xTaskCreate(Task1Function, "Task1", 100, NULL, 1, &xHandleTask1);
	xTaskCreate(Task2Function, "Task2", 100, NULL, 1, NULL);
	xTaskCreate(Task3Function, "Task3", 100, NULL, 1, NULL);

step2:实现累加功能,累加

void Task1Function(void * param)
{
	// 使用volatile 不给程序代码做优化
	volatile int i = 0; 
	// 使用while循环
	while (1)
	{
		// i的初始值为1,循环10000次
		for (i = 0; i < 10000; i++)
			sum++;
		   /*
						1:读取队列中的信息,第一个参数是队列的句柄,要读取的队列是哪一个
						2:第二个参数是buffer的指针,队列的数据会被复制到这个buffer中
						3:第三个是阻塞函数,没有读取到之后的操作0表示没有读取到数据立刻返回,
		          portMAX_DELAY,则会一直阻塞直到有数据可写
		   */
		xQueueSend(xQueueCalcHandle, &sum, 0);
		/* 设置事件0 设置事件标志位*/
		xEventGroupSetBits(xEventGroupCalc, (1<<0));
	}
}

step3: 实现功能的累减,bit1

void Task2Function(void * param)
{
	volatile int i = 0;
	while (1)
	{
		for (i = 0; i < 10000; i++)
			dec--;
		xQueueSend(xQueueCalcHandle, &dec, 0);
		/* 设置事件1 */
		xEventGroupSetBits(xEventGroupCalc, (1<<1));
	}
}

step4:等待事件

使用 xEventGroupWaitBits来等待事件,可以等待某一位、某些位中的任意一个,也可以等待多位;等到期望的事件后,还可以清除某些位。

函数原型如下:

注:

1:第一个参数表示等待哪些事件组

2:第二个参数表示等待哪些位,哪些位要被测试

3:第三个参数表示函数退出前是否清除事件

  •       返回值pdTRUE表示清除uxBitsToWaitFor指定的位
  •       pdFALSE: 不清除

4:第四个参数表示如何测试是AND还是OR

  1.     pdTRUE: 等待的位,全部为1;
  2.     pdFALSE: 等待的位,某一个为1即可

5:最后一个参数表示阻塞等待时间是立即返回还是死等

void Task3Function(void * param)
{
	int val1, val2;
	while (1)
	{
		/*等待事件  */
		xEventGroupWaitBits(xEventGroupCalc, (1<<0)|(1<<1), pdTRUE, pdTRUE, portMAX_DELAY);
	
		xQueueReceive(xQueueCalcHandle, &val1, 0);
		xQueueReceive(xQueueCalcHandle, &val2, 0);
		
		printf("val1 = %d, val2 = %d\r\n", val1, val2);
	}
}

创建事件组

	/* 创建事件组 */
	xEventGroupCalc = xEventGroupCreate();
    // 创建队列,队列项2个,大小是一个int 位4个byte
	xQueueCalcHandle = xQueueCreate(2, sizeof(int));
	// 判断是否创建成功
	if (xQueueCalcHandle == NULL)
	{
		printf("can not create queue\r\n");
	}

全局变量及句柄定义

static int sum = 0;     //  参数++ 的值
static int dec = 0;     //  参数-- 的值
static volatile int flagCalcEnd = 0;   
static volatile int flagUARTused = 0;
static QueueHandle_t xQueueCalcHandle; // 队列的句柄

static EventGroupHandle_t xEventGroupCalc;  //事件组的句柄

完整程序代码

static int sum = 0;     //  参数++ 的值
static int dec = 0;     //  参数-- 的值
static volatile int flagCalcEnd = 0;   
static volatile int flagUARTused = 0;
static QueueHandle_t xQueueCalcHandle; // 队列的句柄

static EventGroupHandle_t xEventGroupCalc;  //事件组的句柄



void Task1Function(void * param)
{
	// 使用volatile 不给程序代码做优化
	volatile int i = 0; 
	// 使用while循环
	while (1)
	{
		// i的初始值为1,循环10000次
		for (i = 0; i < 10000; i++)
			sum++;
		   /*
						1:读取队列中的信息,第一个参数是队列的句柄,要读取的队列是哪一个
						2:第二个参数是buffer的指针,队列的数据会被复制到这个buffer中
						3:第三个是阻塞函数,没有读取到之后的操作0表示没有读取到数据立刻返回,
		          portMAX_DELAY,则会一直阻塞直到有数据可写
		   */
		xQueueSend(xQueueCalcHandle, &sum, 0);
		/* 设置事件0 设置事件标志位*/
		xEventGroupSetBits(xEventGroupCalc, (1<<0));
	}
}

void Task2Function(void * param)
{
	volatile int i = 0;
	while (1)
	{
		for (i = 0; i < 10000; i++)
			dec--;
		xQueueSend(xQueueCalcHandle, &dec, 0);
		/* 设置事件1 */
		xEventGroupSetBits(xEventGroupCalc, (1<<1));
	}
}


void Task3Function(void * param)
{
	int val1, val2;
	while (1)
	{
		/*等待事件  */
		xEventGroupWaitBits(xEventGroupCalc, (1<<0)|(1<<1), pdTRUE, pdTRUE, portMAX_DELAY);
	
		xQueueReceive(xQueueCalcHandle, &val1, 0);
		xQueueReceive(xQueueCalcHandle, &val2, 0);
		
		printf("val1 = %d, val2 = %d\r\n", val1, val2);
	}
}



/*-----------------------------------------------------------*/

int main( void )
{
	TaskHandle_t xHandleTask1;
		
#ifdef DEBUG
  debug();
#endif

	prvSetupHardware();

	printf("Hello, world!\r\n");

	/* 创建事件组 */
	xEventGroupCalc = xEventGroupCreate();
  // 创建队列,队列项2个,大小是一个int 位4个byte
	xQueueCalcHandle = xQueueCreate(2, sizeof(int));
	// 判断是否创建成功
	if (xQueueCalcHandle == NULL)
	{
		printf("can not create queue\r\n");
	}


	xTaskCreate(Task1Function, "Task1", 100, NULL, 1, &xHandleTask1);
	xTaskCreate(Task2Function, "Task2", 100, NULL, 1, NULL);
	xTaskCreate(Task3Function, "Task3", 100, NULL, 1, NULL);
	/* Start the scheduler. */
	vTaskStartScheduler();

	/* Will only get here if there was not enough heap space to create the
	idle task. */
	return 0;
}

 运行结果

同步点案例

本节程序:

  • 21_freertos_example_event_group_task_sync

  • 就是参考书配套源码FreeRTOS_21_event_group_task_sync

  • 过于简单,就不现场写了。

xEventGroupSync退出后,会清除事件吗?

使用xEventGroupSync函数时,在参数uxBisToWaitF-or中指定了等待哪些事件,如果函数成功返回,则会清除uxBitsToWaitFor表示的位。

/* Standard includes. */
#include <stdio.h>

/* Scheduler includes. */
#include "FreeRTOS.h"
#include "task.h"
#include "event_groups.h"

/* Library includes. */
#include "stm32f10x_it.h"

extern 	void UART_Init(unsigned long ulWantedBaud);

/* Demo app includes. */
static void prvSetupHardware( void );


/*-----------------------------------------------------------*/

static void vCookingTask( void *pvParameters );
static void vBuyingTask( void *pvParameters );
static void vTableTask( void *pvParameters );

/*-----------------------------------------------------------*/

/* 事件组句柄 */
EventGroupHandle_t xEventGroup;

/* bit0: 洗菜
 * bit1: 生火
 * bit2: 炒菜
 */
#define TABLE    (1<<0)
#define BUYING   (1<<1)
#define COOKING  (1<<2)
#define ALL      (TABLE | BUYING | COOKING)

int main( void )
{
	prvSetupHardware();
	
    /* 创建递归锁 */
    xEventGroup = xEventGroupCreate( );

	if( xEventGroup != NULL )
	{
		/* 创建3个任务: 洗菜/生火/炒菜
		 */
		xTaskCreate( vCookingTask, "task1", 1000, "A", 1, NULL );
		xTaskCreate( vBuyingTask,  "task2", 1000, "B", 2, NULL );
		xTaskCreate( vTableTask,   "task3", 1000, "C", 3, NULL );

		/* 启动调度器 */
		vTaskStartScheduler();
	}
	else
	{
		/* 无法创建事件组 */
	}

	/* 如果程序运行到了这里就表示出错了, 一般是内存不足 */
	return 0;
}

/*-----------------------------------------------------------*/

/*-----------------------------------------------------------*/
static void vCookingTask( void *pvParameters )
{
	const TickType_t xTicksToWait = pdMS_TO_TICKS( 100UL );		
	int i = 0;
	
	/* 无限循环 */
	for( ;; )
	{
		/* 做自己的事 */
		printf("%s is cooking %d time....\r\n", (char *)pvParameters, i);
		
		/* 表示我做好了, 还要等别人都做好 */
		xEventGroupSync(xEventGroup, COOKING, ALL, portMAX_DELAY);
	
		/* 别人也做好了, 开饭 */
		printf("%s is eating %d time....\r\n", (char *)pvParameters, i++);
		vTaskDelay(xTicksToWait);
	}
}

static void vBuyingTask( void *pvParameters )
{
	const TickType_t xTicksToWait = pdMS_TO_TICKS( 100UL );		
	int i = 0;
	
	/* 无限循环 */
	for( ;; )
	{
		/* 做自己的事 */
		printf("%s is buying %d time....\r\n", (char *)pvParameters, i);
		
		/* 表示我做好了, 还要等别人都做好 */
		xEventGroupSync(xEventGroup, BUYING, ALL, portMAX_DELAY);
	
		/* 别人也做好了, 开饭 */
		printf("%s is eating %d time....\r\n", (char *)pvParameters, i++);
		vTaskDelay(xTicksToWait);
	}
}

static void vTableTask( void *pvParameters )
{
	const TickType_t xTicksToWait = pdMS_TO_TICKS( 100UL );		
	int i = 0;
	
	/* 无限循环 */
	for( ;; )
	{
		/* 做自己的事 */
		printf("%s is do the table %d time....\r\n", (char *)pvParameters, i);
		
		/* 表示我做好了, 还要等别人都做好 */
		xEventGroupSync(xEventGroup, TABLE, ALL, portMAX_DELAY);
	
		/* 别人也做好了, 开饭 */
		printf("%s is eating %d time....\r\n", (char *)pvParameters, i++);
		vTaskDelay(xTicksToWait);
	}
}

程序运行结果

【参考正点原子教程与韦东山文档撰写】

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值