FreeRTOS学习笔记--------事件组

事件组概念

可以使用事件组传递资源状态。

事件组的官方源码如下:

typedef struct EventGroupDef_t
{
    EventBits_t uxEventBits;
    List_t xTasksWaitingForBits; /*< List of tasks waiting for a bit to be set. */

    #if ( configUSE_TRACE_FACILITY == 1 )
        UBaseType_t uxEventGroupNumber;
    #endif

    #if ( ( configSUPPORT_STATIC_ALLOCATION == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) )
        uint8_t ucStaticallyAllocated; /*< Set to pdTRUE if the event group is statically allocated to ensure no attempt is made to free the memory. */
    #endif
} EventGroup_t;


typedef TickType_t               EventBits_t;

#if ( configUSE_16_BIT_TICKS == 1 )
    typedef uint16_t     TickType_t;
    #define portMAX_DELAY              ( TickType_t ) 0xffff
#else
    typedef uint32_t     TickType_t;
    #define portMAX_DELAY              ( TickType_t ) 0xffffffffUL

由官方源码可知,事件组实际上是一个整数,具有以下特点:

  • 每一位表示一个事件
  • 值1表示事件发生了,值0表示事件还没发生
  • 可以等待某一位某些位中的任意一个、或者所有位(不能是某些位中的某几个
  • 高八位不能使用

事件组与队列、信号量不同之处如下表所示:

事件组队列信号量
唤醒谁事件发生后,唤醒所有符合条件的任务只唤醒一个任务只唤醒一个任务
是否清除事件取决于配置数据被读取后就删除计数值减小

事件组操作函数

使用事件组之前,需要向工程中添加event_groups.c文件,完整的工程如下图所示:

添加event_groups.c文件后,会出现以下报错问题,解决方法如图所示:

事件组的操作函数主要如下所示:

/* 创建一个事件组,返回它的句柄。
* 此函数内部会分配事件组结构体
* 返回值: 返回句柄,非NULL表示成功
*/
EventGroupHandle_t xEventGroupCreate( void );

/* 创建一个事件组,返回它的句柄。
* 此函数无需动态分配内存,所以需要先有一个StaticEventGroup_t结构体,并传入它的指针
* 返回值: 返回句柄,非NULL表示成功
*/
EventGroupHandle_t xEventGroupCreateStatic( StaticEventGroup_t *pxEventGroupBuffer );


/*
* xEventGroup: 事件组句柄,你要删除哪个事件组
*/
void vEventGroupDelete( EventGroupHandle_t xEventGroup );


/* 设置事件组中的位
* xEventGroup: 哪个事件组
* uxBitsToSet: 设置哪些位?
* 如果uxBitsToSet的bitX, bitY为1, 那么事件组中的bitX, bitY被设置为1
* 可以用来设置多个位,比如 0x15 就表示设置bit4, bit2, bit0
* 返回值: 返回原来的事件值(没什么意义, 因为很可能已经被其他任务修改了)
*/
EventBits_t xEventGroupSetBits( EventGroupHandle_t xEventGroup,
                                const EventBits_t uxBitsToSet );



/* 设置事件组中的位
* xEventGroup: 哪个事件组
* uxBitsToWaitFor: 等待哪些位
* xClearOnExit: 事件成功发生后,退出函数是是否清除位
* xWaitForAllBits: 等待某一位还是所有位
*/
EventBits_t xEventGroupWaitBits( EventGroupHandle_t xEventGroup,
                                 const EventBits_t uxBitsToWaitFor,
                                 const BaseType_t xClearOnExit,
                                 const BaseType_t xWaitForAllBits,
                                 TickType_t xTicksToWait );

你可以使用xEventGroupWaitBits() 等待期望的事件,它发生之后再使用xEventGroupClearBits()
来清除。但是这两个函数之间,有可能被其他任务或中断抢占,它们可能会修改事件组。可以使用设置xClearOnExit 为pdTRUE,使得对事件组的测试、清零都在xEventGroupWaitBits()
函数内部完成,这是一个原子操作。 

当一个事情需要多个任务协同时,可以使用xEventGroupSync() 函数。

事件组使用

常规使用

事件组的常规使用的步骤为:

  • 创建事件组
  • 设置事件xEventGroupSetBits
  • 读取事件xEventGroupWaitBits

具体示例代码如下:

/*
 * Configure the clocks, GPIO and other peripherals as required by the demo.
 */
static void prvSetupHardware( void );

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

void vmainTask1Function( void * param);
void vmainTask2Function( void * param);
void vmainTask3Function( void * param);
void vmainTask4Function( void * param);

typedef struct taskParamDef_t
{
	  QueueHandle_t queueHandle;
	  EventGroupHandle_t eventGroupHandle;
}taskParam_t;

/*-----------------------------------------------------------*/
int main( void )
{
	static taskParam_t taskParam;
	
	prvSetupHardware();
	
	printf("Hello world!\r\n");
	
	/*1.创建队列和事件组*/
	taskParam.queueHandle = xQueueCreate(2, sizeof(int));
	taskParam.eventGroupHandle = xEventGroupCreate();
	
//	printf("%x\r\n", taskParam.queueHandle);
	xTaskCreate(vmainTask1Function, "task1", 100, (void *)&taskParam, 1, NULL);            // 动态创建任务
	xTaskCreate(vmainTask2Function, "task2", 100, (void *)&taskParam, 1, NULL);            // 动态创建任务
  xTaskCreate(vmainTask3Function, "task3", 100, (void *)&taskParam, 1, NULL);            // 动态创建任务

	/* Start the scheduler. */
	vTaskStartScheduler();

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

static void prvSetupHardware( void )
{
	/* Start with the clocks in their expected state. */
	RCC_DeInit();

	/* Enable HSE (high speed external clock). */
	RCC_HSEConfig( RCC_HSE_ON );

	/* Wait till HSE is ready. */
	while( RCC_GetFlagStatus( RCC_FLAG_HSERDY ) == RESET )
	{
	}

	/* 2 wait states required on the flash. */
	*( ( unsigned long * ) 0x40022000 ) = 0x02;

	/* HCLK = SYSCLK */
	RCC_HCLKConfig( RCC_SYSCLK_Div1 );

	/* PCLK2 = HCLK */
	RCC_PCLK2Config( RCC_HCLK_Div1 );

	/* PCLK1 = HCLK/2 */
	RCC_PCLK1Config( RCC_HCLK_Div2 );

	/* PLLCLK = 8MHz * 9 = 72 MHz. */
	RCC_PLLConfig( RCC_PLLSource_HSE_Div1, RCC_PLLMul_9 );

	/* Enable PLL. */
	RCC_PLLCmd( ENABLE );

	/* Wait till PLL is ready. */
	while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET)
	{
	}

	/* Select PLL as system clock source. */
	RCC_SYSCLKConfig( RCC_SYSCLKSource_PLLCLK );

	/* Wait till PLL is used as system clock source. */
	while( RCC_GetSYSCLKSource() != 0x08 )
	{
	}

	/* Enable GPIOA, GPIOB, GPIOC, GPIOD, GPIOE and AFIO clocks */
	RCC_APB2PeriphClockCmd(	RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB |RCC_APB2Periph_GPIOC
							| RCC_APB2Periph_GPIOD | RCC_APB2Periph_GPIOE | RCC_APB2Periph_AFIO, ENABLE );

	/* SPI2 Periph clock enable */
	RCC_APB1PeriphClockCmd( RCC_APB1Periph_SPI2, ENABLE );


	/* Set the Vector Table base address at 0x08000000 */
	NVIC_SetVectorTable( NVIC_VectTab_FLASH, 0x0 );

	NVIC_PriorityGroupConfig( NVIC_PriorityGroup_4 );

	/* Configure HCLK clock as SysTick clock source. */
	SysTick_CLKSourceConfig( SysTick_CLKSource_HCLK );
	
	serialPortInit();
}

/*-----------------------------------------------------------*/
void vmainTask1Function( void * param)
{
	int i = 0;
	int sum = 0;
	taskParam_t *taskParam = (taskParam_t *)param;
//	printf("%x\r\n", taskParam->queueHandle);
	while(1) {
		for(i = 0; i < 400000; i ++) {
			sum ++;
		}
		xQueueSend(taskParam->queueHandle, &sum, portMAX_DELAY);
		xEventGroupSetBits(taskParam->eventGroupHandle, 1 << 0);
		sum = 0;
	}
}

void vmainTask2Function( void * param)
{
	int i = 0;
	int sum = 0;
	taskParam_t *taskParam = (taskParam_t *)param;
	while(1) {
		for(i = 0; i < 400000; i ++) {
			sum --;
		}
		xQueueSend(taskParam->queueHandle, &sum, portMAX_DELAY);
		xEventGroupSetBits(taskParam->eventGroupHandle, 1 << 1);
		sum = 0;
	}
}

void vmainTask3Function( void * param)
{
	int readVal1 = 0, readVal2 = 0;
	taskParam_t *taskParam = (taskParam_t *)param;
	while(1) {
		/*1.等待所有事件发生,并在退出时清除事件*/
		xEventGroupWaitBits(taskParam->eventGroupHandle, (1<<0)|(1<<1), pdTRUE, pdTRUE, portMAX_DELAY);
		
		/*2.从队列中获取数据*/
		xQueueReceive(taskParam->queueHandle, &readVal1, 0);
		xQueueReceive(taskParam->queueHandle, &readVal2, 0);
		printf("get data = %d  %d\r\n", readVal1, readVal2);
	}
}

实验现象如下所示:

同步点

当一个事情需要多个任务协同时,可以使用xEventGroupSync() 函数。 

示例程序如下:

/*
 * Configure the clocks, GPIO and other peripherals as required by the demo.
 */
static void prvSetupHardware( void );

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

void vmainTask1Function( void * param);
void vmainTask2Function( void * param);
void vmainTask3Function( void * param);
void vmainTask4Function( void * param);

/*-----------------------------------------------------------*/
int main( void )
{
	EventGroupHandle_t eventGroupHandle;
	
	prvSetupHardware();
	
	printf("Hello world!\r\n");
	
	/*1.事件组*/
	eventGroupHandle = xEventGroupCreate();
	
//	printf("%x\r\n", taskParam.queueHandle);
	xTaskCreate(vmainTask1Function, "task1", 100, (void *)eventGroupHandle, 1, NULL);            // 动态创建任务
	xTaskCreate(vmainTask2Function, "task2", 100, (void *)eventGroupHandle, 2, NULL);            // 动态创建任务
  xTaskCreate(vmainTask3Function, "task3", 100, (void *)eventGroupHandle, 3, NULL);            // 动态创建任务

	/* Start the scheduler. */
	vTaskStartScheduler();

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

static void prvSetupHardware( void )
{
	/* Start with the clocks in their expected state. */
	RCC_DeInit();

	/* Enable HSE (high speed external clock). */
	RCC_HSEConfig( RCC_HSE_ON );

	/* Wait till HSE is ready. */
	while( RCC_GetFlagStatus( RCC_FLAG_HSERDY ) == RESET )
	{
	}

	/* 2 wait states required on the flash. */
	*( ( unsigned long * ) 0x40022000 ) = 0x02;

	/* HCLK = SYSCLK */
	RCC_HCLKConfig( RCC_SYSCLK_Div1 );

	/* PCLK2 = HCLK */
	RCC_PCLK2Config( RCC_HCLK_Div1 );

	/* PCLK1 = HCLK/2 */
	RCC_PCLK1Config( RCC_HCLK_Div2 );

	/* PLLCLK = 8MHz * 9 = 72 MHz. */
	RCC_PLLConfig( RCC_PLLSource_HSE_Div1, RCC_PLLMul_9 );

	/* Enable PLL. */
	RCC_PLLCmd( ENABLE );

	/* Wait till PLL is ready. */
	while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET)
	{
	}

	/* Select PLL as system clock source. */
	RCC_SYSCLKConfig( RCC_SYSCLKSource_PLLCLK );

	/* Wait till PLL is used as system clock source. */
	while( RCC_GetSYSCLKSource() != 0x08 )
	{
	}

	/* Enable GPIOA, GPIOB, GPIOC, GPIOD, GPIOE and AFIO clocks */
	RCC_APB2PeriphClockCmd(	RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB |RCC_APB2Periph_GPIOC
							| RCC_APB2Periph_GPIOD | RCC_APB2Periph_GPIOE | RCC_APB2Periph_AFIO, ENABLE );

	/* SPI2 Periph clock enable */
	RCC_APB1PeriphClockCmd( RCC_APB1Periph_SPI2, ENABLE );


	/* Set the Vector Table base address at 0x08000000 */
	NVIC_SetVectorTable( NVIC_VectTab_FLASH, 0x0 );

	NVIC_PriorityGroupConfig( NVIC_PriorityGroup_4 );

	/* Configure HCLK clock as SysTick clock source. */
	SysTick_CLKSourceConfig( SysTick_CLKSource_HCLK );
	
	serialPortInit();
}

/*-----------------------------------------------------------*/
void vmainTask1Function( void * param)
{
	while(1) {
		printf("task1\r\n");
		vTaskDelay(pdMS_TO_TICKS(100UL));
    xEventGroupSync((EventGroupHandle_t)param, 1 << 0, (1 << 0)|(1 << 1)|(1 << 2), portMAX_DELAY);
		printf("all task done, task1\r\n");
	}
}

void vmainTask2Function( void * param)
{
	while(1) {
		printf("task2\r\n");
		vTaskDelay(pdMS_TO_TICKS(100UL));
    xEventGroupSync((EventGroupHandle_t)param, 1 << 1, (1 << 0)|(1 << 1)|(1 << 2), portMAX_DELAY);
		printf("all task done, task2\r\n");
	}
}

void vmainTask3Function( void * param)
{
	while(1) {
		printf("task3\r\n");
		vTaskDelay(pdMS_TO_TICKS(100UL));
    xEventGroupSync((EventGroupHandle_t)param, 1 << 2, (1 << 0)|(1 << 1)|(1 << 2), portMAX_DELAY);
		printf("all task done, task3\r\n");
	}
}

事件组为什么不关中断

使用事件组时只会关闭调度器,并不会关闭中断。因为调用xEventGroupSetBitsFromISR函数时并并会设置uxEventBits的值,只会触发一个守护任务进行设置。因为设置事件的时候,会唤醒所有符合条件的任务,消耗的时间无法确定。

  • 8
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值