FreeRTOS学习笔记8——Task Notifications

FreeRTOS Task Notification


TOC


configUSE_TASK_NOTIFICATIONS, need set to 1 in FreeRTOSConfig.h to use task notification.
当设置为1时,每个task一个Notification State,->Pending or Not-Pending
每个task有一个Notification Value, 类型为uint32_t.

和其它IPC方法的区别

通过中间对象

task通知——直接task通信

Task Notifications 的优缺点

优点

  • 消息传递的更快,效率更高
  • 内存消耗比其它IPC通信相比,明显少很多

缺点

  • 只能从ISR 发送event或者data 到task, 而不能从task发送至ISR.
  • 发出的消息只能被一个task接收,但这个限制很少,因为很少有多个task从同一个通信对象获取消息。
  • 不能缓冲消息,只能保存一个值。而queue可以
  • 不能向多个task广播消息,而event group可以
  • task Notification 不会因为taks的状态为Pengding而去等待发送完成后,再发送。而是会直接覆盖掉。

使用Task Notifications

xTaskNotifyGive()

/**
 * @return 返回结果只有pdPASS
 */
BaseType_t xTaskNotifyGive( TaskHandle_t xTaskToNotify );

vTaskNotifyGiveFromISR()

/**
 * 
 */
void vTaskNotifyGiveFromISR( TaskHandle_t xTaskToNotify,
BaseType_t *pxHigherPriorityTaskWoken );

ulTaskNotifyTake()

/**
 * @input xClearCountOnExit is pdTRUE, the task's notification value will be zero
 *                          is pdFALSE, the task's notification value will be de decremented.
 */
uint32_t ulTaskNotifyTake( BaseType_t xClearCountOnExit, TickType_t xTicksToWait );

examples

使用task notification 代替semaphore

方法1

xClearCountOnExit = pdTRUE

/* The rate at which the periodic task generates software interrupts. */
const TickType_t xInterruptFrequency = pdMS_TO_TICKS( 500UL );

static void vHandlerTask( void *pvParameters )
{
    /* xMaxExpectedBlockTime is set to be a little longer than the maximum expected time
    between events. */
    const TickType_t xMaxExpectedBlockTime = xInterruptFrequency + pdMS_TO_TICKS( 10 );
    uint32_t ulEventsToProcess;

    /* As per most tasks, this task is implemented within an infinite loop. */
    for( ;; )
    {
        /* Wait to receive a notification sent directly to this task from the
        interrupt service routine. */
        ulEventsToProcess = ulTaskNotifyTake( pdTRUE, xMaxExpectedBlockTime );
        if( ulEventsToProcess != 0 )
        {
            /* To get here at least one event must have occurred. Loop here until
            all the pending events have been processed (in this case, just print out
            a message for each event). */
            while( ulEventsToProcess > 0 )
            {
                vPrintString( "Handler task - Processing event.\r\n" );
                ulEventsToProcess--;
            }
        }
        else
        {
        /* If this part of the function is reached then an interrupt did not
        arrive within the expected time, and (in a real application) it may be
        necessary to perform some error recovery operations. */
        }
    }
}
static uint32_t ulExampleInterruptHandler( void )
{
    BaseType_t xHigherPriorityTaskWoken;

    /* The xHigherPriorityTaskWoken parameter must be initialized to pdFALSE as
    it will get set to pdTRUE inside the interrupt safe API function if a
    context switch is required. */
    xHigherPriorityTaskWoken = pdFALSE;

    /* Send a notification directly to the task to which interrupt processing is
    being deferred. */
    vTaskNotifyGiveFromISR( /* The handle of the task to which the notification
                            is being sent. The handle was saved when the task
                            was created. */
                            xHandlerTask,
                            /* xHigherPriorityTaskWoken is used in the usual
                            way. */
                            &xHigherPriorityTaskWoken );

    /* Pass the xHigherPriorityTaskWoken value into portYIELD_FROM_ISR(). If
    xHigherPriorityTaskWoken was set to pdTRUE inside vTaskNotifyGiveFromISR()
    then calling portYIELD_FROM_ISR() will request a context switch. If
    xHigherPriorityTaskWoken is still pdFALSE then calling
    portYIELD_FROM_ISR() will have no effect. The implementation of
    portYIELD_FROM_ISR() used by the Windows port includes a return statement,
    which is why this function does not explicitly return a value. */
    portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
}

