FreeRTOS 原理 --- 任务通知
简介
任务通知核心包含是一个32位的无符号整数和一个8位的通知状态,这两个在任务控制块中,通知任务就是一个任务或者中断改写另外一个任务中的32位的无符号整数,改写这个整数的方式可以有所不同
- 可以让这个整数加1,模拟信号量
- 设置该整数的指定的某些位,模拟事件组
- 直接选择覆盖或者不覆盖写入,模拟消息队列
volatile uint32_t ulNotifiedValue; volatile uint8_t ucNotifyState;
任务的通知状态:任务通知有三种状态
未等待通知状态:就是任务的初始状态
等待通知状态:当任务在没有通知的时候接收通知时(也就是任务没有接收到通知的时候调用了接收通知的函数,则此时必定接收不到通知,把该任务标记为等待通知状态(去等别的任务发给我通知),任务进入阻塞态),这样做的用处是什么呢? 答:当另外一个任务发通知给该任务时,此时发现任务处于等待通知的状态,然后就可以即可把该任务唤醒。
等待接收通知状态:当有其他任务向任务发送通知,但任务还未接收这一通知的这段期间内(当其他任务给该任务发了通知,但是该任务还没有接收,则将该任务标记为等待接收通知状态),这样做的用处就是当该任务调用了接收通知的函数,发现自身的状态为等待接收通知状态,则不用进入阻塞,直接接收通知值。
那为什么任务通知不能以通知值是否为0判断是否有消息呢?
确实模拟信号量确实是怎么做的,但是如果是模拟队列的话,就不能怎么搞了,因为我发送一个0,0也算是数据
相关的API
xTaskNotify()
ulTaskNotifyTake() // 模拟信号量
xTaskNotifyWait() // 模拟队列、事件组
源码分析
xTaskGenericNotify() 发生通知
BaseType_t xTaskGenericNotify( TaskHandle_t xTaskToNotify, uint32_t ulValue, eNotifyAction eAction, uint32_t *pulPreviousNotificationValue ) { TCB_t * pxTCB; BaseType_t xReturn = pdPASS; uint8_t ucOriginalNotifyState; configASSERT( xTaskToNotify ); pxTCB = ( TCB_t * ) xTaskToNotify; taskENTER_CRITICAL(); { if( pulPreviousNotificationValue != NULL ) { *pulPreviousNotificationValue = pxTCB->ulNotifiedValue; } ucOriginalNotifyState = pxTCB->ucNotifyState; pxTCB->ucNotifyState = taskNOTIFICATION_RECEIVED; // 更新任务的通知状态 switch( eAction ) // 根据入参指定的行为,修改任务通知值 { case eSetBits : pxTCB->ulNotifiedValue |= ulValue; break; case eIncrement : ( pxTCB->ulNotifiedValue )++; break; case eSetValueWithOverwrite : pxTCB->ulNotifiedValue = ulValue; break; case eSetValueWithoutOverwrite : if( ucOriginalNotifyState != taskNOTIFICATION_RECEIVED ) { pxTCB->ulNotifiedValue = ulValue; } else { /* The value could not be written to the task. */ xReturn = pdFAIL; } break; case eNoAction: /* The task is being notified without its notify value being updated. */ break; } traceTASK_NOTIFY(); /* If the task is in the blocked state specifically to wait for a notification then unblock it now. */ if( ucOriginalNotifyState == taskWAITING_NOTIFICATION ) // 如果任务当前正在等通知,说明进入阻塞,唤醒它 { ( void ) uxListRemove( &( pxTCB->xStateListItem ) ); prvAddTaskToReadyList( pxTCB ); /* The task should not have been on an event list. */ configASSERT( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) == NULL ); #if( configUSE_TICKLESS_IDLE != 0 ) { /* If a task is blocked waiting for a notification then xNextTaskUnblockTime might be set to the blocked task's time out time. If the task is unblocked for a reason other than a timeout xNextTaskUnblockTime is normally left unchanged, because it will automatically get reset to a new value when the tick count equals xNextTaskUnblockTime. However if tickless idling is used it might be more important to enter sleep mode at the earliest possible time - so reset xNextTaskUnblockTime here to ensure it is updated at the earliest possible time. */ prvResetNextTaskUnblockTime(); } #endif if( pxTCB->uxPriority > pxCurrentTCB->uxPriority ) { /* The notified task has a priority above the currently executing task so a yield is required. */ taskYIELD_IF_USING_PREEMPTION(); } else { mtCOVERAGE_TEST_MARKER(); } } else { mtCOVERAGE_TEST_MARKER(); } } taskEXIT_CRITICAL(); return xReturn; }