FreeRTOS官方翻译文档——第二章 队列管理

摘要:
FreeRTOS中的所有通信和同步机制都是基于队列实现的。队列可以容纳的最大单位数称为队列的“深度”。队列由声明为xQueueHandle的变量引用。创建队列时,FreeRTOS从堆空间分配内存空间。系统提供中断安全版本的xQueueSendToFrontFromISR()和xQueueSend ToBackFromISR),以在中断服务中实现相同的功能。该方案如图23所示。

2.1 概览
基于 FreeRTOS 的应用程序由一组独立的任务构成——每个任务都是具有独立权
限的小程序。这些独立的任务之间很可能会通过相互通信以提供有用的系统功能。
FreeRTOS 中所有的通信与同步机制都是基于队列实现的。

2.2队列的特性
数据存储
队列可以保存有限个具有确定长度的数据单元。队列可以保存的最大单元数目被称
为队列的深度。在队列创建时需要设定其深度和每个单元的大小。
通常情况下,队列被作为 FIFO(先进先出)使用,即数据由队列尾写入,从队列首读
出。当然,由队列首写入也是可能的。
往队列写入数据是通过字节拷贝把数据复制存储到队列中;从队列读出数据使得把
队列中的数据拷贝删除。 图 19 展现了队列的写入与读出过程,以及读写操作对队列中
数据的影响。

可被多任务存取
队列是具有自己独立权限的内核对象,并不属于或赋予任何任务。所有任务都可以
向同一队列写入和读出。一个队列由多方写入是经常的事,但由多方读出倒是很少遇到。

读队列时阻塞
当某个任务试图读一个队列时,其可以指定一个阻塞超时时间。在这段时间中,如
果队列为空,该任务将保持阻塞状态以等待队列数据有效。当其它任务或中断服务例程
往其等待的队列中写入了数据,该任务将自动由阻塞态转移为就绪态。当等待的时间超
过了指定的阻塞时间,即使队列中尚无有效数据,任务也会自动从阻塞态转移为就绪态。
由于队列可以被多个任务读取,所以对单个队列而言,也可能有多个任务处于阻塞
状态以等待队列数据有效。这种情况下,一旦队列数据有效,只会有一个任务会被解除
阻塞,这个任务就是所有等待任务中优先级最高的任务。而如果所有等待任务的优先级
相同,那么被解除阻塞的任务将是等待最久的任务。

写队列时阻塞
同读队列一样,任务也可以在写队列时指定一个阻塞超时时间。这个时间是当被写
队列已满时,任务进入阻塞态以等待队列空间有效的最长时间。
由于队列可以被多个任务写入,所以对单个队列而言,也可能有多个任务处于阻塞
状态以等待队列空间有效。这种情况下,一旦队列空间有效,只会有一个任务会被解除
阻塞,这个任务就是所有等待任务中优先级最高的任务。而如果所有等待任务的优先级
相同,那么被解除阻塞的任务将是等待最久的任务。
FreeRTOS官方翻译文档——第二章 队列管理第1张

FreeRTOS官方翻译文档——第二章 队列管理第2张

2.3 使用队列
xQueueCreate() API 函数
队列在使用前必须先被创建。
队列由声明为 xQueueHandle 的变量进行引用。 xQueueCreate()用于创建一个队
列,并返回一个 xQueueHandle 句柄以便于对其创建的队列进行引用。
当创建队列时, FreeRTOS 从堆空间中分配内存空间。分配的空间用于存储队列数
据结构本身以及队列中包含的数据单元。如果内存堆中没有足够的空间来创建队列,
xQueueCreate()将返回 NULL
FreeRTOS官方翻译文档——第二章 队列管理第3张

xQueueSendToBack() xQueueSendToFront() API 函数
如同函数名字面意思所期望的一样, xQueueSendToBack()用于将数据发送到队列
尾;而 xQueueSendToFront()用于将数据发送到队列首。
xQueueSend()完全等同于 xQueueSendToBack()
但 切 记 不 要 在 中 断 服 务 例 程 中 调 用 xQueueSendToFront()
xQueueSendToBack()。系统提供中断安全版本的 xQueueSendToFrontFromISR()
xQueueSendToBackFromISR()用于在中断服务中实现相同的功能。

FreeRTOS官方翻译文档——第二章 队列管理第4张

FreeRTOS官方翻译文档——第二章 队列管理第5张

FreeRTOS官方翻译文档——第二章 队列管理第6张

FreeRTOS官方翻译文档——第二章 队列管理第7张

xQueueReceive()xQueuePeek() API 函数
xQueueReceive()用于从队列中接收(读取)数据单元。接收到的单元同时会从队列中删除。

xQueuePeek()也是从从队列中接收数据单元,不同的是并不从队列中删出接收到
的单元。 xQueuePeek()从队列首接收到数据后,不会修改队列中的数据,也不会改变
数据在队列中的存储序顺。
切记不要在中断服务例程中调用 xQueueRceive()xQueuePeek()

FreeRTOS官方翻译文档——第二章 队列管理第8张

FreeRTOS官方翻译文档——第二章 队列管理第9张

FreeRTOS官方翻译文档——第二章 队列管理第10张

FreeRTOS官方翻译文档——第二章 队列管理第11张

uxQueueMessagesWaiting() API 函数
uxQueueMessagesWaiting()用于查询队列中当前有效数据单元个数。
切记不要在中断服务例程中调用 uxQueueMessagesWaiting()。应当在中断服务中
使用其中断安全版本 uxQueueMessagesWaitingFromISR()

FreeRTOS官方翻译文档——第二章 队列管理第12张

