FreeRTOS专栏14:优先级翻转与互斥信号量

优先级翻转

优先级翻转简介:

就是高优先级的任务运行起来的效果好像成了低优先级,而低优先级比高优先级先运行。

优先级翻转如下所示:

优先级翻转过程:

为什么会发生优先级翻转?

因为两个任务(L和H)使用了同一个二值信号量,而在这两个任务之间,又有一个中等优先级的任务M,在这种情况下就容易发生优先级翻转。主要就是因为二值信号量产生的,低优先级任务L占用了信号量没有释放,导致高优先级任务请求信号量时无效,此时高优先级任务无法运行。

实验设计

总体思路是,任务L和任务H都请求信号量,当接收到信号量时,任务H立马释放信号量,而任务L做软件延时占用一段时间后再释放信号量,看程序的执行流程。测试代码如下所示:

#include "main.h"
#include "i2c.h"
#include "usart.h"
#include "gpio.h"

#include "bsp_led.h"
#include "bsp_key.h"
#include "bsp_usart.h"
#include "bsp_beep.h"

#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "semphr.h"

#include <string.h>

void start_task(void *pvParameters);
void low_task(void *pvParameters);
void middle_task(void *pvParameters);
void high_task(void *pvParameters);

/* 任务相关参数 */
#define START_TASK_SIZE     256
#define START_TASK_PRIO     1
TaskHandle_t start_Task_Handle;

#define LOW_TASK_SIZE       256
#define LOW_TASK_PRIO       2
TaskHandle_t low_task_Handle;

#define MIDDLE_TASK_SIZE    256
#define MIDDLE_TASK_PRIO    3
TaskHandle_t middle_task_Handle;

#define HIGH_TASK_SIZE      256
#define HIGH_TASK_PRIO      4
TaskHandle_t high_task_Handle;

SemaphoreHandle_t binary_semphr = NULL;   // 二值信号量句柄

int main(void)
{
  HAL_Init();
  SystemClock_Config();
  MX_GPIO_Init();
  MX_USART1_UART_Init();
  MX_I2C1_Init();
  MX_I2C2_Init();

  // 创建开始任务
  xTaskCreate((TaskFunction_t )start_task,
              (char *         )"start_task",
              (uint16_t       )START_TASK_SIZE,
              (void *         )NULL,
              (UBaseType_t    )START_TASK_PRIO,
              (TaskHandle_t * )&start_Task_Handle);

  vTaskStartScheduler(); // 开启调度器
}

void start_task(void *pvParameters)
{
  taskENTER_CRITICAL();

  binary_semphr = xSemaphoreCreateBinary();  // 创建信号量
  if (binary_semphr != NULL)
  {
    printf("\n二值信号量创建成功");
    xSemaphoreGive(binary_semphr);    // 释放信号量
  }
  else
  {
    printf("\n二值信号量创建失败");
  }

  // 创建低优先级任务
  xTaskCreate((TaskFunction_t )low_task,
              (char *         )"low_task",
              (uint16_t       )LOW_TASK_SIZE,
              (void *         )NULL,
              (UBaseType_t    )LOW_TASK_PRIO,
              (TaskHandle_t * )&low_task_Handle);
  // 创建中优先级任务
  xTaskCreate((TaskFunction_t )middle_task,
              (char *         )"middle_task",
              (uint16_t       )MIDDLE_TASK_SIZE,
              (void *         )NULL,
              (UBaseType_t    )MIDDLE_TASK_PRIO,
              (TaskHandle_t * )&middle_task_Handle);
  // 创建高优先级任务
  xTaskCreate((TaskFunction_t )high_task,
              (char *         )"high_task",
              (uint16_t       )HIGH_TASK_SIZE,
              (void *         )NULL,
              (UBaseType_t    )HIGH_TASK_PRIO,
              (TaskHandle_t * )&high_task_Handle);
  taskEXIT_CRITICAL();
  // 删除开始任务
  vTaskDelete(start_Task_Handle);
}

void low_task(void *pvParameters)
{
  BaseType_t error_state;
  uint32_t limit;
  for (;;)
  {
    if (binary_semphr != NULL)
    {
      // 一直等待二值信号量
      error_state = xSemaphoreTake(binary_semphr, portMAX_DELAY);   
      if (error_state == pdTRUE)
      {
        printf("\n低优先级任务获取到信号量,正在占用...");
      }
      else
      {
        printf("\n低优先级任务获取信号量失败");
      }
      #if 1
      // 模拟低优先级占用信号量
      for (limit = 0; limit < 5000000; limit++)
      {
        taskYIELD();      // 一直调用任务切换 
      }
      #else
      HAL_Delay(8000);
      #endif
      // 释放二值信号量
      printf("\n低优先级任务占用信号量结束,即将释放");
      xSemaphoreGive(binary_semphr);
    }
    vTaskDelay(500);
  }
}

