You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在FreeRTOS任务中周期性执行函数?以及在1ms调度任务中无阻塞每10ms执行指定函数的实现方法

在FreeRTOS 1ms调度任务中实现非阻塞式10ms周期函数的方案

嘿,这个需求在嵌入式FreeRTOS开发里太常见了!我给你分享几个完全满足要求的方案,都不会阻塞你的1ms调度任务,咱们一个个说:

方案一:任务内部计数器跟踪(最简单直接)

这个方案不需要额外的FreeRTOS内核对象,纯靠任务内部的静态计数器来实现,特别适合逻辑简单、不需要动态调整周期的场景。

原理很简单:你的任务每1ms被调度一次,每次进入任务就给计数器加1,当计数器累加到10的时候,执行目标函数,然后重置计数器就行。

代码示例:

void Your1msPeriodicTask(void *pvParameters)
{
    // 静态变量,任务重启也会保留值,适合计数
    static uint8_t uxCycleCounter = 0;
    const uint8_t uxTargetInterval = 10; // 对应10ms间隔

    for(;;) // FreeRTOS任务标准死循环
    {
        // 先执行你这个1ms任务本身要做的常规工作
        execute_1ms_required_work();

        // 计数判断逻辑
        uxCycleCounter++;
        if(uxCycleCounter >= uxTargetInterval)
        {
            // 执行每10ms需要跑的函数
            execute_10ms_periodic_work();
            uxCycleCounter = 0; // 重置计数器
        }

        // 让出CPU,确保任务每1ms被调度一次
        vTaskDelay(pdMS_TO_TICKS(1));
    }
}

优点:

  • 零额外资源占用,实现成本极低
  • 完全不阻塞任务,只是简单的计数判断,执行完就继续任务流程
  • 逻辑直观,容易调试

注意点:

  • 计数器尽量用无符号类型(比如uint8_t/uint16_t),避免溢出问题
  • 如果你的1ms任务偶尔被高优先级任务抢占,周期可能会有微小延迟,但绝大多数嵌入式场景下完全可以接受

方案二:FreeRTOS软件定时器(更精准灵活)

如果你需要更精准的周期控制,或者未来可能要动态调整周期、随时启停这个10ms函数,那FreeRTOS的软件定时器是更好的选择。

软件定时器的回调函数是在FreeRTOS的定时器服务任务中执行的,完全独立于你的1ms任务,所以绝对不会阻塞你的任务流程。

代码示例:

// 定义定时器句柄
TimerHandle_t x10msPeriodicTimer = NULL;

// 定时器回调函数:这里就是你要每10ms执行的逻辑
void v10msTimerCallback(TimerHandle_t xTimer)
{
    execute_10ms_periodic_work();
}

// 初始化函数(在任务启动前调用)
void App_Init(void)
{
    // 创建软件定时器
    x10msPeriodicTimer = xTimerCreate(
        "10msTimer",          // 定时器名称(仅调试用)
        pdMS_TO_TICKS(10),    // 周期:10ms
        pdTRUE,               // pdTRUE表示自动重载(周期性执行)
        (void *)0,            // 定时器ID(这里用不到,传0即可)
        v10msTimerCallback    // 回调函数指针
    );

    // 启动定时器(如果创建成功的话)
    if(x10msPeriodicTimer != NULL)
    {
        // 第二个参数是等待启动的超时时间,0表示立即尝试
        xTimerStart(x10msPeriodicTimer, 0);
    }
}

// 你的1ms任务该干嘛干嘛,完全不受定时器影响
void Your1msPeriodicTask(void *pvParameters)
{
    for(;;)
    {
        execute_1ms_required_work();
        vTaskDelay(pdMS_TO_TICKS(1));
    }
}

优点:

  • 周期精准,不受你的1ms任务的执行时长、被抢占情况影响
  • 支持动态调整周期(xTimerChangePeriod)、随时启停(xTimerStop/xTimerStart)
  • 逻辑解耦,1ms任务和10ms周期函数完全独立,代码更清晰

注意点:

  • 需要确保FreeRTOS配置中开启了软件定时器(configUSE_TIMERS设为1)
  • 定时器回调函数里不能调用会阻塞的API(比如vTaskDelay、队列阻塞式读取),因为定时器服务任务优先级固定,阻塞会影响其他定时器

方案三:利用vTaskDelayUntil实现任务内精准延迟(适合特殊场景)

如果你的1ms任务其实大部分时间是等待,只是需要穿插10ms的逻辑,也可以用vTaskDelayUntil来实现,但这个方案更适合单独的周期任务,不太适配你当前“1ms调度一次任务”的场景,不过也可以作为补充了解:

void YourMixedTask(void *pvParameters)
{
    TickType_t xLastWakeTime = xTaskGetTickCount();
    const TickType_t x1msInterval = pdMS_TO_TICKS(1);
    static uint8_t uxCounter = 0;

    for(;;)
    {
        execute_1ms_required_work();

        uxCounter++;
        if(uxCounter >= 10)
        {
            execute_10ms_periodic_work();
            uxCounter = 0;
        }

        // 精准延迟到下一个1ms时间点
        vTaskDelayUntil(&xLastWakeTime, x1msInterval);
    }
}

这个方案和方案一类似,但用vTaskDelayUntil比vTaskDelay的精度更高,因为它是基于绝对时间点的延迟,而不是相对时间。


总结选择建议:

  • 如果逻辑简单、不需要动态调整,优先选方案一,最省心
  • 如果需要精准周期、动态控制,选方案二,扩展性更强

内容的提问来源于stack exchange,提问作者QBKM

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.30 23:02:33