【FreeRTOS】09 消息队列

本节来讲讲freeRTOS的消息队列。

1)什么是消息队列

操作系统里常说的“消息队列”,和数据结构课程里常说的“队列”是同一个概念,都是一个线性的存储表。

只不过数据结构里的“队列”通常指的是先进先出队列(也就是FIFO),有一个写入口和一个读出口,从一端写入数据,另一端读出数据,先写入队列的先被读出;而操作系统里实现的消息队列功能会更丰富一些,比如FreeRTOS的消息队列支持FIFO(先进先出)和LIFO(后进先出)两种存取方式,并且它还解决了多任务访问冲突、实现了任务等待队列时的阻塞功能。

我们以最基本的FIFO队列来讲解一下队列的功能,看下图:

图a初始化时,建立了一个有4个存储空间的队列,读指针指向要读的存储区、写指针指向要写入的存储区,当两个指针指向同一区域时表示队列中没有有效数据;初始时队列中没有数据,读写指针都指向0存储区;

图b表示向队列中写入了一个数据,写指针向后移动了一位,队列中的有效数据为1个;

图c表示向队列中写入了第二个个数据,写指针向后又移动了一位,队列中的有效数据为2个;

图d表示从队列中读取一个数据,由于这是先进先出队列,所以读指针指向的0区域的A1被读走;之后读指针更新到指向存储区1;

图e表示从队列中又读取了一个数据,1存储区的A2被读走,读指针更新到指向存储区2;队列为空。

以上是最基本的先进先出队列。上述的队列,如果不断地写入消息、读出消息,指针不断后移,那么存储区会不够用,所以环形队列应运而生;为了能够更灵活地访问,又有双向队列等等。队列中的数据元素,可以是各种数据类型,也可以是指针。有兴趣的可以另找资料更深入学习。

2)FreeRTOS的消息队列

freeRTOS提供的消息队列,可以实现FIFO或者LIFO,数据元素的大小可以由用户指定(比如每个消息大小1字节、4字节等等)。

还有一点需要注意,freeRTOS的消息队列中传递的是数值,它将要传递的数据复制到了队列存储区中,这与uCOS等操作系统不同,它们一般是传递的数据的地址。

freeRTOS的消息队列操作,常用的函数有以下几个:

创建消息队列:

QueueHandle_t xQueueCreate( UBaseType_t uxQueueLength, UBaseType_t uxItemSize ); //uxQueueLength为消息队列长度,uxItemSize为每个消息的字节数;

这个函数实际上是动态创建的队列,如果想使用静态创建,需要用xQueueCreateStatic函数。

发送消息:

BaseType_t xQueueSend(QueueHandle_t xQueue, const void * pvItemToQueue, TickType_t xTicksToWait); //xQueue消息队列句柄;pvItemToQueue需传递的消息数据地址;xTicksToWait等待时间;

发送消息实际上是在队列尾部入队,也就是先进先出;如果想要反过来后进先出,需要使用xQueueSendToFront()函数,它是在队列头部入队。

xQueueOverwrite()函数,可在队列满时,自动覆盖最旧的消息。

以上发送消息的函数只能用在任务中,在中断服务程序中发送消息时,需要用带ISR结尾的一套函数。

接收消息:

BaseType_t xQueueReceive(QueueHandle_t xQueue, void *pvBuffer, TickType_t xTicksToWait); // xQueue消息队列句柄;pvBuffer接收消息队列数据的地址;xTicksToWait等待时间

BaseType_t xQueuePeek(QueueHandle_t xQueue, void * pvBuffer, TickType_t xTicksToWait);//用于从队列中接收一个消息,但读取之后消息还保留在队列中

同样,在中断中接收消息时,也需要使用带ISR结尾的一套函数。

3)编程试验

下面我们以一个例子来演示消息队列的使用。

和前几期的例子一样,我们还是使用cubemx建立三个任务;

初始化队列时,可以在Tasks and Queues选项卡中添加一个消息队列,如下图所示。

(这里我们仅做讲解,实际编程还是使用xQueueCreate这个函数来创建消息队列)

编写代码如下:

首先是消息队列的创建,另外注意任务的优先级Task03高于Task02:

 

任务Task02每1s发送两个消息,发送消息前打印提示,消息内容为自增的整数:

 

任务Task03初始时等待100ms,之后循环等待消息,一旦收到队列中的消息,则打印出消息内容:

该程序运行结果如下:

分析一下输出结果:

由于高优先级的Task03在初始时等待了100ms,所以Task02可以连续发送出两个消息,然后才进入1s的等待;可以看到前两行Task02打印的发送消息0和1;

Task03等待完100ms后,进入循环接收消息的阶段,收取了消息0和消息1并打印出来;这里也可以看出,消息队列传递的数据是复制过去的,而不是传递的地址,如果传递的是地址,则Task03接收到的两个消息的值会是相同的;

之后消息队列为空,Task03又进入了等待;

到Task02的1s延时完毕后,它又开始发送消息,发送完消息2时,由于Task03的优先级更高,则Task03被唤醒执行,所以Task03马上打印出了消息2;

之后的程序执行同理,都是Task03一直等效消息,Task02一旦发出消息,Task03立即被唤醒执行。

好了,本节的内容就到这里了。

如果觉得有用可以关注作者微信号“小白白学电子”,在公众号可以找到代码和资料下载地址:

  • 7
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值