方法2

xClearCountOnExit = pdFALSE

static void vHandlerTask( void *pvParameters )
{
    /* xMaxExpectedBlockTime is set to be a little longer than the maximum expected time
    between events. */
    const TickType_t xMaxExpectedBlockTime = xInterruptFrequency + pdMS_TO_TICKS( 10 );

    /* As per most tasks, this task is implemented within an infinite loop. */
    for( ;; )
    {
        /* Wait to receive a notification sent directly to this task from the
        interrupt service routine. The xClearCountOnExit parameter is now pdFALSE,
        so the task's notification value will be decremented by ulTaskNotifyTake(),
        and not cleared to zero. */
        if( ulTaskNotifyTake( pdFALSE, xMaxExpectedBlockTime ) != 0 )
        {
            /* To get here an event must have occurred. Process the event (in this
            case just print out a message). */
            vPrintString( "Handler task - Processing event.\r\n" );
        }
        else
        {
        /* If this part of the function is reached then an interrupt did not
        arrive within the expected time, and (in a real application) it may be
        necessary to perform some error recovery operations. */
        }
    }
}
static uint32_t ulExampleInterruptHandler( void )
{
    BaseType_t xHigherPriorityTaskWoken;

    xHigherPriorityTaskWoken = pdFALSE;

    /* Send a notification to the handler task multiple times. The first ‘give’ will
    unblock the task, the following 'gives' are to demonstrate that the receiving
    task's notification value is being used to count (latch) events - allowing the
    task to process each event in turn. */
    vTaskNotifyGiveFromISR( xHandlerTask, &xHigherPriorityTaskWoken );
    vTaskNotifyGiveFromISR( xHandlerTask, &xHigherPriorityTaskWoken );
    vTaskNotifyGiveFromISR( xHandlerTask, &xHigherPriorityTaskWoken );

    portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
}

xTaskNotify() 和 xTaskNotifyFromISR()

xTaskNotify 与 xTaskNotifyGive 的区别

xTaskNotify是比xTaskNotifyGive更加强大的API, 可以通过下面任意一种方式,更新接收task的notification value.

  • 将接收task的notification value 加1; 这里 xTaskNotifyxTaskNotifyGive使用效果一样。
  • 设置一位或多位 接收task的notification value. 这样就以将notification value 视为一种轻量又高效的evnet group
  • notification value写入一个全新的数值,仅当接收task已经读取的先前的值。这样可以赋予notification value 与 长度为1的queue相似的功能。
  • notification value写入一个全新的数值,而不管接收task是否已经读过了。 这样可以赋予notification valuexQueueOverwriteAPI 相似的mailbox 功能

API,xTaskNotify 更加的灵活和强大,但也使用上更加复杂
调用xTaskNotify,总是将接收task的notification state,置为pending.

API原型

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 );

API参数与返回值

eNotifyAction Value Resultant Effect on Receiving Task
eNoAction 接收task的 notification state 会被设置为pending,但是notification value不会被更新,并且.xTaskNotify() ulValue,没有用到。
xTaskNotify() ulValue parameter is not used.eNoAction可以将task notification用作 binary semaphore,轻量又高效
eSetBits 接收任务的 notification value 与 xTaskNotify() ulValue位与,OR操作.
例如ulValue为0x01时,接收任务的 notification valuebit0 位将会被置1;
如果ulValue 为0x06 (binary 0110),那么接收任务的 notification value bit 1 和 bit 2 将会被置1.
The eSetBits 将允许接收任务的notification value用作 轻量又高效的 'event group'.
eIncrement 接收任务的 notification value 加1.
xTaskNotify() 参数 ulValue 没用到.eIncrement 可以将 notification value用作相比于一个 binary 或者counting semaphore 理加轻量而又高效的选择
此时xTaskNotify()xTaskNotifyGive() API 一致
eSetValueWithoutOverwrite 如果在xTaskNotify()调用前,接收任务为pending状态时,xTaskNotify()将会返回pdFAIL
若接收任务不是pending状态,则接收任务的notification value将会设置为xTaskNotify() ulValue
eSetValueWithOverwrite 接收任务的notification value将会设置为xTaskNotify() ulValue, 而不管调用之前的状态。

