FreeRTOS---------消息队列

个人感觉这个消息队列和数组一样,就像是一个全局的数组。

之后可以在任意任务和中断中通过发送消息或者是接收消息函数来写入或者读取其中的内容。(在中断中需要使用带有中断保护的版本)

可以把同等长度的数组直接发送给队列,也可以把队列中的消息直接赋值给同样长度的数组。

u8 data[4]={0};//接受内容的数组
u8 fasong[4]={1,2,3,4};//被发送的数组


QueueHandle_t Recive_Queue;//创建一个长度为4,每个消息字节为4的消息队列
Recive_Queue = xQueueCreate(4,4);


xQueueReceive(Recive_Queue, &data,portMAX_DELAY);//接收消息,把队列中的数值直接赋值给data数组

xQueueSend(Recive_Queue, &fasong ,0);//发送消息,直接把数组fasong中的数值全部发送给消息队列


//结果为data[0]=fasong[0],data[1]=fasong[1],data[2]=fasong[2],data[3]=fasong[3],

实验

按下按键发送一个数组,在消息接收任务里将接收队列消息被赋值的数组中的数值依次打印出来

实验结果 数据正确

队列

队列又称消息队列,是一种常用于任务间通信的数据结构,队列可以在任务与任务间、 中断和任务间传递信息,实现了任务接收来自其他任务或中断的不固定长度的消息,任务 能够从队列里面读取消息,当队列中的消息是空时,读取消息的任务将被阻塞,用户还可 以指定阻塞的任务时间 xTicksToWait,在这段时间中,如果队列为空,该任务将保持阻塞 状态以等待队列数据有效。

当队列中有新消息时,被阻塞的任务会被唤醒并处理新消息; 当等待的时间超过了指定的阻塞时间,即使队列中尚无有效数据,任务也会自动从阻塞态 转为就绪态。消息队列是一种异步的通信方式

通过消息队列服务,任务或中断服务例程可以将一条或多条消息放入消息队列中。同 样,一个或多个任务可以从消息队列中获得消息。当有多个消息发送到消息队列时,通常 是将先进入消息队列的消息先传给任务,也就是说,任务先得到的是最先进入消息队列的 消息。

通常情况下,在 FreeRTOS 中,凡是创建任务,队列, 信号量和互斥量等内核对象都需要使用动态内存分配。

FreeRTOS中使用队列数据结构实现任务异步通信工作,具有如下特性:

  1.  消息支持先进先出方式排队,支持异步读写工作方式。
  2.  读写队列均支持超时机制。
  3.  消息支持后进先出方式排队,往队首发送消息(LIFO)。
  4.  可以允许不同长度(不超过队列节点最大值)的任意类型消息。
  5.  一个任务能够从任意一个消息队列接收和发送消息。
  6.  多个任务能够从同一个消息队列接收和发送消息。
  7.  当队列使用结束后,可以通过删除队列函数进行删除。

消息队列的运作过程具体见图

每个对消息队列读写的函数,都有这种机制,我称之为阻塞机制。假设有一个任务 A对某个队列进行读操 作的时候(也就是我们所说的出队),发现它没有消息,那么此时任务 A有3个选择:第 一个选择,任务A扭头就走,既然队列没有消息,那我也不等了,干其它事情去,这样子任务A不会进入阻塞态;第二个选择,任务A还是在这里等等吧,可能过一会队列就有消 息,此时任务A会进入阻塞状态,在等待着消息的道来,而任务A的等待时间就由我们自 己定义,比如设置1000个系统时钟节拍tick的等待,在这1000个tick到来之前任务 A都 是处于阻塞态,当阻塞的这段时间任务A等到了队列的消息,那么任务A就会从阻塞态变成就绪态,如果此时任务A比当前运行的任务优先级还高,那么,任务A就会得到消息并 且运行;假如1000个tick都过去了,队列还没消息,那任务A就不等了,从阻塞态中唤 醒,返回一个没等到消息的错误代码,然后继续执行任务A的其他代码;第三个选择,任务A死等,不等到消息就不走了,这样子任务A就会进入阻塞态,直到完成读取队列的消息。

而在发送消息操作的时候,为了保护数据,当且仅当队列允许入队的时候,发送者才 能成功发送消息;队列中无可用消息空间时,说明消息队列已满,此时,系统会根据用户 指定的阻塞超时时间将任务阻塞,在指定的超时时间内如果还不能完成入队操作,发送消 息的任务或者中断服务程序会收到一个错误码errQUEUE_FULL,然后解除阻塞状态;当 然,只有在任务中发送消息才允许进行阻塞状态,而在中断中发送消息不允许带有阻塞机 制的,需要调用在中断中发送消息的API函数接口,因为发送消息的上下文环境是在中断 中,不允许有阻塞的情况。


消息队列的应用场景

消息队列可以应用于发送不定长消息的场合,包括任务与任务间的消息交换,队列是 FreeRTOS主要的任务间通讯方式,可以在任务与任务间、中断和任务间传送信息,发送到队列的消息是通过拷贝方式实现的,这意味着队列存储的数据是原数据,而不是原数据的引用。


消息队列常用函数

使用队列模块的典型流程如下:

  •  创建消息队列。
  •  写队列操作。
  •  读队列操作。
  •  删除队列。

一.消息队列创建函数

   xQueueCreate()

