0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

基于STM32F407的FreeRTOS学习笔记(8)

CHANBAEK 来源:实在太懒于是不想取名 作者:电路小白 2023-11-08 11:07 次阅读

前面几期我们介绍过队列、二进制信号量以及计数信号量。但是在使用二进制信号量的时候会有一种优先级反转问题的出现,简而言之就是低优先级任务因为无法及时释放信号量而导致等待信号量发生的高优先级任务迟迟无法进行。

众所周知,FreeRTOS的各任务的运行顺序是由任务的优先级决定的,优先级高的任务比优先级低的任务先执行。

假设我们有三个任务:任务H,任务M,任务L,分别代表高优先级,中优先级以及低优先级。 任务H和任务M同时被挂起,正在等待某一个事件的发生 , 同时任务H和任务L使用同样的全局资源 (意味着当任务L正在占用全局资源时任务H的执行需要等待任务L执行完释放信号量)

图片

  1. 当任务L运行时占用了全局资源。
  2. 任务H的事件被触发后由于其高优先级任务H获得CPU的使用权
  3. 当任务H需要使用全局资源时,由于任务L还没有使用完全局资源,因此任务H被挂起等待任务L的信号量释放即使用函数xSemaphoreTake(SemaphoreHandler,portMAX_DELAY);等待信号量
  4. 此时任务L恢复工作
  5. 任务M的事件触发,此时任务L在等待任务M的结束,而然后任务H此时也在等待着任务M的任务结束。因此这段时间的任务M优先级高于任务H,这种现象就是优先级反转。
  6. 等任务M执行完,任务L继续执行直到释放信号量,任务H得以继续运行。

因此使用信号量就会导致优先级反转的出现,打破原有的任务运行顺序,这在RTOS系统中应当是尽量避免的。

问题解决

为了解决二进制信号量可能带来的优先级反转现象,FreeRTOS中有一种特殊的二进制信号量——互斥信号量也就是互斥锁。

图片

官方的开发者文档中介绍了互斥锁的存在,互斥锁实际上是一个包含了优先级继承机制的二进制信号量。任务在使用资源时则相当于手持一块令牌,其他没有这块令牌的任务无法就使用资源这就是所谓的互斥。当任务结束使用资源时,也就会返回所对应的令牌。

当两个任务使用相同的信号量时,为了避免前面介绍的优先级反转现象, 于是优先级高的任务会把持有令牌的低优先级任务的优先级提升到和自己一样的情况 ,这样子就可以导致中间出现的中优先级任务抢占CPU资源,使得优先级出现反转。

图片

相当于如图,将之后执行的低优先级任务的有限制强制抬高到与自己同一等级,颇有一种富家少爷为了吃葡萄而包下葡萄园的故事感。

这样子的做法可以有效地防止优先级反转现象的出现,也可以使已经出现的优先级反转得到更快的结束。

注意,互斥锁一定一定一定不能在中断中使用,因为中断无法使用延时函数来阻塞事件。

使用互斥锁

在FreeRTOS中有两种方法创建互斥锁,分别是动态创建和静态创建,我们主要介绍一下动态创建互斥锁。

图片

API文档中关于互斥锁的内容很多,主要是介绍互斥锁以及说明使用互斥锁使用的一些细节。

信号量的释放和获取则和二进制信号量一样,参考二进制信号量文章。

我们来测试一下互斥锁。

测试代码

我们需要三个不同优先级的任务,用来测试优先级反转的情况。

我们分别定义高优先级任务,中优先级任务以及低优先级任务。

图片

启动相关宏定义。先测试二进制信号量

void High_Task(void * pvParameters);//高优先级任务
void Mid_Task(void * pvParameters);//中优先级任务
void Low_Task(void * pvParameters);//低优先级任务
void Scan(void * pvParameters);
#define START_TASK_PRIO 1
TaskHandle_t High_Handler;
TaskHandle_t Start_LED_Handler;
TaskHandle_t Mid_Handler;
TaskHandle_t Low_Handler;
TaskHandle_t Scan_Handler;
xSemaphoreHandle TaskSemaphoer_Handler;
void Start_LED(void * pvParameters)
{
  taskENTER_CRITICAL();                            


  TaskSemaphoer_Handler = xSemaphoreCreateBinary();//创建二进制信号量
  if(TaskSemaphoer_Handler!=NULL)
  {
    printf("Semaphore Create Successfullyrn");
  }


  xTaskCreate((TaskFunction_t        )High_Task,//任务函数
              (char *                )"v",//任务名称
              (configSTACK_DEPTH_TYPE) 128,//堆栈空间128Byte
              (void*                 ) NULL,//无返回
              (UBaseType_t          ) 2,//优先级2
              (TaskHandle_t *        )&High_Handler);//任务函数句柄


   xTaskCreate((TaskFunction_t        )Mid_Task,//任务函数
              (char *                )"Mid_Task",//任务名称
              (configSTACK_DEPTH_TYPE) 128,//堆栈空间128Byte
              (void*                 ) NULL,//无返回
              (UBaseType_t          ) 1,//优先级1
              (TaskHandle_t *        )&Mid_Handler);//任务函数句柄


     xTaskCreate((TaskFunction_t        )Low_Task,//任务函数
              (char *                )"Low_Task",//任务名称
              (configSTACK_DEPTH_TYPE) 128,//堆栈空间128Byte
              (void*                 ) NULL,//无返回
              (UBaseType_t          ) 0,//优先级0
              (TaskHandle_t *        )&Low_Handler);//任务函数句柄


  vTaskSuspend(Mid_Handler);//挂起中优先级任务
  vTaskSuspend(High_Handler);//挂起高优先级任务    


  taskEXIT_CRITICAL();
  vTaskDelete(NULL);
}


