freertos入门2(时间管理 队列 列表 信号量 队列集)

222 阅读8分钟

1.时间管理

  • vTaskDelay():相对延时。从执行vTaskDelay()函数开始,直到指定延时的时间结束。

  • xTaskDelayUntil():绝对延时。将整个任务的运行周期视为一个整体,适用于需要以固定频率定期执行的任务。(保证整个任务整体为10ms,假如整个任务有4ms的时间,那么就要延时6ms)指定任务希望取消阻塞的绝对时间。

image.png

  • 当调用了 vTaskSuspendAll()挂起 RTOS 调度器时,不得调用此函数。

2.消息队列

1.什么叫消息队列

答:消息队列是任务到任务、任务到中断、中断到任务数据交流的一种机制(消息传递)。

2.特点

  • FIFO顺序:队列采用先进先出 (FIFO) 的顺序,即先发送的消息会被先接收。

  • 线程安全:队列操作是原子的,确保在多任务环境中的数据完整性。

  • 阻塞和非阻塞操作:任务可以通过阻塞或非阻塞的方式发送和接收消息。如果队列满了或者为空,任务可以选择等待指定的阻塞时间直到有空间或者数据可用,或者立即返回

  •   优先级继承:FreeRTOS 支持基于优先级的消息传递,确保高优先级任务在队列操作期间不会被低优先级任务阻塞。

  • 可变长度项:队列中的项可以是不同长度的数据块,而不是固定大小

  • 对队列进行的每一次操作,都是会进入临界区的(关中断)

各种举例
  • 当多个任务写入消息给一个 “满队列” 时,这些任务都会进入阻塞状态,也就是说有多个任务在等待同一个队列的空间。那当队列有空间时,哪个任务会进入就绪态?
      1. 优先级最高的任务
      1. 如果大家的优先级相同,那等待时间最久的任务进入就绪态。
函数
  • 入队
    • 写入头部
    • 写入尾部当调用 xQueueSendToBack() 函数时,数据会被插入到队列的尾部,即尾指针所指向的位置。插入后,尾指针会向前移动到下一个位置,这个“下一个位置”是指更向队列的尾部,而不是头部。
    • 复写,队列长度只能为1

image.png

image.png

各个api函数的实现逻辑
  • xQueueSend()

image.png

image.png

  • xQueueReceive( ); image.png

3.列表

什么是列表
  • 列表相当于链表,列表项相当于节点,FreeRTOS中的列表是一个双向环形链表。
与数组的区别
  • 列表项间的地址非连续的,是人为的连接到一起的。列表项的数目是由后期添加或删除的个数决定的,随时可以改变 。而数组成员地址是连续的,数组在最初确定了成员数量后,后期将无法改变
列表结构体介绍

image.png

  • 首位是两个宏。用于调试的,默认不开启,真正的需求是列表的数量,还有一个指针用于遍历列表项的指针,以及末尾列表表项
  • 成员变量xListEnd是一个迷你列表项,排在最末尾。
  • 成员pxIndex用于指向列表中的某个列表项,一般用于遍历列表中的所有列表项

image.png

列表项结构体介绍

列表项是列表中用于存放数据的地方列表就是指的这个链表,列表项就是这个链表中的一个节点

image.png

  • 成员变量xItemValue为列表项的值,这个值多用于按升序对列表中的列表项进行排序
  • 成员变量pxNext和pxPrevious分别用于指向列表中列表项的下一个列表项和上一个列表项。
  • 成员变量pxOwner用于指向包含列表项的对象(通常是任务控制块)。
  • 成员变量pxContainer用于执行列表项所在列表。

image.png

迷你列表项
  • 迷你列表项也是列表项,但迷你列表项仅用于标记列表的末尾和挂载其他插入列表中的列表项。 image.png

  • 迷你列表项只用于标记列表的末尾和挂载其他插入列表中的列表项(挂载:相当于头节点,提供一双手来将插入的列表项入列表),因此不需要成员变量pxOwner和pxContainer,以节省内存开销。

image.png

列表和列表项的关系
  • 初始状态

image.png

  • 插入1个列表项

image.png

  • 插入2个列表项

