【问题标题】:Hardware interrupt with FreeRTOS binary semaphoreFreeRTOS 二进制信号量的硬件中断
【发布时间】:2023-04-05 09:08:01
【问题描述】:

我基本上是在按下按钮后让 LED 亮起。以下是应该处理此问题的代码的 sn-p:

void task_player1(void *pvParameters)
{   
    while (1)
    {
        if (xSemaphoreTake(player1_signal, portMAX_DELAY))
        {
            printf(">>>Semaphore taken\n<<<");
            ioport_set_pin_level(L1, HIGH);
            xSemaphoreGive(player1_signal);
        }
        else
        {
            ioport_set_pin_level(L1, LOW);
        }
    }
}

void task_ctrl(void *pvParameters)
{
    bool button1 = 0;       
    
    while (1)
    {
        button1 = ioport_get_pin_level(B1);
        if (button1)
        {
            xSemaphoreGive(player1_signal);
            printf("Semaphore given\n");
        }
    }
}

我的设想是task_ctrl 在按钮按下时提供信号量。 Task_player1 被阻塞,直到它收到信号量,之后它应该打开 LED。

问题是它似乎从不接受信号量。我正在运行printf 语句来向我展示程序可以到达多远,并且它永远不会进入 LED 亮起的部分。但是,当我在 task_ctrl 中没有按下按钮的情况下运行 xSemaphoreGive 时,信号量就会被占用。

奇怪的是,"Semaphore given\n" 语句在按钮单击时被打印出来,这应该意味着信号量也被给出但从未被占用。

这些任务自己工作,我什至设法在没有按钮按下 if 语句的情况下提供信号量,就像我上面所说的那样。

我猜这与按钮按下代码有关,它也可以在任务之外自行运行。那么我做错了什么?我没有正确使用 FreeRTOS?

编辑:包括任务创建代码

#define TASK_STACK_SIZE (2048/ sizeof(portSTACK_TYPE))

xTaskCreate(task_ctrl, (const signed char * const) "Control", TASK_STACK_SIZE, NULL, 1, NULL);
    
xTaskCreate(task_player1, (const signed char * const) "Player1", TASK_STACK_SIZE, NULL, 1, NULL);

编辑 2:可能我应该提到的是我正在使用 FreeRTOS 7.3,因为 Atmel Studio 没有任何更新的版本。因此,据我所知,没有可用的 yield 函数。

【问题讨论】:

  • 任务的优先级是什么?如果 task_ctrl 具有更高或相同的优先级,并且由于它永远不会产生运行状态,那么 task_player1 状态永远不会进入运行状态。
  • 你能显示按钮按下和任务创建代码吗?
  • 标题中提到了“硬件中断”,但是这段代码中并没有用到硬件中断。
  • @seleciii44 是的,task_ctrl 具有更高的优先级,这使得 task_player1 永远不会运行。如果我反转优先级,task_ctrl 永远不会运行,并且如果它们相等,task_player1 在等待信号量时永远不会被阻塞,就像我想象的那样。
  • 这在免费 FreeRTOS 书籍的第一章中有介绍 - 如果您想学习 FreeRTOS,这是一个很好的起点:freertos.org/Documentation/RTOS_book.html

标签: c embedded freertos atmelstudio


【解决方案1】:

task_ctrl 从不阻塞 - 这将阻止任何具有相同或更低优先级的任务永远运行。因此,如果task_player 的优先级相等或更低,即使给出信号量,任务也永远不会被调度。

如果没有按钮轮询似乎很难解释,但你没有显示该代码,所以我无法评论。

你应该做两件事:

  1. 确保task_ctrl 的优先级至少低于task_player1(如果它不阻塞,则低于任何任务 - 在这种情况下,空闲任务将永远不会运行)。
  2. 确保task_ctrl 阻塞 - 即使这只是一个轮询延迟,这样它就不会占用所有可用的 CPU 周期。

while (1)
{
    button1 = ioport_get_pin_level(B1);
    if (button1)
    {
        xSemaphoreGive(player1_signal);
        printf("Semaphore given\n");

        vtaskDelay( 1 ) ; // at least 1 tick
    }
}

task_ctrl 的一个可能问题是,它会在按住按钮时连续不断地重复发出信号量。某种状态变化检测和开关去抖动而不是水平轮询可能更可取。

轮询任务的另一种解决方案是对按钮使用硬件中断,并让轮询任务阻止来自 ISR 的事件,或者让 ISR 直接提供信号量 - 在任何一种情况下,您都必须处理与去抖动。

【讨论】:

  • 您对任务优先级的看法是正确的,我已将其更改为 task_player1 的优先级高于 task_ctrl 但现在出现了不同的问题。 Task_player1 永远不会被阻塞,这意味着无需我按下按钮,LED 就会一直亮着,而且 task_ctrl 甚至都不会运行。当我将它们更改为同等优先级时,两个任务同时运行,并且 task_player1 仍然没有像预期的那样被阻止。
  • @Tisa :你最好发布一个新问题和代码。