1、队列的概念
队列是为了任务与任务、任务与中断之间的通信而准备的,可以存储有限的、大小固定的数据项目。任务与任务、任务与中断之间要交流的数据保存在队列中,叫做队列项目。队列所能保存的最大数据项目数量叫做队列的长度,创建队列的时候会指定数据项目的大小和队列的长度。由于队列用来传递消息的,所以也称为消息队列。
-
freeRTOS中的消息队列传递的既可以是一个实际的数据,也可以是数据的地址。
-
消息队列不属于某个特定的任务,任何的任务都可以向队列中发送消息,也可以从队列中提取消息。
2、队列的管理
2.1、出队方式
通常的队列都是采取先进先出(FIFO)的存储缓冲机制方式,同时freeRTOS也支持后进先出(LIFO)。
2.2、数据存储
freeRTOS的消息队列传递的如果是实际的数据,数据发送到队列中会导致数据拷贝,会浪费一些时间。但是消息被拷贝到了消息队列中,那么原始的数据缓冲区或者数据就可以删除或者改变,达到反复利用的效果。
2.3、队列阻塞
队列阻塞的作用是在当任务从队列中获取消息的时候,所指定的一个等待时间。队列在出队和入队都是可以设置阻塞的。有三种阻塞的时间:
-
不等待:不等待直接执行之后的内容(为0不等待)
-
定时等待:任务等待一个固定的时间间隔,可以为 0 ~ portMAX_DELAY
-
一直等待:设置为 portMAX_DELAY 的时候,会一直等待
2.4、实现示意图
下图展示了队列的FIFO出队方式的示意图。task 1要向task 2发消息。创建了一个长度为4的队列,要传递的单个数据项目是一个int类型的数据,即队列数据项目的大小为4 Bytes。
3、队列的API函数
3.1、创建队列
| |
| |
参数 | 说明 |
| 队列的最大长度,即可容纳的数据项大小 |
| 每个数据项所占的空间大小(单位为 byte) |
返回值 | 说明 |
| 队列创建成功 |
| 如果返回为NULL,表示由于堆空间不够,创建队列失败 |
3.2、删除队列
| |
| |
参数 | 说明 |
| 要删除的队列句柄 |
返回值 | 说明 |
| 空 |
3.3、向队列发送数据
| |
| |
参数 | 说明 |
| 队列句柄,指明要向哪个队列发送数据,创建队列成功以后会返回此队列的队列句柄 |
| 指向要发送的消息,发送的时候会将这个消息拷贝到队列中 |
| 阻塞时间,此参数指示当前队列满的时候任务进入阻塞等待队列空闲的最大时间,如果为 0 的话当队列满的时候就立即返回;当为 portMAX_DELAY 的话就会一直等待,直到队列有空闲的队列项,但是宏 INCLUDE_vTaskSuspend 必须为 1 |
返回值 | 说明 |
| 向队列发送消息成功 |
| 队列已经满了,消息发送失败 |
| |
| |
参数 | 说明 |
| 队列句柄,指明要向哪个队列发送数据,创建队列成功以后会返回此队列的队列句柄 |
| 指向要发送的消息,发送的时候会将这个消息拷贝到队列中 |
| 标记退出此函数以后是否需要进行任务切换,这个变量的值由该函数来设置,不需要用户进行设置,用户只需要提供一个变量来保存这个值就行了。当此值为 pdTRUE 时,退出中断函数之前一定要进行一次任务切换 |
返回值 | 说明 |
| 向队列发送消息成功 |
| 队列已经满了,消息发送失败 |
3.4、从队列接收数据
| |
| |
参数 | 说明 |
| 队列句柄,指明要向哪个队列发送数据 |
| 指向存储被拷贝的数据的存储 |
| 任务处于阻塞状态下等待队列的数据可用的最大时间。如果xTicksToWait为 0,队列为空的情 况下调用此函数会立即返回;如果设置为 port_MAX_DELAY,任务会一直等待,但是宏 INCLUDE_vTaskSuspend 必须设置为 1。 |
返回值 | 说明 |
| 从队列中读取消息成功 |
| 队列为空,读取消息失败 |
| |
| |
参数 | 说明 |
| 队列句柄,指明要向哪个队列发送数据 |
| 指向一个存储区,读取的的消息将被拷贝到存储区中 |
| 标记退出此函数以后是否需要进行任务切换,这个变量的值由这三个函数来设 置,不需要用户进行设置,用户只需要提供一个变量来保存这个值就行了。当此值为 pdTRUE 时,退出中断函数 之前一定要进行一次任务切换 |
返回值 | 说明 |
| 从队列中读取消息成功 |
| 队列已经为空,消息读取失败 |
4、实验
4.1、实验设计
1、创建一个开始任务,在开始任务里面再新建一个队列,task 1和task 2两个任务后,删除开始任务;
2、task 1运行5次后,向队列发送数组,数组值tx[0] = 1,tx[1] = 2,tx[2] = 3;
3、task 1运行10次后,向队列发送数组,数组值tx[0] = 5,tx[1] = 6,tx[2] = 7;
4、task 2一直获取队列的值,并打印出来。
4.2、实验代码
k_task_handle_t task1_handle; //task 1 句柄
#define TSK1_PRIO 3 //task 1 优先级
#define TASK1_STK_SIZE (1*1024) //task 1 分配的堆栈大小
k_task_handle_t task2_handle; //task 2 句柄
#define TSK2_PRIO 2 //task 2 优先级
#define TASK2_STK_SIZE (1*1024) //task 2 分配的堆栈大小
k_task_handle_t start_task_handle; //start task 句柄
#define START_TSK_PRIO 1 //start task 优先级
#define START_TSK_STK_SIZE (1*1024) //start task 分配的堆栈大小
k_msgq_handle_t NewQueue; //定义一个消息队列句柄
#define TEST_TIME_QUANTA 100
void task1(void)
{
uint8_t count = 0;
u_char tx[3] = {0};
while(1)
{
count++;
my_printf("task 1 run %d times!!!\r\n", count);
if(count == 5)
{
tx[0] = 1;
tx[1] = 2;
tx[2] = 3;
csi_kernel_msgq_put(NewQueue, tx, 0, portMAX_DELAY); //发送数据到队列
}
else if(count == 10)
{
tx[0] = 5;
tx[1] = 6;
tx[2] = 7;
csi_kernel_msgq_put(NewQueue, tx, 0, portMAX_DELAY); //发送数据到队列
count = 0;
}
else;
csi_kernel_delay_ms(1000);
}
}
void task2(void)
{
u_char rec[3] = {0};
while(1)
{
my_printf("task 2 is working now!!!\r\n");
csi_kernel_msgq_get(NewQueue, rec, 0);//获取队列的数据
for(int i = 0; i < 3; i++)
{
my_printf("rec[%d] = %d\r\n", i, rec[i]);
}
csi_kernel_delay_ms(1000);
}
}
void start_task(void)
{
//进入临界区
taskENTER_CRITICAL();
//创建队列
NewQueue = csi_kernel_msgq_new(5, 3*sizeof(u_char));
//创建task 1
csi_kernel_task_new((k_task_entry_t)task1,
"task1",
NULL,
TSK1_PRIO,
TEST_TIME_QUANTA,
NULL,
TASK1_STK_SIZE,
&task1_handle);
if (task1_handle == NULL)
{
csi_kernel_sched_resume(0);
my_printf("Fail to create task 1!\r\n");
}
//创建task 2
csi_kernel_task_new((k_task_entry_t)task2,
"task2",
NULL,
TSK2_PRIO,
TEST_TIME_QUANTA,
NULL,
TASK2_STK_SIZE,
&task2_handle);
if (task2_handle == NULL)
{
csi_kernel_sched_resume(0);
my_printf("Fail to create task 2!\r\n");
}
//删除开始任务
if(0 != csi_kernel_task_del(csi_kernel_task_get_cur()))
{
my_printf("Fail to delete start_task!\r\n");
}
//退出临界区
taskEXIT_CRITICAL();
}
void freertos_demo(void)
{
my_printf("\r\n-->this is freertos task test demo!!!\r\n");
//系统初始化
csi_kernel_init();
//创建开始任务
csi_kernel_task_new((k_task_entry_t)start_task,
"start_task",
0,
START_TSK_PRIO,
0,
0,
START_TSK_STK_SIZE,
&start_task_handle);
//任务调度开始
csi_kernel_start();
}
4.3、实验现象
-
start task在创建后,创建好task 1 task 2即被删除
-
初始阶段task 1, task 2并发运行,task 2接收队列的数据是默认值0,0,0
-
task 1运行5次后,向队列里面写了1,2,3 ,task 2立马接收到了队列数据1,2,3
-
task 1运行10次后,向队列里面写了5,6,7 ,task 2立马接收到了队列数据5,6,7