FreeRTOS任务通知 基于STM32

一、任务通知简介

FreeRTOS 从 V8.2.0 版本开始提供任务通知这个功能,每个任务都有一个 32 位的通知 值,在大多数情况下,任务通知可以替代二值信号量、计数信号量、事件组,也可以替代 长度为 1 的队列(可以保存一个 32位整数或指针值)。

相对于以前使用 FreeRTOS 内核通信的资源,必须创建队列、二进制信号量、计数信 号量或事件组的情况,使用任务通知显然更灵活。

按照 FreeRTOS 官方的说法,使用任务 通知比通过信号量等 ICP 通信方式解除阻塞的任务要快 45%,并且更加省 RAM 内存空间 (使用 GCC 编译器,-o2 优化级别),任务通知的使用无需创建队列。想要使用任务通知, 必须将 FreeRTOSConfig.h 中的宏定义
configUSE_TASK_NOTIFICATIONS 设置为 1
,其实 FreeRTOS 默认是为 1 的,所以任务通知是默认使能的。

FreeRTOS 提供以下几种方式发送通知给任务 :

发送通知给任务, 如果有通知未读,不覆盖通知值。

发送通知给任务,直接覆盖通知值。

发送通知给任务,设置通知值的一个或者多个位,可以当做事件组来使用。

发送通知给任务,递增通知值,可以当做计数信号量使用。 通过对以上任务通知方式的合理使用,可以在一定场合下替代 FreeRTOS 的信号量, 队列、事件组等。

当然,凡是都有利弊,不然的话 FreeRTOS 还要内核的 IPC 通信机制干嘛,消息通知 虽然处理更快,RAM 开销更小,但也有以下限制 :

只能有一个任务接收通知消息,因为必须指定接收通知的任务。。

只有等待通知的任务可以被阻塞,发送通知的任务,在任何情况下都不会因为发 送失败而进入阻塞态。

二、任务通知的运作机制

顾名思义,任务通知是属于任务中附带的资源,所以在任务被创建的时候,任务通知 也被初始化的,而在分析队列和信号量的章节中,我们知道在使用队列、信号量前,必须 先创建队列和信号量,目的是为了创建队列数据结构。

比如使用 xQueueCreate()函数创建 队列,用 xSemaphoreCreateBinary()函数创建二值信号量等等。再来看任务通知,由于任务 通知的数据结构包含在任务控制块中,只要任务存在,任务通知数据结构就已经创建完毕, 可以直接使用,所以使用的时候很是方便。

任务通知可以在任务中向指定任务发送通知,也可以在中断中向指定任务发送通知, FreeRTOS 的每个任务都有一个 32 位的通知值,任务控制块中的成员变量 ulNotifiedValue 就是这个通知值。

只有在任务中可以等待通知,而不允许在中断中等待通知。如果任务在等待的通知暂时无效,任务会根据用户指定的阻塞超时时间进入阻塞状态,我们可以将等 待通知的任务看作是消费者;其它任务和中断可以向等待通知的任务发送通知,发送通知 的任务和中断服务函数可以看作是生产者,当其他任务或者中断向这个任务发送任务通知, 任务获得通知以后,该任务就会从阻塞态中解除,这与 FreeRTOS 中内核的其他通信机制 一致。

三、任务通知的函数接口讲解

1. xTaskGenericNotify()

我们先看一下发送通知 API 函数。这类函数比较多,有 6 个。但仔细分析会发现它们 只能完成 3 种操作,每种操作有两个 API 函数,分别为带中断保护版本和不带中断保护版 本。FreeRTOS 将 API 细分为带中断保护版本和不带中断保护版本是为了节省中断服务程 序处理时间,提升性能。

通过前面通信机制的学习,相信大家都了解了 FreeRTOS 的风格, 这里的任务通知发送函数也是利用宏定义来进行扩展的,所有的函数都是一个宏定义,在 任务中发送任务通知的函数均是调用 xTaskGenericNotify()函数进行发送通知,xTaskGenericNotify()函数是一个通用的任务通知发送函数,在任务中发送通知的 API 函 数 , 如 xTaskNotifyGive() 、 xTaskNotify() ,xTaskNotifyAndQuery() , 都 是 以 xTaskGenericNotify()为原型的,只不过指定的发送方式不同而已。

2.xTaskNotifyGive()

xTaskNotifyGive()是一个宏,宏展开是调用函数 xTaskNotify( ( xTaskToNotify ), ( 0 ), eIncrement ),即向一个任务发送通知,并将对方的任务通知值加 1。该函数可以作为二值 信号量和计数信号量的一种轻量型的实现,速度更快,在这种情况下对象任务在等待任务 通 知 的 时 候 应 该 是 使 用 函 数 ulTaskNotifyTake() 而不是 xTaskNotifyWait() 。xTaskNotifyGive() 不 能 在 中 断 里 面 使 用 , 而 是 使 用 具 有 中 断 保 护 功 能 的 vTaskNotifyGiveFromISR()来代替。

xTaskNotifyGive()函数说明

xTaskNotifyGive()函数应用举例

 static void prvTask1( void *pvParameters ); static void prvTask2( void *pvParameters ); /*定义任务句柄 */ static TaskHandle_t xTask1 = NULL, xTask2 = NULL; /* 主函数:创建两个任务,然后开始任务调度 */ void main( void ) {
 xTaskCreate(prvTask1, "Task1", 200, NULL, tskIDLE_PRIORITY, &xTask1);
 xTaskCreate(prvTask2, "Task2", 200, NULL, tskIDLE_PRIORITY, &xTask2);
 vTaskStartScheduler();
 } /*-----------------------------------------------------------*/ static void prvTask1( void *pvParameters ) { for ( ;; ) { /* 向 prvTask2()发送一个任务通知,让其退出阻塞状态 */ xTaskNotifyGive( xTask2 ); /* 阻塞在 prvTask2()的任务通知上
 如果没有收到通知,则一直等待*/ ulTaskNotifyTake( pdTRUE, portMAX_DELAY );
 }
 } /*-----------------------------------------------------------*/ static void prvTask2( void *pvParameters ) { for ( ;; ) { /* 阻塞在 prvTask1()的任务通知上
 如果没有收到通知,则一直等待*/ ulTaskNotifyTake( pdTRUE, portMAX_DELAY ); /* 向 prvTask1()发送一个任务通知,让其退出阻塞状态 */ xTaskNotifyGive( xTask1 );
 }
}

完整代码可进群免费领取!!!

嵌入式物联网的学习之路非常漫长,不少人因为学习路线不对或者学习内容不够专业而错失高薪offer。不过别担心,我为大家整理了一份150多G的学习资源,基本上涵盖了嵌入式物联网学习的所有内容。点击下方链接,0元领取学习资源,让你的学习之路更加顺畅!记得点赞、关注、收藏、转发哦!

点击这里找小助理0元领取:

the end

评论(0)