xTaskNotifyWait()

xTaskNotifyWait()相比于ulTaskNotifyTake()更加强大, 它让task可以等待变为pending状态,并提供超时机制,xTaskNotifyWait()提供了置0notification valuebit位的选项,进入xTaskNotifyWait()和退出xTaskNotifyWait()时。

BaseType_t xTaskNotifyWait( uint32_t ulBitsToClearOnEntry,
                            uint32_t ulBitsToClearOnExit,
                            uint32_t *pulNotificationValue,
                            TickType_t xTicksToWait );
  • ulBitsToClearOnEntry
    • 若调用xTaskNotifyWait()前,task没有notification pending,则ulBitsToClearOnEntry中所有置1的位,会清0notification value所对应的位。
    • ulBitsToClearOnEntry 为0x01时,task的notification value的第0位置清0; ulBitsToClearOnExit 为0xffffffff(ULONG_MAX)时, tasknotification value 所有位将清零
  • ulBitsToClearOnExit
    • 若xTaskNotifyWait()因为有notification pending而退出时,pulNotificationValue将先保存notification value内容之后,ulBitsToClearOnExit中置的位,将清空task notification value中对应的位
  • pulNotificationValue
    • pulNotificationValue 保存notification value被ulBitsToClearOnExit清零任何一位之前的值。
    • pulNotificationValue 是可选的,不需要时置为NULL。
  • xTicksToWait
    • pdMS_TO_TICKS(), portMAX_DELAY, porPeriod_tick_ms

return value
pdTRUE, has no notification within the xTicksToWait.
pdFALSE, time out when waiting for notification.

任务通知在设备驱动中的应用——Uart

  • 伪代码举例,二进制信号量,应用于驱动库中的传输函数
/* Driver library function to send data to a UART. */
BaseType_t xUART_Send( xUART *pxUARTInstance, uint8_t *pucDataSource, size_t uxLength )
{
    BaseType_t xReturn;
    /* Ensure the UART's transmit semaphore is not already available by attempting to take
    the semaphore without a timeout. */
    xSemaphoreTake( pxUARTInstance->xTxSemaphore, 0 );

    /* Start the transmission. */
    UART_low_level_send( pxUARTInstance, pucDataSource, uxLength );

    /* Block on the semaphore to wait for the transmission to complete. If the semaphore
    is obtained then xReturn will get set to pdPASS. If the semaphore take operation times
    out then xReturn will get set to pdFAIL. Note that, if the interrupt occurs between
    UART_low_level_send() being called, and xSemaphoreTake() being called, then the event
    will be latched in the binary semaphore, and the call to xSemaphoreTake() will return
    immediately. */
    xReturn = xSemaphoreTake( pxUARTInstance->xTxSemaphore, pxUARTInstance->xTxTimeout );

    return xReturn;
}

/*-----------------------------------------------------------*/
/* The service routine for the UART's transmit end interrupt, which executes after the
    last byte has been sent to the UART. */
void xUART_TransmitEndISR( xUART *pxUARTInstance )
{
    BaseType_t xHigherPriorityTaskWoken = pdFALSE;

    /* Clear the interrupt. */
    UART_low_level_interrupt_clear( pxUARTInstance );

    /* Give the Tx semaphore to signal the end of the transmission. If a task is Blocked
    waiting for the semaphore then the task will be removed from the Blocked state. */
    xSemaphoreGiveFromISR( pxUARTInstance->xTxSemaphore, &xHigherPriorityTaskWoken );

    portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
}

上述例程可以非常正常的动作, 但是也有几个缺点:

  • 库中使用了多个信号量semaphores, 增加了RAM的用量。
  • 信号量semaphores必须提前创建并初使化后,库才能使用它。
  • 信号量semaphores应用场景非常广泛,可以让多个task进入Blocked状态等待信号量发生,当信号量可用时,可以选择特定的task离开Blocked状态,而上述例程中最多不可能超过一个task等待信号量发生。

