专栏首页嵌入式智能硬件【FreeRTOS】队列管理2

【FreeRTOS】队列管理2

使用队列传递复合数据类型

一个任务从单个队列中接收来自多个发送源的数据是经常的事。通常接收方收到数据后,需要知道数据的来源,并根据数据的来源决定下一步如何处理。一个简单的方式就是利用队列传递结构体,结构体成员中就包含了数据信息和来源信息。

创建一个队列用于保存类型为xData 的结构体数据单元。结构体成员包括了一个数据值和表示数据含义的编码,两者合为一个消息可以一次性发送到队列。 ? 中央控制任务用于完成主要的系统功能。其必须对队列中传来的输入和其它系统状态的改变作出响应。 ? CAN 总线任务用于封装CAN 总线的接口功能。当CAN 总线任务收到并解码一个消息后,其将把解码后的消息放到xData 结构体中发往控制任务。结构体的iMeaning成员用于让中央控制任务知道这个数据是用来干什么的 — 从图中的描述可以看出,这个数据表示电机速度。结构体的iValue 成员可以让中央控制任务知道电机的实际速度值。 ? 人机接口(HMI)任务用于对所有的人机接口功能进行封装。设备操作员可能通过各种方式进行命令输入和参数查询,人机接口任务需要对这些操作进行检测并解析。当接收到一个新的命令后,人机接口任务通过xData 结构将命令发送到中央控制任务。结构体的iMeaning 成员用于让中央控制任务知道这个数据是用来干什么的 — 从图中的描述可以看出,这个数据表示一个新的参数设置。结构体的iValue 成员可以让中央控制任务知道具体的设置值。

例子11:

例11 与例10 类似,只是写队列任务与读队列任务的优先级交换了,即读队列任务的优先级低于写队列任务的优先级。并且本例中的队列用于在任务间传递结构体数据,而非简单的长整型数据。 结构体定义 /* 定义队列传递的结构类型。 */ typedef struct { unsigned char ucValue; unsigned char ucSource; } xData; /* 声明两个xData类型的变量,通过队列进行传递。 */ static const xData xStructsToSend[ 2 ] = { { 100, mainSENDER_1 }, /* Used by Sender1. */ { 200, mainSENDER_2 } /* Used by Sender2. */ }; 在例10 中读队列任务具有最高优先级,所以队列不会拥有一个以上的数据单元。这是因为一旦数据被写队列任务写进队列,读队列任务立即抢占写队列任务,把刚写入的数据单元读走。在例11 中,写队列任务具有最高优先级,所以队列正常情况下一直是处于满状态。这是因为一旦读队列任务从队列中读走一个数据单元,某个写队列任务就会立即抢占读队列任务,把刚刚读走的位置重新写入,之后便又转入阻塞态以等待队列空间有效。 写队列任务的实现: 写队列任务指定了100 毫秒的阻塞超时时间,以便在队列满时转入阻塞态以等待队列空间有效。进入阻塞态后,一旦队列空间有效,或是等待超过了100 毫秒队列空间尚无效,其将解除阻塞。在本例中,将永远不会出现100 毫秒超时的情况,因为读队列任务在不停地从队列中读出数据从而腾出队列数据空间。 读队列任务的实现: 读队列任务的优先级最低,所以只有在所有写队列任务都进入阻塞态后才有机会得到执行。而写队列任务只会在队列满时才会进入阻塞态,所以读队列任务得到执行时队列已满。因此读队列任务只管不停地读取数据,不必设定超时时间。 主函数main()与上一例比起来只作了微小的改动。创建的队列数可以保存三个xData 类型的数据单元,并且交换了写队列任务与读队列任务的优先级 代码实现: 点击(此处)折叠或打开

#include "led.h"
#include "delay.h"
#include "sys.h"
#include "usart.h"
 
// FreeRTOS head file, add here.
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "list.h"
#include "portable.h"
#include "FreeRTOSConfig.h"
 
#define SENDER_1 (unsigned char )1
#define SENDER_2 (unsigned char )2
 