void middle_task(void *pvParameters)
{
  for (;;)
  {
    printf("\n中优先级任务正在运行");
    vTaskDelay(1000);
  }
}

void high_task(void *pvParameters)
{
  for (;;)
  {
    if (binary_semphr != NULL)
    {
      printf("\n高优先级任务正在等待信号量...");
      // 一直等待二值信号量
      xSemaphoreTake(binary_semphr, portMAX_DELAY);   
      printf("\n高优先级任务获取到信号量,即将释放...");
      xSemaphoreGive(binary_semphr);    // 释放信号量
    }
    vTaskDelay(1000);
  }
}

程序运行结果如下所示:

互斥信号量

互斥信号量简介:

创建互斥信号量:

测试实验:修改上一个优先级翻转的实验,看测试结果。

修改过程:将使用二值信号量的地方修改成使用互斥信号量。

修改如下:

void start_task(void *pvParameters)
{
  taskENTER_CRITICAL();

  mutex_semphr = xSemaphoreCreateMutex();  // 创建互斥信号量
  if (mutex_semphr != NULL)
  {
    printf("\n互斥信号量创建成功");
    xSemaphoreGive(mutex_semphr);    // 释放信号量
  }
  else
  {
    printf("\n互斥信号量创建失败");
  }

  // 创建低优先级任务
  xTaskCreate((TaskFunction_t )low_task,
              (char *         )"low_task",
              (uint16_t       )LOW_TASK_SIZE,
              (void *         )NULL,
              (UBaseType_t    )LOW_TASK_PRIO,
              (TaskHandle_t * )&low_task_Handle);
  // 创建中优先级任务
  xTaskCreate((TaskFunction_t )middle_task,
              (char *         )"middle_task",
              (uint16_t       )MIDDLE_TASK_SIZE,
              (void *         )NULL,
              (UBaseType_t    )MIDDLE_TASK_PRIO,
              (TaskHandle_t * )&middle_task_Handle);
  // 创建高优先级任务
  xTaskCreate((TaskFunction_t )high_task,
              (char *         )"high_task",
              (uint16_t       )HIGH_TASK_SIZE,
              (void *         )NULL,
              (UBaseType_t    )HIGH_TASK_PRIO,
              (TaskHandle_t * )&high_task_Handle);
  taskEXIT_CRITICAL();
  // 删除开始任务
  vTaskDelete(start_Task_Handle);
}

void low_task(void *pvParameters)
{
  BaseType_t error_state;
  uint32_t limit;
  for (;;)
  {
    if (mutex_semphr != NULL)
    {
      // 一直等待互斥信号量
      error_state = xSemaphoreTake(mutex_semphr, portMAX_DELAY);   
      if (error_state == pdTRUE)
      {
        printf("\n低优先级任务获取到信号量,正在占用...");
      }
      else
      {
        printf("\n低优先级任务获取信号量失败");
      }
      #if 0
      // 模拟低优先级占用信号量
      for (limit = 0; limit < 5000000; limit++)
      {
        taskYIELD();      // 一直调用任务切换 
      }
      #else
      HAL_Delay(10000);
      #endif
      // 释放互斥信号量
      printf("\n低优先级任务占用信号量结束,即将释放");
      xSemaphoreGive(mutex_semphr);
    }
    vTaskDelay(500);
  }
}

void middle_task(void *pvParameters)
{
  for (;;)
  {
    printf("\n中优先级任务正在运行");
    vTaskDelay(1000);
  }
}

void high_task(void *pvParameters)
{
  for (;;)
  {
    if (mutex_semphr != NULL)
    {
      printf("\n高优先级任务正在等待信号量...");
      // 一直等待互斥信号量
      xSemaphoreTake(mutex_semphr, portMAX_DELAY);   
      printf("\n高优先级任务获取到信号量,即将释放...");
      xSemaphoreGive(mutex_semphr);    // 释放信号量
    }
    vTaskDelay(1000);
  }
}

测试效果如下:

递归互斥信号量:

发布了184 篇原创文章 · 获赞 100 · 访问量 7万+

猜你喜欢

转载自blog.csdn.net/dingyc_ee/article/details/104115367