FreeRTOS_任务通知

  从v8.2.0版本开始,FreeRTOS新增了任务通知(Task Notifictions)这个功能,可以使用任务通知来代替信号量、消息队列、事件标志组等这些东西。使用任务通知的话效率会更高。

任务通知简介

  任务通知在FreeRTOS中是一个可选的功能,要使用任务通知的话就需要将宏 configUSR_TASK_NOTIFICATIONS定义为1。

  FreeRTOS的每个任务都有一个32位的通知值,任务控制款中的成员变量ulNotifiedValue就是这个通知值。任务通知是一个事件,假如某个任务通知的接收任务因为等待任务通知而阻塞的话,向这个接收任务发送任务通知以后就会解除这个任务的阻塞状态。也可以更新接收任务的任务通知值,任务通知可以通过如下方法更新接收任务的通知值:

  1.  不覆盖接收任务的通知值(如果上次发送给接收任务的通知还没有被处理)。

  2. 覆盖接收任务的通知值。

  3. 更新接收任务通知值的一个或多个bit。

  4. 增加接收任务的通知值。

  合理、灵活地使用上面这些更改任务通知值的方法可以在一些场合中替代队列、二值信号量、计数型信号量和时间标志组。使用任务通知来实现二值信号量功能的时候,解除任务阻塞的时间比直接使用二值信号量要快45%(FreeRTOS官方测试结果,使用v8.1.2版本中的二值信号量,GCC编译器,-O2优化条件下测试的,没有使能断言函数configASSERT()),并且使用的RAM更少!

  任务通知的发送使用函数xTaskNotify()或者xTaskNotifyGive()(还有次函数的中断版本)来完成,这个通知值会一直保存着,直到接收任务调用函数xTaskNotifyWait()或者ulTaskNotifyTake()来获取这个通知值。假如接收任务因为等待任务通知而阻塞的话那么在接收到任务通知以后就会解除阻塞态。

  任务通知虽然可以提高速度,并且减少RAM的使用,但是任务通知也是由使用限制的:

  1. FreeRTOS的任务通知只能有一个接收任务,其实大多数的应用都是这种情况。

  2. 接收任务可以因为接收任务通知而进入阻塞态,但是发送任务不会因为任务通知发送失败而阻塞。

 

发送任务通知

  任务通知发送函数有6个,如表:

函数 描述
xTaskNotify() 发送通知,带有通知值并且不保留接收任务的原通知值,用在任务中。
xTaskNotifyFromISR() 发送通知,函数xTaskNotify()的中断版本。
xTaskNotifyGive() 发送通知,不带通知值并且不保留接收任务的原通知值,此函数会将接收任务的通知值加一,用于任务中。
xTaskNotifyGiveFromISR() 发送通知,函数xTaskNotifyGive()的中断版本。
xTaskNotifyAndQuery() 发送通知,带有通知值并且保留接收任务的原通知值,用在任务中。
xTaskNotifyAndQueryFromISR() 发送通知,函数xTaskNotifyAndQuery()的中断版本,用在中断服务函数中。

1. 函数xTaskNotify()

   此函数用于发送任务通知,此函数发送任务通知的时候带有通知值,此函数是个宏,真正执行的函数xTaskGenericNotify(),函数原型如下:

BaseType_t xTaskNotify( TaskHandle_t xTaskToNotify, 
uint32_t ulValue,
eNotifyAction eAction )

参数:

  xTaskToNotify:任务句柄,指定任务通知是发送给哪个任务的。

  ulValue:任务通知值。

  eAction:任务通知更新的方法,eNotifyAction是个枚举类型,在文件task.h中有如下定义:

/* Actions that can be performed when vTaskNotify() is called. */
typedef enum
{
    eNoAction = 0,               /* Notify the task without updating its notify value. */
    eSetBits,                    /* 更新指定bit。Set bits in the task's notification value. */
    eIncrement,                  /* 任务通知值加一。Increment the task's notification value. */
    eSetValueWithOverwrite,      /* 覆写的方式更新通知值。Set the task's notification value to a specific value even if the previous value has not yet been read by the task. */
    eSetValueWithoutOverwrite    /* 不覆写通知值。Set the task's notification value if the previous value has been read by the task. */
} eNotifyAction;

  此参数可以选择枚举类型中的任意一个,不同的应用环境选择也不同。

返回值:

  pdFAIL:但参数eAction设置为eSetValueWithoutOverwrite的时候,如果任务通知值没有更新成功就返回pdFAIL。

  pdPASS:eAction设置为其他选项的时候统一返回pdPASS。

2. 函数xTaskNotifyFromISR()

  此函数用于发送任务通知,是函数xTaskNotify()的中断版本,此函数是个宏,真正执行的是函数xTaskGenericNotifyFromISR(),此函数原型如下:

BaseType_t xTaskNotifyFromISR( TaskHandle_t xTaskToNotify, 
uint32_t ulValue,
eNotifyAction eAction,
BaseType_t *pxHigherPriorityTaskWoken )

