FreeRTOS任务通知、事件、信号量的对比

使用任务通知将事件或数据发送到任务比使用队列,信号量或事件组执行等效操作要快得多。同样,与使用队列,信号量或事件组执行等效操作相比,使用任务通知将事件或数据发送到任务所需的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. 在阻塞状态下等待发送完成

如果通信对象暂时处于无法再写入任何数据或事件的状态(例如,当队列已满时,无法再向该队列发送任何数据),则尝试写入该对象的任务可以(可选)进入“阻塞”状态以等待其写入操作完成。

如果任务尝试将任务通知发送到已经有待处理的通知的任务,则发送任务不可能在“阻塞”状态下等待接收任务重置其通知状态。可以看出,在使用任务通知的实际情况下,这很少是一种限制。

 

你可能感兴趣的:(FreeRTOS)