typedef struct data_type{
 
    unsigned char ucValue;
    unsigned char ucSource;
 
}data_type;
 
 
static const data_type structDateToSend[2] = {
    {100, SENDER_1},
    {200, SENDER_2},
};
 
 
/* declare a queueHandle variable, using to save queue handler. */
xQueueHandle xQueue;
 
 
 
 
void vSendTask(void *pvParameters)
{
    data_type * valueToSend;
    portBASE_TYPE status;
 
    valueToSend = (data_type *) pvParameters;
 
    while(1)
    {
        status = xQueueSendToBack(xQueue, valueToSend, 100 / portTICK_RATE_MS);
        if(status != pdPASS)
        {
            printf("could not send to the queue. \r\n");
        }
 
        taskYIELD();
    }
}
 
 
void vReceiveTask(void *pvParameters)
{
    data_type lReceivedValue;
    portBASE_TYPE status;
 
    while(1)
    {
        if( uxQueueMessagesWaiting( xQueue ) != 3 )
        {
            printf( "Queue should have been full! \r\n");
        }
 
        status = xQueueReceive( xQueue, &lReceivedValue, 0 );
        if( status == pdPASS )
        {
            if(lReceivedValue.ucSource == SENDER_1)
                printf("from sender1 = %d \r\n", lReceivedValue.ucValue);
            else if(lReceivedValue.ucSource == SENDER_2)
                printf("from sender2 = %d \r\n", lReceivedValue.ucValue);
        }
        else
        {
            printf( "Could not receive from the queue.\r\n" );
        }
    }
}
 
int main(void)
{
    // board initialize.
    LED_Init();             
    uart_init(115200);
 
    // create queue, can store 3 value which data type is data_type
    xQueue = xQueueCreate(3, sizeof(data_type));
 
    if(xQueue != NULL) // adjust the return value, to confirm whether create queue successful.
    {
        // Create two write queue task, priority = 2;
        xTaskCreate(vSendTask, "SendTask1", configMINIMAL_STACK_SIZE, (void *)&structDateToSend[0], 2, NULL);
        xTaskCreate(vSendTask, "SendTask2", configMINIMAL_STACK_SIZE, (void *)&structDateToSend[1], 2, NULL);
 
        // create one read queue task, priority = 1;
        xTaskCreate(vReceiveTask, "RecTask", configMINIMAL_STACK_SIZE, NULL, 1, NULL);
 
        // start scheduler now
        vTaskStartScheduler();
    }
    else
    {
        // queue create unsuccessful here. add your code.
    }
 
    return 0;
}

打印结果:

执行流程:

执行流程分析: t1 写队列任务1 得到执行,并往队列中发送数据. t2 写队列任务1 切换到写队列任务2。写队列任务2 往队列中发送数据。 t3 写队列任务2 又切回写队列任务1。写队列任务1 再次将数据写入队列,导致队列满。 t4 写队列任务1 切换到写队列任务2。 t5 写队列任务2 试图往队列中写入数据。但由于队列已满,所以写队列任务2 转入阻塞态以等待队列空间有效。这使得写队列任务1 再次得到执行。 t6 写队列任务1 试图往队列中写入数据。但由于队列已满,所以写队列任务1 也转入阻塞态以等待队列空间有效。此时写队列任务均处于阻塞态,这才使得被赋予最低优先级的读队列任务得以执行。 t7 读队列任务从队列读取数据,并把读出的数据单元从队列中移出。一旦队列空间有效,写队列任务2 立即解除阻塞,并且因为其具有更高优先级,所以抢占读队列任务。写队列任务2 又往队列中写入数据,填充到刚刚被读队列任务腾出的存储空间,使得队列再一次变满。写队列发送完数据后便调用taskYIELD(),但写队列任务1 尚还处理阻塞态,所以写队列任务2 并未被切换出去,继续执行。 t8 写队列任务2 试图往队列中写入数据。但队列已满,所以写队列任务2 转入阻塞态。两个写队列任务再一次同时处于阻塞态,所以读队列任务得以执行。t9 读队列任务从队列读取数据,并把读出的数据单元从队列中移出。一旦队列空间有效,写队列任务1 立即解除阻塞,并且因为其具有更高优先级,所以抢占读队列任务。写队列任务1 又往队列中写入数据,填充到刚刚被读队列任务腾出的存储空间,使得队列再一次变满。写队列发送完数据后便调用taskYIELD(),但写队列任务2 尚还处理阻塞态,所以写队列任务1 并未被切换出去,继续执行。写队列任务1 试图往队列中写入数据。但队列已满,所以写队列任务1 转入阻塞态。