参数:

  xTaskToNotify:任务句柄,指定任务通知是发送给哪个任务的。

  ulValue:任务通知值。

  eAction:任务通知更新的方法。

  pxHigherPriorityTaskWoken :退出此函数以后是否进行任务切换,这个变量的值函数会自动设置的,用户不进行设置,用户只需要提供一个变量来保存这个值就行了。当此值为pdTRUE的时候在退出中断服务函数之前一定要进行一次任务切换。

返回值:

  pdFAIL:当参数eAction设置为eSetValueWithoutOverwrite的时候,如果任务通知值没有更新成功就返回pdFAIL。

  pdPASS:eAction 设置为其他选项的时候统一返回pdPASS。

3. 函数xTaskNotifyGive()

  发送任务通知,相对于函数xTaskNotify(),此函数发送任务通知的时候不带有通知值。此函数只是将任务通知值简单加一。此函数是个宏,真正执行的是函数xTaskGenericNotify(),此函数原型如下:

BaseType_t xTaskNotifyGive( TaskHandle_t xTaskToNotify )

参数:

  xTaskToNotify:任务句柄,指定任务通知是发送给哪个任务的。

返回值:

  pdPASS:此函数只会返回pdPASS。

4. 函数vTaskNotifyGiveFromISR()

  此函数为xTaskNotifyGive()的中断版本,用在中断服务函数中,函数原型如下:

void vTaskNotifyGiveFromISR( TaskHandle_t xTaskHandle, 
BaseType_t *pxHigherPriorityTaskWoken )

参数:

  xTaskToNotify:任务句柄,指定任务通知是发送给哪个任务的。

  pxHigherPriorityTaskWoken:退出此函数以后是否进行任务切换,这个变量的值会自动设置的,用户不用进行设置,用户只需要提供一个变量来保存这个值就行了。当此值为pdTRUE的时候在退出中断服务函数之前一定要进行一次任务切换。

返回值:

  无。

5. 函数xTaskNotifyAndQuery()

  此函数和xTaskNotify()很类似,此函数比xTaskNotify()多一个参数,此函数用来保存更新前的通知值。此函数是个宏,真正执行的是函数xTaskGenericNotify(),此函数原型如下:

BaseType_t xTaskNotifyAndQuery( TaskHandle_t xTaskToNotify, 
uint32_t ulValue,
eNotifyAction eAction,
uint32_t *pulPreviousNotificationValue )

参数:

  xTaskToNotify:任务句柄,指定任务通知是发送给哪个任务的。

  ulValue:任务通知值。

  eAction:任务通知更新的方法。

  pulPreviousNotificationValue:用来保存更新前的任务通知值。

返回值:

  pdFAIL:当参数eAction设置为eSetValueWithoutOverwrite的时候,如果任务通知值没有更新成功就返回pdFAIL。

  pdPASS:eAction设置为其他选项的时候统一返回pdPASS。

6. 函数xTaskNotifyAndQueryFromISR()

  此函数为xTaskNortifyAndQuery()的中断版本,用在中断服务函数中。此函数同样为宏,真正执行的是函数xTaskGenericNotifyFromISR(),此函数的原型如下:

BaseType_t xTaskNotifyFromISR( TaskHandle_t xTaskToNotify, 
uint32_t ulValue,
eNotifyAction eAction,
BaseType_t *pxHigherPriorityTaskWoken )

参数:

  xTaskToNotify:任务句柄,指定任务通知是发送给哪个任务的。

  ulValue:任务通知值。

  eAction:任务通知更新的方法。

  pulPreviousNotificationValue:用来保存更新前的任务通知值。

  pxHigherPriorityTaskWoken:退出此函数以后是否进行任务切换,这个变量的值函数会自动设置,用户不进行设置,用户只需要提供一个变量来保存这个值就行了。当此值为pdTRUE的时候在退出中断服务函数之前一定要进行一次任务切换。

返回值:

  pdFAIL:当参数eAction设置为eSetValueWithoutOverwrite的时候,如果任务通知值没有更新成功就返回pdFAIL。

  pdPASS:eAction设置为其他选项的时候统一返回pdPASS。

 

任务通知通用发送函数

任务级任务通知通用发送函数

  我们学习了3个任务级任务通知发送函数:xTaskNotify()、xTaskNotifyGive()和xTaskNotifyAndQuery(),这三个函数最终调用的都是函数xTaskGenericNotify()!此函数在文件task.c中有如下定义,缩减后函数如下:

。。。

 

任务通知模拟二值信号量

  前面说了,根据FreeRTOS官方的统计,使用任务通知替代二值信号量的时候任务解除阻塞的时间要快45%,并且需要的RAM也更少。其实通过我们上面分析任务通知发送和获取函数的过程可以看出,任务通知的代码量很少,所以执行时间与所需的RAM也就相应的会减少。

  二值信号量就是值最大为1的信号量,这也是名字中“二值”的来源。当任务通知用于替代二值信号量的时候任务通知值会替代信号量值,

 

posted @ 2020-04-02 14:38  doitjust  阅读(1540)  评论(0编辑  收藏  举报