void FreeRTOS_Init()
{
  xTaskCreate((TaskFunction_t        )Start_LED,
              (char *                )"starttask",
              (configSTACK_DEPTH_TYPE) 128,
              (void*                 ) NULL,
              (UBaseType_t          )START_TASK_PRIO,
              (TaskHandle_t *        )&Start_LED_Handler);
  vTaskStartScheduler();
}

接着我们按照会出现优先级反转的情况编写测试代码。

首先挂起高优先级和中优先级任务。

低优先级任务持续打印运行信息,当运行到5次时,恢复高优先级任务持续打印信息,高优先级任务打印三次后等待低优先级任务发送信号量。

当低优先级任务再执行5次后高恢复中优先级任务,再次执行5次后发送信号量示意高优先级任务继续运行。

中优先级任务执行3次后挂起自身。

void Low_Task(void * pvParameters)//参数为 void * pvParameters
{
  int Low_number = 0;
  while(1)
  {
    printf("Low_Task Runing 1111rn");
    Low_number++;
    if(Low_number == 5)
    {
      vTaskResume(High_Handler);//恢复高优先级
    }
    if(Low_number == 10)
    {
      vTaskResume(Mid_Handler);//恢复中优先级任务
    }
    if(Low_number == 15)
    {
      xSemaphoreGive(TaskSemaphoer_Handler);//释放信号量
    }
  }
}


void Mid_Task(void * pvParameters)//参数为 void * pvParameters
{
  int Mid_number = 0;
  while(1)
  {
    printf("Mid_Task Runing 2222rn");
    Mid_number++;
    if(Mid_number == 3)
    {
      vTaskSuspend(NULL);//挂起自身
    }
  }
}


void High_Task(void * pvParameters)
{
  int High_number = 0;
  while(1)
  {
    printf("High_Task Runing 3333rn");
    High_number++;
    if(High_number==3)
    {
      xSemaphoreTake(TaskSemaphoer_Handler,portMAX_DELAY);//等待低优先级任务释放信号量
    }
  }
}

图片

图片

可以看见和预想的一样,高优先级的任务被中优先级任务所挤兑。

之后我们把代码中的二进制信号量换成互斥锁。

图片

图片

可以看到,中优先级的任务根本没有办法实现优先级反转跳到高优先级去。

因此善于使用互斥锁,避免优先级反转现象的出现有利于FreeRTOS系统任务调度顺序的正确性,防止出现意外错误。

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 二进制
    +关注

    关注

    2

    文章

    705

    浏览量

    41250
  • 函数
    +关注

    关注

    3

    文章

    3868

    浏览量

    61309
  • FreeRTOS
    +关注

    关注

    12

    文章

    473

    浏览量

    61347
  • 信号量
    +关注

    关注

    0

    文章

    53

    浏览量

    8257
