【freertos】013-任务通知及其实现细节
前言
参考:
- https://www.freertos.org/RTOS-task-notifications.html
- 原文:https://www.cnblogs.com/lizhuming/p/16557005.html
任务通知实现原理个人构想
任务通知的实现机制和消息队列和事件标志机制不一样。
消息队列和事件标志机制实现需要的资源,数据结构都是需要申请,创建的,独立于内核,分离于任务的组件。
这些组件的实现构想都是拿个内存块组成数据结构,一部分内存做信息传递,一部分内存做阻塞链表等等,然后通过封装各种API实现各种对这个数据结构的骚操作而形成的任务间通信机制。
而任务通知,首先内存源自任务控制块,这个内存的数据结构并未确定,所以我们能把这个内存做信息传递。
阻塞部分,我们可以利用系统的延时链表,做个等待信息阻塞。
不过没有发送阻塞,因为向某个任务发送信息只是向某个任务控制块写入数据的骚操作而已,没有因为没有空间而阻塞的这个说法,因为任务控制块根本就没有给被写入失败而阻塞的链表的内存。
明白了任务通知的实现原理,就知道这玩意是啥了。
任务通知概念
每个RTOS任务都有一个任务通知数组。
每个任务通知都有一个通知状态,可以是pending
或者not pending
,并且有一个32位的通知值。
常量configTASK_NOTIFICATION_ARRAY_ENTRIES
设置任务通知数组中的索引数量。
注意:在FreeRTOS V10.4.0之前,任务只有一个任务通知,而不是一组通知。
任务的通知是直接发送给任务的事件,而不是通过队列、事件组或信号量等中间对象间接发送给任务。
向任务发送直接到任务的通知会将目标任务通知的状态设置为pending
。
就像一个任务可以阻塞一个中间对象,比如一个信号量,以等待该信号量可用一样,一个任务也可以阻塞一个任务通知,以等待该通知的状态变为pending
。
任务通知实现各种IPC
任务通知方式可以实现计数信号量,二值信号量,事件标志组和消息邮箱(消息邮箱就是消息队列长度为1的情况)。
使用方法与前面章节讲解的事件标志组和信号量基本相同,只是换了不同的函数来实现。
任务通知方式实现的计数信号量,二值信号量,事件标志组和消息邮箱是通过修改任务对应任务控制块的ulNotifiedValue
数组成员实现:
- 覆盖该值,无论接收任务是否读取了被覆盖的值。(消息邮箱)
- 仅当接收任务已读取才能写入。(消息队列)
- 在值中设置一个或多个位。(事件组)
- 增加(加1)值。(信号量)
调用xTaskNotifyWait()
或xTaskNotifyWaitIndexed()
来读取一个通知值,将该通知的状态清除为not pending
。
通知状态也可以通过调用xTaskNotifyStateClear()
或xTaskNotifyStateClearIndexed()
显式地设置为not pending
。
任务通知性能优势
使用任务通知比通过信号量等ICP通信方式解除阻塞的任务要快45%,并且更加省RAM内存空间。
任务通知使用注意
- freertos的任务通知功能默认是启用的,可以通过在FreeRTOSConfig.h中将
configUSE_TASK_NOTIFICATIONS
设置为0来关闭。 - RTOS 任务通知只能在只有一个任务可以作为事件的接收者时使用。
- 数组中的每个通知都是独立操作的。一个任务一次只能阻塞数组中的一个通知,并且不会被发送到任何其他数组索引的通知解除阻塞。
- 在使用 RTOS 任务通知代替队列的情况下:虽然接收任务可以在阻塞状态等待通知(因此不消耗任何 CPU 时间),但如果发送不能立即完成,则发送任务不能在阻塞状态下等待发送完成。
- FreeRTOS
Stream
和Message Buffers
在数组索引0
处使用任务通知。如果想在调用Stream
或Message Buffer
API 函数时保持任务通知的状态,则在数组索引大于0
处使用任务通知。
任务通知数据结构
任务通知是任务控制块的资源。
/* 任务控制块 */
typedef struct tskTaskControlBlock
{
volatile StackType_t * pxTopOfStack; /*< 指向放在任务堆栈上的最后一项的位置。这必须是TCB结构体的第一个成员。 */
#if ( portUSING_MPU_WRAPPERS == 1 )
xMPU_SETTINGS xMPUSettings; /*< The MPU settings are defined as part of the port layer. THIS MUST BE THE SECOND MEMBER OF THE TCB STRUCT. */
#endif
ListItem_t xStateListItem; /*< The list that the state list item of a task is reference from denotes the state of that task (Ready, Blocked, Suspended ). */
ListItem_t xEventListItem; /*< Used to reference a task from an event list. */
UBaseType_t uxPriority; /*< The priority of the task. 0 is the lowest priority. */
StackType_t * pxStack; /*< Points to the start of the stack. */
char pcTaskName[ configMAX_TASK_NAME_LEN ]; /*< Descriptive name given to the task when created. Facilitates debugging only. */ /*lint !e971 Unqualified char types are allowed for strings and single characters only. */
#if ( ( portSTACK_GROWTH > 0 ) || ( configRECORD_STACK_HIGH_ADDRESS == 1 ) )
StackType_t * pxEndOfStack; /*< Points to the highest valid address for the stack. */
#endif
#if ( portCRITICAL_NESTING_IN_TCB == 1 )
UBaseType_t uxCriticalNesting; /*< Holds the critical section nesting depth for ports that do not maintain their own count in the port layer. */
#endif
#if ( configUSE_TRACE_FACILITY == 1 )
UBaseType_t uxTCBNumber; /*< Stores a number that increments each time a TCB is created. It allows debuggers to determine when a task has been deleted and then recreated. */
UBaseType_t uxTaskNumber; /*< Stores a number specifically for use by third party trace code. */
#endif
#if ( configUSE_MUTEXES == 1 )
UBaseType_t uxBasePriority; /*< The priority last assigned to the task - used by the priority inheritance mechanism. */
UBaseType_t uxMutexesHeld;
#endif
#if ( configUSE_APPLICATION_TASK_TAG == 1 )
TaskHookFunction_t pxTaskTag;
#endif
#if ( configNUM_THREAD_LOCAL_STORAGE_POINTERS > 0 )
void * pvThreadLocalStoragePointers[ configNUM_THREAD_LOCAL_STORAGE_POINTERS ];
#endif
#if ( configGENERATE_RUN_TIME_STATS == 1 )
configRUN_TIME_COUNTER_TYPE ulRunTimeCounter; /*< Stores the amount of time the task has spent in the Running state. */
#endif
#if ( configUSE_NEWLIB_REENTRANT == 1 )
/* Allocate a Newlib reent structure that is specific to this task.
* Note Newlib support has been included by popular demand, but is not
* used by the FreeRTOS maintainers themselves. FreeRTOS is not
* responsible for resulting newlib operation. User must be familiar with
* newlib and must provide system-wide implementations of the necessary
* stubs. Be warned that (at the time of writing) the current newlib design
* implements a system-wide malloc() that must be provided with locks.
*
* See the third party link http://www.nadler.com/embedded/newlibAndFreeRTOS.html
* for additional information. */
struct _reent xNewLib_reent;
#endif
#if ( configUSE_TASK_NOTIFICATIONS == 1 )
volatile uint32_t ulNotifiedValue[ configTASK_NOTIFICATION_ARRAY_ENTRIES ];
volatile uint8_t ucNotifyState[ configTASK_NOTIFICATION_ARRAY_ENTRIES ];
#endif
/* See the comments in FreeRTOS.h with the definition of
* tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE. */
#if ( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 ) /*lint !e731 !e9029 Macro has been consolidated for readability reasons. */
uint8_t ucStaticallyAllocated; /*< Set to pdTRUE if the task is a statically allocated to ensure no attempt is made to free the memory. */
#endif
#if ( INCLUDE_xTaskAbortDelay == 1 )
uint8_t ucDelayAborted;
#endif
#if ( configUSE_POSIX_ERRNO == 1 )
int iTaskErrno;
#endif
} tskTCB;
ulNotifiedValue
:
- 任务通知值数组。
ucNotifyState
:
- 任务通知状态数组。
- 与任务通知值一一对应。
任务通知为什么没有写阻塞?
通过任务通知的数据结构就知道,其数据结构嵌入在任务控制块中,一个通知对应一个通知状态。
读阻塞实现
读阻塞很容易实现,读取当前任务通知不满足条件时,可以将其阻塞到延时链表或者挂起链表,当有任务往这个任务通知发送数据时,满足当前任务通知要求,可以将当前任务通知从延时链表或者挂起链表解除阻塞。
写阻塞实现(修改内核组件实现)
先按照当前官方任务通知数据结构分析,并不能实现。
试想下,如果不能写,就进入阻塞,可以参考读阻塞一样先插入延时链表或挂起链表,但是怎么唤醒呢?当任务通知可以写时,通过什么方式找到这个写阻塞的任务呢?
任务通知这个对象嵌入到任务控制块中,归属于某个任务,其它任务往这里写,发生阻塞,当可写时,需要解除阻塞时,是找不到这个写任务并将其唤醒的。
以上就是按照freertos任务通知数据结构基础来演进分析。
其实解决了写阻塞的唤醒,就可以解决写阻塞这个问题了。
也很简单,在任务通知这个数据结构中再添加一个写阻塞链表即可。有兴趣的同学可以自己改写下内核源码实现。
任务通知类型
typedef enum
{
eNoAction = 0, /* 通知任务而不更新其通知值 */
eSetBits, /* 事件组。按位或设置。 */
eIncrement, /* 信号量。自增。 */
eSetValueWithOverwrite, /* 邮箱。覆盖希写入 */
eSetValueWithoutOverwrite /* 队列。对应通知为空时才写入。 */
} eNotifyAction;
任务通知状态
/* 任务通知状态 */
#define taskNOT_WAITING_NOTIFICATION ( ( uint8_t ) 0 ) /* 初始值为0,所以默认态也应该为0 */
#define taskWAITING_NOTIFICATION ( ( uint8_t ) 1 )
#define taskNOTIFICATION_RECEIVED ( ( uint8_t ) 2 )
taskNOT_WAITING_NOTIFICATION
:任务没有在等待通知。
taskWAITING_NOTIFICATION
:任务在等待通知。
taskNOTIFICATION_RECEIVED
:任务接收到通知。
发送任务通知基函数
在分析任务通知做信号量前先分析一个任务通知基函数xTaskGenericNotify()
,任务消息、任务邮箱、任务事件这些发送端都是通过封装该API实现的。
xTaskGenericNotify()
:
xTaskToNotify
:任务句柄。即是任务通知的对象。uxIndexToNotify
:- 通知将发送到的目标任务通知值数组中的索引。
uxIndexToNotify
必须小于configTASK_NOTIFICATION_ARRAY_ENTRIES
。
ulValue
:用于更新目标任务的通知值。pulPreviousNotificationValue
:任务原本的通知值返回。(回传)
#if ( configUSE_TASK_NOTIFICATIONS == 1 )
BaseType_t xTaskGenericNotify( TaskHandle_t xTaskToNotify,
UBaseType_t uxIndexToNotify,
uint32_t ulValue,
eNotifyAction eAction,
uint32_t * pulPreviousNotificationValue )
{
TCB_t * pxTCB;
BaseType_t xReturn = pdPASS;
uint8_t ucOriginalNotifyState;
/* 任务通知索引不能溢出 */
configASSERT( uxIndexToNotify < configTASK_NOTIFICATION_ARRAY_ENTRIES );
/* 参数校验 */
configASSERT( xTaskToNotify );
pxTCB = xTaskToNotify;
taskENTER_CRITICAL(); /* 进入临界 */
{
if( pulPreviousNotificationValue != NULL ) /* 需要回传 */
{
/* 回传当前未处理的任务通知值 */
*pulPreviousNotificationValue = pxTCB->ulNotifiedValue[ uxIndexToNotify ];
}
/* 获取该任务通知的状态 */
ucOriginalNotifyState = pxTCB->ucNotifyState[ uxIndexToNotify ];
/* 更新为pendig状态,表示该任务通知接收到通知了 */
pxTCB->ucNotifyState[ uxIndexToNotify ] = taskNOTIFICATION_RECEIVED;
/* 按照不同通知类型进行操作 */
switch( eAction )
{
case eSetBits: /* 事件组。bit处理。与原通知值按位或。 */
pxTCB->ulNotifiedValue[ uxIndexToNotify ] |= ulValue;
break;
case eIncrement: /* 信号量。释放信号量。原通知值+1 */
( pxTCB->ulNotifiedValue[ uxIndexToNotify ] )++;
break;
case eSetValueWithOverwrite: /* 邮箱。覆盖写入。 */
pxTCB->ulNotifiedValue[ uxIndexToNotify ] = ulValue;
break;
case eSetValueWithoutOverwrite: /* 队列。没有通知时才写入。 */
if( ucOriginalNotifyState != taskNOTIFICATION_RECEIVED )
{
pxTCB->ulNotifiedValue[ uxIndexToNotify ] = ulValue;
}
else
{
/* 如果已有通知,则无法写入。 */
xReturn = pdFAIL;
}
break;
case eNoAction:
/* 只触发通知,不更新内容 */
break;
default:
/* 如果通知类型枚举错误,强制断言 */
configASSERT( xTickCount == ( TickType_t ) 0 );
break;
}
traceTASK_NOTIFY( uxIndexToNotify );
if( ucOriginalNotifyState == taskWAITING_NOTIFICATION ) /* 如果任务阻塞等待通知 */
{
/* 解除任务状态 */
listREMOVE_ITEM( &( pxTCB->xStateListItem ) );
/* 重新插入就绪链表 */
prvAddTaskToReadyList( pxTCB );
/* 如果任务等待通知,逻辑上就不会阻塞在事件链表中 */
configASSERT( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) == NULL );
#if ( configUSE_TICKLESS_IDLE != 0 )
{
/* 解锁任务后,更新下下次解锁延时链表的节拍值,否则可能会误导低功耗模式提前唤醒 */
prvResetNextTaskUnblockTime();
}
#endif
if( pxTCB->uxPriority > pxCurrentTCB->uxPriority )
{
/* 解锁的任务优先级更高就需要触发任务切换。(退出临界后) */
taskYIELD_IF_USING_PREEMPTION();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
taskEXIT_CRITICAL(); /* 退出临界 */
return xReturn;
}
#endif
任务事件等待通知基函数
需要明白任务通知并不是独立的IPC通信组件,而是基于任务控制块的通信组件,所以只能运行该任务时调用等待发给该任务的通知,所以上下文只能是线程形,没有中断形。这样,实现的API只需要实现任务版即可。
接收任务通知的基函数是xTaskNotifyWaitIndexed()
:
uxIndexToWait
:等待的通知索引。ulBitsToClearOnEntry
:没有接到通知时才生效的参数。标记接收通知前清空通知。- 通知值=通知值& ~(ulBitsToClearOnEntry)
ulBitsToClearOnExit
:收到通知后需要清除的通知的值。- 通知值=通知值& ~(ulBitsToClearOnExit)
pulNotificationValue
:回传退出清除通知前的通知的容器。xTicksToWait
:等待阻塞超时时间。
#if ( configUSE_TASK_NOTIFICATIONS == 1 )
BaseType_t xTaskGenericNotifyWait( UBaseType_t uxIndexToWait,
uint32_t ulBitsToClearOnEntry,
uint32_t ulBitsToClearOnExit,
uint32_t * pulNotificationValue,
TickType_t xTicksToWait )
{
BaseType_t xReturn;
configASSERT( uxIndexToWait < configTASK_NOTIFICATION_ARRAY_ENTRIES );
taskENTER_CRITICAL();
{
/* 当前没有通知 */
if( pxCurrentTCB->ucNotifyState[ uxIndexToWait ] != taskNOTIFICATION_RECEIVED )
{
/* 接收通知前需要清空通知对应的bit */
pxCurrentTCB->ulNotifiedValue[ uxIndexToWait ] &= ~ulBitsToClearOnEntry;
/* 标记下当前任务等待当前通知 */
pxCurrentTCB->ucNotifyState[ uxIndexToWait ] = taskWAITING_NOTIFICATION;
if( xTicksToWait > ( TickType_t ) 0 ) /* 需要阻塞 */
{
/* 从就绪链表迁移到延时链表或者挂起链表。即是进入阻塞态 */
prvAddCurrentTaskToDelayedList( xTicksToWait, pdTRUE );
traceTASK_NOTIFY_WAIT_BLOCK( uxIndexToWait );
/* 触发任务调度。(退出临界后才执行实际的调度) */
portYIELD_WITHIN_API();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
else /* 当前已经有通知了 */
{
mtCOVERAGE_TEST_MARKER();
}
}
taskEXIT_CRITICAL(); /* 退出临界 */
/* 如果前面进入了阻塞,再跑回这里是唤醒后了 */
taskENTER_CRITICAL(); /* 进入临界 */
{
traceTASK_NOTIFY_WAIT( uxIndexToWait );
if( pulNotificationValue != NULL ) /* 需要回传 */
{
/* 回传退出前的通知 */
*pulNotificationValue = pxCurrentTCB->ulNotifiedValue[ uxIndexToWait ];
}
if( pxCurrentTCB->ucNotifyState[ uxIndexToWait ] != taskNOTIFICATION_RECEIVED ) /* 没有通知 */
{
/* 没有通知,要么没有进入阻塞,要么进入阻塞后因非通知原因而解锁,如阻塞超时或被强制解锁。 */
xReturn = pdFALSE; /* 返回接收失败 */
}
else /* 收到通知 */
{
/* 收到通知的可能:1. 接收前已经收到通知;2. 阻塞时收到通知。 */
pxCurrentTCB->ulNotifiedValue[ uxIndexToWait ] &= ~ulBitsToClearOnExit;
xReturn = pdTRUE; /* 返回接收成功 */
}
/* 更新通知状态为初始态 */
pxCurrentTCB->ucNotifyState[ uxIndexToWait ] = taskNOT_WAITING_NOTIFICATION;
}
taskEXIT_CRITICAL(); /* 退出临界 */
return xReturn;
}
#endif /* configUSE_TASK_NOTIFICATIONS */
任务全功能通知函数
xTaskNotify()
和xTaskNotifyIndexed()
,对比内部通用函数,只是没有回传功能。
#define xTaskNotify( xTaskToNotify, ulValue, eAction ) \
xTaskGenericNotify( ( xTaskToNotify ), ( tskDEFAULT_INDEX_TO_NOTIFY ), ( ulValue ), ( eAction ), NULL )
#define xTaskNotifyIndexed( xTaskToNotify, uxIndexToNotify, ulValue, eAction ) \
xTaskGenericNotify( ( xTaskToNotify ), ( uxIndexToNotify ), ( ulValue ), ( eAction ), NULL )
中断版:xTaskNotifyFromISR()
和xTaskNotifyIndexedFromISR()
,对比内部通用函数中断版,只是没有回传功能。
#define xTaskNotifyFromISR( xTaskToNotify, ulValue, eAction, pxHigherPriorityTaskWoken ) \
xTaskGenericNotifyFromISR( ( xTaskToNotify ), ( tskDEFAULT_INDEX_TO_NOTIFY ), ( ulValue ), ( eAction ), NULL, ( pxHigherPriorityTaskWoken ) )
#define xTaskNotifyIndexedFromISR( xTaskToNotify, uxIndexToNotify, ulValue, eAction, pxHigherPriorityTaskWoken ) \
xTaskGenericNotifyFromISR( ( xTaskToNotify ), ( uxIndexToNotify ), ( ulValue ), ( eAction ), NULL, ( pxHigherPriorityTaskWoken ) )
任务通知全功能等待通知函数
xTaskNotifyWait()
和xTaskNotifyWaitIndexed()
#define xTaskNotifyWait( ulBitsToClearOnEntry, ulBitsToClearOnExit, pulNotificationValue, xTicksToWait ) \
xTaskGenericNotifyWait( tskDEFAULT_INDEX_TO_NOTIFY, ( ulBitsToClearOnEntry ), ( ulBitsToClearOnExit ), ( pulNotificationValue ), ( xTicksToWait ) )
#define xTaskNotifyWaitIndexed( uxIndexToWaitOn, ulBitsToClearOnEntry, ulBitsToClearOnExit, pulNotificationValue, xTicksToWait ) \
xTaskGenericNotifyWait( ( uxIndexToWaitOn ), ( ulBitsToClearOnEntry ), ( ulBitsToClearOnExit ), ( pulNotificationValue ), ( xTicksToWait ) )
任务通知之信号量
通过学习了上述两个内部通用任务通知API后,我们可以通过封装这两个API来实现对应IPC通信概念的专用API。
释放信号量
任务通知之释放信号量:xTaskNotifyGive()
和xTaskGenericNotify()
#define xTaskNotifyGive( xTaskToNotify ) \
xTaskGenericNotify( ( xTaskToNotify ), ( tskDEFAULT_INDEX_TO_NOTIFY ), ( 0 ), eIncrement, NULL )
#define xTaskNotifyGiveIndexed( xTaskToNotify, uxIndexToNotify ) \
xTaskGenericNotify( ( xTaskToNotify ), ( uxIndexToNotify ), ( 0 ), eIncrement, NULL )
中断版:(内部源码可以自己分析,参考上述通用API中的信号量逻辑,只是没有阻塞机制)
#define vTaskNotifyGiveFromISR( xTaskToNotify, pxHigherPriorityTaskWoken ) \
vTaskGenericNotifyGiveFromISR( ( xTaskToNotify ), ( tskDEFAULT_INDEX_TO_NOTIFY ), ( pxHigherPriorityTaskWoken ) );
#define vTaskNotifyGiveIndexedFromISR( xTaskToNotify, uxIndexToNotify, pxHigherPriorityTaskWoken ) \
vTaskGenericNotifyGiveFromISR( ( xTaskToNotify ), ( uxIndexToNotify ), ( pxHigherPriorityTaskWoken ) );
获取信号量
ulTaskNotifyTake()
和ulTaskNotifyTakeIndexed()
如果参数xClearCountOnExit
设置为pdTRUE
则用于任务通知的二值信号量。
#define ulTaskNotifyTake( xClearCountOnExit, xTicksToWait ) \
ulTaskGenericNotifyTake( ( tskDEFAULT_INDEX_TO_NOTIFY ), ( xClearCountOnExit ), ( xTicksToWait ) )
#define ulTaskNotifyTakeIndexed( uxIndexToWaitOn, xClearCountOnExit, xTicksToWait ) \
ulTaskGenericNotifyTake( ( uxIndexToWaitOn ), ( xClearCountOnExit ), ( xTicksToWait ) )
任务通知之消息邮箱
xTaskNotifyAndQuery()
和xTaskNotifyAndQueryIndexed()
#define xTaskNotifyAndQuery( xTaskToNotify, ulValue, eAction, pulPreviousNotifyValue ) \
xTaskGenericNotify( ( xTaskToNotify ), ( tskDEFAULT_INDEX_TO_NOTIFY ), ( ulValue ), ( eAction ), ( pulPreviousNotifyValue ) )
#define xTaskNotifyAndQueryIndexed( xTaskToNotify, uxIndexToNotify, ulValue, eAction, pulPreviousNotifyValue ) \
xTaskGenericNotify( ( xTaskToNotify ), ( uxIndexToNotify ), ( ulValue ), ( eAction ), ( pulPreviousNotifyValue ) )
xTaskNotifyAndQueryIndexed()
执行与xTaskNotifyIndexed()
相同的操作,另外它还在附加的pulPreviousNotifyValue
参数中返回目标任务的之前的通知值(函数被调用时的通知值,而不是函数返回时的通知值)。(对应的xTaskNotifyAndQuery()
和xTaskNotify()
差别也一样)。
中断版:
#define xTaskNotifyAndQueryIndexedFromISR( xTaskToNotify, uxIndexToNotify, ulValue, eAction, pulPreviousNotificationValue, pxHigherPriorityTaskWoken ) \
xTaskGenericNotifyFromISR( ( xTaskToNotify ), ( uxIndexToNotify ), ( ulValue ), ( eAction ), ( pulPreviousNotificationValue ), ( pxHigherPriorityTaskWoken ) )
#define xTaskNotifyAndQueryFromISR( xTaskToNotify, ulValue, eAction, pulPreviousNotificationValue, pxHigherPriorityTaskWoken ) \
xTaskGenericNotifyFromISR( ( xTaskToNotify ), ( tskDEFAULT_INDEX_TO_NOTIFY ), ( ulValue ), ( eAction ), ( pulPreviousNotificationValue ), ( pxHigherPriorityTaskWoken ) )
任务通知之事件组标志
并没有封装出专门的事件标志API,但是可以使用全能版的API实现。用户也可以自己封装。
清除任务通知状态
如果一个通知被发送到通知数组中的一个索引,那么该索引的通知被称为pending
,直到任务读取它的通知值或通过调用xTaskNotifyStateClear()
显式地清除通知状态为not pending
。
xTaskNotifyStateClear()
和xTaskNotifyStateClearIndexed()
#define xTaskNotifyStateClear( xTask ) \
xTaskGenericNotifyStateClear( ( xTask ), ( tskDEFAULT_INDEX_TO_NOTIFY ) )
#define xTaskNotifyStateClearIndexed( xTask, uxIndexToClear ) \
xTaskGenericNotifyStateClear( ( xTask ), ( uxIndexToClear ) )
内部实现:xTaskGenericNotifyStateClear()
:
#if ( configUSE_TASK_NOTIFICATIONS == 1 )
BaseType_t xTaskGenericNotifyStateClear( TaskHandle_t xTask,
UBaseType_t uxIndexToClear )
{
TCB_t * pxTCB;
BaseType_t xReturn;
configASSERT( uxIndexToClear < configTASK_NOTIFICATION_ARRAY_ENTRIES );
/* 如果在这里传递null,那么当前任务的通知状态被清除 */
pxTCB = prvGetTCBFromHandle( xTask );
taskENTER_CRITICAL(); /* 进入临界 */
{
if( pxTCB->ucNotifyState[ uxIndexToClear ] == taskNOTIFICATION_RECEIVED )
{
pxTCB->ucNotifyState[ uxIndexToClear ] = taskNOT_WAITING_NOTIFICATION; /* 如果处于收到通知的状态,则可清除,回到没有等待通知的状态 */
xReturn = pdPASS;
}
else
{
xReturn = pdFAIL;
}
}
taskEXIT_CRITICAL(); /* 退出临界 */
return xReturn;
}
#endif
清除任务通知值
ulTaskNotifyValueClear()
和ulTaskNotifyValueClearIndexed()
:
清除任务通知值,返回的是清除前的任务通知值。
注意:是按bit清除。
#define ulTaskNotifyValueClear( xTask, ulBitsToClear ) \
ulTaskGenericNotifyValueClear( ( xTask ), ( tskDEFAULT_INDEX_TO_NOTIFY ), ( ulBitsToClear ) )
#define ulTaskNotifyValueClearIndexed( xTask, uxIndexToClear, ulBitsToClear ) \
ulTaskGenericNotifyValueClear( ( xTask ), ( uxIndexToClear ), ( ulBitsToClear ) )
内部通用函数:ulTaskGenericNotifyValueClear()
#if ( configUSE_TASK_NOTIFICATIONS == 1 )
uint32_t ulTaskGenericNotifyValueClear( TaskHandle_t xTask,
UBaseType_t uxIndexToClear,
uint32_t ulBitsToClear )
{
TCB_t * pxTCB;
uint32_t ulReturn;
/* 如果在这里传递null,那么当前任务的通知值被清除 */
pxTCB = prvGetTCBFromHandle( xTask );
taskENTER_CRITICAL(); /* 进入临界 */
{
/* 返回清除位之前的通知,然后清除位掩码 */
ulReturn = pxTCB->ulNotifiedValue[ uxIndexToClear ];
pxTCB->ulNotifiedValue[ uxIndexToClear ] &= ~ulBitsToClear;
}
taskEXIT_CRITICAL(); /* 退出临界 */
return ulReturn;
}
#endif /* configUSE_TASK_NOTIFICATIONS */
【freertos】013-任务通知及其实现细节的更多相关文章
- 【freertos】006-任务切换实现细节
前言 任务调度实现的两个核心: 调度器实现:(上一章节已描述调度基础) 任务切换实现. 接口层实现. 原文:李柱明博客:https://www.cnblogs.com/lizhuming/p/1608 ...
- FreeRTOS --(17)任务通知浅析
转载自https://blog.csdn.net/zhoutaopower/article/details/107467305 在 FreeRTOS 中,还有一个东西也可以用作任务与任务,中断与任务的 ...
- IOS之推送通知(本地推送和远程推送)
推送通知和NSNotification是有区别的: NSNotification:是看不到的 推送通知:是可以看到的 IOS中提供了两种推送通知 本地推送通知:(Local Notification) ...
- iOS推送通知
推送通知 此通知非彼通知. NSNotification是抽象的,看不见的,但是可以监听,属于观察者模式的一种设计模式. 推送通知是可见的,能用肉眼看见的,是真正的和用户打交道的通知. 推送通知分为两 ...
- iOS推送服务细节回顾
iOS推送服务细节回顾 之前在做推送功能时候,就总结过一系列证书的制作,OC代码实现和服务器搭建等经验.又过了一段时间了,前前后后对推送服务做了多次的完善和优化,有iOS客户端的,还有本地服务器端的. ...
- Spring(三)--AOP【面向切面编程】、通知类型及使用、切入点表达式
1.概念:Aspect Oriented Programming 面向切面编程 在方法的前后添加方法 2.作用:本质上来说是一种简化代码的方式 继承机制 封装方法 动 ...
- 第三部分:Android 应用程序接口指南---第二节:UI---第七章 通知
第7章 通知 一个通知是一条消息他是显示于你应用程序之外的一个界面中.当你告诉系统要发布一个通知时,它首先作为一个icon出现在通知区域.为了看见通知的细节,用户可以点击通知区域展开一个新的界面.下面 ...
- 不可被忽视的操作系统( FreeRTOS )【2】
本文章总结基于官方FreeRTOS手册,测试系统为ESP32的IDF 4.0 本篇续上一篇<不可被忽视的操作系统( FreeRTOS )[1]> 其中上一篇主要内容为: FreeRTOS介 ...
- iOS面试题
一个区分度很大的面试题 考察一个面试者基础咋样,基本上问一个 @property 就够了: @property 后面可以有哪些修饰符? 线程安全的: atomic,nonatomic 访问权限的 re ...
随机推荐
- 程序分析与优化 - 4 工作列表(worklist)算法
本章是系列文章的第四章,介绍了worklist算法.Worklist算法是图分析的核心算法,可以说学会了worklist算法,编译器的优化方法才算入门.这章学习起来比较吃力,想要融汇贯通的同学,建议多 ...
- [C++STL] set 容器的入门
set 容器的入门 unordered_set:另外头文件,乱序排放,使用哈希表(便于查找) multiset:可以重复存在的集合.用count()读取个数 创建set的几种方式 常规 set< ...
- hexo + typora 图片插入解决办法
Typora 是一款知名的 Markdown 编辑器,简单好用,体验良好.使用 hexo 搭建好博客后,主要是用 Markdown 来编写博客,typora 便是我的首选编辑器.但直接使用 typor ...
- C#获取PLC信息 (KepServer)二
具体应用呢,不多说了,上代码,取长补短就是原创 using OPCAutomation; using System; using System.Collections.Generic; using S ...
- Java内存分析——JavaSE基础
内存分析 堆:存放new的对象和数组,可以被所有线程共享,不会存放别的对象引用 栈 存放基本变量类型(会包含这个基本类型的具体数值) 引用对象的变量(会存放这个引用在堆里的具体地址) 方法区(属于堆的 ...
- java中关于@override注解的使用
@Override是伪代码,表示重写,作用有:1.可以当注释用,方便阅读:2.编译器可以给你验证@Override下面的方法名是否是你父类中所有的,如果没有则报错.例如:如果想重写父类的方法,比如to ...
- SAP Easy tree
*&---------------------------------------------------------------------* *& Include SIMPLE_T ...
- Linux安装MySQL,数据库工具连接Linux的MySQL
1.centOS中默认安装了MariaDB,需要先进行卸载 rpm -qa | grep -i mariadb rpm -e --nodeps 上面查出来的mariadb 2.下载MySQL仓库并安装 ...
- Qt数据可视化(散点图、折线图、柱状图、盒须图、饼状图、雷达图)开发实例
目录 散点图 折线图 柱状图 水平柱状图 水平堆叠图 水平百分比柱状图 盒须图 饼状图 雷达图 Qt散点图.折线图.柱状图.盒须图.饼状图.雷达图开发实例. 在开发过程中我们会使用多各种各样的图 ...
- InheritableThreadLocal 在线程池中进行父子线程间消息传递出现消息丢失的解析
在日常研发过程中,我们经常面临着需要在线程内,线程间进行消息传递,比如在修改一些开源组件源码的过程中,需要将外部参数透传到内部,如果进行方法参数重载,则涉及到的改动量过大,这样,我们可以依赖Threa ...