系统提供了两个任务延时函数:相对延时函数vTaskDelay()和绝对延时函数vTaskDelayUntil()

相对延时是指:vTaskDelay()开始执行到退出执行的时间固定

/* 相对延时函数 */
void vTaskDelay(const TickType_t xTicksToDelay)
{BaseType_t xAlreadyYielded = pdFALSE;/* 延时时间大于0 */if(xTicksToDelay > (TickType_t)0U){configASSERT(uxSchedulerSuspended == 0);/* 调度器挂起 */vTaskSuspendAll();{traceTASK_DELAY();/* 将任务添加到延时列表 */prvAddCurrentTaskToDelayedList(xTicksToDelay, pdFALSE);}/* 解除调度器挂起,解除的时候可能请求调度 */xAlreadyYielded = xTaskResumeAll();}else{mtCOVERAGE_TEST_MARKER();}/* 解除调度器挂起的时候没有请求调度 */if(xAlreadyYielded == pdFALSE){/* 请求切换任务 */portYIELD_WITHIN_API();}else{mtCOVERAGE_TEST_MARKER();}
}

绝对延时是指:相邻两次vTaskDelayUntil()唤醒的时间固定

/* 绝对延时 */
void vTaskDelayUntil(TickType_t *const pxPreviousWakeTime, const TickType_t xTimeIncrement)
{TickType_t xTimeToWake;BaseType_t xAlreadyYielded, xShouldDelay = pdFALSE;configASSERT(pxPreviousWakeTime);configASSERT((xTimeIncrement > 0U));configASSERT(uxSchedulerSuspended == 0);/* 挂起调度器 */vTaskSuspendAll();{const TickType_t xConstTickCount = xTickCount;/* 下一次任务唤醒时间 */xTimeToWake = *pxPreviousWakeTime + xTimeIncrement;/* 前一次唤醒时间大于系统当前节拍,说明系统节拍已经溢出 */if(xConstTickCount < *pxPreviousWakeTime){/* 下一次唤醒时间小于前一次唤醒时间,说明下一次唤醒时间溢出 *//* 下一次唤醒时间大于系统当前节拍 *//* 综上,下一次唤醒时间在当前系统节拍的后面 */if((xTimeToWake < *pxPreviousWakeTime) && (xTimeToWake > xConstTickCount)){/* 应该延时 */xShouldDelay = pdTRUE;}else{mtCOVERAGE_TEST_MARKER();}}/* 系统节拍没有溢出 */else{/* 下一次唤醒时间小于前一次唤醒时间,说明下一次唤醒时间溢出 *//* 或者下一次唤醒时间大于当前系统节拍 *//* 综上,下一次唤醒时间在当前系统节拍的后面 */if((xTimeToWake < *pxPreviousWakeTime) || (xTimeToWake > xConstTickCount)){/* 应该延时 */xShouldDelay = pdTRUE;}else{mtCOVERAGE_TEST_MARKER();}}/* 将下一次唤醒时间赋值给pxPreviousWakeTime,留作下一次使用 */*pxPreviousWakeTime = xTimeToWake;/* 需要延时 */if(xShouldDelay != pdFALSE){traceTASK_DELAY_UNTIL(xTimeToWake);/* 将任务添加到延时列表,延时时长为当前节拍到唤醒时间 */prvAddCurrentTaskToDelayedList(xTimeToWake - xConstTickCount, pdFALSE);}else{mtCOVERAGE_TEST_MARKER();}}/* 解除调度器挂起,解除的时候可能切换任务 */xAlreadyYielded = xTaskResumeAll();/* 解除调度器挂起的时候没有切换任务 */if(xAlreadyYielded == pdFALSE){/* 请求切换任务 */portYIELD_WITHIN_API();}else{mtCOVERAGE_TEST_MARKER();}
}

系统节拍采用32位计数,所以最终肯定会溢出,同样唤醒时间也存在溢出。系统一共维护了两个延时列表,分别是延时列表和溢出延时列表,两条列表都是按照唤醒时间的从小到大进行排列。当唤醒时间溢出的时候,就不能和没溢出的插入同一个列表了。系统将唤醒时间没有溢出的任务放入延时列表,将唤醒时间溢出的任务放入溢出延时列表。

