本节主要介绍以下内容:
1,队列简介(了解)
2,队列结构体介绍(熟悉)
3,队列相关API函数介绍(熟悉)
4,队列操作实验(掌握)
5,队列相关API函数解析(熟悉)
6,课堂总结(掌握)
一,队列简介(了解)
1.1 队列的相关概念
队列是任务到任务、任务到中断、中断到任务数据交流的一种机制(消息传递)
类似全局变量?假设有一个全局变量a = 0,现有两个任务都在写这个变量a
全局变量的弊端:数据无保护,导致数据不安全,当多个任务同时对该变量操作时,数据易受损
使用队列的情况如下:
读写队列做好了保护,防止多任务同时访问冲突;我们只需要直接调用API函数即可,简单易用!
FreeRTOS基于队列, 实现了多种功能,其中包括队列集、互斥信号量、计数型信号量、二值信号量、 递归互斥信号量,因此很有必要深入了解 FreeRTOS 的队列 。
在队列中可以存储数量有限、大小固定的数据。队列中的每一个数据叫做“队列项目”,队列能够存储“队列项目”的最大数量称为队列的长度
在创建队列时,就要指定队列长度以及队列项目的大小!
1.2 FreeRTOS队列特点:
1、数据入队出队方式:队列通常采用“先进先出”(FIFO)的数据存储缓冲机制,即先入队的数据会 先从队列中被读取,FreeRTOS中也可以配置为“后进先出”LIFO方式;
2、数据传递方式:FreeRTOS中队列采用实际值传递,即将数据拷贝到队列中进行传递, FreeRTOS采用拷贝数据传递,也可以传递指针,所以在传递较大的数据的时候采用指针传递
3、多任务访问:队列不属于某个任务,任何任务和中断都可以向队列发送/读取消息
4、出队、入队阻塞:当任务向一个队列发送消息时,可以指定一个阻塞时间,假设此时当队列已满无法入队
1.2.1 入队阻塞:
队列满了,此时写不进去数据;
①将该任务的状态列表项挂载在pxDelayedTaskList;
②将该任务的事件列表项挂载在xTasksWaitingToSend;
1.2.2 出队阻塞:
队列为空,此时读取不了数据;
①将该任务的状态列表项挂载在pxDelayedTaskList;
②将该任务的事件列表项挂载在xTasksWaitingToReceive;
问题:当多个任务写入消息给一个“满队列”时,这些任务都会进入阻塞状态,也就是说有多个任务 在等待同一 个队列的空间。那当队列中有空间时,哪个任务会进入就绪态?
当多个任务写入消息给一个“满队列”时,任务优先级最高的任务会进入阻塞态,
当队列中有空间时,优先级最高的会进入就绪态,当任务优先级相同时,等待时间最久的任务会进入就绪态。
1.3 队列操作基本过程
①创建队列
②往队列写入第一个消息
③往队列写入第二个消息
④从队列读取第一个消息
二,队列结构体介绍(熟悉)
当用于队列使用时:
当用于互斥信号量和递归互斥信号量时 :
队列结构体整体示意图:
三、队列相关API函数介绍(掌握)
使用队列的主要流程:创建队列-> 写队列 -> 读队列。
3.1 创建队列相关API函数介绍:
函数 | 描述 |
xQueueCreate() | 动态方式创建队列 |
xQueueCreateStatic() | 静态方式创建队列 |
动态和静态创建队列之间的区别:队列所需的内存空间由 FreeRTOS 从 FreeRTOS 管理的堆中分配,而静态创建需要用户自行分配内存。
此函数用于使用动态方式创建队列,队列所需的内存空间由 FreeRTOS 从 FreeRTOS 管理的堆中分配
形参 | 描述 |
uxQueueLength | 队列长度 |
uxItemSize | 队列项目的大小 |
返回值 | 描述 |
NULL | 队列创建失败 |
其他值 | 队列创建成功,返回队列句柄 |
前面说 FreeRTOS 基于队列实现了多种功能,每一种功能对应一种队列类型,队列类型的 queue.h 文件中有定义:
3.2 往队列写入消息API函数介绍:
函数 | 描述 |
xQueueSend() | 往队列的尾部写入消息 |
xQueueSendToBack() | 同 xQueueSend() |
xQueueSendToFront() | 往队列的头部写入消息 |
xQueueOverwrite() | 覆写队列消息(只用于队列长度为 1 的情况) |
xQueueSendFromISR() | 在中断中往队列的尾部写入消息 |
xQueueSendToBackFromISR() | 同 xQueueSendFromISR() |
xQueueSendToFrontFromISR() | 在中断中往队列的头部写入消息 |
xQueueOverwriteFromISR() | 在中断中覆写队列消息(只用于队列长度为 1 的情况) |
3.2.1 队列写入消息
可以看到这几个写入函数调用的是同一个函数xQueueGenericSend( ),只是指定了不同的写入位置!
队列一共有 3 种写入位置 :
注意:覆写方式写入队列,只有在队列的队列长度为 1 时,才能够使用
往队列写入消息函数入口参数解析:
形参 | 描述 |
xQueue | 待写入的队列 |
pvItemToQueue | 待写入消息 |
xTicksToWait | 阻塞超时时间 |
xCopyPosition | 写入的位置 |
返回值 | 描述 |
pdTRUE | 队列写入成功 |
errQUEUE_FULL | 队列写入失败 |
3.3 从队列读取消息API函数介绍:
函数 | 描述 |
xQueueReceive() | 从队列头部读取消息,并删除消息 |
xQueuePeek() | 从队列头部读取消息 |
xQueueReceiveFromISR() | 在中断中从队列头部读取消息,并删除消息 |
xQueuePeekFromISR() | 在中断中从队列头部读取消息 |
3.3.1 从队列头部读取消息,并删除消息
BaseType_t xQueueReceive( QueueHandle_t xQueue, void * const pvBuffer, TickType_t xTicksToWait )
此函数用于在任务中,从队列中读取消息,并且消息读取成功后,会将消息从队列中移除。
形参 | 描述 |
xQueue | 待读取的队列 |
pvBuffer | 信息读取缓冲区 |
xTicksToWait | 阻塞超时时间 |
返回值 | 描述 |
pdTRUE | 读取成功 |
pdFALSE | 读取失败 |
3.3.2 从队列头部读取消息
BaseType_t xQueuePeek( QueueHandle_t xQueue, void * const pvBuffer, TickType_t xTicksToWait )
此函数用于在任务中,从队列中读取消息, 但与函数 xQueueReceive()不同,此函数在成功读取消息后,并不会移除已读取的消息!
形参 | 描述 |
xQueue | 待读取的队列 |
pvBuffer | 信息读取缓冲区 |
xTicksToWait | 阻塞超时时间 |
返回值 | 描述 |
pdTRUE | 读取成功 |
pdFALSE | 读取失败 |
四、队列操作实验(掌握)
4.1、实验目的:
学习 FreeRTOS 的队列相关API函数的使用 ,实现队列的入队和出队操作。
4.2、实验设计:
将设计三个任务:start_task、task1、task2、task3
四个任务的功能如下:
- start_task:用来创建task1和task2以及task3任务
- task1:当按键key0或key1按下,将键值拷贝到队列key_queue(入队),当按键key_up按下,将传输大数据,这里拷贝大数据的地址到队列big_date_queue中
- task2:读取队列key_queue中的消息(出队),打印出接收到的键值
-
从队列big_date_queue读取大数据地址,通过地址访问大数据
4.3 实验代码
demo.c
今天没空格了。明日再更
五,队列相关API函数解析(熟悉)
又到了撸源码的时刻了......