使用队列传递复合数据类型
一个任务从单个队列中接收来自多个发送源的数据是经常的事。通常接收方收到数
据后,需要知道数据的来源,并根据数据的来源决定下一步如何处理。一个简单的方式
就是利用队列传递结构体,结构体成员中就包含了数据信息和来源信息。 图 23 对这一方案进行了展现。
FreeRTOS官方翻译文档——第二章 队列管理第13张

从图 23 中可以看出:
创建一个队列用于保存类型为 xData 的结构体数据单元。结构体成员包括了一个数
据值和表示数据含义的编码,两者合为一个消息可以一次性发送到队列。
中央控制任务用于完成主要的系统功能。 其必须对队列中传来的输入和其它系统状
态的改变作出响应。
• CAN 总线任务用于封装 CAN 总线的接口功能。当 CAN 总线任务收到并解码一个消
息后,其将把解码后的消息放到 xData 结构体中发往控制任务。结构体的 iMeaning
成员用于让中央控制任务知道这个数据是用来干什么的 从图中的描述可以看
出,这个数据表示电机速度。结构体的 iValue 成员可以让中央控制任务知道电机的
实际速度值。
人机接口(HMI)任务用于对所有的人机接口功能进行封装。设备操作员可能通过各种
方式进行命令输入和参数查询,人机接口任务需要对这些操作进行检测并解析。当
接收到一个新的命令后,人机接口任务通过 xData 结构将命令发送到中央控制任务。
结构体的 iMeaning 成员用于让中央控制任务知道这个数据是用来干什么的
图中的描述可以看出,这个数据表示一个新的参数设置。结构体的 iValue 成员可以
让中央控制任务知道具体的设置值。

 工作于大型数据单元
如果队列存储的数据单元尺寸较大,那最好是利用队列来传递数据的指针而不是对
数据本身在队列上一字节一字节地拷贝进或拷贝出。传递指针无论是在处理速度上还是
内存空间利用上都更有效。但是,当你利用队列传递指针时,一定要十分小心地做到以下两点:

1. 指针指向的内存空间的所有权必须明确
当任务间通过指针共享内存时,应该从根本上保证所不会有任意两个任务同时
修改共享内存中的数据,或是以其它行为方式使得共享内存数据无效或产生一致性
问题。原则上,共享内存在其指针发送到队列之前,其内容只允许被发送任务访问;
共享内存指针从队列中被读出之后,其内容亦只允许被接收任务访问。
2. 指针指向的内存空间必须有效
如果指针指向的内存空间是动态分配的,只应该有一个任务负责对其进行内存
释放。当这段内存空间被释放之后,就不应该有任何一个任务再访问这段空间。
切忌用指针访问任务栈上分配的空间。因为当栈帧发生改变后,栈上的数据将不再有效。
 这个时候,再回过头去看看上一篇随笔,为什么官方demo使用结构体指针,因为这样效率更高。

 

免责声明:文章转载自《FreeRTOS官方翻译文档——第二章 队列管理》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇python xlwings chart模块各种问题今天都遇到了Notepad++ 快捷键 大全下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

提升权限令牌

提权相关函数: GetCurrentProcess() 介绍: 功能:检索当前进程的伪句柄。 函数原型:HANDLE GetCurrentProcess(); 返回值:返回值是当前进程的伪句柄。 OpenProcessToken() 介绍:功能:打开与进程相关联的访问令牌。 函数原型:BOOL WINAPI OpenProcessToken(      ...

java集合 LinkedList 添加元素 add() 的底层

双向链表支撑的数据结构, 对于链表,每一个链子的节称为节点,节点包括3个元素,数据(数据域),头部指针,指向下一个元素,尾部指针(引用域)指向上一个元素; 开头的元素和结尾的元素? 如果不能指向另一个元素则指针指空;* 第一次添加元素:size=0,所添加的元素的顺序是自然合法的; public LinkedList() { } 创建一个空的link...

Debug与Release版本的区别

  Debug 和 Release 并没有本质的区别,他们只是VC预定义提供的两组编译选项的集合,编译器只是按照预定的选项行动。如果我们愿意,我们完全可以把Debug和Release的行为完全颠倒过来。当然也可以提供其他的模式,例如自己定义一组编译选项,然后命名为MY_ABC等。习惯上,我们仍然更愿意使用VC已经定义好的名称。    Debug版本包括调试...

用 ThreadPoolExecutor/ThreadPoolTaskExecutor 线程池技术提高系统吞吐量(附带线程池参数详解和使用注意事项)

1、概述 在Java中,我们一般通过集成Thread类和实现Runnnable接口,调用线程的start()方法实现线程的启动。但如果并发的数量很多,而且每个线程都是执行很短的时间便结束了,那样频繁的创建线程和销毁进程会大大的降低系统运行的效率。线程池正是为了解决多线程效率低的问题而产生的,他使得线程可以被复用,就是线程执行结束后不被销毁,而是可以继续执行...

20文件

  1. 文件的概念 1.1 文件的概念和作用 计算机的 文件, 就是存储在某种 长期储存设备 上的一段 数据 长期储存设备包括: 硬盘,U盘,移动硬盘,光盘... 文件的作用 将数据长期保持下来, 在需要的时候使用 1.2 文件的储存方式 在计算机中, 文件是以 二进制 的方式保持在磁盘上的 文本文件和二进制文件 文本文件 可以使用 文本编辑软...

Linux多进程开发IV

1.共享内存。允许两个或多个进程共享物理内存的同一块区域(段)。 共享内存是进程用户空间的一部分,因此这种IPC机制需要更少的内核处理。一个进程将数据复制到共享内存中,那么这部分数据就会对其他所有共享同一个段的进程可用。 与管道等要求发送进程将数据从用户空间的缓冲区复制到内核内存和接收进程将数据从内核内存复制进用户空间的缓冲区的做法相比,这种IPC技术...