FreeRTOS中的两个Delay函数(vTaskDelay vs vTaskDelayUntil)

一、核心概念先理清:Tick(系统节拍)

在讲延时函数前,先明确一个关键概念:系统节拍(Tick)

  • 它是 FreeRTOS 的最小时间单位,由 SysTick 定时器周期性中断产生(常见配置为 1ms/Tick)
  • 所有延时函数的参数,本质上都是基于 "Tick 数" 来计算的
  • 延时时间 ≈ Tick 数 × 单个 Tick 的周期(比如 1000 Tick ≈ 1000ms,即 1 秒)

二、vTaskDelay:相对延时,"从调用时起,等多久"

1. 函数原型与参数说明

复制代码
void vTaskDelay( const TickType_t xTicksToDelay );
  • xTicksToDelay:需要等待的 Tick 数(相对当前调用时刻的延时)
  • 功能:任务进入阻塞态,至少等待指定数量的 Tick 中断后,才会重新进入就绪态

2. 核心原理与特点

  • 相对计时 :延时的起点是 "调用vTaskDelay的那一刻",和任务上一次执行的结束时间无关
  • 会产生周期漂移:如果任务内业务逻辑耗时不固定,会导致整体执行周期越来越不准
  • CPU 利用率高 :延时期间任务主动让出 CPU,不会像裸机while(1)延时那样 "忙等"

3. 代码示例:基础用法

复制代码
void vTaskExample(void *pvParameters)
{
    while(1)
    {
        // 业务逻辑(耗时不确定)
        printf("任务执行中...\n");
        vTaskDelay(pdMS_TO_TICKS(1000)); // 延时1000ms(1秒),从调用这一刻开始计时
    }
}

三、vTaskDelayUntil:绝对延时,"固定周期,精准唤醒"

1. 函数原型与参数说明

复制代码
BaseType_t xTaskDelayUntil( TickType_t * const pxPreviousWakeTime,
                            const TickType_t xTimeIncrement );
  • pxPreviousWakeTime上一次任务被唤醒的 Tick 时间指针 ,首次调用前必须用xTaskGetTickCount()初始化
  • xTimeIncrement:期望的固定周期(Tick 数,比如 1000 Tick=1 秒)
  • 功能:任务会阻塞到 "*pxPreviousWakeTime + xTimeIncrement" 这个绝对时刻再唤醒,保证整体周期恒定

2. 核心原理与特点

  • 绝对计时:延时的起点是 "上一次任务被唤醒的时刻",会自动补偿任务内业务逻辑的耗时
  • 无周期漂移 :无论单次任务内耗时多少,两次唤醒的时间间隔始终等于xTimeIncrement
  • 适合周期性任务:比如传感器定时采集、定时串口发送等需要固定周期的场景

3. 代码示例:标准用法

复制代码
void vTaskExample(void *pvParameters)
{
    TickType_t xLastWakeTime;
    const TickType_t xCycleTime = pdMS_TO_TICKS(1000); // 固定周期1000ms

    xLastWakeTime = xTaskGetTickCount(); // 首次调用前初始化上一次唤醒时间

    while(1)
    {
        // 业务逻辑(耗时不确定)
        printf("任务执行中...\n");
        // 阻塞到下一个周期的绝对时间点
        vTaskDelayUntil(&xLastWakeTime, xCycleTime);
    }
}

四、两者核心区别对比

特性 vTaskDelay vTaskDelayUntil
计时方式 相对调用时刻计时 相对上一次唤醒时刻的绝对计时
周期稳定性 会漂移,任务内耗时会影响周期 无漂移,自动补偿任务内耗时
核心用途 非周期性延时(按键消抖、单次等待) 周期性任务(定时采集、定时控制)
使用复杂度 简单,直接传 Tick 数即可 需额外初始化xLastWakeTime变量
典型场景 单次延时、非固定频率任务 传感器定时采集、PWM 输出、定时通信

五、实战避坑指南

1. 关于 pdMS_TO_TICKS ()

不要直接写数字!用pdMS_TO_TICKS(ms)宏函数,自动把毫秒转换成对应 Tick 数,避免手动计算出错:

复制代码
// 推荐写法(1000ms → 对应Tick数)
vTaskDelay(pdMS_TO_TICKS(1000));
// 不推荐写法(硬编码,Tick配置改变后会出错)
vTaskDelay(1000); 

2. vTaskDelayUntil 必须初始化

首次调用前,一定要用xTaskGetTickCount()初始化xLastWakeTime,否则会导致延时异常:

复制代码
// 错误写法(未初始化)
TickType_t xLastWakeTime;
vTaskDelayUntil(&xLastWakeTime, pdMS_TO_TICKS(1000));

// 正确写法
TickType_t xLastWakeTime = xTaskGetTickCount();
vTaskDelayUntil(&xLastWakeTime, pdMS_TO_TICKS(1000));

3. 不要在中断里调用

两个延时函数都只能在任务上下文 中调用,中断服务函数里请用vTaskDelayUntilFromISR或其他中断安全 API。

4. 延时时间不能为 0

xTicksToDelayxTimeIncrement传 0 时,vTaskDelay会直接让出 CPU,而vTaskDelayUntil会立即返回,无法实现延时效果。

相关推荐
烤麻辣烫2 小时前
计算机思维--经典互联网应用
开发语言·学习·搜索引擎·数据库开发
wuxinyan1232 小时前
工业级大模型学习之路018:LangChain零基础入门教程(第一篇):LangChain架构与生态介绍
人工智能·python·学习·langchain
Jackyzhe3 小时前
从零学习Kafka:调优
分布式·学习·kafka
噜噜噜阿鲁~3 小时前
python学习笔记 | 11.0、面向对象高级编程
笔记·python·学习
暗冰ཏོ3 小时前
PHP 全栈开发学习手册:从基础到高级实战、Laravel、Redis、面试题完整版
学习·php·laravel
fake_ss19811 小时前
AI时代学习全栈项目开发的新范式
java·人工智能·学习·架构·个人开发·学习方法
Upsy-Daisy12 小时前
AI Agent 项目学习笔记(二):Spring AI 与 ChatClient 主链路解析
人工智能·笔记·学习
C+++Python13 小时前
C++ 进阶学习完整指南
java·c++·学习
sulikey14 小时前
个人Linux操作系统学习笔记2 - gcc与库的理解
linux·笔记·学习·操作系统·gcc·