FreeRTOS 学习七:任务通知(notification)

版权声明:本文为博主原创文章,欢迎转载,请尊重原创,转载注明链接。 https://blog.csdn.net/qqliyunpeng/article/details/53873858

1. 简介:

每一个任务在创建的时候都会有一个32位的notification 值,在创建的时候被初始化为0,RTOS notification 值是一个直接发送给别的任务的可以解除阻塞状态的值(这里需要注意的是接触的这个阻塞是因为等待通知而引起的),当发送到别的任务的时候,会可以配置的更新接收任务的notification值。
可以配置/更新接收任务的notification值的方式可以是如下的方式:

  • 直接写一个32位的notification到接收任务中
  • 让接收任务的notification自加1
  • 置位notification的某些位
  • 不改变接收任务的notification

在用任务通知实现一个轻量级、快速的 二值量/计数信号量 时,需要使用xTaskNotifyGive和ulTaskNotifyTake() 。
推荐用任务通知来实现二值量/计数信号量,用任务通知更快,用更少的RAM。
任务通知功能可以被关闭,如果关闭只需要设置configUSE_TASK_NOTIFICATIONS = 0 即可,禁止后每个任务节省8字节的内存。
有优点,也有缺点:

  • 只能有一个任务接收notification
  • 接收任务可以进入阻塞态,但发送任务不能阻塞,即使没有唤醒处于阻塞态的接收任务

2. 函数:

2.1 作为二值信号量的通知量的给出

typedef long BaseType_t;
BaseType_t xTaskNotifyGive( TaskHandle_t xTaskToNotify );
/* 中断中使用 */
void vTaskNotifyGiveFromISR(
                     TaskHandle_t xTaskToNotify,
                     BaseType_t *pxHigherPriorityTaskWoken );

说明:

替换二值信号量的功能要跟 ulTaskNotifyTake() 函数配合使用,而不是和 xTaskNotifyWait() 配合
参数:
xTaskToNotify,将要被通知的任务的句柄
pxHigherPriorityTaskWoken,必须初始化为0,如果这个函数引起一个阻塞的任务突出阻塞态,则被置位pdTRUE,并且在中断退出前需要有个上下文切换的步骤

例子:
两个乒乓操作型的例子:

/* Prototypes of the two tasks created by main(). */
static void prvTask1( void *pvParameters );
static void prvTask2( void *pvParameters );

/* Handles for the tasks create by main(). */
static TaskHandle_t xTask1 = NULL, xTask2 = NULL;

/* Create two tasks that send notifications back and forth to each other, then
start the RTOS scheduler. */
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( ;; )
    {
        /* Send a notification to prvTask2(), bringing it out of the Blocked
        state. */
        xTaskNotifyGive( xTask2 );

        /* Block to wait for prvTask2() to notify this task. */
        ulTaskNotifyTake( pdTRUE, portMAX_DELAY );
    }
}
/*-----------------------------------------------------------*/

static void prvTask2( void *pvParameters )
{
    for( ;; )
    {
        /* Block to wait for prvTask1() to notify this task. */
        ulTaskNotifyTake( pdTRUE, portMAX_DELAY );

        /* Send a notification to prvTask1(), bringing it out of the Blocked
        state. */
        xTaskNotifyGive( xTask1 );
    }
}

中断中程序的例子:

/* This is an example of a transmit function in a generic peripheral driver.  An
RTOS task calls the transmit function, then waits in the Blocked state (so not
using an CPU time) until it is notified that the transmission is complete.  The
transmission is performed by a DMA, and the DMA end interrupt is used to notify
the task. */

static TaskHandle_t xTaskToNotify = NULL;

/* The peripheral driver's transmit function. */
void StartTransmission( uint8_t *pcData, size_t xDataLength )
{
    /* At this point xTaskToNotify should be NULL as no transmission is in
    progress.  A mutex can be used to guard access to the peripheral if
    necessary. */
    configASSERT( xTaskToNotify == NULL );

    /* Store the handle of the calling task. */
    xTaskToNotify = xTaskGetCurrentTaskHandle();

    /* Start the transmission - an interrupt is generated when the transmission
    is complete. */
    vStartTransmit( pcData, xDatalength );
}
/*-----------------------------------------------------------*/

/* The transmit end interrupt. */
void vTransmitEndISR( void )
{
BaseType_t xHigherPriorityTaskWoken = pdFALSE;

    /* At this point xTaskToNotify should not be NULL as a transmission was
    in progress. */
    configASSERT( xTaskToNotify != NULL );

    /* Notify the task that the transmission is complete. */
    vTaskNotifyGiveFromISR( xTaskToNotify, &xHigherPriorityTaskWoken );

    /* There are no transmissions in progress, so no tasks to notify. */
    xTaskToNotify = NULL;

    /* If xHigherPriorityTaskWoken is now set to pdTRUE then a context switch
    should be performed to ensure the interrupt returns directly to the highest
    priority task.  The macro used for this purpose is dependent on the port in
    use and may be called portEND_SWITCHING_ISR(). */
    portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
}
/*-----------------------------------------------------------*/

