如何在 FreeRTOS 中清楚地告诉任务结束

How to cleanly tell a task to die in FreeRTOS

我正在用 ESP32 做灯,我选择的 HomeKit 库使用 FreeRTOS 和 esp-idf,我不熟悉。

目前,我有一个函数,每当应该改变灯光的颜色时都会调用它,它只是一步一步地改变它。我想让它在颜色之间淡入淡出,这将需要一个 运行s 持续一两秒的函数。将此块作为程序的主要执行显然会使它反应迟钝,因此我需要将它 运行 作为一项任务。

我面临的问题是我一次只想 运行ning 一个 fading 函数的副本,如果它在完成之前被调用第二次,第一个副本应该退出(无需等待完全淡入淡出时间)在开始第二个副本之前。

我找到了 vTaskDelete,但如果我只是在任意点终止淡入淡出功能,一些变量和 LED 本身将处于未知状态。为了解决这个问题,我想到了使用一个 'kill flag' 全局变量,衰落函数将检查它的每个循环。

这是我想到的伪代码:

update_light {
    kill_flag = true
    wait_for_fade_to_die
    xTaskCreate fade
}

fade {
    kill_flag = false
    loop_1000_times {
        (fading code involving local and global variables)
        .
        .
        if kill_flag, vTaskDelete(NULL)
        vTaskDelay(2 / portTICK_RATE_MS)
    }
}

我的主要问题是:

很抱歉,我的印象是您在尝试解决具体问题时走错了路。 你写的是你不熟悉 FreeRTOS 和 esp-idf,所以我建议你首先熟悉 freeRTOS(或者一般的 RTOS 或任何其他 RTOS 的想法,将这些知识转移到 freeRTOS,......) .

在这样做的过程中,您会注意到(除了一些特定示例之外)任务函数 完全不同已为单个作业的顺序 "batch" 处理而编写。

模型与理论

通常,在嵌入式系统中设计一个好的 RTOS 任务时,最有帮助的模型状态机接收事件并做出反应,可能会改变其状态 and/or执行一些动作其起点和有效负载取决于状态机接收到的事件以及检测到事件时它所处的状态。 虽然没有事件,但任务不应 idle 而是 block 在 RTOS 功能创建的某个障碍处,该功能应该传递下一个相关事件.

实现这样的任务意味着编写一个任务函数,该函数由一个简短的初始化块和一个无限循环组成,该循环首先调用 RTOS 库以获取下一个逻辑事件(见右下方...),然后是代码处理该逻辑事件。 现在,逻辑事件不必由 RTOS 事件表示(虽然这可能发生在简单的情况下),但也可以由 RTOS 队列、邮箱或其他实现。 在这样的设计模式中,您的基于 RTOS 的软件的任务存在 "forever",等待下一个作业执行。

如何将理论应用于您的问题

你必须检查如何将你的编程问题分解成不同的任务。

Currently, I have a function that's called whenever the colour of the light should be changed, which just changes it in a step. I'd like to have it fade between colours instead, which will require a function that runs for a second or two. Having this block the main execution of the program would obviously make it quite unresponsive, so I need to have it run as a task.

希望我正确理解了您申请的目标: 系统正在驱动多个不同颜色的光源,一些"request source"正在选择下一个要显示的颜色。 当请求不同的颜色时,更改不应立即执行,而是在一定时间段内会有一些 "fading"。 即使发生淡入淡出,系统(及其请求源)也应保持响应,可能会在中间改变淡入淡出的方向。

我想你没有说颜色请求的来源。 因此,我猜测这个请求源可能是一些按钮、一个串行接口或一个复杂的算法(或随机数生成器?)运行 在后台。现在真的不重要了。

The issue I'm facing is that I only want one copy of the fading function to be running at a time, and if it's called a second time before it's finished, the first copy should exit (without waiting for the full fade time) before starting the second copy.

您实际上要寻找的是如何随时更改 状态(此处:光褪色的目标颜色),以便旧的、正在进行的褪色过程变得过时但输出(=光)行为不会以不连续的方式改变。

我建议您设置以下任务:

  • 一个(或多个)任务来生成颜色更改请求......无论你在这里需要什么。

  • 评估当前应输出哪种颜色混合的任务。 该任务应准备好接收

    1. 新颜色请求(在不改变当前颜色混合值的情况下改变 "target colour" 状态)
    2. 周期性滴答事件(例如,来自硬件或软件计时器) 这会导致颜色混合值更新为当前目标颜色的方向
  • 通过驱动系统的输出特性(例如,配置 GPIO 或 PWM,或通过串行连接传输信息...我们不不知道)。 如果调整输出部分只是分配一些寄存器,那么 "Zero" 对你来说是正确的。否则,尝试 "one or multiple".

现在做什么

I found vTaskDelete, but if I were to just kill the fade function at an arbitrary point, some variables and the LEDs themselves will be in an unknown state. To get around this, I thought of using a 'kill flag' global variable which the fading function will check on each of its loops.

只是不要那样做。 杀死一个任务,即使是一个没有准备好从内部被杀死的任务也会导致后续管理和清理软件输出内容的要求,你最终会想知道为什么你甚至开始使用 RTOS。

我确实知道,在您从未这样做过的情况下开始以这种方式进行设计和编程是一项巨大的努力,就像跳入冷水一样开始。 请相信我,通过这种方式,您将学习如何设计和实现优秀嵌入式系统的基础知识。 专业教育公司以数千美元/欧元/英镑的价格提供有关 RTOS 集成、响应式编程和状态机设计的课程,这是此类工作知识的一个很好的指标。

祝你好运! 在此过程中,您会遇到很多细节问题,欢迎您 post 到此版块(或找到更早的答案)。