FreeRTOS中的状态机程序设计——switch语句中的vTaskStartScheduler

State machine program design in FreeRTOS - vTaskStartScheduler in a switch statement

我在 FreeRTOS 中有一个程序设计问题:

我有一个有 4 个状态和 6 个任务的状态机。在每个状态下,必须执行不同的任务,但 Task1 除外,它始终处于活动状态:

状态 1:任务 1、任务 2、任务 3
状态 2:任务 1、任务 2、任务 3、任务 4
状态 3:任务 1、任务 5
状态 4:任务 1、任务 6

Task1、Task3、Task4、Task5 和 Task6 是周期性的,每个读取不同的传感器。
Task2 是非周期性的,它仅在达到阈值时才发送 GPRS 警报。

状态之间的切换由来自每个任务的传感器输入的事件决定。

main()的最初设计思路是有一个开关来控制状态,并根据状态暂停和激活相应的任务:

void main ()
{
    /* initialisation of hw and variables*/
    system_init(); 

    /* creates FreeRTOS tasks and suspends all tasks except Task1*/
    task_create();

    /* Start the scheduler so FreeRTOS runs the tasks */
    vTaskStartScheduler(); 

    while(true)
    {
        switch STATE:
            case 1:
                suspend(Task4, Task5, Task6);
                activate(Task2, Task3);
                break;
            case 2:
                suspend(Task5, Task6);
                activate(Task2, Task3, Task4);
                break;
            case 3:
                suspend(Task2, Task3, Task4, Task6); 
                activate(Task5);
                break;
            case 4: 
                suspend(Task2, Task3, Task4, Task5);
                activate(Task6);
                break;
    }
}

我的问题是:我应该在哪里调用与开关相关的 vTaskStartScheduler()?在我看来,在这段代码中,一旦调用了vTaskStartScheduler,程序就永远不会进入switch语句。

我是否应该创建另一个始终处于活动状态的任务来控制状态机,其中包含前面的 while 和 switch 语句,例如以下伪代码?

task_control()
{
    while(true)
    {
        switch STATE:
            case 1: 
                   suspend(Task4, Task5, Task6);  
                   execute(Task2, Task3); 
            and so on...
    }
}  

任何建议将不胜感激...

为了回答您的问题,vTaskStartScheduler() 顾名思义,将启动调度程序。它之后的任何代码只会在调度程序停止时执行,这在大多数情况下是程序结束时,所以永远不会。这就是为什么您的 switch 不会 运行.

正如您已经避开的那样,对于您的设计,您可以使用 'main' 任务来控制其他任务。在调用 vTaskStartScheduler().

之前,您需要创建它并向调度程序注册它

附带说明一下,如果您确实采用这种方法,您只想 suspend/resume 您的任务在第一次进入某个状态时执行,而不是在 'main' 任务的每次迭代时执行。

例如:

static bool s_first_state_entry = true;

task_control()
{
    while (true)
    {
        switch (STATE)
        {
        case 1:
            if (s_first_state_entry)
            {  
                // Only do this stuff once              
                s_first_state_entry = false;
                suspend(Task4, Task5, Task6);  
                execute(Task2, Task3); 
            }
            // Do this stuff on every iteration
            // ...
            break;
        default:
            break;
        }
    }
}  

void set_state(int state)
{
    STATE = state;
    s_first_state_entry = true;
}

正如 Ed King 所提到的,您的解决方案包含一个主要的设计缺陷。也就是说 - 在启动调度程序之后,在调度程序停止之前,在主函数中指定的任何代码都不会执行。

我建议在 Idle 任务中实现你的状态逻辑(记住在你的任务中包含延迟,不要让 Idle 挂钩因处理时间而饿死)。空闲任务可以根据信号量的当前状态阻塞和解除阻塞任务。不过请记住,Idle 挂钩是一项优先级最低的任务,因此在设计系统时要小心。当任务消耗大部分处理时间不允许空闲任务切换状态时,我建议的解决方案可能是完全错误的。

或者,您可以创建 Ed King 提到的具有最高优先级的高级任务。

老实说,一切都取决于任务真正在做什么。