下面这个例程会使用task notification代替,二进制信号量binary semaphore,避免了上述几个缺陷
注意: 如果库中使用了信号量, 库的说明文档中要明确说明,调用库中的函数会改变task的notification statenotification value

  • xUART 结构体中的成员变量被替换为xTaskToNotify,它的类型是TaskHandle_t,保存了等待UART操作完成的task的句柄handle.
  • xTaskGetCurrentTaskHandle(), 是FreeRTOS的API函数用于获取当前运行的task的句柄handle.
  • 库中没有创建任何FreeRTOS对象,所有不会发生内存溢出,也不需要明确的初使化.
  • task notification直接发送给等待UART操作完成的task,所以没有多余的逻辑执行过程。

xUART结构体成员xTaskToNotify,同时被task和ISR使用,更新数据时需要注意以下几点

  • 如果xTAskToNotify 可以在一个内存写操作完成时,那么它就可以在非临界区域更新数据,例如当FreeRTOS运行在32位处理器上,xTAskToNotify为32-bit变量时
  • 如果需要一个以上的内存写操作才能完成更新xTAskToNotify,则xTAskToNotify必须在临界区域内更新。例如当FreeRTOS运行在16-bit处理器上时

FreeRTOS内部实现,TaskHandle_t是个指针,sizeof(TaskHandle_t) 恒等于 sizeof(void *).

/* Driver library function to send data to a UART. */
BaseType_t xUART_Send( xUART *pxUARTInstance, uint8_t *pucDataSource, size_t uxLength )
{
    BaseType_t xReturn;

    /* Save the handle of the task that called this function. The book text contains notes as to
    whether the following line needs to be protected by a critical section or not. */
    pxUARTInstance->xTaskToNotify = xTaskGetCurrentTaskHandle();

    /* Ensure the calling task does not already have a notification pending by calling
    ulTaskNotifyTake() with the xClearCountOnExit parameter set to pdTRUE, and a block time of 0
    (don't block). */
    ulTaskNotifyTake( pdTRUE, 0 );

    /* Start the transmission. */
    UART_low_level_send( pxUARTInstance, pucDataSource, uxLength );

    /* Block until notified that the transmission is complete. If the notification is received
    then xReturn will be set to 1 because the ISR will have incremented this task's notification
    value to 1 (pdTRUE). If the operation times out then xReturn will be 0 (pdFALSE) because
    this task's notification value will not have been changed since it was cleared to 0 above.
    Note that, if the ISR executes between the calls to UART_low_level_send() and the call to
    ulTaskNotifyTake(), then the event will be latched in the task’s notification value, and the
    call to ulTaskNotifyTake() will return immediately.*/
    xReturn = ( BaseType_t ) ulTaskNotifyTake( pdTRUE, pxUARTInstance->xTxTimeout );

    return xReturn;
}
/*-----------------------------------------------------------*/
/* The ISR that executes after the last byte has been sent to the UART. */
void xUART_TransmitEndISR( xUART *pxUARTInstance )
{
    BaseType_t xHigherPriorityTaskWoken = pdFALSE;

    /* This function should not execute unless there is a task waiting to be notified. Test this
    condition with an assert. This step is not strictly necessary, but will aid debugging.
    configASSERT() is described in section 11.2.*/
    configASSERT( pxUARTInstance->xTaskToNotify != NULL );

    /* Clear the interrupt. */
    UART_low_level_interrupt_clear( pxUARTInstance );

    /* Send a notification directly to the task that called xUART_Send(). If the task is Blocked
    waiting for the notification then the task will be removed from the Blocked state. */
    vTaskNotifyGiveFromISR( pxUARTInstance->xTaskToNotify, &xHigherPriorityTaskWoken );

    /* Now there are no tasks waiting to be notified. Set the xTaskToNotify member of the xUART
    structure back to NULL. This step is not strictly necessary but will aid debugging. */
    pxUARTInstance->xTaskToNotify = NULL;
    portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
}

task notification 也可以用于接收函数

  • xUART_Receive()函数不包含任何互斥逻辑, 如果多个task使用xUART_Receive()函数, 需要增加互斥锁
  • UART的中断服务函数将接收的内容保存在ram buffer中, xUART_Receive()函数返回ram buffer的内容。
  • xUART_Receive()参数uxWantedBytes,while循环将检测buffer中的数据长度,当数据长度满足或者超时时退出循环。
  • 调用接收函数的task会多次进入Block状态,xUART_Receive()
