入门RTOS第九篇(队列-分辨数据源)

当有多个发送任务,通过同一个队列发出的数据,接受任务如何分辨数据来源?

数据本身带有“来源”信息,比如写入队列的数据是同一个结构体,结构体中的IDataSourceID用来表示数据来源

typedef struct {
    ID_t eDataID;
    int32_t lDataValue;
}Data_t;

不同的发送任务,先构造好结构体,填入自己的 eDataID ,再写队列;接收任务读出数据后,根据 eDataID 就可以知道数据来源了

本篇会创建一个队列,然后创建两个发送任务,一个接受任务。

创建的队列,用来发送结构体:

数据大小是结构体的大小 发送任务优先级为2,

分别往队列中写入自己的结构体,结构体中会标明数据来源 接收任务优先级为1,读队列、

根据数据来源打印信息

main函数中创建了队列、创建了发送任务、接收任务,代码如下:

/* 定义2种数据来源(ID) */
typedef enum
{
    eMotorSpeed,
    eSpeedSetPoint
} ID_t;
/* 定义在队列中传输的数据的格式 */
typedef struct {
    ID_t eDataID;
    int32_t lDataValue;
}Data_t;
发送任务的函数中,不断往队列中写入数值,代码如下:
/* 定义2个结构体 */
static const Data_t xStructsToSend[ 2 ] =
{
    { eMotorSpeed, 10 }, /* CAN任务发送的数据 */
    { eSpeedSetPoint, 5 } /* HMI任务发送的数据 */
};
/* vSenderTask被用来创建2个任务,用于写队列
* vReceiverTask被用来创建1个任务,用于读队列
*/
static void vSenderTask( void *pvParameters );
static void vReceiverTask( void *pvParameters );
/*-----------------------------------------------------------*/
/* 队列句柄, 创建队列时会设置这个变量 */
QueueHandle_t xQueue;
int main( void )
{
    prvSetupHardware();
    /* 创建队列: 长度为5,数据大小为4字节(存放一个整数) */
    xQueue = xQueueCreate( 5, sizeof( Data_t ) );
    if( xQueue != NULL )
    {
        /* 创建2个任务用于写队列, 传入的参数是不同的结构体地址
        * 任务函数会连续执行,向队列发送结构体
        * 优先级为2
        */
        xTaskCreate(vSenderTask, "CAN Task", 1000, (void *) &
        (xStructsToSend[0]), 2, NULL);
        xTaskCreate(vSenderTask, "HMI Task", 1000, (void *) &(
        xStructsToSend[1]), 2, NULL);
        /* 创建1个任务用于读队列
        * 优先级为1, 低于上面的两个任务
        * 这意味着发送任务优先写队列,队列常常是满的状态
        */
        xTaskCreate( vReceiverTask, "Receiver", 1000, NULL, 1, NULL );
        /* 启动调度器 */
        vTaskStartScheduler();
    }
    else
    {
        /* 无法创建队列 */
    }
        /* 如果程序运行到了这里就表示出错了, 一般是内存不足 */
        return 0;
}

发送任务的函数中,不断往队列中写入数值,代码如下:

static void vSenderTask( void *pvParameters )
{
    BaseType_t xStatus;
    const TickType_t xTicksToWait = pdMS_TO_TICKS( 100UL );
    /* 无限循环 */
    for( ;; )
    {
        /* 写队列
        * xQueue: 写哪个队列
        * pvParameters: 写什么数据? 传入数据的地址, 会从这个地址把数据复制进队列
        * xTicksToWait: 如果队列满的话, 阻塞一会
        */
        xStatus = xQueueSendToBack( xQueue, pvParameters, xTicksToWait );
        if( xStatus != pdPASS )
        {
            printf( "Could not send to the queue.\r\n" );
        }
    }
}

接收任务的函数中,读取队列、判断返回值、打印,代码如下:

static void vReceiverTask( void *pvParameters )
{
    /* 读取队列时, 用这个变量来存放数据 */
       Data_t xReceivedStructure;
    BaseType_t xStatus;
    /* 无限循环 */
    for( ;; )
    {
        /* 读队列
        * xQueue: 读哪个队列
        * &xReceivedStructure: 读到的数据复制到这个地址
        * 0: 没有数据就即刻返回,不阻塞
        */
        xStatus = xQueueReceive( xQueue, &xReceivedStructure, 0 );
        if( xStatus == pdPASS )
        {
            /* 读到了数据 */
            if( xReceivedStructure.eDataID == eMotorSpeed )
            {
                printf( "From CAN, MotorSpeed = %d\r\n",
                xReceivedStructure.lDataValue );
            }
            else if( xReceivedStructure.eDataID == eSpeedSetPoint )
            {
                printf( "From HMI, SpeedSetPoint = %d\r\n",
                xReceivedStructure.lDataValue );
            }
        }
            else
            {
                /* 没读到数据 */
                printf( "Could not receive from the queue.\r\n" );
            }
    }
}

运行结果如下:
任务调度情况如下图所示:


t1:HMI是最后创建的最高优先级任务,它先执行,一下子向队列写入5个数据,把队列都写满了
t2:队列已经满了,HMI任务再发起第6次写操作时,进入阻塞状态。这时CAN任务是最高优先级
的就绪态任务,它开始执行
t3:CAN任务发现队列已经满了,进入阻塞状态;接收任务变为最高优先级的就绪态任务,它开始
运行
t4:现在,HMI任务、CAN任务的优先级都比接收任务高,它们都在等待队列有空闲的空间;一旦
接收任务读出1个数据,会马上被抢占。被谁抢占?谁等待最久?HMI任务!所以在t4时刻,切换
到HMI任务。
t5:HMI任务向队列写入第6个数据,然后再次阻塞,这是CAN任务已经阻塞很久了。接收任务变
为最高优先级的就绪态任务,开始执行。
t6:现在,HMI任务、CAN任务的优先级都比接收任务高,它们都在等待队列有空闲的空间;一旦
接收任务读出1个数据,会马上被抢占。被谁抢占?谁等待最久?CAN任务!所以在t6时刻,切换
到CAN任务。
t7:CAN任务向队列写入数据,因为仅仅有一个空间供写入,所以它马上再次进入阻塞状态。这时
HMI任务、CAN任务都在等待空闲空间,只有接收任务可以继续执行。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

No Bugs ToDay

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值