使用任务通知将事件或数据发送到任务比使用队列,信号量或事件组执行等效操作要快得多。同样,与使用队列,信号量或事件组执行等效操作相比,使用任务通知将事件或数据发送到任务所需的RAM要少得多。这是因为必须先创建每个通信对象(队列,信号灯或事件组),然后才能使用它,而启用任务通知功能具有固定的开销,镶嵌在任务控制块TCB中,如下:
#if( configUSE_TASK_NOTIFICATIONS == 1 )
volatile uint32_t ulNotifiedValue;
volatile uint8_t ucNotifyState;
#endif
对于信号量和队列,在每次使用之前都需要先创建,其创建的控制块大小如下:
typedef struct QueueDefinition /* The old naming convention is used to prevent breaking kernel aware debuggers. */
{
int8_t *pcHead; /*< Points to the beginning of the queue storage area. */
int8_t *pcWriteTo; /*< Points to the free next place in the storage area. */
union
{
QueuePointers_t xQueue; /*< Data required exclusively when this structure is used as a queue. */
SemaphoreData_t xSemaphore; /*< Data required exclusively when this structure is used as a semaphore. */
} u;
List_t xTasksWaitingToSend; /*< List of tasks that are blocked waiting to post onto this queue. Stored in priority order. */
List_t xTasksWaitingToReceive; /*< List of tasks that are blocked waiting to read from this queue. Stored in priority order. */
volatile UBaseType_t uxMessagesWaiting;/*< The number of items currently in the queue. */
UBaseType_t uxLength; /*< The length of the queue defined as the number of items it will hold, not the number of bytes. */
UBaseType_t uxItemSize; /*< The size of each items that the queue will hold. */
volatile int8_t cRxLock; /*< Stores the number of items received from the queue (removed from the queue) while the queue was locked. Set to queueUNLOCKED when the queue is not locked. */
volatile int8_t cTxLock; /*< Stores the number of items transmitted to the queue (added to the queue) while the queue was locked. Set to queueUNLOCKED when the queue is not locked. */
#if( ( configSUPPORT_STATIC_ALLOCATION == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) )
uint8_t ucStaticallyAllocated; /*< Set to pdTRUE if the memory used by the queue was statically allocated to ensure no attempt is made to free the memory. */
#endif
#if ( configUSE_QUEUE_SETS == 1 )
struct QueueDefinition *pxQueueSetContainer;
#endif
#if ( configUSE_TRACE_FACILITY == 1 )
UBaseType_t uxQueueNumber;
uint8_t ucQueueType;
#endif
} xQUEUE;
事件组:
typedef struct EventGroupDef_t
{
EventBits_t uxEventBits;
List_t xTasksWaitingForBits; /*< List of tasks waiting for a bit to be set. */
#if( configUSE_TRACE_FACILITY == 1 )
UBaseType_t uxEventGroupNumber;
#endif
#if( ( configSUPPORT_STATIC_ALLOCATION == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) )
uint8_t ucStaticallyAllocated; /*< Set to pdTRUE if the event group is statically allocated to ensure no attempt is made to free the memory. */
#endif
} EventGroup_t;
从上可以看出,与通信对象相比,任务通知速度更快并且使用的RAM更少,但是任务通知不能在所有情况下都使用,下面列举了几种情况:
1. 将事件或数据发送到ISR
通信对象可用于从ISR向任务以及从任务向ISR发送事件和数据。
任务通知可用于将事件和数据从ISR发送到任务,但不能用于将事件或数据从任务发送到ISR。
2. 启用多个接收任务
任何知道其句柄(可能是队列句柄,信号量句柄或事件组句柄)的任务或ISR都可以访问该通信对象。任何数量的任务和ISR都可以处理发送到任何给定通信对象的事件或数据。
任务通知直接发送到接收任务,因此只能由发送通知的任务处理。但是,这在实际情况下很少有限制,因为尽管有多个任务和ISR发送到同一通信对象是很常见的,但是,很少有多个任务和ISR从同一个通信对象接收。
3. 缓冲多个数据项
队列是一种通讯对象,一次可以容纳多个数据项。已发送到队列但尚未从队列接收的数据被缓冲在队列对象中。
任务通知通过更新接收任务的通知值将数据发送到任务。任务的通知值一次只能保存一个值。
4. 广播多个任务
事件组是一个通信对象,可用于一次将事件发送到多个任务。
任务通知直接发送到接收任务,因此只能由接收任务处理。
5. 在阻塞状态下等待发送完成
如果通信对象暂时处于无法再写入任何数据或事件的状态(例如,当队列已满时,无法再向该队列发送任何数据),则尝试写入该对象的任务可以(可选)进入“阻塞”状态以等待其写入操作完成。
如果任务尝试将任务通知发送到已经有待处理的通知的任务,则发送任务不可能在“阻塞”状态下等待接收任务重置其通知状态。可以看出,在使用任务通知的实际情况下,这很少是一种限制。