/* 将任务添加到延时列表 */
static void prvAddCurrentTaskToDelayedList(TickType_t xTicksToWait, const BaseType_t xCanBlockIndefinitely)
{TickType_t xTimeToWake;const TickType_t xConstTickCount = xTickCount;#if (INCLUDE_xTaskAbortDelay == 1){pxCurrentTCB->ucDelayAborted = pdFALSE;}#endif/* 当前任务肯定挂接在就绪列表中,将其移除。该就绪列表中没有任何任务 */if(uxListRemove(&(pxCurrentTCB->xStateListItem)) == (UBaseType_t)0){/* 清除任务优先级记录中的优先级 */portRESET_READY_PRIORITY(pxCurrentTCB->uxPriority, uxTopReadyPriority);}else{mtCOVERAGE_TEST_MARKER();}#if (INCLUDE_vTaskSuspend == 1){/* 延时时间为portMAX_DELAY并且允许无限期阻塞 */if((xTicksToWait == portMAX_DELAY) && (xCanBlockIndefinitely != pdFALSE)){/* 将任务挂接到挂起列表 */vListInsertEnd(&xSuspendedTaskList, &(pxCurrentTCB->xStateListItem));}else{/* 计算唤醒时间 */xTimeToWake = xConstTickCount + xTicksToWait;/* 设置状态列表项值为唤醒时间 */listSET_LIST_ITEM_VALUE(&(pxCurrentTCB->xStateListItem), xTimeToWake);/* 唤醒时间小于当前节拍说明已经溢出了 */if(xTimeToWake < xConstTickCount){/* 将当前任务挂接到延时溢出列表 */vListInsert(pxOverflowDelayedTaskList, &(pxCurrentTCB->xStateListItem));}/* 唤醒时间大于当前节拍说明没有溢出 */else{/* 将当前任务挂接到延时列表 */vListInsert(pxDelayedTaskList, &(pxCurrentTCB->xStateListItem));/* 当前任务唤醒时间,小于原先下一个解除阻塞任务的唤醒时间 */if(xTimeToWake < xNextTaskUnblockTime){/* 更新当前任务唤醒时间为下一个解除阻塞任务的唤醒时间 */xNextTaskUnblockTime = xTimeToWake;}else{mtCOVERAGE_TEST_MARKER();}}}}#else{xTimeToWake = xConstTickCount + xTicksToWait;listSET_LIST_ITEM_VALUE( &( pxCurrentTCB->xStateListItem ), xTimeToWake );if( xTimeToWake < xConstTickCount ){vListInsert( pxOverflowDelayedTaskList, &( pxCurrentTCB->xStateListItem ) );}else{vListInsert(pxDelayedTaskList, &(pxCurrentTCB->xStateListItem));if(xTimeToWake < xNextTaskUnblockTime){xNextTaskUnblockTime = xTimeToWake;}else{mtCOVERAGE_TEST_MARKER();}}(void)xCanBlockIndefinitely;}#endif
}

当前系统节拍到达最近的唤醒时间时,系统会将应该唤醒的任务从延时列表中清除,并挂接到就绪列表中。

/* 系统节拍加一 */
BaseType_t xTaskIncrementTick(void)
{TCB_t *pxTCB;TickType_t xItemValue;BaseType_t xSwitchRequired = pdFALSE;traceTASK_INCREMENT_TICK(xTickCount);/* 调度器没有被挂起 */if(uxSchedulerSuspended == (UBaseType_t)pdFALSE){/* 系统节拍计数器加一 */const TickType_t xConstTickCount = xTickCount + (TickType_t)1;xTickCount = xConstTickCount;/* 系统节拍溢出 */if(xConstTickCount == (TickType_t)0U){/* 切换延时列表,更新下一次解除阻塞的时间 */taskSWITCH_DELAYED_LISTS();}else{mtCOVERAGE_TEST_MARKER();}/* 处理延时列表中超时的任务 */if(xConstTickCount >= xNextTaskUnblockTime){for(;;){/* 如果延时列表已经为空,则将下一次解除时间设为最大 */if(listLIST_IS_EMPTY(pxDelayedTaskList) != pdFALSE){xNextTaskUnblockTime = portMAX_DELAY;break;}/* 延时列表不是空的 */else{/* 获取延时列表中需要最先解除的任务 */pxTCB = listGET_OWNER_OF_HEAD_ENTRY(pxDelayedTaskList);xItemValue = listGET_LIST_ITEM_VALUE(&(pxTCB->xStateListItem));/* 最先需要解除的任务都没有超时 */if(xConstTickCount < xItemValue){/* 更新下一次解除时间 */xNextTaskUnblockTime = xItemValue;break;}else{mtCOVERAGE_TEST_MARKER();}/* 将超时任务从延时列表中移除 */(void)uxListRemove(&(pxTCB->xStateListItem));/* 如果该任务被挂接到某个事件列表,还需要从事件列表中移除 */if(listLIST_ITEM_CONTAINER(&(pxTCB->xEventListItem)) != NULL){(void)uxListRemove(&(pxTCB->xEventListItem));}else{mtCOVERAGE_TEST_MARKER();}/* 将任务加入就绪列表中 */prvAddTaskToReadyList(pxTCB);......}}}......}/* 调度器被挂起 */else{......}......return xSwitchRequired;
}

一旦系统节拍出现溢出,系统会对延时列表和溢出延时列表进行切换。由于系统节拍溢出,原来溢出延时列表变成了延时列表,而原来的延时列表里的任务在系统节拍溢出之后肯定已经全部被超时唤醒,之后这条原先的延时列表则被拿来作为溢出延时列表。

/* 切换延时列表 */
#define taskSWITCH_DELAYED_LISTS()                                                                  \
{                                                                                                   \List_t *pxTemp;                                                                                    \\configASSERT((listLIST_IS_EMPTY(pxDelayedTaskList)));     /* 延时列表必须为空 */  \\/* 交换延时列表和溢出延时列表 */                                                                           \pxTemp = pxDelayedTaskList;                                                   \pxDelayedTaskList = pxOverflowDelayedTaskList;                                                    \pxOverflowDelayedTaskList = pxTemp;                                                               \\xNumOfOverflows++;                      /* 系统节拍溢出次数加一 */                            \prvResetNextTaskUnblockTime(); /* 更新下一个要解除阻塞的时间 */                     \
}

