UCOS消息队列的使用【转+原创】

本文介绍消息队列的基本配置步骤及使用方法,包括配置宏定义、创建队列、发送接收消息等操作,并探讨了队列传递数据类型的特性和解决方案。
 

消息队列的使用
    1、 需在以下文件中配置如下内容
        OS_CFG.H
        OS_MAX_QS      N     你需要的值
        根据需要自己配置
 #define OS_Q_EN                   1    /* Enable (1) or Disable (0) code generation for QUEUES         */
 #define OS_Q_ACCEPT_EN            1    /*     Include code for OSQAccept()                             */
 #define OS_Q_DEL_EN               1    /*     Include code for OSQDel()                                */
 #define OS_Q_FLUSH_EN             1    /*     Include code for OSQFlush()                              */
 #define OS_Q_POST_EN              1    /*     Include code for OSQPost()                               */
 #define OS_Q_POST_FRONT_EN        1    /*     Include code for OSQPostFront()                          */
 #define OS_Q_POST_OPT_EN          1    /*     Include code for OSQPostOpt()                            */
 #define OS_Q_QUERY_EN             1    /*     Include code for OSQQuery()                              */


    2、 建立一个指向消息数组的指针和数组的大小,该指针数组必须申明为void类型,如下:
        void    *MyArrayOfMsg[SIZE];


    3、 声明一个OS_EVENT类型的指针指向生成的队列,如下:
        OS_EVENT  *QSem;


    4、 调用OSQcreate()函数创建消息队列,如下:
 QSem      =  OSQcreate(&MyArrayOfMsg[0],SIZE);


    5、 等待消息队列中的消息,OSQPend()。

void  *OSQPend (OS_EVENT *pevent, INT16U timeout, INT8U *err):
 必须保证消息队列已经被建立。
 timeout定义的是等待超时时间,如果为0则表示无期限的等待
 err表示的是在等待消息队列出错时的返回类型,有以下几种:
 OS_ERR_PEVENT_NULL     //消息队列不存在
 OS_ERR_EVENT_TYPE
 OS_TIMEOUT  //消息队列等待超时
 OS_NO_ERR              //消息队列接收到消息

 获得消息队列示例
 type    *GETQ;
 INT8U   err;
 GETQ    =  (type *)OSQPend(QSem, time, &err);
 if(err  ==  OS_NO_ERR){
  无错处理
 }
 else{
  出错处理
 }
 

6.1 向消息队列发送一则消息(FIFO),OSQPost();  INT8U  OSQPost (OS_EVENT *pevent, void *msg):
 函数返回值有:
 OS_ERR_PEVENT_NULL
 OS_ERR_POST_NULL_PTR
 OS_ERR_EVENT_TYPE
 OS_Q_FULL
 OS_NO_ERR
 参数:pevent,*msg
    6.2 向消息队列发送一则消息(LIFO) INT8U  OSQPostFront (OS_EVENT *pevent, void *msg)
    6.3 向消息队列发送一则消息(LIFO或者FIFO) INT8U  OSQPostOpt (OS_EVENT *pevent, void *msg, INT8U opt)
 参数: opt
  如果经opt参数中的OS_POST_OPT_BROADCAST位置为1,则所有正在等待消息的任务都能接收到这则消息,并且被OS_EventTaskRdy()从等待列表中删除
  如果不是广播方式,则只有等待消息的任务中优先级最高的任务能够进入就绪态。然后,OS_EventTaskRdy()从等待列表中把等待消息的任务中优先级最高的任务删除。
 注: 如果此函数由ISR调用,则不会发生任务切换,直到中断嵌套的最外层中断服务子程序调用OSIntExit()函数时,才能进行任务切换


    7、 无等待的从消息队列中获得消息,OSQAccept(); void  *OSQAccept (OS_EVENT *pevent, INT8U *err)
 err可能的返回值:
 OS_ERR_PEVENT_NULL
 OS_Q_EMPTY
 OS_NO_ERR
 函数的返回值:消息,0


    8、 清空消息队列 INT8U  OSQFlush (OS_EVENT *pevent)
 函数返回值:
 OS_ERR_PEVENT_NULL
 OS_ERR_EVENT_TYPE
 OS_NO_ERR


    9、 获取消息队列的状态,OSQQuery(); INT8U  OSQQuery (OS_EVENT *pevent, OS_Q_DATA *p_q_data)
 函数返回值:
 OS_ERR_PEVENT_NULL
 OS_ERR_EVENT_TYPE
 OS_NO_ERR
 OS_Q_DATA数据结构在ucos_ii.h中

==================================华丽的分割线===================================

现在来说一下这个消息队列所传递的数据类型和特性。

首先,这个消息队列不是真正意义上的“队列”,它呢只会把每次存入的数据指针排队,而不会保存该数据。所以调用OSQPost时如果每次都是相同的指针,那就说明 白放了,因为读取的时候读到的是同一个指针,他总是最新的数据,老的没了,何来队列?

其次,该队列可以保存void*类型指针,即所有数据类型都适用,管他是整型还是结构体。

