FreeRTOS 队列

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、创建队列

QueueHandle_t xQueueCreate(UBaseType_t uxQueueLength, UBaseType_t uxItemSize)

  • 该函数主要用于创建队列

参数

说明

uxQueueLength

队列的最大长度,即可容纳的数据项大小

uxItemSize

每个数据项所占的空间大小(单位为 byte)

返回值

说明

非NULL

队列创建成功

NULL

如果返回为NULL,表示由于堆空间不够,创建队列失败

3.2、删除队列

void vQueueDelete(TaskHandle_t pxQueueToDelete)

  • 该函数主要用于删除队列

参数

说明

pxQueueToDelete

要删除的队列句柄

返回值

说明

void

3.3、向队列发送数据

BaseType_t xQueueSend(QueueHandle_t xQueue, const void* pvItemToQueue, TickType_t xTickToWait)

  • 该函数主要用于任务向队列发送数据

参数

说明

xQueue

队列句柄,指明要向哪个队列发送数据,创建队列成功以后会返回此队列的队列句柄

pvItemToQueue

指向要发送的消息,发送的时候会将这个消息拷贝到队列中

xTickToWait

阻塞时间,此参数指示当前队列满的时候任务进入阻塞等待队列空闲的最大时间,如果为 0 的话当队列满的时候就立即返回;当为 portMAX_DELAY 的话就会一直等待,直到队列有空闲的队列项,但是宏 INCLUDE_vTaskSuspend 必须为 1

返回值

说明

pdPASS

向队列发送消息成功

errQUEUE_FULL

队列已经满了,消息发送失败

BaseType_t xQueueSendFromISR(QueueHandle_t xQueue,

const void* pvItemToQueue,

BaseType_t* pxHigherPriorityTaskWoken)

  • 该函数主要用于中断向队列发送数据

参数

说明

xQueue

队列句柄,指明要向哪个队列发送数据,创建队列成功以后会返回此队列的队列句柄

pvItemToQueue

指向要发送的消息,发送的时候会将这个消息拷贝到队列中

pxHigherPriorityTaskWoken

标记退出此函数以后是否需要进行任务切换,这个变量的值由该函数来设置,不需要用户进行设置,用户只需要提供一个变量来保存这个值就行了。当此值为 pdTRUE 时,退出中断函数之前一定要进行一次任务切换

返回值

说明

pdPASS

向队列发送消息成功

errQUEUE_FULL

队列已经满了,消息发送失败

3.4、从队列接收数据

BaseType_t xQueueReceive(QueueHandle_t xQueue,

void* const pvBuffer,

TickType_t xTicksToWait)

  • 该函数主要用于任务接收队列的数据

参数

说明

xQueue

队列句柄,指明要向哪个队列发送数据

pvBuffer

指向存储被拷贝的数据的存储

xTicksToWait

任务处于阻塞状态下等待队列的数据可用的最大时间。如果xTicksToWait为 0,队列为空的情 况下调用此函数会立即返回;如果设置为 port_MAX_DELAY,任务会一直等待,但是宏 INCLUDE_vTaskSuspend 必须设置为 1。

返回值

说明

pdPASS

从队列中读取消息成功

errQUEUE_EMPTY

队列为空,读取消息失败

BaseType_t xQueueReceiveFromISR(QueueHandle_t xQueue,

const void* pxItemToQueue,

BaseType_t* pxHigherPriorityTaskWoken)

  • 该函数主要用于中断接收队列的数据

参数

说明

xQueue

队列句柄,指明要向哪个队列发送数据

pvItemToQueue

指向一个存储区,读取的的消息将被拷贝到存储区中

pxHigherPriorityTaskWoken

标记退出此函数以后是否需要进行任务切换,这个变量的值由这三个函数来设 置,不需要用户进行设置,用户只需要提供一个变量来保存这个值就行了。当此值为 pdTRUE 时,退出中断函数 之前一定要进行一次任务切换

返回值

说明

pdPASS

从队列中读取消息成功

pdFAIL

队列已经为空,消息读取失败

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

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值