FreeRTOS学习笔记6(任务通知)

1、任务通知函数及其知识点的介绍

        下面是任务通知得一些特点:

        1:我们使用队列、信号量、事件组等等方法时,并不知道对方是谁。使用任务通知时,可以明确指定:通 知哪个任务。

        2:使用任务通知时,任务结构体TCB中就包含了内部对象,可以直接接收别人发过来的"通知",使用队列、信号量、事件组时,我们都要事先创建对应的结构体,双方通过中间的结构体通信。

        下面是任务通知的优势和局限:

任务通知的优势:

        效率更高:使用任务通知来发送事件、数据给某个任务时,效率更高。比队列、信号量、事件组都 有大的优势。

更节省内存:使用其他方法时都要先创建对应的结构体,使用任务通知时无需额外创建结构体。 任务通知的限制:

        不能发送数据给ISR: ISR并没有任务结构体,所以无法使用任务通知的功能给ISR发送数据。但是ISR可以使用任务通知 的功能,发数据给任务。

        数据只能给该任务独享 使用队列、信号量、事件组时,数据保存在这些结构体中,其他任务、ISR都可以访问这些数据。 使用任务通知时,数据存放入目标任务中,只有它可以访问这些数据。 在日常工作中,这个限制影响不大。因为很多场合是从多个数据源把数据发给某个任务,而不是把 一个数据源的数据发给多个任务。

        无法缓冲数据 使用队列时,假设队列深度为N,那么它可以保持N个数据。 使用任务通知时,任务结构体中只有一个任务通知值,只能保持一个数据。 无法广播给多个任务 使用事件组可以同时给多个任务发送事件。

        使用任务通知,只能发个一个任务。 如果发送受阻,发送方无法进入阻塞状态等待 假设队列已经满了,使用 xQueueSendToBack() 给队列发送数据时,任务可以进入阻塞状态等待 发送完成。

        使用任务通知时,即使对方无法接收数据,发送方也无法阻塞等待,只能即刻返回错误。

2、任务通知函数的介绍

        创建的任务函数都具有TCB结构体,这个结构体中含有两个链表成员,介绍如下图所示:(使用任务通知的时候,就会使用到这两个成员)。

        任务通知有两套函数,如下图所示的两套函数:(简单版是从专业版简化出来的)

        函数的入口参数的使用,参考手册就可以,可以实现各种轻量级的使用。任务通知的通知值可以有很多种:计数值、 位(类似事件组)、 任意数值。

3、任务通知实现轻量级的信号量

        这样实现有两种有点:不要再在主函数中创建任何通道,可以将数据对应的传输到指定的任务函数中,只需要传入对应的任务函数的句柄就可以了。

        下面代码是使用简化版的任务通知的示例:

        xTaskNotifyGive()函数每次调用,使得通知值加一 并使得通知状态变为"pending",也就是 taskNOTIFICATION_RECEIVED ,表示有数据了、待处理。

        ulTaskNotifyTake()任务函数的返回值就是”通知值“,函数具体返回值状况要看函数入口参数的配置,配置形式如下图所示:(如果函数第一个入口参数设置为:pdTRUE,那么函数只task一次通知值,就会删除通知值)

static void vCheckTask1( void *pvParameters )
{
	uint16_t data=1;
  for( ;; )
	{
		data++;
		printf("task1 complete\r\n");
		xQueueSend(QueueUsart1,&data,0);
		for(int i=0;i<10;i++)
		 xTaskNotifyGive(vCheckTask2_hander);
		vTaskDelay(10);
	}
}


static void vCheckTask2( void *pvParameters )
{
	uint16_t data=2;
	uint32_t data2;
  for( ;; )
	{
		data2=ulTaskNotifyTake(pdFALSE,portMAX_DELAY);
		data++;
		printf("data2:%d\r\n",data2);
		xQueueSend(QueueUsart2,&data,0);
		for(int i=0;i<10;i++)
		{
			xTaskNotifyGive(vCheckTask3_hander);
		}
		vTaskDelay(10);
	}
}


static void vCheckTask3( void *pvParameters )
{
	uint16_t data1,data2;
	uint32_t data3;
	while(1)
	{
		data3=ulTaskNotifyTake(pdFALSE,portMAX_DELAY);
		printf("data3:%d\r\n",data3);
		xQueueReceive(QueueUsart1,&data1,0);
		xQueueReceive(QueueUsart2,&data2,0);
		printf("data1:%d,data2:%d\r\n",data1,data2);
		vTaskDelay(10);
	}
}


int main( void )
{
	prvSetupHardware();
	QueueUsart1=xQueueCreate(2,sizeof(int));
	QueueUsart2=xQueueCreate(2,sizeof(int));
  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调试的结果如下图所示:

4、任务通知实现轻量级队列

         任务通知实现的轻量级队列有以下的优点和缺点。

        优点:代码简单,无需创建信号量,消耗内存更少,效率更高。

        缺点:传输的数据只能是一个32位的数据,接收任务只能从自己的通知值中得到 数据,来源明确。

        这次使用的是全功能的任务通知函数,xTaskNotify()函数中有的入口参数中有是否覆盖数据的选项,这个入口参数在循环调用xTaskNotify()函数传入数据的时候,会使用到这个是否覆盖的入口参数。下面是测试代码:

static void vCheckTask1( void *pvParameters )
{
	uint16_t data=1;
  for( ;; )
	{
		data++;
		printf("task1 complete\r\n");
		xQueueSend(QueueUsart1,&data,0);
		xTaskNotify(vCheckTask3_hander,data,eSetValueWithOverwrite);
		vTaskDelay(10);
	}
}


static void vCheckTask2( void *pvParameters )
{
	uint16_t data=2;
	uint32_t data2;
  for( ;; )
	{
		data++;
		printf("task2 complete\r\n");
		xQueueSend(QueueUsart2,&data,0);
		xTaskNotify(vCheckTask3_hander,data,eSetValueWithOverwrite);
		vTaskDelay(10);
	}
}


static void vCheckTask3( void *pvParameters )
{
	uint16_t data1,data2;
	uint32_t data3[2];
	while(1)
	{
		xTaskNotifyWait(0,0,&data3[0],portMAX_DELAY);
		xTaskNotifyWait(0,0,&data3[1],portMAX_DELAY);
		xQueueReceive(QueueUsart1,&data1,0);
		xQueueReceive(QueueUsart2,&data2,0);
		printf("data3:%d,data4:%d\r\n",data3[0],data3[1]);
		printf("data1:%d,data2:%d\r\n",data1,data2);
		vTaskDelay(10);
	}
}


int main( void )
{
	prvSetupHardware();
	QueueUsart1=xQueueCreate(2,sizeof(int));
	QueueUsart2=xQueueCreate(2,sizeof(int));
  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的测试结果:

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值