FreeRTOS学习笔记5

1、事件组的介绍

事件组可以简单地认为就是一个整数:

        每一位表示一个事件 每一位事件的含义由程序员决定,

        比如:Bit0表示用来串口是否就绪,Bit1表示按键是否被按下 这些位,值为1表示事件发生了,值为0表示事件没发生 一个或多个任务、ISR都可以去写这些位;

        一个或多个任务、ISR都可以去读这些位 可以等待某一位、某些位中的任意一个,也可以等待多位。

        需要注意的是,事件组并不传递数据,数据的传递需要另想办法,比如使用队列进行数据的传递,事件的等待,不能只等待一位或者几位,需要在等待函数里面进行配置。但是每次等待所有事件还是一个事件是指定的,不能只的等待某个事件。使用事件的等待函数的时候会体会到这一点。

        关于事件的一些使用函数,这里就不在一一说明,看东山老师的FreeRTOS完全手册就可以了。

2、事件组的使用

        首先介绍几个事件组要使用的函数:(这里只介绍动态创建的,静态的使用的比较少,就不再介绍了)

        EventGroupHandle_t xEventGroupCreate( void );是事件创建函数。只需要利用返回值就可以了。

        void vEventGroupDelete( EventGroupHandle_t xEventGroup );是事件组的删除函数,传入事件组的句柄就可以了。

        xEventGroupSetBits();设置事件函数:

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

        EventBits_t xEventGroupWaitBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToWaitFor, const BaseType_t xClearOnExit, const BaseType_t xWaitForAllBits, TickType_t xTicksToWait );等待事件函数。

 

         下面是关于事件的一个使用的实例,事件是不能传递数据的,这里使用的队列进行数据的传递,在使用队列时发现,两个task同时使用一个队列的时候,数据的传输会有问题,然后就改用两个队列针对两个task进行队列数据的传输,数据的传输正常。代码的逻辑就是两个task在设置事件之前,将数据传输到对应的队列中,然后设置事件,第三个task3首先开始等待事件,当事件等待完成之后,就开始接受队列中的数据,然后串口打印出来。

TaskHandle_t vCheckTask1_hander;
TaskHandle_t vCheckTask2_hander;
TaskHandle_t vCheckTask3_hander;

QueueHandle_t QueueUsart1;
QueueHandle_t QueueUsart2;
QueueHandle_t QueueUsart3;
QueueSetHandle_t  xQueueUsartHandle;
QueueHandle_t SemaphoreHandle;
QueueHandle_t xSemaMutexHandle;
QueueHandle_t xSemaRecursiveHandle;
EventGroupHandle_t EventGroupHandle;


static void vCheckTask1( void *pvParameters )
{
	uint16_t data=10;
  for( ;; )
	{
		printf("task1\r\n");
		xQueueSend(QueueUsart1,&data,portMAX_DELAY);
		xEventGroupSetBits(EventGroupHandle,(0x00|(1<<0)));
	}
}


static void vCheckTask2( void *pvParameters )
{
	uint16_t data=20;
  for( ;; )
	{
		printf("task2\r\n");
		xQueueSend(QueueUsart2,&data,portMAX_DELAY);
		xEventGroupSetBits(EventGroupHandle,(0x00|(1<<1)));
	}
}


static void vCheckTask3( void *pvParameters )
{
	uint16_t data1,data2;
	while(1)
	{
		xEventGroupWaitBits(EventGroupHandle,(0x00|(1<<1)|(1<<0)),pdTRUE,pdTRUE,portMAX_DELAY);
		xQueueReceive(QueueUsart1,&data1,0);
		xQueueReceive(QueueUsart2,&data2,0);
		printf("data1:%d,data2:%d\r\n",data1,data2);
	}
}


int main( void )
{
	prvSetupHardware();
	EventGroupHandle=xEventGroupCreate();
	QueueUsart1=xQueueCreate(2,sizeof(int));
	QueueUsart2=xQueueCreate(2,sizeof(int));
	xSemaRecursiveHandle=xSemaphoreCreateRecursiveMutex();
  xTaskCreate( vCheckTask1, "Check1", 100, NULL,1, &vCheckTask1_hander);
	xTaskCreate( vCheckTask2, "Check2", 100, NULL,1, &vCheckTask2_hander);
	xTaskCreate( vCheckTask3, "Check3", 100, NULL,2, &vCheckTask3_hander);
	vTaskStartScheduler();
	return 0;
}

下面是debug调试的结果:

         下面代码是,数据队列传输等待事件为0,也就不等待的代码,需要添加一些演示函数,不然task1和task2交替运行会产生问题。

static void vCheckTask1( void *pvParameters )
{
	uint16_t data=10;
  for( ;; )
	{
		printf("task1\r\n");
		xQueueSend(QueueUsart1,&data,0);
		xEventGroupSetBits(EventGroupHandle,(0x00|(1<<0)));
		vTaskDelay(10);
	}
}


