【问题标题】:Two Task Synchronisation With Mutex In FreeRTOS在 FreeRTOS 中使用 Mutex 进行两个任务同步
【发布时间】:2020-06-18 14:03:53
【问题描述】:

我正在尝试在 IAR Workbench IDE 中使用 STM32 F401RE MCU 上的 FreeRTOS 打开和关闭 LED。

LED 属于 STM32 核板。有两个任务一个打开 Led,另一个任务关闭同一个 Led。

代码如下:

主要代码:

SemaphoreHandle_t xMutex;
int main()
{  

  if ( xMutex == NULL )  
  {
      xMutex = xSemaphoreCreateMutex();  

      if ( ( xMutex ) != NULL )
        xSemaphoreGive( ( xMutex ) ); 

   }

   xTaskCreate(LedOn, "Led On", 100, NULL,  1, NULL);
   xTaskCreate(LedOff, "Led Off", 100, NULL, 1, NULL);
   vTaskStartScheduler();
   while(1){}

}

任务:

void LedOn(void *argument)
{
   for(;;)
   {  
      xSemaphoreTake( xMutex, ( TickType_t )5000 ) ;     
      HAL_GPIO_WritePin(GPIOA,GPIO_PIN_5,GPIO_PIN_SET);
      vTaskDelay(5000); 
      xSemaphoreGive(xMutex);
    }
}

void LedOff(void *argument)
{
   for(;;)
   {  
      xSemaphoreTake( xMutex, ( TickType_t )5000 ) ;     
      HAL_GPIO_WritePin(GPIOA,GPIO_PIN_5,GPIO_PIN_RESET);
      vTaskDelay(5000); 
      xSemaphoreGive(xMutex);
    }
}

我的意思是:

Led on task负责开启Led 5s

Led off 任务负责 5s 开启 Led

所以这会一直持续到断电

我的问题是:

在初始情况下,LED 保持亮起 5 秒,然后 LED 保持亮起 5 秒,在两次切换 LED 保持亮起后,它仅适用于两次上下文切换。

当我在两次切换后调试时,断点不会命中任务

经过一番尝试,我想我找到了答案:

每个任务都应该有它的延迟时间,所以我们需要添加一个延迟时间才能让任务继续其操作,但我认为我在 xTakeSemaphorexGiveSemaphore 方法之间添加了延迟时间,是互斥体延迟时间,它说明资源应该如何被锁定而不是任务延迟时间。

解决办法是:

void LedOn(void *argument)
{
   for(;;)
   {  
       if(xSemaphoreTake(xMutex, portMAX_DELAY)== pdTRUE)
       {
          HAL_GPIO_WritePin(GPIOA,GPIO_PIN_5,GPIO_PIN_SET); 
          vTaskDelay(pdMS_TO_TICKS(5000));
          xSemaphoreGive(xMutex);
          vTaskDelay(pdMS_TO_TICKS(5000));
       }    
    }
}

void LedOff(void *argument)
{
   for(;;)
   {
       if( xSemaphoreTake( xMutex, portMAX_DELAY)== pdTRUE)
       {
          HAL_GPIO_WritePin(GPIOA,GPIO_PIN_5,GPIO_PIN_RESET); 
          vTaskDelay(pdMS_TO_TICKS(5000));
          xSemaphoreGive(xMutex);  
          vTaskDelay(pdMS_TO_TICKS(5000));
       }  
   }

}

【问题讨论】:

    标签: c synchronization mutex stm32 freertos


    【解决方案1】:

    假设“任务 A”是 LedOn,而“任务 B”是 LedOff。或者,反之亦然,因为它对问题无关紧要。

    假设任务 B 已获取互斥体。

    您的问题是任务 A 的 xSemaphoreTake [可能] 超时,没有获取互斥锁。

    你应该检查返回码。

    原因是它的超时值为 5000 滴答。但是,任务 B 执行vTaskDelay(5000)。而且,在执行此操作时,它的互斥锁锁定

    因此,很可能任务 A 的 xSemaphoreTake 将在任务 B 释放互斥锁之前 超时。

    然后,您翻转 LED 值,并进行延迟。但是,然后您在任务 A 已锁定的互斥体上执行 xSemaphoreGive

    换句话说,你有一个竞争条件。

    要么在 take 调用中设置一个无限超时,要么至少设置一个比你给延迟函数的值更大的值。

    尝试取值(例如)10000

    【讨论】:

      【解决方案2】:

      优先级调度 RTOS 的通常前提是准备好运行的最高优先级任务/线程抢占以较低优先级运行的任务/线程。

      LedOffLedOn 任务都是以相同的优先级创建的,因此当信号量发出信号时不会立即发生上下文切换。

      当它绕过循环并尝试再次获取信号量时,可能会发生上下文切换。现在有两个任务在争夺它。

      谁获胜本质上是 FreeRTOS 的一个实现细节——尤其是信号量上的信号量获取操作是否以严格的 FIFO 顺序发生——VxWorks(FreeRTOS 似乎在很大程度上模仿它)可以选择执行此操作的 ISTR。

      在 POSIX 线程(FreeRTOS 也支持)中看到的另一种方法是唤醒等待线程,然后首先安排该线程要使用的信号量 - 这肯定是一个已经在运行的线程。

      作为更一般的观点,您将具有两个状态的有限状态机过度复杂化了。实现它的一种高度可靠的方法是:

      void LedFlasher(void *argument)
      {
         for(;;)
         {      
            HAL_GPIO_WritePin(GPIOA,GPIO_PIN_5,GPIO_PIN_SET);
            vTaskDelay(5000); 
            HAL_GPIO_WritePin(GPIOA,GPIO_PIN_5,GPIO_PIN_RESET);
            vTaskDelay(5000); 
         }
      }
      

      【讨论】:

      • 如果显然不是事实。如果您不启用循环切换(没有人这样做,因为它在 RTOS 环境中没有太大意义)。当任务不能使用互斥量或信号量时,它会立即设置为 STOP 模式并进行任务切换。
      • 循环切换是大多数 RTOS 的默认设置。它在 FreeRTOS、Embos、SafeRTOS 等中默认启用。如果另一个相同优先级的任务没有产生,确保相同优先级的任务获得执行时间非常重要。在 RTOS 行业工作多年,我还没有遇到任何人关闭此功能。
      【解决方案3】:

      永远等待互斥锁

      xSemaphoreTake( xMutex, portMAX_DELAY);
      

      在这两个任务中。

      【讨论】:

        【解决方案4】:

        有点晚了.. ;) 但我认为您的程序中存在错误。 互斥体的初始版本(主要的 xSemaphoreGive)会破坏它。 一个互斥体被创建准备好被使用,即处于释放状态。

        【讨论】:

        • 感谢您的澄清。很高兴知道。下次应该先使用源;)
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-04-25
        • 1970-01-01
        • 1970-01-01
        • 2018-05-17
        • 2019-01-23
        • 1970-01-01
        相关资源
        最近更新 更多