xQueueCreate()用于创建一个新的队列并返回可用于访问这个队列的队列句柄。队列句 柄其实就是一个指向队列数据结构类型的指针。 队列就是一个数据结构,用于任务间的数据的传递。每创建一个新的队列都需要为其 分配 RAM,一部分用于存储队列的状态,剩下的作为队列消息的存储区域。使用 xQueueCreate()创建队列时,使用的是动态内存分配,所以要想使用该函数必须在 FreeRTOSConfig.h 中把 configSUPPORT_DYNAMIC_ALLOCATION 定义为 1 来使能,这 是个用于使能动态内存分配的宏,这个宏默认在FreeRTOS.h头文 件中已经使能(即定义为 1)。


二.消息队列删除函数

vQueueDelete( QueueHandle_t xQueue )

xQueue是vQueueDelete()函数的形参,是消息队列句柄,表示的是要删除哪个消息队列。

队列删除函数是根据消息队列句柄直接删除的,删除之后这个消息队列的所有信息都 会被系统回收清空,而且不能再次使用这个消息队列了,但是需要注意的是,如果某个消息队列没有被创建,那也是无法被删除的。

消息队列删除函数vQueueDelete()的使用也是很简单的,只需传入要删除的消息队列 的句柄即可,调用函数时,系统将删除这个消息队列。需要注意的是调用删除消息队列函 数前,系统应存在xQueueCreate()或xQueueCreateStatic()函数创建的消息队列。此外 vQueueDelete()也可用于删除信号量。如果删除消息队列时,有任务正在等待消息,则不应该进行删除操作(官方说的是不允许进行删除操作,但是源码并没有禁止删除的操作,使用的时候注意一下就行了)。


三.向消息队列发送消息函数

   xQueueSend()         xQueueSendToBack()

xQueueSend() 等 同 于 xQueueSendToBack()。

xQueueSend()用于向队列尾部发送一个队列消息。消息以拷贝的形式入队,而不是以 引用的形式。该函数绝对不能在中断服务程序里面被调用,中断中必须使用带有中断保护功能的 xQueueSendFromISR()来代替。

   xQueueSendFromISR()

xQueueSendToBackFromISR等同于xQueueSendFromISR ()。

xQueueSendFromISR()是一个宏,宏展开是调用函数xQueueGenericSendFromISR()。该 宏是xQueueSend()的中断保护版本,用于在中断服务程序中向队列尾部发送一个队列消息, 等价于xQueueSendToBackFromISR()。

   xQueueSendToFron()

xQueueSendToFron()是一个宏,宏展开也是调用函数xQueueGenericSend()。 xQueueSendToFront()用于向队列队首发送一个消息。消息以拷贝的形式入队,而不是以引 用的形式。该函数绝不能在中断服务程序里面被调用,而是必须使用带有中断保护功能的 xQueueSendToFrontFromISR ()来代替。

xQueueSendToFrontFromISR()

xQueueSendToFrontFromISR()是一个宏,宏展开是调用函数 xQueueGenericSendFromISR()。该宏是xQueueSendToFront()的中断保护版本,用于在中断 服务程序中向消息队列队首发送一个消息。xQueueSendToFromISR()函数具体说明见表格 17-6,使用方式与xQueueSendFromISR()函数一致。


四.读取消息函数

  xQueueReceive()        xQueuePeek()

xQueueReceive()是一个宏,宏展开是调用函数xQueueGenericReceive()。 xQueueReceive()用于从一个队列中接收消息并把消息从队列中删除。接收的消息是以拷贝 的形式进行的,所以我们必须提供一个足够大空间的缓冲区。具体能够拷贝多少数据到缓 冲区,这个在队列创建的时候已经设定。该函数绝不能在中断服务程序里面被调用,而是 必须使用带有中断保护功能的xQueueReceiveFromISR ()来代替。

看到这里,有人就问了如果我接收了消息不想删除怎么办呢?其实,你能想到的东西, FreeRTOS看到也想到了,如果不想删除消息的话,就调用xQueuePeek()函数。 其实这个函数与xQueueReceive()函数的实现方式一样,连使用方法都一样,只不过 xQueuePeek()函数接收消息完毕不会删除消息队列中的消息而已。

xQueueReceiveFromISR()          xQueuePeekFromISR()

xQueueReceiveFromISR()是xQueueReceive ()的中断版本,用于在中断服务程序中接收 一个队列消息并把消息从队列中删除;xQueuePeekFromISR()是xQueuePeek()的中断版本, 用于在中断中从一个队列中接收消息,但并不会把消息从队列中移除。 说白了这两个函数只能用于中断,是不带有阻塞机制的,并且是在中断中可以安全调用。


在使用FreeRTOS提供的消息队列函数的时候,需要了解以下几点:

1. 使用xQueueSend()、xQueueSendFromISR()、xQueueReceive()等这些函数之前应先 创建需消息队列,并根据队列句柄进行操作。

2. 队列读取采用的是先进先出(FIFO)模式,会先读取先存储在队列中的数据。当 然也 FreeRTOS 也支持后进先出(LIFO)模式,那么读取的时候就会读取到后进 队列的数据。

3.在获取队列中的消息时候,我们必须要定义一个存储读取数据的地方,并且该数 据区域大小不小于消息大小,否则,很可能引发地址非法的错误。

4. 无论是发送或者是接收消息都是以拷贝的方式进行,如果消息过于庞大,可以将 消息的地址作为消息进行发送、接收。

5. 队列是具有自己独立权限的内核对象,并不属于任何任务。所有任务都可以向同 一队列写入和读出。一个队列由多任务或中断写入是经常的事,但由多个任务读 出倒是用的比较少。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值