一、什么是队列
学习外国人的东西最好还是从字面意思去理解,外国的文化毕竟没有我们的文化深厚,没有那么深层次的意思。如果自己把那些名词想得太深奥,那就是给自己找麻烦。本人也很讨厌各种书本上翻译过来的高深词汇,搞得云里雾里。跑题了。
队列就是队列,中小学做操、升国旗是排的那个队就是队列。
人排的队,就是人的队列;牛排的队就是牛的队列,猪排的队,就是猪的队列......
所以,队列里面的东西可以随便,随便什么数据格式。
队列有长有短,那个长短指的是队列里面数据的个数,这个个数就是队列的长度length;
RTOS的这个队列,不是按照高矮来排的,而是按照时间顺序来排的。谁先来谁就排在第一个,然后第一个离开,这就是所谓的先进先出FIFO,first in first out。
当然了,这个社会怎么会有绝对的规则,绝对的公平,只要办了VIP你就可以插队了。在RTOS系统里面也是一样的。
来看一个生活中的队列问题吧--去医院挂号
看看这个流程:
1、去取号机取个号,第一个就是1号、第2个就是2号、第3个就是3号......
2、但是这个医院比较小,只能容纳20个病人,那最多只能20个病人来排队了。
3、人少的时候也无所谓了,病人来了就进医院等医生叫号就诊;
4、人多了,超过20个人了,就表示队列满了。那从第21个病人开始就只能在外面等着,等里面的病人诊完了,出来一个人,就进去一个人;其他的人就继续在外面等着;
5、本来这是很和谐的场面,但是从第21个人开始,那些在外面的人不能进医院,就不能取号,该让谁先进去呢?简单,大家都是平等的,谁在外面等的最久谁就可以先进来。但是前面说了,这个社会不是绝对公平的,人也是分等级的,比如外面有个高级的人物,那就让这个高级的人物先进去。如果这个人物还有特权,他甚至可以进去直接插队,想插哪就插哪。
二、创建队列
创建队列函数,这里只说动态创建,静态创建用得少,有机会再说。
xQueueCreate( uxQueueLength, uxItemSize )
队列的要素:
uxQueueLength,队列的长度,就是有多少个数据的意思
uxItemSize,每个数据的大小,以字节为单位
//创建一个深度为5的队列,队列里面的数据格式是uint32_t
xQueue = xQueueCreate(5, sizeof(uint32_t));
三、 队列的基本使用
创建三个函数,两个函数分别往队列中读写数据,一个函数从队列中读数据
#include "FreeRTOS.h"
#include "task.h"
#include "main.h"
#include "gpio.h"
#include "usart.h"
#include "queue.h"
/*定义任务句柄*/
TaskHandle_t myTask01_Handle;
TaskHandle_t myTask02_Handle;
TaskHandle_t myTask03_Handle;
/*定义队列句柄*/
QueueHandle_t xQueue;
/*声明任务*/
void SendTask(void *argument);
void ReceiveTask(void *argument);
void FREERTOS_Init(void);
/**
* @brief 在FreeRTOS初始化的时候把任务函数创建了
*/
void FREERTOS_Init(void) {
taskENTER_CRITICAL();
//创建一个深度为5的队列,队列里面的数据格式是uint32_t
xQueue = xQueueCreate(5, sizeof(uint32_t));
if(xQueue != NULL)
{
xTaskCreate( SendTask, "SendTask01", 1000, (void*)100,1,&myTask01_Handle );
xTaskCreate( SendTask, "SendTask02", 1000, (void*)200,1,&myTask02_Handle );
xTaskCreate( ReceiveTask, "ReceiveTask01", 1000, NULL, 2, &myTask03_Handle );
}
taskEXIT_CRITICAL();
}
//SendTask函数主体
void SendTask(void *argument)
{
uint32_t ldataToSend;
BaseType_t xStatus;
ldataToSend = (uint32_t)argument;
/*大循环*/
for(;;)
{
/*任务的具体内容*/
RLED_TOGGLE();
xStatus = xQueueSendToBack(xQueue,&ldataToSend, 0);
if(xStatus !=pdPASS)
{
printf("can not send to the queue!!!");
}
/*任务阻塞*/
//vTaskDelay(1000);
}
}
//ReceiveTask函数主体
void ReceiveTask(void *argument)
{
uint32_t lReceiveData;
BaseType_t xStatus;
for(;;)
{
BLED_TOGGLE();
xStatus = xQueueReceive(xQueue,&lReceiveData,100);
if(xStatus == pdPASS)
{
printf("Receive data = %d\r\n", lReceiveData);
}else{
printf("can not receive from the queue!!!\r\n");
}
//vTaskDelay(1000);
}
}
四、识别数据来源
前面是两个任务都往队列里面写数据,一个任务从队列读数据,但是读出来的数据并不知道来自谁,这在控制系统中是不妥的。
既然要识别数据来源,那就需要给数据加个ID,有几个任务往队列中写数据,就是几个ID
//数据来源
typedef enum {
SendTask1,
SendTask2
} ID_t;
再把ID和数据绑在一起,那就是结构体了
//数据结构体
typedef struct{
ID_t eDataId;
uint32_t lDataValue;
} Data_t;
格式定好了,那就定义数据吧,测试的时候就定个全局变量,当然在以后具体的任务中,可以在任务中定义拘捕变量。
//定义两个结构体
static const Data_t xStructToSend[2] = {
{SendTask1, 100},
{SendTask2, 200}
};
接下来就是创建任务往队列里读写数据了,两个任务去写数据,一个任务来读数据
xTaskCreate( SendTask, "SendTask01", 1000, (void*)&xStructToSend[0],2,&myTask01_Handle );
xTaskCreate( SendTask, "SendTask02", 1000, (void*)&xStructToSend[1],2,&myTask02_Handle );
xTaskCreate( ReceiveTask, "ReceiveTask01", 1000, NULL, 1, &myTask03_Handle );
貌似写数据的任务优先级高,读数据的任务优先级低,自己也可以去调整下优先级看看效果。
具体的写队列任务函数和读队列任务函数
//SendTask函数主体
void SendTask(void *argument)
{
//uint32_t ldataToSend;
BaseType_t xStatus;
//ldataToSend = (uint32_t)argument;
/*大循环*/
for(;;)
{
/*任务的具体内容*/
RLED_TOGGLE();
xStatus = xQueueSendToBack(xQueue,argument, 100);
if(xStatus !=pdPASS)
{
printf("can not send to the queue!!!");
}
/*任务阻塞*/
//vTaskDelay(1000);
}
}
//ReceiveTask函数主体
void ReceiveTask(void *argument)
{
Data_t lReceiveData;
BaseType_t xStatus;
for(;;)
{
BLED_TOGGLE();
xStatus = xQueueReceive(xQueue,&lReceiveData,0);
if(xStatus == pdPASS)
{
if(lReceiveData.eDataId == SendTask1)
{
printf("from SendTask1, data = %d\r\n", lReceiveData.lDataValue);
} else if(lReceiveData.eDataId == SendTask2){
printf("from SendTask2, data = %d\r\n", lReceiveData.lDataValue);
}
}else{
printf("can not receive from the queue!!!\r\n");
}
//vTaskDelay(1000);
}
}
结果就不展示了,感兴趣的自己去调试吧。
队列相关的函数还有复位队列、删除队列、查询队列......