:区分delay_ms 与 vTaskDelay。vTaskSuspendAll、xTaskResumeAll、prvAddCurrentTaskToDelayedList)
50函数 vTaskDelay//形参为 0 表示强制调度任务voidvTaskDelay(constTickType_t xTicksToDelay)// tasks.c 1182{BaseType_t xAlreadyYieldedpdFALSE;if(xTicksToDelay(TickType_t)0U)// 当形参大于 0{configASSERT(uxSchedulerSuspended0);// 要求调度器没有被挂起vTaskSuspendAll();// 该函数实际上是禁止了任务调度递增全局变量 uxSchedulerSuspended{traceTASK_DELAY();// 一般是统计//当调度器禁止后事件链表或阻塞链表里的任务是不会加入到就绪链表里的直到调度器恢复prvAddCurrentTaskToDelayedList(xTicksToDelay,pdFALSE);//void prvAddCurrentTaskToDelayedList(TickType_t xTicksToWait, BaseType_t xCanBlockIndefinitely );}xAlreadyYieldedxTaskResumeAll();// 其返回值 T 表 xTaskResumeAll 已发起了任务调度}elsemtCOVERAGE_TEST_MARKER();/* Force a reschedule if xTaskResumeAll has not already done so, we may have put ourselves to sleep. */if(xAlreadyYieldedpdFALSE)//如果 xTaskResumeAll 尚未完成则强制重新调度我们可能已经进入休眠状态。portYIELD_WITHIN_API();elsemtCOVERAGE_TEST_MARKER();//xTaskResumeAll 已发起了任务调度这里就不必再进行任务调度了。}// vTaskDelay51vTaskDelay 调用了 vTaskSuspendAll// 本函数 vTaskSuspendAll 不是把所有就绪任务转移到了挂起链表里而是直接禁止了任务切换禁止了调度器voidvTaskSuspendAll(void)// tasks.c 1880{//不需要进入临界区不用关中断来保护 uxSchedulerSuspended// 原因BaseType_t 在目标平台是原子访问类型单条指令完成自增/* A critical section is not required as the variable is of type BaseType_t. *///防止编译器优化、CPU 乱序执行保证 uxSchedulerSuspended 这个写操作的顺序对内核其他地方可见。//很多硬件端口下这两个宏是空模拟器 / 软仿真端口才会有实际实现。portSOFTWARE_BARRIER();// 空的宏//static volatile UBaseType_t uxSchedulerSuspended ( UBaseType_t ) pdFALSE;//计数器临时挂起调度器为 0 才会切换任务uxSchedulerSuspended;portMEMORY_BARRIER();// 空的宏}doubao 的讲解52vTaskDelay 也调用了 xTaskResumeAllBaseType_txTaskResumeAll(void)// tasks.c 1964{TCB_t*pxTCBNULL;BaseType_t xAlreadyYieldedpdFALSE;// 作为返回值表本函数是否已经中断触发了任务切换T表已触发中断// 如果 uxSchedulerSuspended 为零则此函数与之前调用的 vTaskSuspendAll() 不匹配。configASSERT(uxSchedulerSuspended);// uxSchedulerSuspended 是计数器此处其值为 0 意味着错误//在调度器挂起时ISR 可能导致某个任务从事件列表中被移除。//如果是这种情况那么被移除的任务将被添加到 xPendingReadyList 中。//一旦调度器恢复运行就可以安全地将该列表中的所有待就绪任务移动到它们相应的就绪列表中.taskENTER_CRITICAL();// 进入临界区{--uxSchedulerSuspended;// 递减这个计数器if(uxSchedulerSuspended(UBaseType_t)pdFALSE)// 可以恢复调度器了{if(uxCurrentNumberOfTasks(UBaseType_t)0U)//全局量当前系统里的任务总数至少包含空闲任务{/* Move any readied tasks from the pending list into the appropriate ready list. *///宏函 listLIST_IS_EMPTY( pxList ) ( pxList - uxNumberOfItems 0 ? pdTRUE : pdFALSE )while(listLIST_IS_EMPTY(xPendingReadyList)pdFALSE){//从待就绪链表里拿出一个任务节点pxTCBlistGET_OWNER_OF_HEAD_ENTRY((xPendingReadyList));//注释过listREMOVE_ITEM((pxTCB-xEventListItem));// 把该任务从事件链表里删除portMEMORY_BARRIER();listREMOVE_ITEM((pxTCB-xStateListItem));// 把该任务从状态链表里删除prvAddTaskToReadyList(pxTCB);//宏函数已注释 // 转移该任务到相应的就绪链表/* If the moved task has a priority higher than or equal to the current task then a yield must be performed. *///新任务的优先级只要等于或者高于当前任务都要随后发起调度if(pxTCB-uxPrioritypxCurrentTCB-uxPriority)xYieldPendingpdTRUE;// 这是全局标志量延时调度elsemtCOVERAGE_TEST_MARKER();}//调度器处于挂起状态时某项任务被解锁这可能导致下一次解锁时间无法重新计算//在这种情况下现在重新计算它。这对低功耗无滴答实现尤为重要因这可防止不必要地退出低功耗状态。if(pxTCB!NULL)prvResetNextTaskUnblockTime();//1如果调度器暂停期间发生了任何 tick则现在应立即处理这些t ick。// 这可以确保 tick 计数不会丢失并且延迟的任务会在正确的时间恢复执行。//2禁止任务调度期间每 1ms 的 tick 依然来到其数量记录到了 xPendedCounts 里// 当可以调度时要更新全局量 xTickCount 为正确的数值就是加上 xPendedCounts 的值{//TickType_t xPendedTicks 是全局量TickType_t xPendedCountsxPendedTicks;/* Non-volatile copy. */if(xPendedCounts(TickType_t)0U)// 一般肯定是大于 1ms 的{do// 这里是以循环的方式xPendedCounts 次来更新 xTickCount 的值并处理函数调度{if(xTaskIncrementTick()!pdFALSE)// 该函数见文篇 7xYieldPendingpdTRUE;//这是全局变量elsemtCOVERAGE_TEST_MARKER();--xPendedCounts;}while(xPendedCounts(TickType_t)0U);xPendedTicks0;}elsemtCOVERAGE_TEST_MARKER();}// 这里只是大括号if(xYieldPending!pdFALSE)// 表示需要任务切换{#if(configUSE_PREEMPTION!0)xAlreadyYieldedpdTRUE;// 这是本函数的返回值表示需要调度#endiftaskYIELD_IF_USING_PREEMPTION();// 本函数其实就是触发 pendSV 中断//宏taskYIELD_IF_USING_PREEMPTION() portYIELD_WITHIN_API() portYIELD vPortYield()}elsemtCOVERAGE_TEST_MARKER();}// if( uxCurrentNumberOfTasks ( UBaseType_t ) 0U )}elsemtCOVERAGE_TEST_MARKER();// if( uxSchedulerSuspended ( UBaseType_t ) pdFALSE )}taskEXIT_CRITICAL();// taskENTER_CRITICAL();returnxAlreadyYielded;}// BaseType_t xTaskResumeAll( void ) 返回值是是否已经中断触发了任务切换53vTaskDelay 也调用了 prvAddCurrentTaskToDelayedList//若无限期等待或 xCanBlockIndefinitely 为 TRUE则插入挂起链表否则插入 delay 延时链表。// prvAddCurrentTaskToDelayedList( xTicksToDelay, pdFALSE ); 可以这样调用staticvoidprvAddCurrentTaskToDelayedList(TickType_t xTicksToWait,//Indefinitely 无固定期限constBaseType_t xCanBlockIndefinitely)//无固定期限的阻塞{TickType_t xTimeToWake;// 唤醒时刻的绝对滴答数constTickType_t xConstTickCountxTickCount;// 全局变量统计当前系统的滴答数#if(INCLUDE_xTaskAbortDelay1)// #define INCLUDE_xTaskAbortDelay 1 /* 中断任务延时 */{//即将进入延迟列表因此请确保将 ucDelayAborted 标志重置为 pdFALSE// 以便在任务离开阻塞状态时能被检测到已设置为 pdTRUE 或者仍为 pdFALSE.pxCurrentTCB-ucDelayAbortedpdFALSE;}#endif//本函数返回该链表里剩余的节点数量。从就绪链表里删除本任务节点。调用本函数的任务肯定之前在就绪链表的。if(uxListRemove((pxCurrentTCB-xStateListItem))(UBaseType_t)0){portRESET_READY_PRIORITY(pxCurrentTCB-uxPriority,uxTopReadyPriority);//宏portRESET_READY_PRIORITY( uxPriority, uxReadyPriorities ) ( uxReadyPriorities ~(1UL uxPriority) )}elsemtCOVERAGE_TEST_MARKER();#if(INCLUDE_vTaskSuspend1){// 对于无限期等待的任务插入到挂起链表里if((xTicksToWaitportMAX_DELAY)(xCanBlockIndefinitely!pdFALSE)){// 挂起链表同就绪链表一样仍然使用 TCB 的 xStateListItem 状态节点listINSERT_END(xSuspendedTaskList,(pxCurrentTCB-xStateListItem));}else{//计算如果事件未发生时任务应被唤醒的时间绝对 tick值 。这可能会溢出但没关系内核会正确处理。xTimeToWakexConstTickCountxTicksToWait;listSET_LIST_ITEM_VALUE((pxCurrentTCB-xStateListItem),xTimeToWake);//使用 TCB 的状态节点//宏 listSET_LIST_ITEM_VALUE( pxListItem, xValue ) ( pxListItem - xItemValue xValue )if(xTimeToWakexConstTickCount)// 说明发生了 tick 滴答溢出要插入到溢出链表{/* Wake time has overflowed. Place this item in the overflow list. */vListInsert(pxOverflowDelayedTaskList,(pxCurrentTCB-xStateListItem));//见 8a 文篇}else{/* The wake time has not overflowed, so the current block list is used. */vListInsert(pxDelayedTaskList,(pxCurrentTCB-xStateListItem));/* If the task entering the blocked state was placed at the head of the list of blocked tasks then xNextTaskUnblockTime needs to be updated too. *///更新第一个要被唤醒的任务的唤醒时刻绝对 tickif(xTimeToWakexNextTaskUnblockTime)// xNextTaskUnblockTime 是全局静态变量xNextTaskUnblockTimexTimeToWake;//static volatile TickType_t xNextTaskUnblockTimeelsemtCOVERAGE_TEST_MARKER();}}}#else/* INCLUDE_vTaskSuspend *///..... 这里是灰色无效的#endif/* INCLUDE_vTaskSuspend */}// void prvAddCurrentTaskToDelayedList(...)54谢谢