在RTOS中验证互斥量有效解决优先级反转现象

        我们在stm32f103c8t6单片机上验证RTOS互斥量有效解决优先级反转现象,利用stm32cube进行RTOS的配置。裸机的时钟源默认是 SysTick,但是开启 FreeRTOS 后,FreeRTOS会占用 SysTick (用来生成1ms 定时,用于任务调度),所以我们开启TIM2当做裸机的时钟源,为其他总线提供另外的时钟源。

验证的功能比较简单,选择V1 版本的内核完全够用。

一、验证的思路及需要使用的函数及互斥量的说明

1.验证思路

做两组实验进行对比,一组用二值型信号量,另一组用互斥型信号量,看一下有优先级反转和无优先级反转的情况区别

2.需用函数

二值型信号量

建立二值信号量SemaphoreHandle_t xSemaphoreCreateBinary( void )

返回值: 成功,返回对应二值信号量的句柄; 失败,返回 NULL

获取二值信号量BaseType_t xSemaphoreTake( SemaphoreHandle_t xSemaphore, TickType_t xTicksToWait );

xSemaphore:要获取的信号量句柄

xTicksToWait:超时时间,0 表示不超时,portMAX_DELAY表示卡死等待;

返回值: 成功,返回 pdPASS ; 失败,返回 errQUEUE_FULL 。

释放二值信号量BaseType_t xSemaphoreGive( SemaphoreHandle_t xSemaphore )

xSemaphore:要释放的信号量句柄

返回值: 成功,返回 pdPASS ; 失败,返回 errQUEUE_FULL 。

互斥型信号量

建立互斥型信号量SemaphoreHandle_t xSemaphoreCreateMutex( void )

返回值: 成功,返回对应互斥量的句柄; 失败,返回 NULL

互斥型信号量信号的获取与释放所用的函数同二值型信号量

一样,只不过函数的参数不一样,即句柄不一样

3.互斥量的说明

互斥型信号量可以有效解决优先级反转现象。可发生优先级继承:当一个互斥信号量正在被一个低优先级的任务持有时, 如果此时有个高优先级的任 务也尝试获取这个互斥信号量,那么这个高优先级的任务就会被阻塞。不过这个高优先级的任务 会将低优先级任务的优先级提升到与自己相同的优先级。 优先级继承并不能完全的消除优先级翻转的问题,它只是尽可能的降低优先级翻转带来的影响。

听不懂没关系,我给你讲故事。

现在有High,Middle,Low三个人,我们分别用H,M,L来代替。其中H的优先级最高,M其次,L最低。H的工作就是在整洁的床上睡觉,M的工作就是闲的没事在H醒来之后打酱油,L的工作就是负责给H整理床铺。

下面我就用这个故事情节给大家说一下有无翻转优先级的区别

有翻转优先级的时候

按照优先级的排序,故事开始应该是这样发展的。H开始睡觉,睡了一会,H醒了走人。M过来打一会酱油,走了。接着L开始为H铺床,这个回合结束。按道理说H应该要睡觉了,但是L却没有把床铺好,因此H无法睡觉,只能等着不工作。这个时候L应该早点把床整理好,让H睡觉。但是M偏偏就是这么浪,嫌弃L整理的时间太长,自己的优先级又比L高,H又不工作,没人管它,M就欺负L,让L无法工作,自己又去打酱油。他打酱油了,L就无法工作了。那么L就只能等着M浪完在工作,别看此时H等级高,也只能等着M浪完,L把床叠好后在休息。

发现了吗?在使用二值信号量并没有使用互斥信号量的时候,本来应该正常工作的L却因M的优先级比自己高,而不能工作。在我们的执行流程中,就相当于低优先级的工作时间还没结束,便被翻转为中优先级,这就是典型的优先级翻转。

二、有优先级防转时STM32cube的配置

SYS

RCC

在RTOS中验证互斥量有效解决优先级反转现象_第1张图片

RTOS版本选择

在RTOS中验证互斥量有效解决优先级反转现象_第2张图片

在Times and Semapores里

选择Binary semaphores(二值信号),生成二值信号配置如下图所示

在RTOS中验证互斥量有效解决优先级反转现象_第3张图片

在Task and Queues里

配置三个任务

分别是TaskHigh,TaskMiddle,TaskLow,