/* Driver library function to receive data from a UART. */
size_t xUART_Receive( xUART *pxUARTInstance, uint8_t *pucBuffer, size_t uxWantedBytes )
{
    size_t uxReceived = 0;
    TickType_t xTicksToWait;
    TimeOut_t xTimeOut;

    /* Record the time at which this function was entered. */
    vTaskSetTimeOutState( &xTimeOut );

    /* xTicksToWait is the timeout value - it is initially set to the maximum receive
    timeout for this UART instance. */
    xTicksToWait = pxUARTInstance->xRxTimeout;

    /* Save the handle of the task that called this function. The book text contains notes
    as to whether the following line needs to be protected by a critical section or not. */
    pxUARTInstance->xTaskToNotify = xTaskGetCurrentTaskHandle();

    /* Loop until the buffer contains the wanted number of bytes, or a timeout occurs. */
    while( UART_bytes_in_rx_buffer( pxUARTInstance ) < uxWantedBytes )
    {
        /* Look for a timeout, adjusting xTicksToWait to account for the time spent in this
        function so far. */
        if( xTaskCheckForTimeOut( &xTimeOut, &xTicksToWait ) != pdFALSE )
        {
            /* Timed out before the wanted number of bytes were available, exit the loop. */
            break;
        }

        /* The receive buffer does not yet contain the required amount of bytes. Wait for a
        maximum of xTicksToWait ticks to be notified that the receive interrupt service
        routine has placed more data into the buffer. It does not matter if the calling
        task already had a notification pending when it called this function, if it did, it
        would just iteration around this while loop one extra time. */
        ulTaskNotifyTake( pdTRUE, xTicksToWait );
    }

    /* No tasks are waiting for receive notifications, so set xTaskToNotify back to NULL.
    The book text contains notes as to whether the following line needs to be protected by
    a critical section or not. */
    pxUARTInstance->xTaskToNotify = NULL;

    /* Attempt to read uxWantedBytes from the receive buffer into pucBuffer. The actual
    number of bytes read (which might be less than uxWantedBytes) is returned. */
    uxReceived = UART_read_from_receive_buffer( pxUARTInstance, pucBuffer, uxWantedBytes );

    return uxReceived;
}


/*-----------------------------------------------------------*/
/* The interrupt service routine for the UART's receive interrupt */
void xUART_ReceiveISR( xUART *pxUARTInstance )
{
    BaseType_t xHigherPriorityTaskWoken = pdFALSE;

    /* Copy received data into this UART's receive buffer and clear the interrupt. */
    UART_low_level_receive( pxUARTInstance );

    /* If a task is waiting to be notified of the new data then notify it now. */
    if( pxUARTInstance->xTaskToNotify != NULL )
    {
        vTaskNotifyGiveFromISR( pxUARTInstance->xTaskToNotify, &xHigherPriorityTaskWoken );
        portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
    }
}

任务通知在设备驱动中的应用——ADC

下面例程展示了如何用vTaskNotifyFromISR(),传递数据给task, 而上面的例程中使用的vTaskNotifyGiveFromISR()只能传递事件给task.

  • ADC task
/* A task that uses an ADC. */
void vADCTask( void *pvParameters )
{
    uint32_t ulADCValue;
    BaseType_t xResult;
    /* The rate at which ADC conversions are triggered. */
    const TickType_t xADCConversionFrequency = pdMS_TO_TICKS( 50 );
    for( ;; )
    {
        /* Wait for the next ADC conversion result. */
        xResult = xTaskNotifyWait(
                        /* The new ADC value will overwrite the old value, so there is no need to clear any bits before waiting for the new notification value. */
                        0,
                        /* Future ADC values will overwrite the existing value, so there is no need to clear any bits before exiting xTaskNotifyWait(). */
                        0,
                        /* The address of the variable into which the task's notification value (which holds the latest ADC conversion result) will be copied. */
                        &ulADCValue,
                        /* A new ADC value should be received every xADCConversionFrequency ticks. */
                        xADCConversionFrequency * 2 );

        if( xResult == pdPASS )
        {
            /* A new ADC value was received. Process it now. */
            ProcessADCResult( ulADCValue );
        }
        else
        {
            /* The call to xTaskNotifyWait() did not return within the expected time,something must be wrong with the input that triggers the ADC conversion, or with the ADC itself. Handle the error here. */
        }
    }
}