工作于大型数据单元

如果队列存储的数据单元尺寸较大,那最好是利用队列来传递数据的指针而不是对数据本身在队列上一字节一字节地拷贝进或拷贝出。传递指针无论是在处理速度上还是内存空间利用上都更有效。但是,当你利用队列传递指针时,一定要十分小心地做到以下两点: 1. 指针指向的内存空间的所有权必须明确 当任务间通过指针共享内存时,应该从根本上保证所不会有任意两个任务同时 修改共享内存中的数据,或是以其它行为方式使得共享内存数据无效或产生一致性 问题。原则上,共享内存在其指针发送到队列之前,其内容只允许被发送任务访问; 共享内存指针从队列中被读出之后,其内容亦只允许被接收任务访问。 2. 指针指向的内存空间必须有效 如果指针指向的内存空间是动态分配的,只应该有一个任务负责对其进行内存释放。当这段内存空间被释放之后,就不应该有任何一个任务再访问这段空间。 切忌用指针访问任务栈上分配的空间。因为当栈帧发生改变后,栈上的数据将不再有效。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 【FreeRTos】队列管理1

    写队列任务在每次循环中都调用taskYIELD()。taskYIELD()通知调度器立即进行任务切换,而不必等到当前任务的时间片耗尽。某个任务调用taskY...

    心跳包
  • UCOSii的理解和应用之消息队列

    1、 建立一个指向消息数组的指针和数组的大小,该指针数组必须申明为void类型,如下:

    心跳包
  • AVRCP

    AVRCP全称:The Audio/Video Remote Control Profile (AVRCP) 翻译成中文就是:音视频远程控制协议。 概念:AV...

    心跳包
  • 三分钟基础:什么是队列?

    像线程池、异步队列、消息队列等有限的资源容器中,往往存储大量的任务事件,这些大量的任务事件需要进行有条理的进行任务分发以及各种情况处理,为了能够使得资源容器的正...

    帅地
  • 栈与队列:总结篇!

    相信不仅仅是C++中有这些问题,那么大家使用其他编程语言,也可以考虑一下这四个问题,栈和队列是如何实现的。

    代码随想录
  • 算法与数据结构(二) 栈与队列的线性和链式表示(Swift版)

    数据结构中的栈与队列还是经常使用的,栈与队列其实就是线性表的一种应用。因为线性队列分为顺序存储和链式存储,所以栈可以分为链栈和顺序栈,队列也可分为顺序队列和链队...

    lizelu
  • 前端中的数据结构——队列篇

    队列是数据结构中的一种,它与实际生活中的排队相似:在一条队伍中,先来的人总是能够先得到服务,后来的人只能排在队伍末尾等候。队列也是一样,它符合先进先出 FIFO...

    企鹅号小编
  • 【FreeRTos】队列管理1

    写队列任务在每次循环中都调用taskYIELD()。taskYIELD()通知调度器立即进行任务切换,而不必等到当前任务的时间片耗尽。某个任务调用taskY...

    心跳包
  • Hadoop FairScheduler

    本文档描述FairScheduler,一个允许YARN应用程序公平共享集群资源的调度插件。

    用户1217611
  • 【数据结构(C语言版)系列三】 队列

    队列是一种先进先出的线性表,它只允许在表的一端进行插入,而在另一端删除元素。这和我们日常生活中的排队是一致的,最早进入队列的元素最早离开。在队列中,允许插入的一...

    闪电gogogo

扫码关注云+社区

领取腾讯云代金券