版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_33894122/article/details/85337189
前言
freertos在V8.0.0版本之后就加入了task notify 的功能,据说比信号量 队列传递消息等更快更高效且占系统资源更少,在每个TCB结构体中多占用5个字节空间而已,相对于queue实现的信号量等来说着实轻便了很多;但是有一个很大的问题,RTOS通知任务才可使用只有一个任务可以被该接收者的事件,虽然这种情况在大多数真实应用中。
可以通过一些特定参数设置来实现二值信号量,二值信号邮箱等操作。
代码实现
通知接收
task通过调用ulTaskNotifyTake
、xTaskNotifyWait
来等待任务通知,ulTaskNotifyTake的返回值是收到的任务通知值;xTaskNotifyWait的返回值是是否收到任务通知。
这两个函数的用处还是有点不一样的,注意从代码上去区分。
uint32_t ulTaskNotifyTake( BaseType_t xClearCountOnExit, TickType_t xTicksToWait )
{
uint32_t ulReturn;
taskENTER_CRITICAL();
{
/*只有当通知计数不是非零时才阻塞。只有当通知计数 是 零时才阻塞。*/
if( pxCurrentTCB->ulNotifiedValue == 0UL ){
/* 标注这个task正在等待通知*/
pxCurrentTCB->ucNotifyState = taskWAITING_NOTIFICATION;
if( xTicksToWait > ( TickType_t ) 0 )
{//等待tick数大于0的话,需要加入 pxDelayedTaskList中去
prvAddCurrentTaskToDelayedList( xTicksToWait, pdTRUE );
/* 执行可移植 导致任务切换的函数*/
portYIELD_WITHIN_API();
}
}
}
taskEXIT_CRITICAL();//真正的·1任务切换发生在这里
taskENTER_CRITICAL();
{
ulReturn = pxCurrentTCB->ulNotifiedValue;
/*此处应该是已经返回,有两种情况
1.有task2将通知发送给本任务了,然后加入到readylists被执行了
2.xTicksToWait过完了*/
if( ulReturn != 0UL ){
if( xClearCountOnExit != pdFALSE ){//如果需要清空任务通知值的话
pxCurrentTCB->ulNotifiedValue = 0UL;
}
}
/*修改任务通知状态 为 不需要等待 任务通知*/
pxCurrentTCB->ucNotifyState = taskNOT_WAITING_NOTIFICATION;
}
taskEXIT_CRITICAL();
return ulReturn;
}
BaseType_t xTaskNotifyWait( uint32_t ulBitsToClearOnEntry, uint32_t ulBitsToClearOnExit,
uint32_t *pulNotificationValue, TickType_t xTicksToWait ){
BaseType_t xReturn;
taskENTER_CRITICAL();{
/* 只有在通知尚未挂起时才阻塞。*/
if( pxCurrentTCB->ucNotifyState != taskNOTIFICATION_RECEIVED ){
/*可能在发送的通知的 task或中断 中 ,清除任务通知的相应的bit
这可以用来清除 该位 为 0。*/
pxCurrentTCB->ulNotifiedValue &= ~ulBitsToClearOnEntry;
/*标注这个task正在等待通知*/
pxCurrentTCB->ucNotifyState = taskWAITING_NOTIFICATION;
if( xTicksToWait > ( TickType_t ) 0 ){
prvAddCurrentTaskToDelayedList( xTicksToWait, pdTRUE );
/* 执行可移植 导致任务切换的函数*/
portYIELD_WITHIN_API();
}
}
}
taskEXIT_CRITICAL();
taskENTER_CRITICAL();{
if( pulNotificationValue != NULL ){
/* 输出当前通知值,该值可以更改,也可以不更改。*/
*pulNotificationValue = pxCurrentTCB->ulNotifiedValue;
}
/* 如果设置了ucNotifyValue,那么要么任务从未输入阻塞状态
(因为通知已经挂起)或任务因通知解除阻塞。
否则,任务由于超时而解除阻塞*/
if( pxCurrentTCB->ucNotifyState == taskWAITING_NOTIFICATION ){
/* 没有接收到任务通知 */
xReturn = pdFALSE;
}else{/* 接收到任务通知的话 */
/* A notification was already pending or a notification was
received while the task was waiting.
通知已经挂起或已经挂起在任务等待时收到。*/
pxCurrentTCB->ulNotifiedValue &= ~ulBitsToClearOnExit;
xReturn = pdTRUE;
}
/*修改任务通知状态 为 不需要等待 任务通知*/
pxCurrentTCB->ucNotifyState = taskNOT_WAITING_NOTIFICATION;
}
taskEXIT_CRITICAL();
return xReturn;
}
通知发送
BaseType_t xTaskGenericNotify( TaskHandle_t xTaskToNotify, uint32_t ulValue,
eNotifyAction eAction, uint32_t *pulPreviousNotificationValue ){
TCB_t * pxTCB;
BaseType_t xReturn = pdPASS;
uint8_t ucOriginalNotifyState;
pxTCB = ( TCB_t * ) xTaskToNotify;
taskENTER_CRITICAL();
{/*pulPreviousNotificationValue 和 ucOriginalNotifyState 保存原来任务通知值和通知状态*/
if( pulPreviousNotificationValue != NULL ){
*pulPreviousNotificationValue = pxTCB->ulNotifiedValue;
}
ucOriginalNotifyState = pxTCB->ucNotifyState;
pxTCB->ucNotifyState = taskNOTIFICATION_RECEIVED;
switch( eAction )
{/*根据设置的任务通知操作来 操作 被通知任务的通知值*/
case eSetBits ://设置相应的位
pxTCB->ulNotifiedValue |= ulValue;
break;
case eIncrement ://仅仅给通知值+1
( pxTCB->ulNotifiedValue )++;
break;
case eSetValueWithOverwrite ://不管原来任务通知值有没有,直接覆盖
pxTCB->ulNotifiedValue = ulValue;
break;
case eSetValueWithoutOverwrite :
//如果原来任务已经接收到了通知值,那么不操作返回通知失败,否则就通知,即不覆盖式操作
if( ucOriginalNotifyState != taskNOTIFICATION_RECEIVED ){
pxTCB->ulNotifiedValue = ulValue;
}else{
/* 通知值 通知不到 应该被通知的task */
xReturn = pdFAIL;
}
break;
case eNoAction://不操作,仅仅告诉被通知任务 有任务通知了 而已
break;
}
/* 如果任务处于阻塞状态,特别是等待通知,那么现在就解除阻塞。
就是常规列表操作*/
if( ucOriginalNotifyState == taskWAITING_NOTIFICATION )
{
( void ) uxListRemove( &( pxTCB->xStateListItem ) );
prvAddTaskToReadyList( pxTCB );
if( pxTCB->uxPriority > pxCurrentTCB->uxPriority ){
/*被通知任务的优先级高于当前执行任务,因此需要任务切换。*/
taskYIELD_IF_USING_PREEMPTION();
}
}
}
taskEXIT_CRITICAL();
return xReturn;
}
由于中断不能有延时或者休眠操作,所以等待通知是不能在中断中的,而freertos提供了中断中的发送任务通知的函数,其操作跟普通notify很相似,之后再分析所有系统api在中断中的版本。