FreeRTOS源码解析(9)任务通知

news2026/5/19 4:04:20
1.任务通知本质直接操作目标任务的 TCB 字段。它不自带控制块、不分配独立存储、不维护自己的等待列表——全程只做一件事读写目标任务 TCB 里已有的ulNotifiedValue和ucNotifyState必要时将对方从延迟列表移到就绪列表。正因如此它成为 FreeRTOS 中最快、最省 RAM 的通信机制。1.1 发送通知功能向任务xTaskToNotify发送一个通知并可选择性地更新该任务的通知值。根据eAction的不同可以模拟二值信号量、计数信号量、事件组、消息邮箱等多种行为。如果目标任务正在等待通知则唤醒它。参数xTaskToNotify目标任务句柄。uxIndexToNotify通知索引任务可以有多个通知由configTASK_NOTIFICATION_ARRAY_ENTRIES定义数量。ulValue通知携带的值具体含义由eAction决定。eAction通知动作类型决定如何更新目标任务的通知值eSetBits将ulValue的位与目标任务的通知值进行按位或。eIncrement将目标任务的通知值加 1ulValue被忽略。eSetValueWithOverwrite直接用ulValue覆盖目标任务的通知值。eSetValueWithoutOverwrite仅在目标任务的通知未被消费状态不为taskNOTIFICATION_RECEIVED时才写入ulValue否则操作失败返回pdFAIL。eNoAction仅唤醒任务不修改通知值。pulPreviousNotificationValue可选指针用于获取更新前目标任务的通知值可为NULL。返回值pdPASS操作成功。pdFAIL操作失败仅eSetValueWithoutOverwrite时可能失败。BaseType_t xTaskGenericNotify( TaskHandle_t xTaskToNotify, UBaseType_t uxIndexToNotify, uint32_t ulValue, eNotifyAction eAction, uint32_t * pulPreviousNotificationValue ) { TCB_t * pxTCB; //指向目标任务 TCB 的指针方便访问。 BaseType_t xReturn pdPASS; //返回值初始为 pdPASS仅在写入失败时修改。 uint8_t ucOriginalNotifyState; //保存目标任务通知更新前的状态用于判断任务是否正在等待通知决定是否需要唤醒。 /* 断言检查需用户自定义实现 */ configASSERT( uxIndexToNotify configTASK_NOTIFICATION_ARRAY_ENTRIES ); //通知索引范围必须小于任务通知数组的大小防止越界访问 configASSERT( xTaskToNotify ); //目标任务有效xTaskToNotify 不能为 NULL pxTCB xTaskToNotify; //获取目标任务 TCB taskENTER_CRITICAL(); //程序进入临界区保证对目标任务 TCB 中通知状态和通知值的修改是原子的防止任务自身或被中断并发访问 { /* 如果调用者提供了 pulPreviousNotificationValue 指针则将更新前的通知值写入它。 * 这允许发送方获取接收方之前的状态例如在模拟计数信号量时可以获取递增前的计数值 */ if( pulPreviousNotificationValue ! NULL ) { *pulPreviousNotificationValue pxTCB-ulNotifiedValue[ uxIndexToNotify ]; } /* 先保存任务通知的原始状态用于后续判断是否需要唤醒任务 */ ucOriginalNotifyState pxTCB-ucNotifyState[ uxIndexToNotify ]; /* 无论之前是什么状态立即将状态设置为 taskNOTIFICATION_RECEIVED表示该通知槽已被发送方填充等待接收方消费。 */ pxTCB-ucNotifyState[ uxIndexToNotify ] taskNOTIFICATION_RECEIVED; /* 根据 eAction 更新通知值 */ switch( eAction ) { /* 用 ulValue 进行按位或操作。这是模拟事件标志组的核心动作可以同时“设置”多个事件位。 */ case eSetBits: pxTCB-ulNotifiedValue[ uxIndexToNotify ] | ulValue; break; /* 忽略 ulValue将通知值加 1。这是实现计数信号量或任务通知版“Give”操作的基础 */ case eIncrement: ( pxTCB-ulNotifiedValue[ uxIndexToNotify ] ); break; /* 无条件覆盖通知值。模拟消息邮箱覆盖旧消息 */ case eSetValueWithOverwrite: pxTCB-ulNotifiedValue[ uxIndexToNotify ] ulValue; break; /* 仅在没有待消费的通知值时才写入。如果目标已经有一个未消费的通知值操作失败并返回 pdFAIL。 * 这模拟了非覆盖式消息发送或轻量级零拷贝消息传递 */ case eSetValueWithoutOverwrite: if( ucOriginalNotifyState ! taskNOTIFICATION_RECEIVED ) { pxTCB-ulNotifiedValue[ uxIndexToNotify ] 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; /* 通过 xTickCount 0 的断言触发异常防止未定义的枚举值传入。这是一个编译时未知、运行时永远为假的断言用于捕捉非法枚举。 */ default: /* Should not get here if all enums are handled. * Artificially force an assert by testing a value the * compiler cant assume is const. */ configASSERT( xTickCount ( TickType_t ) 0 ); //断言检查需用户自定义实现 break; } traceTASK_NOTIFY( uxIndexToNotify ); //调试宏需用户自定义实现 /* If the task is in the blocked state specifically to wait for a * notification then unblock it now. */ /* 判断原状态只有目标任务因等待通知而处于阻塞态taskWAITING_NOTIFICATION时才执行唤醒逻辑 */ if( ucOriginalNotifyState taskWAITING_NOTIFICATION ) { listREMOVE_ITEM( ( pxTCB-xStateListItem ) ); //xStateListItem 用于跟踪任务的阻塞时间将其从延迟列表中移除因为任务不再需要超时等待。 prvAddTaskToReadyList( pxTCB ); //将任务加入到对应优先级的就绪列表中使其可以被调度 /* The task should not have been on an event list. */ /* 断言检查需用户自定义实现 */ /* 任务等待通知时不应同时在事件列表上xEventListItem 的容器应为 NULL因为任务通知不依赖事件组或队列而是直接通过 TCB 中的状态字和值来通信。 * 这确保任务通知的阻塞是“纯”的没有混合其他内核对象的等待。 */ 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 tasks 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. */ /* 如果任务是因为收到通知而被唤醒而非超时原来为它设置的超时时间 * 可能还残留在 xNextTaskUnblockTime 中。为防止系统在 tickless 模式下 * 根据这个过期的时间点错误地规划休眠时长这里重新计算所有阻塞任务中 * 最早的超时时间确保芯片能尽可能早地进入低功耗模式并在正确的时间点唤醒。 */ 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(); //任务切换由于当前正处于临界区内该函数只是设置一个 xYieldPending 或直接挂起 PendSV实际的切换会在临界区退出时发生 } else { mtCOVERAGE_TEST_MARKER(); //代码覆盖率测试实际为空 } } else { mtCOVERAGE_TEST_MARKER(); //代码覆盖率测试实际为空 } } taskEXIT_CRITICAL(); //程序退出临界区 return xReturn; //返回 pdPASS 或 pdFAIL告知调用者操作是否成功 }1.2 接收通知1.2.1 通知取出计数模式功能当前任务等待并获取由发送端xTaskNotifyGive或xTaskGenericNotify使用eIncrement生成的计数值。其行为等同于一个专属于本任务的计数信号量 Take 操作。如果当前计数值为 0任务可选择阻塞等待。参数uxIndexToWait要等待的通知索引。xClearCountOnExitpdTRUE返回时清零通知值类似于获取信号量时“拿走所有”计数值。pdFALSE返回时将通知值减 1类似于获取一个信号量。xTicksToWait最大阻塞时间。0 表示非阻塞。返回值调用时或唤醒时的通知值即发送端通过eIncrement累计的计数。返回值为 0 通常表示超时见下文细节。uint32_t ulTaskGenericNotifyTake( UBaseType_t uxIndexToWait, BaseType_t xClearCountOnExit, TickType_t xTicksToWait ) { uint32_t ulReturn; //用于暂存获取到的通知值并在函数结束时返回 /* 断言检查需用户自定义实现 */ configASSERT( uxIndexToWait configTASK_NOTIFICATION_ARRAY_ENTRIES ); //确保通知索引不越界 taskENTER_CRITICAL(); //程序进入临界区 { /* Only block if the notification count is not already non-zero. */ /* 检查计数值判断是否需要阻塞 */ if( pxCurrentTCB-ulNotifiedValue[ uxIndexToWait ] 0UL ) //通知计数为 0需要阻塞 { /* Mark this task as waiting for a notification. */ /* 将通知状态设为 taskWAITING_NOTIFICATION以便发送端在调用 xTaskGenericNotify 时识别并唤醒本任务 */ pxCurrentTCB-ucNotifyState[ uxIndexToWait ] taskWAITING_NOTIFICATION; /* 判断是否需要等待 */ if( xTicksToWait ( TickType_t ) 0 ) //需要等待 { /* 将当前任务移入延迟列表并设置超时时间。 */ /* 第二个参数 pdTRUE 表示任务是因为等待事件通知而阻塞而非纯延时。 */ prvAddCurrentTaskToDelayedList( xTicksToWait, pdTRUE ); /* 调试宏需用户自定义实现 */ traceTASK_NOTIFY_TAKE_BLOCK( uxIndexToWait ); /* All ports are written to allow a yield in a critical * section (some will yield immediately, others wait until the * critical section exits) - but it is not something that * application code should ever do. */ portYIELD_WITHIN_API(); //抢占式调度开启下执行任务切换 } else //不为0不需要等待 { mtCOVERAGE_TEST_MARKER(); //代码覆盖率测试实际为空 } } else //通知计数不为 0不需要阻塞 { mtCOVERAGE_TEST_MARKER(); //代码覆盖率测试实际为空 } } taskEXIT_CRITICAL(); //程序退出临界区 /* 无论任务是被通知唤醒还是超时唤醒都会执行这段代码 */ /* 通知唤醒ulReturn ! 0根据 xClearCountOnExit 清零或减 1 * 超时唤醒ulReturn 0跳过修改直接重置状态 */ taskENTER_CRITICAL(); //程序进入临界区 { traceTASK_NOTIFY_TAKE( uxIndexToWait ); //调试宏需用户自定义实现 ulReturn pxCurrentTCB-ulNotifiedValue[ uxIndexToWait ]; //存储获取到的通知值 if( ulReturn ! 0UL ) //通知值不为0 { if( xClearCountOnExit ! pdFALSE ) //清除通知值 { pxCurrentTCB-ulNotifiedValue[ uxIndexToWait ] 0UL; //通知值清0 } else //不清除减1 { pxCurrentTCB-ulNotifiedValue[ uxIndexToWait ] ulReturn - ( uint32_t ) 1; //通知值减1 } } else //通知值为0 { mtCOVERAGE_TEST_MARKER(); //代码覆盖率测试实际为空 } /* 最后将通知状态重置为 taskNOT_WAITING_NOTIFICATION表示本通知槽已空闲可以接收下一次通知。这是接收端状态机闭环的关键一步与发送端将状态设为 taskNOTIFICATION_RECEIVED 形成对称。 */ pxCurrentTCB-ucNotifyState[ uxIndexToWait ] taskNOT_WAITING_NOTIFICATION; } taskEXIT_CRITICAL(); //程序退出临界区 /* 返回 0 通常表示阻塞超时且期间无通知到来。 * 非阻塞调用xTicksToWait 0且计数为 0 时也会返回 0 * 调用者需根据上下文如是否传入了超时区分这两种情况。 */ return ulReturn; }1.2.2 通知等待位模式功能当前任务等待指定索引的通知。在等待前可选择性清除通知值的指定位在被唤醒后可再次选择性清除指定位并通过指针返回收到的通知值。这是任务通知中最灵活的等待函数可以模拟事件组等待、消息接收等多种场景。参数uxIndexToWait要等待的通知索引。ulBitsToClearOnEntry进入等待前需要清除的通知值位掩码用于清零某些旧状态位为接收新通知做准备。ulBitsToClearOnExit成功收到通知后退出前需要清除的通知值位掩码常用于“消费”某些事件位类似事件组的自动清除。pulNotificationValue可选指针用于接收通知值可为NULL。xTicksToWait最大阻塞时间0 表示非阻塞。返回值pdTRUE收到了通知。pdFALSE超时未收到通知。BaseType_t xTaskGenericNotifyWait( UBaseType_t uxIndexToWait, uint32_t ulBitsToClearOnEntry, uint32_t ulBitsToClearOnExit, uint32_t * pulNotificationValue, TickType_t xTicksToWait ) { BaseType_t xReturn; //用于标记等待结果收到通知为 pdTRUE超时为 pdFALSE。 /* 断言检查确保通知索引不越界需用户自定义实现 */ configASSERT( uxIndexToWait configTASK_NOTIFICATION_ARRAY_ENTRIES ); /* 判断是否已有通知否则阻塞 */ taskENTER_CRITICAL(); //程序进入临界区 { /* Only block if a notification is not already pending. */ if( pxCurrentTCB-ucNotifyState[ uxIndexToWait ] ! taskNOTIFICATION_RECEIVED ) //无通知 { /* Clear bits in the tasks notification value as bits may get * set by the notifying task or interrupt. This can be used to * clear the value to zero. */ /* 等待前清除用户指定的位 */ /* 典型用法调用者先清除某些旧事件位然后再进入等待确保不会被上一次通知的残留位干扰。这类似于“先清掉不关心的旧状态再等新事件” */ pxCurrentTCB-ulNotifiedValue[ uxIndexToWait ] ~ulBitsToClearOnEntry; /* Mark this task as waiting for a notification. */ /* 设置等待状态将通知状态设为 taskWAITING_NOTIFICATION使发送端能识别并唤醒本任务。 */ pxCurrentTCB-ucNotifyState[ uxIndexToWait ] taskWAITING_NOTIFICATION; if( xTicksToWait ( TickType_t ) 0 ) //需要等待 { /* 将当前任务加入延迟列表 */ /* 第二个参数 pdTRUE 表示任务是因为等待事件通知而阻塞而非纯延时。 */ prvAddCurrentTaskToDelayedList( xTicksToWait, pdTRUE ); traceTASK_NOTIFY_WAIT_BLOCK( uxIndexToWait ); //调试宏需用户自定义实现 /* All ports are written to allow a yield in a critical * section (some will yield immediately, others wait until the * critical section exits) - but it is not something that * application code should ever do. */ portYIELD_WITHIN_API(); // 在临界区内请求一次上下文切换。切换不会立即发生而是登记后等到 taskEXIT_CRITICAL() 真正退出临界区时执行。 } else { mtCOVERAGE_TEST_MARKER(); //代码覆盖率测试实际为空 } } else //有通知 { mtCOVERAGE_TEST_MARKER(); //代码覆盖率测试实际为空 } } taskEXIT_CRITICAL(); //程序退出临界区 /* 读取结果、判断通知、清除退出位 */ taskENTER_CRITICAL(); //程序进入临界区 { traceTASK_NOTIFY_WAIT( uxIndexToWait ); //调试宏需用户自定义实现 /* 如果调用者提供了 pulNotificationValue 指针将当前通知值写入。 */ if( pulNotificationValue ! NULL ) { /* Output the current notification value, which may or may not * have changed. */ *pulNotificationValue pxCurrentTCB-ulNotifiedValue[ uxIndexToWait ]; } /* If ucNotifyValue is set then either the task never entered the * blocked state (because a notification was already pending) or the * task unblocked because of a notification. Otherwise the task * unblocked because of a timeout. */ /* 判断超时还是收到了通知 */ if( pxCurrentTCB-ucNotifyState[ uxIndexToWait ] ! taskNOTIFICATION_RECEIVED ) // 未收到通知超时唤醒或非阻塞调用且无通知到达 { /* A notification was not received. */ /* 标记未收到通知 */ xReturn pdFALSE; } else //收到通知 { /* A notification was already pending or a notification was * received while the task was waiting. */ /* 成功收到通知后清除用户指定的位。 */ pxCurrentTCB-ulNotifiedValue[ uxIndexToWait ] ~ulBitsToClearOnExit; xReturn pdTRUE; //标记为收到 } /* 将通知状态重置为 taskNOT_WAITING_NOTIFICATION使通知槽恢复空闲准备接收下一次通知。 * 这与发送端将状态设为 taskNOTIFICATION_RECEIVED 形成对称闭环。 */ pxCurrentTCB-ucNotifyState[ uxIndexToWait ] taskNOT_WAITING_NOTIFICATION; } taskEXIT_CRITICAL(); //程序退出临界区 return xReturn; //返回接收状态 }1.2.3 对比总结ulTaskGenericNotifyTake【计数模式】xTaskGenericNotifyWait【位模式】核心定位计数信号量接收端等待通知值可模拟事件组、消息传递匹配发送动作eIncrementeSetBits、eSetValueWithOverwrite、eSetValueWithoutOverwrite、eNoAction数据处理整数计数器清零或减1位掩码或完整数据字按掩码清除返回值通知值本身0 无通知布尔值pdTRUE/pdFALSE值输出方式函数返回值指针参数pulNotificationValue消费方式清零取走全部计数或减1取走一个计数按ulBitsToClearOnExit掩码清除指定位进入前清理无ulBitsToClearOnEntry可先清除旧位典型场景任务间计数、资源管理、事件次数统计多事件位等待、覆盖/非覆盖式写入一个值、传递数据计数场景用Take位操作/数据传递用Wait。2.任务通知模拟信号量2.1 发送通知2.1.1 源码解析功能向指定任务发送一个“计数信号量”通知实现类似xSemaphoreGive的释放操作。该宏是对xTaskGenericNotify的封装每次调用将目标任务的通知值加 1完成一次轻量级的私有计数信号量“Give”。参数xTaskToNotify要通知的目标任务句柄。调用内部固定展开为uxIndexToNotifytskDEFAULT_INDEX_TO_NOTIFY通常为 0使用任务通知的默认槽适用于绝大多数单通知场景。ulValue0。在eIncrement动作下该值被忽略实际效果是对通知值执行操作因此传 0 即可。eActioneIncrement指定通知动作为“递增”专用于实现计数信号量的 Give。pulPreviousNotificationValueNULL表示不关心递增前的旧值无需获取历史计数。【该宏定义具体指向的发送通知函数已在本篇章1.1发送通知中进行了讲解分析】#define xTaskNotifyGive( xTaskToNotify ) \ xTaskGenericNotify( ( xTaskToNotify ), ( tskDEFAULT_INDEX_TO_NOTIFY ), ( 0 ), eIncrement, NULL )2.1.2 使用示例BaseType_t res 0; /* 发送任务通知 */ res xTaskNotifyGive(task2_handle); if(res pdPASS) { printf(task1向task2发送任务通知成功...\r\n); }2.2 接收通知2.2.1 源码解析功能当前任务等待并获取一个“计数信号量”通知实现类似xSemaphoreTake的获取操作。该宏是对ulTaskGenericNotifyTake的封装用于从默认通知槽中取出计数完成一次轻量级的私有计数信号量“Take”。参数xClearCountOnExit控制消费方式。pdTRUE返回时将通知值清零相当于一次性取走所有累积的 Give 次数“取走全部信号量”。pdFALSE返回时将通知值减 1相当于每次取走一个 Give 次数“取走一个信号量”实现经典计数信号量的 P 操作。xTicksToWait最大阻塞时间系统节拍数。若当前通知值为 0任务将进入阻塞态等待通知到达。传 0 表示非阻塞portMAX_DELAY表示无限等待。内部固定展开为uxIndexToWaittskDEFAULT_INDEX_TO_NOTIFY通常为 0使用任务通知的默认槽与发送端xTaskNotifyGive使用的默认索引一致。【该宏定义具体指向的接收通知函数已在本篇章1.2.1通知取出计数模式中进行了讲解分析】#define ulTaskNotifyTake( xClearCountOnExit, xTicksToWait ) \ ulTaskGenericNotifyTake( ( tskDEFAULT_INDEX_TO_NOTIFY ), ( xClearCountOnExit ), ( xTicksToWait ) )2.2.2 使用示例uint32_t notify_value 0; notify_value ulTaskNotifyTake( pdFALSE, // 接受完通知后是否对通知置清零 pdTRUE 清零 pdFALSE 不清零通知值-1 portMAX_DELAY // 等待任务通知的最大阻塞时间 ); printf(Task2接收到通知值%d\r\n,notify_value);3.任务通知模拟消息队列/事件标志组3.1 发送通知3.1.1 源码解析功能向指定任务发送一个通用通知可模拟事件标志组置位、消息传递、信号量释放等多种操作。该宏是对xTaskGenericNotify的便捷封装使用默认通知槽适合单一通知场景下的全功能发送。参数xTaskToNotify目标接收任务句柄。ulValue通知携带的值其含义由eAction决定eSetBits将ulValue作为位掩码与目标任务通知值进行按位或操作实现事件标志组的“置位”。eSetValueWithOverwrite用ulValue覆盖目标任务当前通知值无论旧值是否已被消费实现覆盖式消息传递。eSetValueWithoutOverwrite仅当目标任务通知槽空闲无待消费通知时才将ulValue写入否则操作失败返回pdFAIL实现非覆盖式消息传递。eIncrement忽略ulValue将目标任务通知值加 1模拟计数信号量 Give。eNoAction忽略ulValue不修改通知值仅唤醒可能阻塞的任务用于纯唤醒目的。eAction通知动作枚举决定如何更新目标任务的通知值见上述说明。内部固定展开为uxIndexToNotifytskDEFAULT_INDEX_TO_NOTIFY通常为 0使用任务通知的默认槽。pulPreviousNotificationValueNULL不返回更新前的旧值。【该宏定义具体指向的发送通知函数已在本篇章1.1发送通知中进行了讲解分析】#define xTaskNotify( xTaskToNotify, ulValue, eAction ) \ xTaskGenericNotify( ( xTaskToNotify ), ( tskDEFAULT_INDEX_TO_NOTIFY ), ( ulValue ), ( eAction ), NULL )3.1.2 使用示例3.1.2.1 模拟消息队列BaseType_t res 0; uint8_t key 1; /* 发送任务通知 */ res xTaskNotify( task2_handle, // 接收方的任务句柄 key, // 要发送的通知值 eSetValueWithOverwrite // 写入的行为强行覆盖 ); if (res pdPASS) { printf(向task2发送任务通知[%d]成功...\r\n, key); }3.1.2.2 模拟事件标志组/* 事件位定义 */ #define EVENTBIT_0 (1 0) // 传感器数据就绪 #define EVENTBIT_1 (1 1) // 按键按下 /* 发送方1任务或中断中通知接收任务 EVENTBIT_0 已发生 */ BaseType_t res; res xTaskNotify( receiver_task_handle, // 接收方的任务句柄 EVENTBIT_0, // 要设置的事件位 eSetBits // 按位“或”不影响其他已设置的位 ); if (res pdPASS) { printf(EVENTBIT_0 置位成功\r\n); } /* 发送方2另一任务或中断中通知接收任务 EVENTBIT_1 已发生 */ res xTaskNotify( receiver_task_handle, EVENTBIT_1, eSetBits // 同样使用 eSetBits累积事件 ); if (res pdPASS) { printf(EVENTBIT_1 置位成功\r\n); }3.2 接收通知3.2.1 源码解析功能当前任务在默认通知槽上等待通知并可选择性在等待前后清除指定位。该宏是对xTaskGenericNotifyWait的封装用于接收事件标志组置位、消息传递等通用通知是任务通知最灵活的等待接口。参数ulBitsToClearOnEntry进入等待前需清除的通知值位掩码。用于清除旧的、不关心的事件位确保等待时不被上一次残留事件干扰。若无需清除传 0。ulBitsToClearOnExit成功收到通知后需清除的通知值位掩码。用于“消费”已处理的事件位保留未处理的其他位。若无需清除传 0。pulNotificationValue可选指针用于接收通知值不含控制位。即使任务因超时唤醒该指针指向的值也会被更新此时有效性需结合返回值判断。若不需要接收通知值可传NULL。xTicksToWait最大阻塞时间。若进入等待时无待消费的通知任务将阻塞等待通知到达。传 0 表示非阻塞portMAX_DELAY表示无限等待。内部固定使用默认通知槽tskDEFAULT_INDEX_TO_NOTIFY通常为 0与xTaskNotify、xTaskNotifyGive等宏的发送端保持一致。【该宏定义具体指向的接收通知函数已在本篇章1.2.2通知等待位模式中进行了讲解分析】#define xTaskNotifyWait( ulBitsToClearOnEntry, ulBitsToClearOnExit, pulNotificationValue, xTicksToWait ) \ xTaskGenericNotifyWait( tskDEFAULT_INDEX_TO_NOTIFY, ( ulBitsToClearOnEntry ), ( ulBitsToClearOnExit ), ( pulNotificationValue ), ( xTicksToWait ) )3.2.2 使用示例3.2.2.1 模拟消息队列uint32_t notify_value 0; BaseType_t res 0; res xTaskNotifyWait( 0x00000000, // 接收通知前是否清理通知值全0表示32bit的都是0都不清理 0xffffffff, // 接收到通知值后是否清理通知值 全1表示32bit都是1都要清零 notify_value, // 用来保存读取到的通知值 portMAX_DELAY); if (res pdTRUE) { printf(Task2接收到通知值%d\r\n, notify_value); }3.2.2.2 模拟事件标志组/* 示例1等待任意事件OR且不清除 */ uint32_t notify_value; BaseType_t res; /* 等待任意事件位被置位OR 语义 */ res xTaskNotifyWait( 0, // ulBitsToClearOnEntry: 进入前不清除任何位 0, // ulBitsToClearOnExit: 退出后也不自动清除 notify_value, // 获取当前所有事件位 portMAX_DELAY ); if (res pdTRUE) { if (notify_value EVENTBIT_0) { printf(处理 EVENTBIT_0\r\n); /* 处理完后可手动清除该位通过下一次 xTaskNotifyWait 的 ulBitsToClearOnEntry */ } if (notify_value EVENTBIT_1) { printf(处理 EVENTBIT_1\r\n); } } /* 示例2等待两个事件都发生 AND累积消费 */ #define ALL_EVENTS (EVENTBIT_0 | EVENTBIT_1) uint32_t accumulated 0; uint32_t consumed 0; BaseType_t res; while (accumulated ! ALL_EVENTS) { /* * 每次进入等待前清除上次已消费的位防止重复处理。 * 退出时不自动清除因为可能还有其他未处理位需要保留。 */ res xTaskNotifyWait(consumed, 0, notify_value, portMAX_DELAY); if (res pdTRUE) { /* 记录本次唤醒后哪些位被处理了 */ uint32_t this_consumed 0; if (notify_value EVENTBIT_0) { printf(处理 EVENTBIT_0\r\n); this_consumed | EVENTBIT_0; } if (notify_value EVENTBIT_1) { printf(处理 EVENTBIT_1\r\n); this_consumed | EVENTBIT_1; } accumulated | this_consumed; consumed this_consumed; // 下次进入前只清除本次消费过的位 } } printf(两个事件都已发生期望条件满足\r\n);4.任务通知的局限性与选型指南任务通知以速度和 RAM 开销见长但它用“舍弃通用性”换取了“极致轻量”。理解其边界才能在选型时做出正确决策。4.1 五大核心局限局限性说明典型后果单一接收者每个通知槽只能被发送方指定的一个任务接收无法广播。多任务需要等待同一事件时必须改用事件标志组。无优先级继承模拟信号量时没有互斥量的优先级继承机制。高优先级任务因等通知而阻塞时低优先级“持有者”无法被临时提升可能发生优先级反转。无法同时等待多槽Take/Wait只指定一个索引不能像select()那样阻塞在多个通知源上。需多通道监听时必须改用队列集或将事件合并到同一槽的不同位上。事件累积需应用层配合AND 累积等待“事件A和B都发生过”没有原生支持需任务自行维护累积变量。增加应用层代码复杂度。无数据缓冲只有一个uint32_t通知值没有 FIFO 队列缓冲。非覆盖模式下发送过快会丢失后续数据覆盖模式下会丢失旧数据。4.2 选型速查表需求场景推荐方案不推荐单任务计数信号量xTaskNotifyGiveulTaskNotifyTake—单任务事件组OR 等待xTaskNotifyeSetBitsxTaskNotifyWait—单任务事件组AND 累积xTaskNotifyeSetBitsxTaskNotifyWait 本地变量—单任务最新值传递xTaskNotifyeSetValueWithOverwritexTaskNotifyWait—中断中发通知vTaskNotifyGiveFromISR/xTaskNotifyFromISR任务版 API多任务等同一事件事件标志组任务通知需要优先级继承互斥量任务通知模拟信号量需要FIFO 缓冲队列任务通知同时等待多个独立对象队列集任务通知4.3 定位任务通知是 FreeRTOS IPCInter-Process Communication进程间通信 工具箱中最轻最快的单线通信手段——专为单接收者、无缓冲、高频场景优化。它不是队列/信号量/事件组的替代品而是它们的互补工具。选型时记住一个原则只要场景中存在“多个接收者”或“需要缓冲历史数据”就回到传统 IPC。5.声明1Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.2文中代码来自FreeRTOS遵循MIT许可证许可证可参考https://opensource.org/licenses/MIT/* * FreeRTOS Kernel V10.5.1 * Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. * * SPDX-License-Identifier: MIT * * Permission is hereby granted, free of charge, to any person obtaining a copy of * this software and associated documentation files (the Software), to deal in * the Software without restriction, including without limitation the rights to * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of * the Software, and to permit persons to whom the Software is furnished to do so, * subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * * https://www.FreeRTOS.org * https://github.com/FreeRTOS * */【以上内容为个人在学习FreeRTOS过程中的源码解读笔记欢迎大家在评论区讨论指正。】【如果本篇内容对你有帮助不妨点个关注你的支持是我持续更新的动力】

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2623806.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SpringBoot-17-MyBatis动态SQL标签之常用标签

文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…

wordpress后台更新后 前端没变化的解决方法

使用siteground主机的wordpress网站,会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后,网站没有变化的情况。 不熟悉siteground主机的新手,遇到这个问题,就很抓狂,明明是哪都没操作错误&#x…

网络编程(Modbus进阶)

思维导图 Modbus RTU(先学一点理论) 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议,由 Modicon 公司(现施耐德电气)于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…

UE5 学习系列(二)用户操作界面及介绍

这篇博客是 UE5 学习系列博客的第二篇,在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下: 【Note】:如果你已经完成安装等操作,可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作,重…

IDEA运行Tomcat出现乱码问题解决汇总

最近正值期末周,有很多同学在写期末Java web作业时,运行tomcat出现乱码问题,经过多次解决与研究,我做了如下整理: 原因: IDEA本身编码与tomcat的编码与Windows编码不同导致,Windows 系统控制台…

利用最小二乘法找圆心和半径

#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …

使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式

一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明&#xff1a;假设每台服务器已…

XML Group端口详解

在XML数据映射过程中&#xff0c;经常需要对数据进行分组聚合操作。例如&#xff0c;当处理包含多个物料明细的XML文件时&#xff0c;可能需要将相同物料号的明细归为一组&#xff0c;或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码&#xff0c;增加了开…

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造&#xff0c;完美适配AGV和无人叉车。同时&#xff0c;集成以太网与语音合成技术&#xff0c;为各类高级系统&#xff08;如MES、调度系统、库位管理、立库等&#xff09;提供高效便捷的语音交互体验。 L…

(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)

题目&#xff1a;3442. 奇偶频次间的最大差值 I 思路 &#xff1a;哈希&#xff0c;时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况&#xff0c;哈希表这里用数组即可实现。 C版本&#xff1a; class Solution { public:int maxDifference(string s) {int a[26]…

【大模型RAG】拍照搜题技术架构速览:三层管道、两级检索、兜底大模型

摘要 拍照搜题系统采用“三层管道&#xff08;多模态 OCR → 语义检索 → 答案渲染&#xff09;、两级检索&#xff08;倒排 BM25 向量 HNSW&#xff09;并以大语言模型兜底”的整体框架&#xff1a; 多模态 OCR 层 将题目图片经过超分、去噪、倾斜校正后&#xff0c;分别用…

【Axure高保真原型】引导弹窗

今天和大家中分享引导弹窗的原型模板&#xff0c;载入页面后&#xff0c;会显示引导弹窗&#xff0c;适用于引导用户使用页面&#xff0c;点击完成后&#xff0c;会显示下一个引导弹窗&#xff0c;直至最后一个引导弹窗完成后进入首页。具体效果可以点击下方视频观看或打开下方…

接口测试中缓存处理策略

在接口测试中&#xff0c;缓存处理策略是一个关键环节&#xff0c;直接影响测试结果的准确性和可靠性。合理的缓存处理策略能够确保测试环境的一致性&#xff0c;避免因缓存数据导致的测试偏差。以下是接口测试中常见的缓存处理策略及其详细说明&#xff1a; 一、缓存处理的核…

龙虎榜——20250610

上证指数放量收阴线&#xff0c;个股多数下跌&#xff0c;盘中受消息影响大幅波动。 深证指数放量收阴线形成顶分型&#xff0c;指数短线有调整的需求&#xff0c;大概需要一两天。 2025年6月10日龙虎榜行业方向分析 1. 金融科技 代表标的&#xff1a;御银股份、雄帝科技 驱动…

观成科技:隐蔽隧道工具Ligolo-ng加密流量分析

1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具&#xff0c;该工具基于TUN接口实现其功能&#xff0c;利用反向TCP/TLS连接建立一条隐蔽的通信信道&#xff0c;支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式&#xff0c;适应复杂网…

铭豹扩展坞 USB转网口 突然无法识别解决方法

当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…

未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?

编辑&#xff1a;陈萍萍的公主一点人工一点智能 未来机器人的大脑&#xff1a;如何用神经网络模拟器实现更智能的决策&#xff1f;RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战&#xff0c;在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…

Linux应用开发之网络套接字编程(实例篇)

服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …

华为云AI开发平台ModelArts

华为云ModelArts&#xff1a;重塑AI开发流程的“智能引擎”与“创新加速器”&#xff01; 在人工智能浪潮席卷全球的2025年&#xff0c;企业拥抱AI的意愿空前高涨&#xff0c;但技术门槛高、流程复杂、资源投入巨大的现实&#xff0c;却让许多创新构想止步于实验室。数据科学家…

深度学习在微纳光子学中的应用

深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向&#xff1a; 逆向设计 通过神经网络快速预测微纳结构的光学响应&#xff0c;替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…