对应的优先级分别是osPriorityAboveNormal, osPriorityNormal, osPriorityBelowNormal,

对应的入口函数分别是StartTaskHigh,StartTaskMiddle,StartTaskLow

三、有优先级防转时的代码

usart.c

#include "stdio.h"

int fputc(int ch, FILE *f)

{

unsigned char temp[1]={ch};

HAL_UART_Transmit(&huart1,temp,1,0xffff);

return ch;

}

同时打开“魔术棒”,勾选Use MicroLIB,点击OK。这样就可以进行串口打印了。

freertos.c

#include "FreeRTOS.h"

#include "task.h"

#include "main.h"

#include "cmsis_os.h"

/* Private includes ----------------------------------------------------------*/

/* USER CODE BEGIN Includes */

#include "stdio.h"

/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/

/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/

/* USER CODE BEGIN PD */

/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/

/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/

/* USER CODE BEGIN Variables */

/* USER CODE END Variables */

osThreadId TaskHighHandle;

osThreadId TaskMiddleHandle;

osThreadId TaskLowHandle;

osSemaphoreId myBinarySemHandle;

/* Private function prototypes -----------------------------------------------*/

/* USER CODE BEGIN FunctionPrototypes */

/* USER CODE END FunctionPrototypes */

void StartTaskHigh(void const * argument);

void StartTaskMiddle(void const * argument);

void StartTaskLow(void const * argument);

void MX_FREERTOS_Init(void); /* (MISRA C 2004 rule 8.1) */

/* GetIdleTaskMemory prototype (linked to static allocation support) */

void vApplicationGetIdleTaskMemory( StaticTask_t **ppxIdleTaskTCBBuffer, StackType_t **ppxIdleTaskStackBuffer, uint32_t *pulIdleTaskStackSize );

/* USER CODE BEGIN GET_IDLE_TASK_MEMORY */

static StaticTask_t xIdleTaskTCBBuffer;

static StackType_t xIdleStack[configMINIMAL_STACK_SIZE];

void vApplicationGetIdleTaskMemory( StaticTask_t **ppxIdleTaskTCBBuffer, StackType_t **ppxIdleTaskStackBuffer, uint32_t *pulIdleTaskStackSize )

{

  *ppxIdleTaskTCBBuffer = &xIdleTaskTCBBuffer;

  *ppxIdleTaskStackBuffer = &xIdleStack[0];

  *pulIdleTaskStackSize = configMINIMAL_STACK_SIZE;

  /* place for user code */

}

/* USER CODE END GET_IDLE_TASK_MEMORY */

/**

  * @brief  FreeRTOS initialization

  * @param  None

  * @retval None

  */

void MX_FREERTOS_Init(void) {

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* USER CODE BEGIN RTOS_MUTEX */

  /* add mutexes, ... */

  /* USER CODE END RTOS_MUTEX */

  /* Create the semaphores(s) */

  /* definition and creation of myBinarySem */

  osSemaphoreDef(myBinarySem);

  myBinarySemHandle = osSemaphoreCreate(osSemaphore(myBinarySem), 1);

  /* USER CODE BEGIN RTOS_SEMAPHORES */

  /* add semaphores, ... */

  /* USER CODE END RTOS_SEMAPHORES */

  /* USER CODE BEGIN RTOS_TIMERS */

  /* start timers, add new ones, ... */

  /* USER CODE END RTOS_TIMERS */

  /* USER CODE BEGIN RTOS_QUEUES */

  /* add queues, ... */

  /* USER CODE END RTOS_QUEUES */

  /* Create the thread(s) */

  /* definition and creation of TaskHigh */

  osThreadDef(TaskHigh, StartTaskHigh, osPriorityAboveNormal, 0, 128);

  TaskHighHandle = osThreadCreate(osThread(TaskHigh), NULL);

  /* definition and creation of TaskMiddle */

  osThreadDef(TaskMiddle, StartTaskMiddle, osPriorityNormal, 0, 128);

  TaskMiddleHandle = osThreadCreate(osThread(TaskMiddle), NULL);

  /* definition and creation of TaskLow */

  osThreadDef(TaskLow, StartTaskLow, osPriorityBelowNormal, 0, 128);

  TaskLowHandle = osThreadCreate(osThread(TaskLow), NULL);

  /* USER CODE BEGIN RTOS_THREADS */

  /* add threads, ... */

  /* USER CODE END RTOS_THREADS */

}