static void vCheckTask2( void *pvParameters )
{
	uint16_t data=20;
  for( ;; )
	{
		printf("task2\r\n");
		xQueueSend(QueueUsart2,&data,0);
		xEventGroupSetBits(EventGroupHandle,(0x00|(1<<1)));
		vTaskDelay(10);
	}
}


static void vCheckTask3( void *pvParameters )
{
	uint16_t data1,data2;
	while(1)
	{
		xEventGroupWaitBits(EventGroupHandle,(0x00|(1<<1)|(1<<0)),pdTRUE,pdTRUE,portMAX_DELAY);
		xQueueReceive(QueueUsart1,&data1,0);
		xQueueReceive(QueueUsart2,&data2,0);
		printf("data1:%d,data2:%d\r\n",data1,data2);
	}
}


int main( void )
{
	prvSetupHardware();
	EventGroupHandle=xEventGroupCreate();
	QueueUsart1=xQueueCreate(2,sizeof(int));
	QueueUsart2=xQueueCreate(2,sizeof(int));
	xSemaRecursiveHandle=xSemaphoreCreateRecursiveMutex();
  xTaskCreate( vCheckTask1, "Check1", 100, NULL,1, &vCheckTask1_hander);
	xTaskCreate( vCheckTask2, "Check2", 100, NULL,1, &vCheckTask2_hander);
	xTaskCreate( vCheckTask3, "Check3", 100, NULL,2, &vCheckTask3_hander);
	vTaskStartScheduler();
	return 0;
}

3、时间组之间的配合使用

        利用时间组之间的配合实现同步点的使用,在配合使用的时候,需要注意事件等待函数是否清除标志位的情况。下面是同步点的一种使用 ,在时间等待函数的使用下,实现了三个任务函数之间的等待。

        

static void vCheckTask1( void *pvParameters )
{
	uint16_t data=10;
  for( ;; )
	{
		printf("task1 complete\r\n");
		xQueueSend(QueueUsart1,&data,0);
		xEventGroupSetBits(EventGroupHandle,(0x00|(1<<0)));
		xEventGroupWaitBits(EventGroupHandle,(0x00|(1<<2)),pdTRUE,pdTRUE,portMAX_DELAY);
		vTaskDelay(10);
	}
}


static void vCheckTask2( void *pvParameters )
{
	uint16_t data=20;
  for( ;; )
	{
		xEventGroupWaitBits(EventGroupHandle,(0x00|(1<<0)),pdTRUE,pdFALSE,portMAX_DELAY);
		printf("task2 complete\r\n");
		xQueueSend(QueueUsart2,&data,0);
		xEventGroupSetBits(EventGroupHandle,(0x00|(1<<1)));
		vTaskDelay(10);
	}
}


static void vCheckTask3( void *pvParameters )
{
	uint16_t data1,data2;
	while(1)
	{
		xEventGroupWaitBits(EventGroupHandle,(0x00|(1<<1)|(1<<0)),pdTRUE,pdFALSE,portMAX_DELAY);
		xQueueReceive(QueueUsart1,&data1,0);
		xQueueReceive(QueueUsart2,&data2,0);
		printf("data1:%d,data2:%d\r\n",data1,data2);
		xEventGroupSetBits(EventGroupHandle,(0x00|(1<<2)));
	}
}


int main( void )
{
	prvSetupHardware();
	EventGroupHandle=xEventGroupCreate();
	QueueUsart1=xQueueCreate(2,sizeof(int));
	QueueUsart2=xQueueCreate(2,sizeof(int));
	xSemaRecursiveHandle=xSemaphoreCreateRecursiveMutex();
  xTaskCreate( vCheckTask1, "Check1", 100, NULL,1, &vCheckTask1_hander);
	xTaskCreate( vCheckTask2, "Check2", 100, NULL,2, &vCheckTask2_hander);
	xTaskCreate( vCheckTask3, "Check3", 100, NULL,3, &vCheckTask3_hander);
	vTaskStartScheduler();
	return 0;
}

        上面代码的逻辑:首先创建了三个任务函数,首先是任务函数task3首先运行(任务的优先级最高),进入task3之后,首先等待另外两个任务函数中的事件完成,进入休眠等待,然后进入优先级次之地task2,在task2中等待task1事件地完成,进入休眠状态,然后程序执行到task1,在task1中发送数据,设置事件,并且清除之前地事件标志位。然后task2被唤醒开始执行,并且设置自己地事件标志位,之后task3任务函数等待到task1和task2地两个标志位,开始执行task3任务函数。

        需要注意地是:执行地过程中任务函数task2和task3中地等待事件地函数,不能清除标志位。

        下面是debug的仿真调试的结果:

 EventBits_t xEventGroupSync( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet, const EventBits_t uxBitsToWaitFor, TickType_t xTicksToWait );

        上面这个函数是同步点使用的函数,特点就是设置事件的位和等待事件的位可以同时使用这个函数进行设置。下图是该函数的入口参数:

 下面是这个函数的一个应用的实例:

