消息队列
- 一、消息队列基本概念讲解
- 1、消息队列基本概念
- 2、消息池
- 2.1、消息池概念
- 2.2、消息池初始化
- 2.3、消息队列的运作机制
- 2.4、消息队列的阻塞机制
- 2.5、消息队列的应用场景
- 二、消息队列创建步骤
-
- 三、消息队列相关函数说明
- 1、创建消息队列函数OSQCreate()
- 2、消息队列删除函数 OSQDel()
- 3、消息队列发送函数 OSQPost()
- 4、 消息队列获取函数 OSQPend()
- 四、例程
一、消息队列基本概念讲解
1、消息队列基本概念
队列又称消息队列,是一种常用于任务间通信的数据结构,队列可以在任务与任务间、中断和任务间传递信息***(类似于裸机系统中用于传递数据用的数组)***,实现了任务接收来自其他任务或中断的不固定长度的消息。任务能够从队列里面读取消息,当队列中的消息是空时,读取消息的任务将被阻塞,用户还可以指定阻塞的任务时间 timeout,在这段时间中,如果队列为空,该任务将保持阻塞状态以等待队列数据有效。当队列中有新消息时,被阻塞的任务会被唤醒并处理新消息;当等待的时间超过了指定的阻塞时间,即使队列中尚无有效数据,任务也会自动从阻塞态转为就绪态。消息队列是一种异步的通信方式。通过消息队列服务,任务或中断服务程序可以将消息放入消息队列中。同样,一个或多个任务可以从消息队列中获得消息。uCOS 支持先进先出原则(FIFO)以及后进先出原则(LIFO)两种模式。
uCOS 中使用队列数据结构实现任务异步通信工作,具有如下特性:
(1)、消息支持先进先出方式排队,支持异步读写工作方式(FIFO)。
(2)、消息支持后进先出方式排队,往队首发送消息(LIFO)。
(3)、读消息队列支持超时机制。
(4)、可以允许不同长度的任意类型消息(因为是引用方式传递,无论多大的数据都只是一个指针)。
(5)、一个任务能够从任意一个消息队列接收和发送消息。
(6)、多个任务能够从同一个消息队列接收和发送消息。
(7)、当队列使用结束后,可以通过删除队列函数进行删除。
2、消息池
2.1、消息池概念
在 μCOS-III 中定义了一个数组 OSCfg_MsgPool[OS_CFG_MSG_POOL_SIZE],在系统初始化OSInit() 的时候就将这个大数组的各个元素串成单向链表,组成我们说的消息池,而这些元素我们称之为消息。消息池的大小OS_CFG_MSG_POOL_SIZE 由用户自己定义,该宏定义在 os_cfg_app.h 头文件中。这样子的处理很快,并且共用了资源,系统中所有被创建的队列都可以从消息池中取出消息,挂载到自身的队列上,以表示消息队列拥有消息,当消息使用完毕,则又会被释放回到消息池中,其他队列也可以从中取出消息,这样子的消息资源是能被系统所有的消息队列反复使用。
2.2、消息池初始化
OS_MsgPoolInit() 函数就是用来初始化消息池的,OS_MsgPoolInit() 函数的定义位于os_msg.c 文件中。
2.3、消息队列的运作机制
uCOS 的消息队列控制块由多个元素组成,当消息队列被创建时,编译器会静态为消息队列分配对应的内存空间(因为我们需要自己定义一个消息队列控制块),用于保存消息队列的一些信息如队列的名字,队列可用的最大消息个数,入队指针、出队指针等。在创建成功的时候,这些内存就被占用了,创建队列的时候用户指定队列的最大消息个数,无法再次更改,每个消息空间可以存放任意类型的数据(因为采用引用方式传递,无论什么类型的多大的数据都只是一个指针)。
任务或者中断服务程序都可以给消息队列发送消息,当发送消息时,如果队列未满,uCOS 会将从消息池中取出一个消息,将消息挂载到队列的尾部,消息中的成员变量MsgPtr 指向要发送的消息。如果队列已满,则返回错误代码,入队失败。
2.4、消息队列的阻塞机制
我们使用的消息队列一般不是属于某个任务的队列,在很多时候,我们创建的队列,是每个任务都可以去对他进行读写操作,但是为了保护每个任务对它进行读操作的过程(uCOS 队列的写操作是没有阻塞的),我们必须要有阻塞机制,在某个任务对它读操作的时候,必须保证该任务能正常完成读操作,而不受后来的任务干扰。
任务对消息队列进行读操作的时候有三种阻塞状态:
(1)、不等待,队列没有消息,任务不进入阻塞态,接着干别的事情去
(2)、等待一段时间(用户设置),任务会在等待时间内接收到消息后或者超出等待时间后,退出阻塞态变成就绪态。
(3)、永久等待,任务会一直等待消息,直至完成读取队列消息,退出阻塞态,变成就绪态。
注:如有多个任务阻塞在一个消息队列中,那么这些阻塞的任务将按照任务优先级进行排序,优先级高的任务将优先获得队列的访问权。如果发送消息的时候用户选择广播消息,那么在等待中的任务都会收到一样的消息。
2.5、消息队列的应用场景
消息队列可以应用于发送不定长消息的场合,包括任务与任务间的消息交换,队列是uCOS 中任务与任务间、中断与任务间主要的通讯方式,发送到队列的消息是通过引用方式实现的,这意味着队列存储的是数据的地址,我们可以通过这个地址将这个数据读取出来,这样子,无论数据量是多大,其操作时间都是一定的,只是一个指向数据地址指针。
二、消息队列创建步骤
1、定义消息队列
在app.c中定义
OS_Q queue;
2、创建消息队列
我们使用创建消息队列函数 OSQCreate()在app.c文件中的起始任务AppTaskStart ()中创建。
OSQCreate ((OS_Q *)&queue,
(CPU_CHAR *)"Queue For Test",
(OS_MSG_QTY )20,
(OS_ERR *)&err);
三、消息队列相关函数说明
1、创建消息队列函数OSQCreate()
创建消息队列函数,函数位于os_q.c文件中
void OSQCreate (OS_Q *p_q,
CPU_CHAR *p_name,
OS_MSG_QTY max_qty,
OS_ERR *p_err)
2、消息队列删除函数 OSQDel()
队列删除函数是根据队列结构(队列句柄)直接删除的,删除之后这个消息队列的所有信息都会被系统清空,而且不能再次使用这个消息队列了,一般来说,在删除消息队列之前,首先要删除所有可以访问该消息队列的任务。不过,强烈建议不要在运行时删除内核对象。但是需要注意的是,如果某个消息队列没有被定义,那也是无法被删除的。想要使用消息队列删除函数就必须将OS_CFG_Q_DEL_EN 宏定义配置为 1。该宏位于os_cfg.h文件中、函数位于os_q.c文件中。
OS_OBJ_QTY OSQDel (OS_Q *p_q,
OS_OPT opt,
OS_ERR *p_err)
opt | 选项说明 |
---|
OS_OPT_DEL_NO_PEND | 仅在没有等待消息的任务时才删除队列 |
OS_OPT_DEL_ALWAYS | 总是删除队列,而不管任务是否正在等待消息 |
3、消息队列发送函数 OSQPost()
任务或者中断服务程序都可以给消息队列发送消息,当发送消息时,如果队列未满,就说明运行信息入队**。uCOS 会从消息池中取出一个消息,挂载到消息队列的末尾(FIFO发送方式),如果是 LIFO 发送方式,则将消息挂载到消息队列的头部,然后将消息中MsgPtr 成员变量指向要发送的消息(此处可以理解为添加要发送的信息到消息(块)中)**,如果系统有任务阻塞在消息队列中,那么在发送了消息队列的时候,会将任务解除阻塞。
void OSQPost (OS_Q *p_q,
void *p_void,
OS_MSG_SIZE msg_size,
OS_OPT opt,
OS_ERR *p_err)
OSQPost ((OS_Q *)&queue,
(void *)"Fire uC/OS-III",
(OS_MSG_SIZE )sizeof ( "Fire uC/OS-III" ),
(OS_OPT )OS_OPT_POST_FIFO | OS_OPT_POST_ALL,
(OS_ERR *)&err);
opt | 选项说明 |
---|
OS_OPT_POST_FIFO | 默认采用 FIFO 方式发送 |
OS_OPT_POST_LIFO | 采用 LIFO 方式发送消息 |
OS_OPT_POST_1 | 将消息发布到最高优先级的等待任务 |
OS_OPT_POST_ALL | 向所有等待的任务广播消息 |
OS_OPT_POST_NO_SCHED | 发送消息但是不进行任务调度 |
4、 消息队列获取函数 OSQPend()
当任务试图从队列中的获取消息时,用户可以指定一个阻塞超时时间,当且仅当消息队列中有消息的时候,任务才能获取到消息。在这段时间中,如果队列为空,该任务将保持阻塞状态以等待队列消息有效。当其他任务或中断服务程序往其等待的队列中写入了数据,该任务将自动由阻塞态转为就绪态。当任务等待的时间超过了用户指定的阻塞时间,即使队列中尚无有效消息,任务也会自动从阻塞态转为就绪态。
void *OSQPend (OS_Q *p_q,
OS_TICK timeout,
OS_OPT opt,
OS_MSG_SIZE *p_msg_size,
CPU_TS *p_ts,
OS_ERR *p_err)
pMsg = OSQPend ((OS_Q *)&queue,
(OS_TICK )0,
(OS_OPT )OS_OPT_PEND_BLOCKING,
(OS_MSG_SIZE *)&msg_size,
(CPU_TS *)0,
(OS_ERR *)&err);
opt | 选项 |
---|
OS_OPT_PEND_BLOCKING | 没有获取到信号量就等待 |
OS_OPT_PEND_NON_BLOCKING | 没有获取到信号量就也不等待 |
注:timeout参数在指定时应该设置为0(永远等待消息)。因为使用OS_OPT_PEND_NON_BLOCKING选项时,超时值与滴答时钟不同步。
四、例程
app.c
#include <includes.h>
OS_Q queue;
static OS_TCB AppTaskStartTCB;
static OS_TCB AppTaskPostTCB;
static OS_TCB AppTaskPendTCB;
static CPU_STK AppTaskStartStk[APP_TASK_START_STK_SIZE];
static CPU_STK AppTaskPostStk [ APP_TASK_POST_STK_SIZE ];
static CPU_STK AppTaskPendStk [ APP_TASK_PEND_STK_SIZE ];
static void AppTaskStart (void *p_arg);
static void AppTaskPost ( void * p_arg );
static void AppTaskPend ( void * p_arg );
int main (void)
{
OS_ERR err;
OSInit(&err);
OSTaskCreate((OS_TCB *)&AppTaskStartTCB,
(CPU_CHAR *)"App Task Start",
(OS_TASK_PTR ) AppTaskStart,
(void *) 0,
(OS_PRIO ) APP_TASK_START_PRIO,
(CPU_STK *)&AppTaskStartStk[0],
(CPU_STK_SIZE) APP_TASK_START_STK_SIZE / 10,
(CPU_STK_SIZE) APP_TASK_START_STK_SIZE,
(OS_MSG_QTY ) 5u,
(OS_TICK ) 0u,
(void *) 0,
(OS_OPT )(OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR),
(OS_ERR *)&err);
OSStart(&err);
}
static void AppTaskStart (void *p_arg)
{
CPU_INT32U cpu_clk_freq;
CPU_INT32U cnts;
OS_ERR err;
(void)p_arg;
BSP_Init();
CPU_Init();
cpu_clk_freq = BSP_CPU_ClkFreq();
cnts = cpu_clk_freq / (CPU_INT32U)OSCfg_TickRate_Hz;
OS_CPU_SysTickInit(cnts);
Mem_Init();
#if OS_CFG_STAT_TASK_EN > 0u
OSStatTaskCPUUsageInit(&err);
#endif
CPU_IntDisMeasMaxCurReset();
OSQCreate ((OS_Q *)&queue,
(CPU_CHAR *)"Queue For Test",
(OS_MSG_QTY )20,
(OS_ERR *)&err);
OSTaskCreate((OS_TCB *)&AppTaskPostTCB,
(CPU_CHAR *)"App Task Post",
(OS_TASK_PTR ) AppTaskPost,
(void *) 0,
(OS_PRIO ) APP_TASK_POST_PRIO,
(CPU_STK *)&AppTaskPostStk[0],
(CPU_STK_SIZE) APP_TASK_POST_STK_SIZE / 10,
(CPU_STK_SIZE) APP_TASK_POST_STK_SIZE,
(OS_MSG_QTY ) 5u,
(OS_TICK ) 0u,
(void *) 0,
(OS_OPT )(OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR),
(OS_ERR *)&err);
OSTaskCreate((OS_TCB *)&AppTaskPendTCB,
(CPU_CHAR *)"App Task Pend",
(OS_TASK_PTR ) AppTaskPend,
(void *) 0,
(OS_PRIO ) APP_TASK_PEND_PRIO,
(CPU_STK *)&AppTaskPendStk[0],
(CPU_STK_SIZE) APP_TASK_PEND_STK_SIZE / 10,
(CPU_STK_SIZE) APP_TASK_PEND_STK_SIZE,
(OS_MSG_QTY ) 5u,
(OS_TICK ) 0u,
(void *) 0,
(OS_OPT )(OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR),
(OS_ERR *)&err);
OSTaskDel ( & AppTaskStartTCB, & err );
}
static void AppTaskPost ( void * p_arg )
{
OS_ERR err;
(void)p_arg;
while (DEF_TRUE) {
OSQPost ((OS_Q *)&queue,
(void *)"Fire uC/OS-III",
(OS_MSG_SIZE )sizeof ( "Fire uC/OS-III" ),
(OS_OPT )OS_OPT_POST_FIFO | OS_OPT_POST_ALL,
(OS_ERR *)&err);
OSTimeDlyHMSM ( 0, 0, 0, 500, OS_OPT_TIME_DLY, & err );
}
}
static void AppTaskPend ( void * p_arg )
{
OS_ERR err;
OS_MSG_SIZE msg_size;
CPU_SR_ALLOC();
char * pMsg;
(void)p_arg;
while (DEF_TRUE) {
pMsg = OSQPend ((OS_Q *)&queue,
(OS_TICK )0,
(OS_OPT )OS_OPT_PEND_BLOCKING,
(OS_MSG_SIZE *)&msg_size,
(CPU_TS *)0,
(OS_ERR *)&err);
if ( err == OS_ERR_NONE )
{
OS_CRITICAL_ENTER();
printf ( "\r\n接收消息的长度:%d字节,内容:%s\r\n", msg_size, pMsg );
OS_CRITICAL_EXIT();
}
}
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)