/* USER CODE BEGIN Header_StartTaskHigh */

/**

  * @brief  Function implementing the TaskHigh thread.

  * @param  argument: Not used

  * @retval None

  */

/* USER CODE END Header_StartTaskHigh */

void StartTaskHigh(void const * argument)

{

  /* USER CODE BEGIN StartTaskHigh */

  /* Infinite loop */

  for(;;)

  {

     xSemaphoreTake(myBinarySemHandle, portMAX_DELAY);

   printf("TaskH:我要睡觉了。\r\n");

   HAL_Delay(1000);

//osDelay(1000);

   printf("TaskH:我醒了,我走了。\r\n");

   xSemaphoreGive(myBinarySemHandle);

              osDelay(1000);

  }

  /* USER CODE END StartTaskHigh */

}

/* USER CODE BEGIN Header_StartTaskMiddle */

/**

* @brief Function implementing the TaskMiddle thread.

* @param argument: Not used

* @retval None

*/

/* USER CODE END Header_StartTaskMiddle */

void StartTaskMiddle(void const * argument)

{

  /* USER CODE BEGIN StartTaskMiddle */

  /* Infinite loop */

  for(;;)

  {

   

   printf("TaskM:啦啦啦,我来打酱油了。\r\n");

   osDelay(1000);

  }

  /* USER CODE END StartTaskMiddle */

}

/* USER CODE BEGIN Header_StartTaskLow */

/**

* @brief Function implementing the TaskLow thread.

* @param argument: Not used

* @retval None

*/

/* USER CODE END Header_StartTaskLow */

void StartTaskLow(void const * argument)

{

  /* USER CODE BEGIN StartTaskLow */

  /* Infinite loop */

  for(;;)

  {

     xSemaphoreTake(myBinarySemHandle, portMAX_DELAY);

   printf("TaskL:我要整理床了。\r\n");

   HAL_Delay(3000);

//osDelay(1000);

   printf("TaskL:床铺整理好了。\r\n");

   xSemaphoreGive(myBinarySemHandle);

              osDelay(1000);

  }

  /* USER CODE END StartTaskLow */

}

运行的结果如下图所示,和我们的故事是吻合的

在RTOS中验证互斥量有效解决优先级反转现象_第4张图片

无翻转优先级的时候

按照优先级的排序,故事开始应该是这样发展的。H开始睡觉,睡了一会,H醒了走人。M过来打一会酱油,走了。接着L开始为H铺床,这个回合结束。按道理说H应该要睡觉了,但是L却没有把床铺好,因此H无法睡觉,只能等着不工作。这个时候L应该早点把床整理好,让H睡觉。但是M偏偏就是这么浪,嫌弃L整理的时间太长,自己的优先级又比L高,H又不工作,没人管它,M就想着欺负L,让L无法工作,但这一次,虽然H不工作却很严肃的对M说:“L现在在为我工作,看见L就相当看见我,它的优先级和我一样高”。这时候,就发生了优先级继承,M不敢惹L,L依旧可以工作,直到把床铺好,让H上床休息。L铺床和H上床之间是没有M横插一杠的。

发现了吗?在使用互斥信号量的时候,本来应该正常工作的L可以正常工作,不会因为M的优先级比自己高,而不能工作。在我们的执行流程中,就相当于低优先级的工作时间结束后才自动转到别的优先级,这就是没有发生优先级翻转。

四、无优先级防转时STM32cube的配置

首先把二值信号量删除,接着创建互斥量,其余的不变

在RTOS中验证互斥量有效解决优先级反转现象_第5张图片

五、无优先级防转时的代码

把xSemaphoreGive(myBinarySemHandle)和xSemaphoreTake(myBinarySemHandle)里面的参数换成myMutexHandle就可以了。

下图是无优先级防转时运行的情况,可以看出L在运行时并没有遭到M的打断,发生了优先级继承,和我们的故事情况一样。

在RTOS中验证互斥量有效解决优先级反转现象_第6张图片

你可能感兴趣的:(RTOS,stm32cube,互斥量有效解决优先级反转现象)