TaskHandle_t vCheckTask1_hander;
TaskHandle_t vCheckTask2_hander;
TaskHandle_t vCheckTask3_hander;

QueueHandle_t QueueUsart1;
QueueHandle_t QueueUsart2;
QueueHandle_t QueueUsart3;
QueueSetHandle_t  xQueueUsartHandle;
QueueHandle_t SemaphoreHandle;
QueueHandle_t xSemaMutexHandle;
QueueHandle_t xSemaRecursiveHandle;
EventGroupHandle_t EventGroupHandle;


static void vCheckTask1( void *pvParameters )
{
	uint16_t data=1;
  for( ;; )
	{
		data++;
		printf("task1 complete\r\n");
		xQueueSend(QueueUsart1,&data,0);
		xEventGroupSync(EventGroupHandle,(0x00|1<<0),(0x00|1<<2),portMAX_DELAY);
		vTaskDelay(10);
	}
}


static void vCheckTask2( void *pvParameters )
{
	uint16_t data=2;
  for( ;; )
	{
		data++;
		printf("task2 complete\r\n");
		xQueueSend(QueueUsart2,&data,0);
		xEventGroupSync(EventGroupHandle,(0x00|1<<1),(0x00|1<<0),portMAX_DELAY);
		vTaskDelay(10);
	}
}


static void vCheckTask3( void *pvParameters )
{
	uint16_t data1,data2;
	while(1)
	{
		xEventGroupSync(EventGroupHandle,(0x00|1<<2),(0x00|1<<0|1<<1),portMAX_DELAY);
		xQueueReceive(QueueUsart1,&data1,0);
		xQueueReceive(QueueUsart2,&data2,0);
		printf("data1:%d,data2:%d\r\n",data1,data2);
	}
}


int main( void )
{
	prvSetupHardware();
	EventGroupHandle=xEventGroupCreate();
	QueueUsart1=xQueueCreate(2,sizeof(int));
	QueueUsart2=xQueueCreate(2,sizeof(int));
	xSemaRecursiveHandle=xSemaphoreCreateRecursiveMutex();
  xTaskCreate( vCheckTask1, "Check1", 100, NULL,1, &vCheckTask1_hander);
	xTaskCreate( vCheckTask2, "Check2", 100, NULL,2, &vCheckTask2_hander);
	xTaskCreate( vCheckTask3, "Check3", 100, NULL,3, &vCheckTask3_hander);
	vTaskStartScheduler();
	return 0;
}

下面是上面程序的debug仿真结果:

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
实验报告三:(1)设计 Task0、Task1 两个任务:任务 Task0 不断地挂起自己,再被任务 Task1 解挂,两个任务不断地切换执行,并输出两个任务在各个时刻的状态。Task0 的主要功能是显示 Task1 的状态,Task1 的主要功能是显示 Task0 的状态。整个应用的运行流程如图 1 所示,其描述如下:在 main 函数创建起始任务 TaskStart,其优先级为 0。TaskStart 任务主要完成创建 2 个应用任务 Task0、Task1,并挂起自己(不再被其它任务唤醒)。之后整个系统的运行流程如下:t1 时刻,Task0 开始执行,它运行到 t2 时刻挂起自己;t2 时刻,系统调度处于就绪状态的优先级最高任务 Task1 执行,它在 t3 时刻唤醒 Task0,后者由于优先级较高而抢占 CPU;Task0 执行到 t4 时刻又挂起自己,内核调度 Task1执行;Task1 运行至 t5 时刻再度唤醒 Task0; 注意:图中的栅格并不代表严格的时间刻度,而仅仅表现各任务启动和执行的相对先后关系。 (2)设计 MyTask、YouTask、KeyTask 三个任务:MyTask 任务输出 M;YouTask 任务输出 Y,并输出 MyTask 任务的状态;KeyTask 任务从键盘接收字符 Y 或 N,当接收 Y 时挂起 MyTask 任务,当接收 N 时恢复 MyTask 任务。 (3)设计 KeyTask 任务,当从键盘输入+号时动态创建任务,最多可以创建 10 个任务,这 10个任务都执行一个函数 MyTask,要求优先级是(PRIO_BASE+0,1,2,3,4,5,6,7,8,9),还要向不同的任务传递不同的参数(0,1,2,3,4,5,6,7,8,9)给 MyTask 函数,优先级为(PRIO_BASE+0,1,2,3,4,5,6,7,8,9)的任务分别输出数字(0,1,2,3,4,5,6,7,8,9)。当从键盘输入-号时动态的删除刚创建的一个任务,最多可删除 10 个任务。提示:多个任务可以执行一个函数。运行结果如下图所示。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值