image.png

4.信号量

简介

  • 什么是信号量
    • 信号量是一种解决同步问题的机制可以实现对共享资源的有序访问。信号量的基本操作包括“获取”和“释放”。信号量可以是二进制的(只能取0或1)也可以是计数型的(可以是任意正整数)。信号量的基本操作包括“获取”和“释放”。

队列与信号量的对比

image.png

二值信号量

  • 二值信号量的本质是一个队列长度为1的队列,该队列就只有空和满两种情况。这就是二值信号量。

  • 注意:二值信号量通常用于互斥访问任务同步,与互斥信号量比较类似,但是二值信号量有可能会导致优先级翻转的问题,所以二值信号量更适合用于同步!!!

计数型信号量

1、计数型信号量介绍

答:计数型信号量相当于队列长度为1的队列,因此计数型信号量能够容纳多个资源,这在计数型信号量被创建的时候确定的。

计数型信号量适用场合:

  • 事件计数: 当每次事件发生后,在事件处理函数中释放计数型信号量(计数值+1),其他任务会获取计数型信号量(计数值-1),这种场合一般在创建时将初始化计数值设置为0.
  • 资源管理 : 信号量表示有效资源数量。任务必须先获取信号量(信号计数值-1)才能获取资源控制权。当计数值减为0时表示没有资源。
  • 当任务使用完资源后,必须释放信号量(信号量计数值+1)。信号量创建时计数值应等于最大资源数目。

优先级反转

  • 现象描述:高优先级的任务反而没执行,低优先级的任务反而优先执行
  • 在使用二值信号量的时候,经常会遇到优先级翻转的问题。(本质就是,一个低优先级任务获取了信号量之后没释放,导致高优先级也要获取信号量的任务被堵塞

image.png

互斥信号量(解决优先级反转)

  • 互斥信号量是包含优先级继承机制的二进制信号量。(互斥信号量其实就是一个 拥有优先级翻转的二值信号量。)

    • 二值信号量更适用于同步的应用
    • 互斥信号量更适合那些需要互斥访问的应用(资源紧缺,需要资源保护)。
  • 优先级继承是一种解决实时系统中任务调度引起的优先级翻转问题的机制。在具体的任务调度中,当一个高优先级任务等待一个低优先级任务所持有的资源时,系统会提升低优先级任务的优先级,以避免高优先级任务长时间等待的情况。用完之后又会将提高优先级之后的任务优先级降低)。但需要注意优先级继承无法完全解决优先级翻转,只是在某些情况下将影响降至最低。

image.png

  • 不能在中断中使用互斥信号量,原因如下:

    • 互斥信号量使用的优先级继承机制要求从任务中(而不是从中断中)获取和释放互斥信号量。

    • 中断无法保持阻塞来等待一个被互斥信号量保护的资源。

5.队列集

介绍

  • 一个队列只允许任务间传递的消息为同一种数据类型,如果需要在任务间传递不同数据类型的消息时,那么就可以使用队列集!!!

  • 作用:用于对多个队列或信号量进行“监听”,其中不管哪一个消息到来,都可让任务退出阻塞状态

  • 在多任务系统中,任务之间可能需要共享数据,而这些数据可能存储在不同的队列中。队列集的作用就是为了更方便地管理这些相关队列,使得任务能够轻松地访问和处理多个队列的数据。

特点

  • 队列集的特点和用法:

    • 集中管理多个队列:队列集允许你将多个相关联的队列组织在一起,方便集中管理。

    • 单一 API 调用:通过单一的 API 调用,任务可以同时操作多个队列,而无需分别处理每个队列。

    •   简化任务代码:对于需要处理多个相关队列的任务,使用队列集可以简化代码,提高可读性和维护性。

    • 提高系统效率:在需要协同工作的任务之间共享和传递数据时,队列集可以提高系统的效率。

    • 协同工作 :任务可以更方便地协同工作,共享数据,实现更复杂的任务间通信和同步。

    • 需要注意,二值信号量想要放入队列集中时不能用vSemaphoreCreateBinary会失败,因为v开头会释放信号量,信号量释放了就是信号量不为0,不为空加入队列集需要为空