FreeRTOS任务延时函数相关推荐

  1. 【11】FreeRTOS的延时函数

    目录 1.延时函数-介绍 2.相对延时函数-解析 2.1函数`prvAddCurrentTaskToDelayedList`-解析 2.3滴答定时器中断服务函数`xPortSysTickHandler ...

  2. FreeRTOS延时函数

    vTaskDelay() 相对延时函数 vTaskDelay()延时固定数量的tick中断,将调用任务置于阻塞状态.(vTaskDelay() 函数只有在宏 INCLUDE_vTaskDelay 置1 ...

  3. FreeRTOS一天一个小知识之任务延时函数vTaskDelay

    想必各位嵌入式工程师对于Delay延时函数再也熟悉不过了~ 但对于各位刚入RTOS的小白来说,有操作系统的延时函数,真的和裸机中的延时函数一样吗?FreeRTOS的任务调度是怎么调度的?如何分配系统的 ...

  4. 嵌入式开发(7)系统定时器(SysTick)之延时函数运用

    目录 一.系统定时器 1. 简介 2.工作原理 3.频率的概念 二.库函数SysTick定时器操作 系统定时器配置 三.寄存器SysTick定时器操作 1.系统定时器的用途 2.寄存器 3.官方示例 ...

  5. NO.2 QN9021 延时函数

    本章主要讲解的是QN9021在使用过程中经常使用到的延时函数,由于咱们这颗蓝牙SoC没有SystemTick这个功能,所以就不能使用系统节拍来作为准确的延时工具,这里我推荐的是使用定时器时钟来作为基本 ...

  6. c语言随机数生成0 99函数,C语言生成随机数的函数、延时函数

    该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 下面C语言代码使用了生成随机数的函数.延时函数.请大家仔细观察其显示效果. 从以下代码,我们可以得出一个重要的结论:当上述两类函数被放入循环时,应作出一定 ...

  7. 延时函数介绍和呼吸灯的实现

    文章目录 延时函数介绍 呼吸灯原理 杨桃32学习笔记,本文图片文字皆为转述 延时函数介绍 分为delay_s秒,delay_ms毫秒,delay_us微秒延时,最大参数不能超过65535. 呼吸灯原理 ...

  8. Linux Shell中的延时函数

    Linux Shell中的延时函数 在 linux shell 脚本中经常需要做一些延时处理. 所以经常要用到 sleep 或 usleep 函数. 下面来说一下  sleep 和 usleep 的区 ...

  9. 已解决:大家使用原子哥的延时函数delay_ms,delay_us会出现进入延时函数出不来的情况

    首说明先原子哥的延时函数没有问题.原子哥的延时函数有的是用在STM32F103中的,要看自己的开发板是那个类型.这里给出STM32F103和STM32F030的延时函数. (1)用在STM32F030 ...

最新文章

  1. Ubuntu18.04 误删/usr/bin/python3的解决方案
  2. Windows Phone xml数据的解析与绑定
  3. MySQL 集群方案介绍
  4. java多线程基础篇第一篇-JMM
  5. [转载]Runtime详解
  6. json格式化的网站
  7. 编译 scintilla 并且缩小 SciLexer的 体积的做法
  8. 移动硬盘只显示盘符,不显示容量,也不能打开
  9. android webview 广告过滤,使用webView屏蔽广告(实操版)
  10. 【网络科学导论】【复杂网络】基础知识总结
  11. 关于ajax同步状态及sucess,complete的顺序的理解
  12. 电脑有网但是浏览器无法显示网页
  13. vant 表单按钮置灰_Vant Button 按钮
  14. 如何查看win10专业版是否永久激活
  15. 均方误差损失函数(MSE,mean squared error)
  16. 关闭VM中虚拟机与主机时间同步
  17. 拓扑排序及逆拓扑排序
  18. Java面向对象编程(求长方体体积)
  19. 操作系统(四)操作系统的主要功能
  20. 墨言教育:看过很多配色理论还是学不会?来看这里!

热门文章

  1. 编写jmeter测试用例_Jmeter | 实现接口自动化设计说明
  2. java字符串剪切函数,java用substring函数截取string中一段字符串,substringstring
  3. 中艺人脸识别考勤机使用方法_人脸识别考勤机的使用方法及注意事项 - 全文
  4. 卷积神经网络算法python实现_自动色彩均衡(ACE)快速算法python实现
  5. hadoop大数据开发基础_Java大数据开发(三)Hadoop(2)经典的Hadoop
  6. (王道408考研操作系统)第二章进程管理-第三节5:用信号量实现进程互斥、同步和前驱关系
  7. 回溯算法之电话号码的字母组合
  8. 数据结构-树与二叉树
  9. 64位MinGW和MSYS的安装
  10. Python redis的订阅发布机制(publish、pubsub)