/*-----------------------------------------------------------*/
/* The interrupt service routine that executes each time an ADC conversion completes. */

void ADC_ConversionEndISR( xADC *pxADCInstance )
{
    uint32_t ulConversionResult;
    BaseType_t xHigherPriorityTaskWoken = pdFALSE, xResult;

    /* Read the new ADC value and clear the interrupt. */
    ulConversionResult = ADC_low_level_read( pxADCInstance );

    /* Send a notification, and the ADC conversion result, directly to vADCTask(). */
    xResult = xTaskNotifyFromISR( xADCTaskToNotify,                /* xTaskToNotify parameter. */
                                   ulConversionResult,            /* ulValue parameter. */


                                   eSetValueWithoutOverwrite,     /* eAction parameter. */
                                   &xHigherPriorityTaskWoken );
    /* If the call to xTaskNotifyFromISR() returns pdFAIL then the task is not keeping up
with the rate at which ADC values are being generated. configASSERT() is described
in section 11.2.*/
    configASSERT( xResult == pdPASS );
    portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
}

任务通知在设备驱动中的应用——Task

向server task 发送队列的数据类型和数据结构

typedef enum CloudOperations
{
    eRead,            /* Send data to the cloud server. */
    eWrite            /* Receive data from the cloud server. */
} Operation_t;


typedef struct CloudCommand
{
    Operation_t eOperation;     /* The operation to perform (read or write). */
    uint32_t ulDataID;             /* Identifies the data being read or written. */
    uint32_t ulDataValue;         /* Only used when writing data to the cloud server. */
    TaskHandle_t xTaskToNotify;    /* The handle of the task performing the operation. */
} CloudCommand_t;

cloud read

/* ulDataID identifies the data to read. pulValue holds the address of the variable into
which the data received from the cloud server is to be written. */
BaseType_t CloudRead( uint32_t ulDataID, uint32_t *pulValue )
{
    CloudCommand_t xRequest;
    BaseType_t xReturn;

    /* Set the CloudCommand_t structure members to be correct for this read request. */
    xRequest.eOperation = eRead;        /* This is a request to read data. */
    xRequest.ulDataID = ulDataID;         /* A code that identifies the data to read. */
    xRequest.xTaskToNotify = xTaskGetCurrentTaskHandle(); /* Handle of the calling task. */

    /* Ensure there are no notifications already pending by reading the notification value with a block time of 0, then send the structure to the server task. */
    xTaskNotifyWait( 0, 0, NULL, 0 );
    xQueueSend( xServerTaskQueue, &xRequest, portMAX_DELAY );

    /* Wait for a notification from the server task. The server task writes the value received from the cloud server directly into this task’s notification value, so there is no need to clear any bits in the notification value on entry to or exit from the xTaskNotifyWait() function. The received value is written to *pulValue, so pulValue is passed as the address to which the notification value is written. */

    xReturn = xTaskNotifyWait( 0,                    /* No bits cleared on entry. */
                               0,                    /* No bits to clear on exit. */
                               pulValue,            /* Notification value into *pulValue. */
                               pdMS_TO_TICKS( 250 ) ); /* Wait a maximum of 250ms. */

    /* If xReturn is pdPASS, then the value was obtained. If xReturn is pdFAIL, then the request timed out. */
    return xReturn;
}

server task

void ServerTask( void *pvParameters )
{
    CloudCommand_t xCommand;
    uint32_t ulReceivedValue;
    for( ;; )
    {
        /* Wait for the next CloudCommand_t structure to be received from a task. */
        xQueueReceive( xServerTaskQueue, &xCommand, portMAX_DELAY );
        switch( xCommand.eOperation ) /* Was it a read or write request? */
        {
            case eRead:

                /* Obtain the requested data item from the remote cloud server. */
                ulReceivedValue = GetCloudData( xCommand.ulDataID );
                /* Call xTaskNotify() to send both a notification and the value received from the
cloud server to the task that made the request. The handle of the task is
obtained from the CloudCommand_t structure. */
                xTaskNotify( xCommand.xTaskToNotify,     /* The task’s handle is in the structure. */
                            ulReceivedValue,             /* Cloud data sent as notification value. */
                            eSetValueWithOverwrite );
                break;

                /* Other switch cases go here. */
        }
    }
}