收藏 人收藏

    评论

    相关推荐

    基于STM32F407FreeRTOS学习笔记(1)

    其实从很早之前就想学实时操作系统(RTOS)了,但是一直没有时间去学,以前使用STM32单片机一直停留在逻辑开发以及前后台系统,而真正被广泛使用的则是RTOS。
    的头像 发表于 11-07 11:37 414次阅读
    基于<b class='flag-5'>STM32F407</b>的<b class='flag-5'>FreeRTOS</b><b class='flag-5'>学习</b><b class='flag-5'>笔记</b>(1)

    基于STM32F407FreeRTOS学习笔记(2)

    上一期配置完FreeRTOS的环境后,这一期记录自己关于任务创建的学习过程。
    的头像 发表于 11-07 11:39 386次阅读
    基于<b class='flag-5'>STM32F407</b>的<b class='flag-5'>FreeRTOS</b><b class='flag-5'>学习</b><b class='flag-5'>笔记</b>(2)

    基于STM32F407FreeRTOS学习笔记(3)

    上一期学习了任务的创建和删除,这一期学习任务的挂起与恢复。
    的头像 发表于 11-07 11:41 355次阅读
    基于<b class='flag-5'>STM32F407</b>的<b class='flag-5'>FreeRTOS</b><b class='flag-5'>学习</b><b class='flag-5'>笔记</b>(3)

    基于STM32F407FreeRTOS学习笔记(4)

    CPU工作的时候,各个任务运行会占用CPU的资源,在Windows系统中我们可以通过任务管理器来看各任务(进程)占用系统资源的情况。
    的头像 发表于 11-07 11:43 606次阅读
    基于<b class='flag-5'>STM32F407</b>的<b class='flag-5'>FreeRTOS</b><b class='flag-5'>学习</b><b class='flag-5'>笔记</b>(4)

    基于STM32F407FreeRTOS学习笔记(5)

    在数据结构中有一种很重要的数据结构叫做队列,其特点是数据先进先出。在FreeRTOS中也有一类队列,我们利用这类队列在FreeRTOS中实现任务与任务间的消息传递,所以也可以称之为消息队列。
    的头像 发表于 11-07 11:43 376次阅读
    基于<b class='flag-5'>STM32F407</b>的<b class='flag-5'>FreeRTOS</b><b class='flag-5'>学习</b><b class='flag-5'>笔记</b>(5)

    基于STM32F407FreeRTOS学习笔记(6)

    信号量(Semaphore)也被称为信号灯。有时被称为信号灯,是在多线程环境下使用的一种设施,是可以用来保证两个或多个关键代码段不被并发调用。在进入一个关键代码段之前,线程必须获取一个信号量;一旦该关键代码段完成了,那么该线程必须释放信号量。其它想进入该关键代码段的线程必须等待直到第一个线程释放信号量(来自百度百科)
    的头像 发表于 11-08 11:00 364次阅读
    基于<b class='flag-5'>STM32F407</b>的<b class='flag-5'>FreeRTOS</b><b class='flag-5'>学习</b><b class='flag-5'>笔记</b>(6)

    基于STM32F407FreeRTOS学习笔记(7)

    本期在二进制信号量的基础上介绍计数信号量
    的头像 发表于 11-08 11:04 291次阅读
    基于<b class='flag-5'>STM32F407</b>的<b class='flag-5'>FreeRTOS</b><b class='flag-5'>学习</b><b class='flag-5'>笔记</b>(7)

    基于STM32F407FreeRTOS学习笔记(9)

    在嵌入式编程中,定时器是一个非常重要且强大的功能,用来帮我们定时性的调用中断服务函数来帮助我们处理程序。定时器通常是用硬件来实现的,例如STM32F407就有8个硬件定时器。
    的头像 发表于 11-08 11:08 415次阅读
    基于<b class='flag-5'>STM32F407</b>的<b class='flag-5'>FreeRTOS</b><b class='flag-5'>学习</b><b class='flag-5'>笔记</b>(9)

    基于STM32F407FreeRTOS学习笔记(10)

    在介绍二进制信号量时曾经讲过,二进制信号量可以代替我们裸机开发中的标志位来使用。在裸机开发中我们使用标志位来表示某个事件是否发生,并且其他程序利用标记位的状态来判断程序是否可以继续进行。但是这种大量使用标记位的情况会导致代码的逻辑异常的复杂。
    的头像 发表于 11-08 11:10 332次阅读
    基于<b class='flag-5'>STM32F407</b>的<b class='flag-5'>FreeRTOS</b><b class='flag-5'>学习</b><b class='flag-5'>笔记</b>(10)

    基于STM32F407FreeRTOS学习笔记(11)

    在前面的学习中例如信号量、队列中经常会出现提示:在许多情况下,“任务通知”可以提供二进制信号量的轻量级替代方案。
    的头像 发表于 11-13 17:23 464次阅读
    基于<b class='flag-5'>STM32F407</b>的<b class='flag-5'>FreeRTOS</b><b class='flag-5'>学习</b><b class='flag-5'>笔记</b>(11)

    基于STM32F407FreeRTOS学习笔记(12)

    在上一期内容中我们简单的介绍了任务通知的几个函数以及简单的使用了任务通知来实现两个信号之间的通信。
    的头像 发表于 11-13 17:27 434次阅读
    基于<b class='flag-5'>STM32F407</b>的<b class='flag-5'>FreeRTOS</b><b class='flag-5'>学习</b><b class='flag-5'>笔记</b>(12)

    如何使用STM32F407去移植FreeRTOS

    如何使用STM32F407去移植FreeRTOS呢?有哪些操作步骤?
    发表于 10-21 09:45

    STM32F407 FreeRTOS开发指南

    参考资料:正点原子《littleVGL 开发指南》正点原子《STM32F407 FreeRTOS 开发指南》硬件平台:stm32f407开发板(或最小系统)、4.3寸TFTLCD以正点原子的例程为
    发表于 01-05 06:55

    STM32F407标准库学习

    STM32F407标准库学习笔记-AFIO- 头文件 gpio.h#define GPIO_PinSource0((uint8_t)0x00)#define GPIO_PinSourc
    发表于 01-24 06:42

    STM32F407 FreeRTOS开发手册 V1.1

    STM32F407 FreeRTOS开发手册_V1.1PDF文件下载。
    发表于 07-31 11:16 44次下载