【问题标题】:FreeRTOS vTaskDelayUntil() finishes immediatelyFreeRTOS vTaskDelayUntil() 立即完成
【发布时间】:2017-01-14 16:28:24
【问题描述】:

我有一个问题,vTaskDelayUntil() 函数没有延迟但立即完成。代码如下:

TickType_t xLastWakeTime = xTaskGetTickCount();
while(1){
    if (xSemaphoreTake(xSemaphoreRS485, portMAX_DELAY) == pdTRUE) {
        printf("S display data %d\n", xTaskGetTickCount());
        sendDisplayData();
        printf("E display data %d\n", xTaskGetTickCount());
        xSemaphoreGive(xSemaphoreRS485);
        printf("W display data %d\n", xLastWakeTime);
        vTaskDelayUntil(&xLastWakeTime, 2000);
    }
}

由此我得到以下输出:

S display data 29928
E display data 30534
W display data 3919
S display data 30534
E display data 31140
W display data 5919
S display data 31140
E display data 31746
W display data 7919
S display data 31746
E display data 32352
W display data 9919

函数 sendDisplayData() 执行大约需要 670 毫秒,xTaskGetTickCount() 确认它。然后任务应该等待大约 1230 毫秒,因此整个迭代可能需要 2000 毫秒。但是 vTaskDelayUntil() 立即完成。第一次执行在 30534 结束,第二次也从 30534 开始。 xTaskGetTickCount() 返回的值证明 vTaskDelayUntil() 没有引入延迟。我也可以通过 sendDisplayData() 的输出频率看到它。

第二个有趣的事情是 xLastWakeTime 显示完全不同的值,这些值实际上增加了 2000。它不应该存储 xTaskGetTickCount() 返回的相似值吗?

【问题讨论】:

  • 你的配置文件中有#define INCLUDE_vTaskDelayUntil 1 吗?你确定你没有重新定义 TickType_t 让我们说签名 16 位?尝试移动 TickType_t xLastWakeTime = xTaskGetTickCount();如果使用信号量后,它现在会显示正确的值吗? (我知道它不会仅仅为了简单地检查你想要的,这是否是因为在第一次到达 vTaskDelayUntil 时你已经超过了 xLastWakeTime+2000。
  • 是的,我有这个定义,TickType_t 是 32 位类型。移动 xLastWakeTime 的声明和初始化有帮助!我想知道怎么做。当然,我的原始代码并不完美,因为接收信号可能需要 10 秒,并且第一个 vTaskDelayUntil() 会立即退出。但我仍然不知道为什么在第二次迭代中该变量设置为 5919 而不是 30534。

标签: c freertos stm32f4


【解决方案1】:

在您的第一次迭代中,xLastWakeTime 的值为 3919,并且您请求增加 2000,因此延迟到 5919,但您在时间 30534 调用了它。

来自vTaskDelayUntil()的文档

需要注意的是,vTaskDelayUntil()如果用于指定已经过去的唤醒时间,会立即返回(不阻塞)。

您的任务在初始信号量上阻塞了 26009 个滴答 (29928 - 3919)。您的目标 2000 滴答增量早已过去。

我建议以下内容至少更接近您的意图

for(;;)
{
    if (xSemaphoreTake(xSemaphoreRS485, portMAX_DELAY) == pdTRUE)  // Lock
    {
        TickType_t xLastWakeTime = xTaskGetTickCount();
        sendDisplayData();
        xSemaphoreGive(xSemaphoreRS485); // Unlock

        vTaskDelayUntil(&xLastWakeTime, 2000);
}

这将使循环迭代总共花费 2000 个滴答声,包括执行 sendDisplayData() 所花费的时间加上等待 RS485 资源可用的时间,我认为这是您的意图。

【讨论】:

  • 这里的信号量用作同步访问 RS485 链路的互斥锁。该任务需要在执行 sendDisplayData() 之前获取信号量。当 RS485 链路没有被其他任务使用时,我希望这个任务每两秒执行一次 sendDisplayData()。请查看后续迭代。 5919 和 7919 与 30534 的关系如何解释?
  • @grzegorz :很公平,但是在初始同步之后,您并没有同步任何内容,因为您在获取信号量之前和在相同的上下文中提供信号量。 Jour“理由”更多地表明您没有理解我的答案,而不是代码是这样的原因。可能您仍然需要信号量等待,但您需要删除 sem give,因为这会破坏您的同步 - 这是一个不同的问题,不在您的问题范围内 - 此答案将解决您描述的问题。
  • 关于其他迭代 - 是的,它们在这个答案中得到了解释。您从 26000 滴答开始,它需要进行多次迭代,将目标时间增加 2000,然后才能赶上当前时间。从您的调试输出中可以清楚地看出发生了什么。
  • 此信号量作为互斥体工作,并以非常常见的方式处理:在使用资源之前获取,并在此任务不再使用资源时返回。在延迟期间有一个上下文切换,第二个任务获取信号量并使用资源(RS485 链接)。我不能一直持有互斥锁。感谢您对 vTaskDelayUntil() 的说明。我认为它将 xLastWakeTime 设置为当前的滴答数,但实际上它只是添加了第二个参数以赶上。在我的项目中,它不会发生,因为信号量被其他任务占用太频繁且时间太长。
  • @grzegorz:好的;我没有意识到 FreeRTOS 中的常规信号量和互斥量都使用了相同的 sem give/take API 调用。不喜欢 FreeTROS 添加到我的列表中的又一个理由。
【解决方案2】:

如上所述,如果指定的唤醒时间已经过去,vTaskDelayUntil() 将立即返回。建议改用vTaskDelay()

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-09-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多