cloud write

/* Status bits used by the cloud write operation. */
#define SEND_SUCCESSFUL_BIT                ( 0x01 << 0 )
#define OPERATION_TIMED_OUT_BIT            ( 0x01 << 1 )
#define NO_INTERNET_CONNECTION_BIT         ( 0x01 << 2 )
#define CANNOT_LOCATE_CLOUD_SERVER_BIT     ( 0x01 << 3 )

/* A mask that has the four status bits set. */
#define CLOUD_WRITE_STATUS_BIT_MASK ( SEND_SUCCESSFUL_BIT |
                                      OPERATION_TIMED_OUT_BIT |
                                      NO_INTERNET_CONNECTION_BIT |
                                      CANNOT_LOCATE_CLOUD_SERVER_BIT )

uint32_t CloudWrite( uint32_t ulDataID, uint32_t ulDataValue )
{
    CloudCommand_t xRequest;
    uint32_t ulNotificationValue;

    /* Set the CloudCommand_t structure members to be correct for this write request. */
    xRequest.eOperation = eWrite;             /* This is a request to write data. */
    xRequest.ulDataID = ulDataID;             /* A code that identifies the data being written. */
    xRequest.ulDataValue = ulDataValue;     /* Value of the data written to the cloud server. */
    xRequest.xTaskToNotify = xTaskGetCurrentTaskHandle(); /* Handle of the calling task. */

    /* Clear the three status bits relevant to the write operation by calling
xTaskNotifyWait() with the ulBitsToClearOnExit parameter set to
CLOUD_WRITE_STATUS_BIT_MASK, and a block time of 0\. The current notification value is
not required, so the pulNotificationValue parameter is set to NULL. */
    xTaskNotifyWait( 0, CLOUD_WRITE_STATUS_BIT_MASK, NULL, 0 );

    /* Send the request to the server task. */
    xQueueSend( xServerTaskQueue, &xRequest, portMAX_DELAY );

    /* Wait for a notification from the server task. The server task writes a bitwise status
code into this task’s notification value, which is written to ulNotificationValue. */
    xTaskNotifyWait( 0,                             /* No bits cleared on entry. | */
                    CLOUD_WRITE_STATUS_BIT_MASK,     /* Clear relevant bits to 0 on exit. */
                    &ulNotificationValue,             /* Notified value.*/
                    pdMS_TO_TICKS( 250 ) );         /* Wait a maximum of 250ms. */

    /* Return the status code to the calling task. */
    return ( ulNotificationValue & CLOUD_WRITE_STATUS_BIT_MASK );

server task

void ServerTask( void *pvParameters )
{
    CloudCommand_t xCommand;
    uint32_t ulBitwiseStatusCode;

    for( ;; )
    {
        /* Wait for the next message. */
        xQueueReceive( xServerTaskQueue, &xCommand, portMAX_DELAY );

        /* Was it a read or write request? */
        switch( xCommand.eOperation )
        {
            case eWrite:

                /* Send the data to the remote cloud server. SetCloudData() returns a bitwise status code that only uses the bits defined by the CLOUD_WRITE_STATUS_BIT_MASK definition (shown in Listing 161). */
                ulBitwiseStatusCode = SetCloudData( xCommand.ulDataID, xCommand.ulDataValue );

                /* Send a notification to the task that made the write request. The eSetBits action is used so any status bits set in ulBitwiseStatusCode will be set in the notification value of the task being notified. All the other bits remain unchanged. The handle of the task is obtained from the CloudCommand_t structure. */
                xTaskNotify( xCommand.xTaskToNotify, /* The task’s handle is in the structure. */
                             ulBitwiseStatusCode,    /* Cloud data sent as notification value. */
                             eSetBits );
                break;

                /* Other switch cases go here. */
        }
    }
}




posted @ 2021-01-06 13:29  JerryZheng2020  阅读(2481)  评论(0编辑  收藏  举报