现在这个队列不是真队列,那怎么解决?莫急,看下面:

我这里提一种简单的方法,复杂的各位资格解决吧。

1、定义一个存放数据的缓冲区数组,这样每次向对列添加时就可以用不同的地址。例如struct my_struct data_array[10]

2、然后就是怎么变这个数组下标的问题,直接定义一个变量来寻找,然后使用++,到最后再折回来就好了,例如:

int idx=0;

idx++;

idx %= BUF_SIZE;

3、对于速度能及时解决的任务,那下标这样搞就可以,如果不行,那可能要重新考虑覆盖的问题了。


队列是为了任务任务任务与中断之间的通信而准备的,可以在任务任务任务与中 断之间传递消息,队列中可以存储有限的、大小固定的数据项目。任务任务任务与中断之 间要交流的数据保存在队列中,叫做队列项目。队列所能保存的最大数据项目数量叫做队列的 长度,创建队列的时候会指定数据项目的大小和队列的长度。由于队列用来传递消息的,所以 也称为消息队列。FreeRTOS 中的信号量的也是依据队列实现的!所以有必要深入的了解 FreeRTOS 的队列。 1、数据存储 通常队列采用先进先出(FIFO)的存储缓冲机制,也就是往队列发送数据的时候(也叫入队)永 远都是发送到队列的尾部,而从队列提取数据的时候(也叫出队)是从队列的头部提取的。但是 也可以使用 LIFO 的存储缓冲,也就是后进先出,FreeRTOS 中的队列也提供了 LIFO 的存储缓 冲机制。 数据发送到队列中会导致数据拷贝,也就是将要发送的数据拷贝到队列中,这就意味着在 队列中存储的是数据的原始值,而不是原数据的引用(即只传递数据的指针),这个也叫做值传 递。学过 UCOS 的同学应该知道,UCOS消息队列采用的是引用传递,传递的是消息指针。 采用引用传递的话消息内容就必须一直保持可见性,也就是消息内容必须有效,那么局部变量 这种可能会随时被删掉的东西就不能用来传递消息,但是采用引用传递会节省时间啊!因为不 用进行数据拷贝。 采用值传递的话虽然会导致数据拷贝,会浪费一点时间,但是一旦将消息发送到队列中原 始的数据缓冲区就可以删除掉或者覆写,这样的话这些缓冲区就可以被重复的使用。FreeRTOS使用队列传递消息的话虽然使用的是数据拷贝,但是也可以使用引用来传递消息啊,我直接 往队列中发送指向这个消息的地址指针不就可以了!这样当我要发送的消息数据太大的时候就 可以直接发送消息缓冲区的地址指针,比如在网络应用环境中,网络的数据量往往都很大的, 采用数据拷贝的话就不现实。 2、多任务访问 队列不是属于某个特别指定的任务的,任何任务都可以向队列中发送消息,或者从队列中 提取消息。 3、出队阻塞 当任务尝试从一个队列中读取消息的时候可以指定一个阻塞时间,这个阻塞时间就是当任 务从队列中读取消息无效的时候任务阻塞的时间。出队就是就从队列中读取消息,出队阻塞是 针对从队列中读取消息的任务而言的。比如任务 A 用于处理串口接收到的数据,串口接收到数 据以后就会放到队列 Q 中,任务 A 从队列 Q 中读取数据。但是如果此时队列 Q 是空的,说明 还没有数据,任务 A 这时候来读取的话肯定是获取不到任何东西,那该怎么办呢?任务 A 现在 有三种选择,一:二话不说扭头就走,二:要不我在等等吧,等一会看看,说不定一会就有数 据了,三:死等,死也要等到你有数据!选哪一个就是由这个阻塞时间决定的,这个阻塞时间 单位是时钟节拍数。阻塞时间为 0 的话就是不阻塞,没有数据的话就马上返回任务继续执行接 下来的代码,对应第一种选择。如果阻塞时间为 0~ portMAX_DELAY,当任务没有从队列中获 取到消息的话就进入阻塞态,阻塞时间指定了任务进入阻塞态的时间,当阻塞时间到了以后还 没有接收到数据的话就退出阻塞态,返回任务接着运行下面的代码,如果在阻塞时间内接收到 了数据就立即返回,执行任务中下面的代码,这种情况对应第二种选择。当阻塞时间设置为 portMAX_DELAY 的话,任务就会一直进入阻塞态等待,直到接收到数据为止!这个就是第三种选择。 4、入队阻塞 入队说的是向队列中发送消息,将消息加入到队列中。和出队阻塞一样,当一个任务向队 列发送消息的话也可以设置阻塞时间。比如任务 B 向消息队列 Q 发送消息,但是此时队列 Q 是 满的,那肯定是发送失败的。此时任务 B 就会遇到和上面任务 A 一样的问题,这两种情况的处 理过程是类似的,只不过一个是向队列 Q 发送消息,一个是从队列 Q 读取消息而已。 ———————————————— 版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,载请附上原文出处链接和本声明。 原文链接:https://blog.youkuaiyun.com/qq_61672347/article/details/125568639
最新发布
08-17
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值