/* The task that initiates the transmission, then enters the Blocked state (so
not consuming any CPU time) to wait for it to complete. */
void vAFunctionCalledFromATask( uint8_t ucDataToTransmit, size_t xDataLength )
{
uint32_t ulNotificationValue;
const TickType_t xMaxBlockTime = pdMS_TO_TICKS( 200 );

    /* Start the transmission by calling the function shown above. */
    StartTransmission( ucDataToTransmit, xDataLength );

    /* Wait for the transmission to complete. */
    ulNotificationValue = ulTaskNotifyTake( pdFALSE, xMaxBlockTime );

    if( ulNotificationValue == 1 )
    {
        /* The transmission ended as expected. */
    }
    else
    {
        /* The call to ulTaskNotifyTake() timed out. */
    }
}

【1】这个程序中需要注意的是在中断程序中需要用到任务的句柄需要在任务中得到任务的句柄,在 StartTransmission 中


2.2 通知量的获得

uint32_t ulTaskNotifyTake( BaseType_t xClearCountOnExit,
                           TickType_t xTicksToWait );
说明:
阻塞的取notification值,限时xTicksToWait
参数:
xClearCountOnExit,如果这个值设置成pdFALSE,则任务在接收到通知值时,在这个函数退出前,notification值减小,相当于信号量的操作
xTicksToWait,阻塞等待的时间,放弃cpu时间,pdMS_TO_TICKS()函数用于将ms转化成ticks
返回值:
在减小或者清零前的notification

2.3 通知量的给出

BaseType_t xTaskNotify( TaskHandle_t xTaskToNotify,
                        uint32_t ulValue,
                        eNotifyAction eAction );
/* 中断中使用 */
BaseType_t xTaskNotifyFromISR( TaskHandle_t xTaskToNotify,
                                uint32_t ulValue,
                                eNotifyAction eAction,
                                BaseType_t *pxHigherPriorityTaskWoken );
BaseType_t xTaskNotifyAndQuery( TaskHandle_t xTaskToNotify,
                                 uint32_t ulValue,
                                 eNotifyAction eAction,
                                 uint32_t *pulPreviousNotifyValue );
/* 中断中使用 */
BaseType_t xTaskNotifyAndQueryFromISR(
                                 TaskHandle_t xTaskToNotify,
                                 uint32_t ulValue,
                                 eNotifyAction eAction,
                                 uint32_t *pulPreviousNotifyValue,
                                 BaseType_t *pxHigherPriorityTaskWoken );
参数:
xTaskToNotify,要通知的任务句柄
ulValue,用于更新任务的notification值
eAction,是一个枚举变量,用于实现相关的行为
pulPreviousNotifyValue ,将任务中之前的notification值放到这个指针指向的变量,如果不使用,指向NULL
pxHigherPriorityTaskWoken,指向的内容需要初始化为pdFALSE(0),如果发送notification引起了一个任务从阻塞态变成了非阻塞态,并且这个任务的优先级高于当前任务的优先级,指向的内容变成pdTRUE,如果用户手动的设置成初始值是pdTRUE,中断被退出前将会进行内容切换。这个参数可以可选的,如果不使用,指向NULL
成员 描述
eNoAction 目标任务接收notification值,当时不更新本身的notification,此时ulValue不使用
eSetBits 接收任务的notification按位与ulValue进行或操作(|)
eIncrement 接收任务接收到notification后本身的notification自增,此时ulValue不使用,使用xTaskNotify()和xTaskNotifyGive()实现的功能相同
eSetValueWithOverwrite 接收任务的notification无条件的被发送来的notification替换,这种方式是xQueueOverwrite()的一种轻量级的替换
eSetValueWithoutOverwrite <此处不全,不是很理解>如果通知的任务没有挂起(pending)的notification,则notification值将会被替换成ulValue,如果任务之前已经有一个挂起(pending)的notification,并且任务还没将这个值更新到任务中,即没有使用,则不会替换成新的值。这种机制是队列长度是1的xQueueSend()的轻量级的替换
返回值:
pdPASS ,一般会都返回这个值,但是如果eAction 设置成eSetValueWithoutOverwrite ,并且通知的任务因为通知的任务已经有一个挂起的notification而不再更新,此时将返回 pdFALSE

xTaskNotifyAndQueryFromISR() 使用的例子:

void vAnISR( void )
{
BaseType_t xHigherPriorityTaskWoken = pdFALSE. /* Muse be Initialised to pdFALSE! */
uint32_t ulPreviousValue;

    /* Set bit 8 in the notification value of the task referenced by xTask1Handle.
    Store the task's previous notification value (before bit 8 is set) in
    ulPreviousValue. */
    xTaskNotifyAndQueryFromISR( xTask1Handle,
                                ( 1UL << 8UL ),
                                eSetBits,
                                &ulPreviousValue,
                                &xHigherPriorityTaskWoken );

    /* The task's previous notification value is saved in ulPreviousValue. */

    /* If the task referenced by xTask1Handle was in the Blocked state, waiting
    for the notification, then it will now have been moved from the Blocked
    state to the Ready state.  If its priority is higher than the priority of
    the currently executing task (the task this interrupt interrupted) then
    xHigherPriorityTaskWoken will have been set to pdTRUE, and passing the
    variable into a call to portYIELD_FROM_ISR() will result in the interrupt
    returning directly to the unblocked task.  If xHigherPriorityTaskWoken is
    still pdFALSE then passing it into portYIELD_FROM_ISR() will have no
    effect. */
    portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
}

最后的一大段的英文解释:
如果xTask1Handle指向的任务处于阻塞状态一直等待着notification,这段代码之后,任务现在将会是准备好状态了,如果要唤醒的任务的优先级正好大于目前的任务,xHigherPriorityTaskWoken指向的变量就会是pdTRUE,将这个变量传递进portYIELD_FROM_ISR(),将会导致中断之后直接去执行非阻塞的任务,如果xHigherPriorityTaskWoken值没有变成pdTRUE,则调用portYIELD_FROM_ISR()函数不会有影响。


2.4 通知量的获取

BaseType_t xTaskNotifyWait( uint32_t ulBitsToClearOnEntry,
                            uint32_t ulBitsToClearOnExit,
                            uint32_t *pulNotificationValue,
                            TickType_t xTicksToWait );
说明:
设定时间的获取通知量
参数:
ulBitsToClearOnEntry,在使用notification值之前,先将任务的通知值与这个参数按位取反值按位与操作。如果将它设置成0xffffffff(ULONG_MAX),表示清零任务通知值。如果将它设置成0x01,则任务的notification值的bit0会先清零,然后才被任务所使用
ulBitsToClearOnExit,如果任务接收到了一个notification值,在退出这个函数之前,会将notification值先取反再按位与操作。
pulNotificationValue,用于向外传递任务的通知值,这个通知值在ulBitsToClearOnExit起作用前将通知值拷贝到*pulNotificationValue中,如果没需求,可以设置成NULL
xTicksToWait,等待notification挂起的最大时间,当任务阻塞等待的时候不消耗cpu任何时间,单位是tick,可以使用pdMS_TO_TICKS()宏将ms转换成tick,如果设置成portMAX_DELAY,则一直等待

返回值:
如果一个notification接收到了,或者当xTaskNotifyWait()被调用时notification已经挂起,返回pdTRUE
如果超时了还没有接收到一个notification,则返回pdFALSE
一个例子:

/* This task shows bits within the RTOS task notification value being used to pass
different events to the task in the same way that flags in an event group might
be used for the same purpose. */
void vAnEventProcessingTask( void *pvParameters )
{
uint32_t ulNotifiedValue;

    for( ;; )
    {
        /* Block indefinitely (without a timeout, so no need to check the function's
        return value) to wait for a notification.

        Bits in this RTOS task's notification value are set by the notifying
        tasks and interrupts to indicate which events have occurred. */
        xTaskNotifyWait( 0x00,      /* Don't clear any notification bits on entry. */
                         ULONG_MAX, /* Reset the notification value to 0 on exit. */
                         &ulNotifiedValue, /* Notified value pass out in
                                              ulNotifiedValue. */
                         portMAX_DELAY );  /* Block indefinitely. */

        /* Process any events that have been latched in the notified value. */

        if( ( ulNotifiedValue & 0x01 ) != 0 )
        {
            /* Bit 0 was set - process whichever event is represented by bit 0. */
            prvProcessBit0Event();
        }

        if( ( ulNotifiedValue & 0x02 ) != 0 )
        {
            /* Bit 1 was set - process whichever event is represented by bit 1. */
            prvProcessBit1Event();
        }

        if( ( ulNotifiedValue & 0x04 ) != 0 )
        {
            /* Bit 2 was set - process whichever event is represented by bit 2. */
            prvProcessBit2Event();
        }

        /* Etc. */
    }
}

2.5 清挂起的通知

BaseType_t xTaskNotifyStateClear( TaskHandle_t xTask );
如果一个任务阻塞的等待一个通知,当通知到来时,任务将会立即退出阻塞状态,并且通知不会保持挂起(pending)状态。如果一个任务没有等待通知,当通知到来时,将会挂起,直到接收的任务去读通知。这个函数用于清除保持挂起的通知,这个函数不会改变通知值,只是改变他的状态。
参数:
xTask,将要清除通知的任务句柄。如果是NULL,清除的是本任务的通知。
返回值:
pdTRUE,当处理的任务有挂起的通知,并且成功的清除了通知
pdFALSE,处理的任务没有一个挂起的通知,

问题:什么时候需要手动的清除通知

猜你喜欢

转载自blog.csdn.net/qqliyunpeng/article/details/53873858