消息队列管理(MESSAGE QUEUE MANAGEMENT)
1、消息队列定义
消息队列好比人们排队买票,排队的人好比是消息,每来一个人,都要到队伍的尾部去排队,叫添加一条消息到队列中。售票员卖票给先到的人,叫从对列中接收一条消息。这种队列处理方式叫先进先出。这个队伍的最大长度就是消息对列的容器。
消息队列是一个数据流,是由多个数据块构成的串,像是一节节车厢,故称队列。其实就是把要传输的数据依次放在队列中,然后再依次读出,好比是数据搬家。
消息队列是一个存放消息的容器,它能存放多少条消息,每条消息的内容是什么,所以在创建消息队列时,要先指定好“消息队列的容器大小”,具体每条消息多少字节,没有被给出,但定义了一个指针,指向消息数据块的首地址,也就是说,每条消息是一个数据块。
pq-> OSQStart:”消息队列容器的起始指针”
pq->OSQEnd:”消息队列容器的结束指针”
pq->OSQIn:
按照先进先出方式,就是入列指针,是指针的指针,用来指向消息数据块地址;
pq->OSQOut:
按照先进先出方式,就是出列指针,是指针的指针,用来指向消息数据块地址;
按照后进先出方式,既是入列指针,也是出列指针,是指针的指针,用来指向消息数据块地址;
pq->OSQEntries:消息队列计数器,表示当前有多少条消息等待读取
pevent->OSEventType:事件类型
pevent->OSEventPtr://消息队列控制块指针
以上是我个人根据代码提炼出来的理解,可能和其他人理解有出入。
在程序中,要么使用先进先出方式,要么选择后进先出方式。两种方式都用,程序会乱,除非你能控制好。
应用场景:
在需要异步处理时,如:发送短信验证码
一次性无法处理的大块数据传输,就需要分流装载发送或接收。
2、创建消息队列
在创建消息队列指针之前,先要定义好消息容器的大小,并定义一个指针数组,用来存放”消息数据的指针”,如下定义
#define MessageQueueContainer_Size 12
//消息队列的容器大小,最多可以存放12条消息
void *MessageQueueContainer[MessageQueueContainer_Size];//定义”消息队列的容器”
用法:
MessageQueuePointer=OSQCreate(&MessageQueueContainer[0],MessageQueueContainer_Size);
//建立一个消息队列,其指针为MessageQueuePointer
3、消息队列几个很重要的函数
OS_EVENT *OSQCreate (void **start, INT16U size)
INT8U OSQPost (OS_EVENT *pevent, void *pmsg)
void *OSQPend (OS_EVENT *pevent, INT32U timeout, INT8U *perr)
INT8U OSQPostFront (OS_EVENT *pevent, void *pmsg)
void *OSQAccept (OS_EVENT *pevent, INT8U *perr)
INT8U OSQFlush (OS_EVENT *pevent)
INT8U OSQPostOpt (OS_EVENT *pevent, void *pmsg, INT8U opt)
4、函数功能介绍
OSQCreate()建立一个消息队列,并返回消息队列指针
OSQPend()若有消息,则将消息队列的数据块首地址返回;若无消息,则挂起任务等待消息到来;
OSQAccept()若有消息,则将消息队列的数据块首地址返回,令perr =OS_ERR_NONE;若无消息,则令perr = OS_ERR_Q_EMPTY,且返回值为0
OSQFlush()清除”消息对列”中的所有消息,返回值为OS_ERR_NONE,表示操作正确
OSQPost()按照先入先出方式,添加一条消息到“消息队列容器”;若消息队列容器为空,则不入列,直接将消息发送给”等待消息队列的任务”;
OSQPostFront()按照后入先出方式,添加一条消息到“消息队列容器”;若消息队列容器为空,则不入列,直接将消息发送给”等待消息队列的任务”;
OSQPostOpt()
若消息队列容器已满,则直接将消息发给因消息队列而等待的任务,否则,将消息按照指定的方式入列
若opt = OS_POST_OPT_BROADCAST=0x01,群发消息,不用入列,直接把当前的消息发给因消息队列而等待的任务
若“消息队列容器满了”,就不用添加消息入列,返回值为OS_ERR_Q_FULL
若“消息队列容器没有满”且opt=OS_POST_OPT_FRONT,按照后入先出添加消息到队列中
若“消息队列容器没有满”且opt!=OS_POST_OPT_FRONT,按照先入先出添加消息到队列中
//返回值为OS_ERR_Q_FULL,表示“入列因消息队列容器已满而失败”
//返回值为OS_ERR_NONE,表示入列成功
5、读程序,分析函数功能
1) 、OS_EVENT *OSQCreate (void **start, INT16U size)
//函数功能: 建立一个消息队列,并返回消息队列指针
// start为”消息队列容器的起始指针”
// size为”消息队列容器的大小”
OS_EVENT *OSQCreate (void **start,
INT16U size)
{
OS_EVENT *pevent;
OS_Q *pq;
#if OS_CRITICAL_METHOD == 3u
OS_CPU_SR cpu_sr = 0u;
#endif
#ifdef OS_SAFETY_CRITICAL_IEC61508
if (OSSafetyCriticalStartFlag == OS_TRUE)
{
OS_SAFETY_CRITICAL_EXCEPTION();
}
#endif
if (OSIntNesting > 0u)
{
return ((OS_EVENT *)0);
}
OS_ENTER_CRITICAL();//进入临界区(无法被中断打断),需要定义cpu_sr变量
pevent = OSEventFreeList;//来自系统
/*把”空闲的事件控制块指针” 保存到pevent中,将用作”消息队列指针”*/
if (OSEventFreeList != (OS_EVENT *)0)
{//”空闲的事件控制块指针”有效
OSEventFreeList = (OS_EVENT *)OSEventFreeList->OSEventPtr;
//指向”事件控制块的列表”
}
OS_EXIT_CRITICAL();//退出临界区(可以被中断打断)
if (pevent != (OS_EVENT *)0)
{//”空闲的事件控制块指针”有效
OS_ENTER_CRITICAL();//进入临界区(无法被中断打断),需要定义cpu_sr变量
pq = OSQFreeList;/*指向消息队列控制块的列表*/
if (pq != (OS_Q *)0) /*已经获取到空闲的队列控制块*/
{//”消息队列控制块的列表指针”有效
OSQFreeList = OSQFreeList->OSQPtr;
/*调节空闲列表指针指向下一个空闲列表*/
OS_EXIT_CRITICAL();//退出临界区(可以被中断打断)
pq->OSQStart = start;//初始化”消息队列容器的起始指针”
pq->OSQEnd= &start[size]; //初始化”消息队列容器的结束指针”
pq->OSQIn = start;//初始化”入列指针”
pq->OSQOut = start; //初始化”出列指针”
pq->OSQSize = size; //初始化”消息队列容器的大小”
pq->OSQEntries = 0u; //消息队列计数器
pevent->OSEventType = OS_EVENT_TYPE_Q;//设置事件类型为消息队列
pevent->OSEventCnt = 0u;
pevent->OSEventPtr = pq;//消息队列控制块指针
#if OS_EVENT_NAME_EN > 0u
pevent->OSEventName = (INT8U *)(void *)"?";
#endif
OS_EventWaitListInit(pevent);/*Initalize the wait list*/
}
else//”消息队列控制块的列表指针”无效
{
pevent->OSEventPtr = (void *)OSEventFreeList;
/* No, Return event control block on error */
OSEventFreeList = pevent;
OS_EXIT_CRITICAL();//退出临界区(可以被中断打断)
pevent = (OS_EVENT *)0;//创建消息队列失败
}
}
return (pevent);
}
2)、INT8U OSQPost (OS_EVENT *pevent, void *pmsg)
函数功能:按照先入先出方式,添加一条消息到“消息队列容器”;若消息队列容器为空,则不入列,直接将消息发送给”等待消息队列的任务”;
// pevent为消息队列指针
// pmsg为准备入列的消息指针
//返回值为OS_ERR_NONE,表示“入列正确”
//返回值为OS_ERR_Q_FULL,表示“入列因消息队列容器已满而失败”
INT8U OSQPost (OS_EVENT *pevent,
void *pmsg)
{
OS_Q *pq;
#if OS_CRITICAL_METHOD == 3u
OS_CPU_SR cpu_sr = 0u;
#endif
#if OS_ARG_CHK_EN > 0u
if (pevent == (OS_EVENT *)0)
{//pevent指针为0
return (OS_ERR_PEVENT_NULL);
}
#endif
if (pevent->OSEventType != OS_EVENT_TYPE_Q)
{//事件类型不是消息队列类型
return (OS_ERR_EVENT_TYPE);
}
OS_ENTER_CRITICAL();//进入临界区(无法被中断打断),需要定义cpu_sr变量
if (pevent->OSEventGrp != 0u)
{//发现因消息队列而挂起的任务,或者有更高优先级任务在等待消息队列
//说明当前的消息队列是空的,非空的消息队列不会引起任务挂起
(void)OS_EventTaskRdy(pevent, pmsg, OS_STAT_Q, OS_STAT_PEND_OK);
//消息不用”入列”,直接将pmsg所指向的数据块地址发送给等待任务
OS_EXIT_CRITICAL();//退出临界区(可以被中断打断)
OS_Sched();//有更高优先级在准备运行,执行任务调度
return (OS_ERR_NONE);
}
///没有发现因消息队列而挂起的任务///
pq = (OS_Q *)pevent->OSEventPtr; //读取消息队列控制块指针
if (pq->OSQEntries >= pq->OSQSize)
{//”消息队列计数器”超过”消息队列容器的最大值”, 消息队列容器满了
OS_EXIT_CRITICAL();//退出临界区(可以被中断打断)
return (OS_ERR_Q_FULL);//不添加消息,返回OS_ERR_Q_FULL
}
//消息队列容器没有满//
//按照先进先出方式添加消息/
*pq->OSQIn++ = pmsg;//将消息添加到消息队列容器,”入列指针”加1
pq->OSQEntries++;//消息队列计数器加1
if (pq->OSQIn == pq->OSQEnd)
{//若”入列指针”到达”消息队列容器的结束指针”
pq->OSQIn = pq->OSQStart;
//将”入列指针”设置为 ”消息队列容器的起始指针”
}
OS_EXIT_CRITICAL();//退出临界区(可以被中断打断)
return (OS_ERR_NONE);
}
3)、 void *OSQPend (OS_EVENT *pevent, INT32U timeout, INT8U *perr)
函数功能:若有消息,则将消息队列的数据块首地址返回;若无消息,则挂起任务等待消息到来;
// perr =OS_ERR_NONE,且返回值不为0,表示读到消息
timeout=0任务会一直挂起,直到收到新消息
timeout!=0任务在timeout个节拍内,若收到新消息,则退出,否则,超时退出消息消息接收
void *OSQPend (OS_EVENT *pevent,
INT32U timeout,
INT8U *perr)
{
void *pmsg;
OS_Q *pq;
#if OS_CRITICAL_METHOD == 3u
OS_CPU_SR cpu_sr = 0u;
#endif
#ifdef OS_SAFETY_CRITICAL
if (perr == (INT8U *)0)
{//perr指针为0
OS_SAFETY_CRITICAL_EXCEPTION();
}
#endif
#if OS_ARG_CHK_EN > 0u
if (pevent == (OS_EVENT *)0)
{//pevent指针为0
*perr = OS_ERR_PEVENT_NULL;
return ((void *)0);
}
#endif
if (pevent->OSEventType != OS_EVENT_TYPE_Q)
{//事件类型不是消息队列类型
*perr = OS_ERR_EVENT_TYPE;
return ((void *)0);
}
if (OSIntNesting > 0u)
{//如果是中断服务程序调用,则返回0
*perr = OS_ERR_PEND_ISR;//中断服务程序不能挂起
return ((void *)0);
}
if (OSLockNesting > 0u)
{//调度器上锁访问
*perr = OS_ERR_PEND_LOCKED;//当调度器上锁时,任务不能被挂起
return ((void *)0);
}
OS_ENTER_CRITICAL();//进入临界区(无法被中断打断),需要定义cpu_sr变量
pq = (OS_Q *)pevent->OSEventPtr; //读取消息队列控制块指针
if (pq->OSQEntries > 0u)
{//”消息队列计数器”大于0,说明消息队列里有未读消息
pmsg = *pq->OSQOut++;//将”出列指针”保存到pmsg,然后将”出列指针”加1
pq->OSQEntries--; //”消息队列计数器”减1
if (pq->OSQOut == pq->OSQEnd)
{//若”出列指针”到达”消息队列容器的结束指针”
pq->OSQOut = pq->OSQStart;
//将出列指针设置为”消息队列容器的起始指针”
}
OS_EXIT_CRITICAL();//退出临界区(可以被中断打断)
*perr = OS_ERR_NONE;
return (pmsg);//读到消息
}
///”消息队列计数器”等于0,说明消息队列里无可读消息///”
OSTCBCur->OSTCBStat |= OS_STAT_Q;//任务将挂起
OSTCBCur->OSTCBStatPend = OS_STAT_PEND_OK;
OSTCBCur->OSTCBDly = timeout;//装载时间到TCB任务控制块
OS_EventTaskWait(pevent);// 挂起任务,直到事件到来或时间超时
OS_EXIT_CRITICAL();//退出临界区(可以被中断打断)
OS_Sched();//有更高优先级在准备运行,执行任务调度
OS_ENTER_CRITICAL();//进入临界区(无法被中断打断),需要定义cpu_sr变量
switch (OSTCBCur->OSTCBStatPend)
{
case OS_STAT_PEND_OK://任务挂起OK,不再挂起或挂起完成
pmsg = OSTCBCur->OSTCBMsg;//保存列读取消息的指针,读到消息
*perr = OS_ERR_NONE;
break;
case OS_STAT_PEND_ABORT://任务等待失败
pmsg = (void *)0;
*perr = OS_ERR_PEND_ABORT;
break;
case OS_STAT_PEND_TO://任务挂起导致时间超时溢出
default:
OS_EventTaskRemove(OSTCBCur, pevent);
pmsg = (void *)0;
*perr = OS_ERR_TIMEOUT;
break;
}
OSTCBCur->OSTCBStat = OS_STAT_RDY; //设置任务进入准备状态
OSTCBCur->OSTCBStatPend = OS_STAT_PEND_OK; //清除挂起状态
OSTCBCur->OSTCBEventPtr = (OS_EVENT *)0;//清除当前的事件指针
#if (OS_EVENT_MULTI_EN > 0u)
OSTCBCur->OSTCBEventMultiPtr = (OS_EVENT **)0;
#endif
OSTCBCur->OSTCBMsg = (void *)0; //清除接收消息
OS_EXIT_CRITICAL();//退出临界区(可以被中断打断)
return (pmsg);
}
4)、INT8U OSQPostFront (OS_EVENT *pevent, void *pmsg)
函数功能:按照后入先出方式,添加一条消息到“消息队列容器”;若消息队列容器为空,则不入列,直接将消息发送给”等待消息队列的任务”;
// pevent为消息队列指针
// pmsg为准备入列的消息指针
//返回值为OS_ERR_NONE,表示“入列正确”
//返回值为OS_ERR_Q_FULL,表示“入列因消息队列容器已满而失败”
INT8U OSQPostFront (OS_EVENT *pevent,
void *pmsg)
{
OS_Q *pq;
#if OS_CRITICAL_METHOD == 3u
OS_CPU_SR cpu_sr = 0u;
#endif
#if OS_ARG_CHK_EN > 0u
if (pevent == (OS_EVENT *)0)
{
return (OS_ERR_PEVENT_NULL);
}
#endif
if (pevent->OSEventType != OS_EVENT_TYPE_Q)
{
return (OS_ERR_EVENT_TYPE);
}
OS_ENTER_CRITICAL();//进入临界区(无法被中断打断),需要定义cpu_sr变量
if (pevent->OSEventGrp != 0u)
{//发现因消息队列而挂起的任务,或者有更高优先级任务在等待消息队列
//说明当前的消息队列是空的,非空的消息队列不会引起任务挂起
(void)OS_EventTaskRdy(pevent, pmsg, OS_STAT_Q, OS_STAT_PEND_OK);
//消息不用”入列”,直接将pmsg所指向的数据块地址发送给等待任务
OS_EXIT_CRITICAL();//退出临界区(可以被中断打断)
OS_Sched();//有更高优先级在准备运行,执行任务调度
return (OS_ERR_NONE);
}
pq = (OS_Q *)pevent->OSEventPtr; //读取消息队列控制块指针
if (pq->OSQEntries >= pq->OSQSize)
{//”消息队列计数器”超过”消息队列容器的最大值”, 消息队列容器满了
OS_EXIT_CRITICAL();//退出临界区(可以被中断打断)
return (OS_ERR_Q_FULL); //不添加消息,返回OS_ERR_Q_FULL
}
//消息队列容器没有满//
//按照后入先出方式添加消息/
if (pq->OSQOut == pq->OSQStart)
{//若”出列指针”等于”消息队列容器的起始指针”
pq->OSQOut = pq->OSQEnd;
//将”出列指针”设置为”消息队列容器的结束指针”
}
pq->OSQOut--;//”出列指针”减1
*pq->OSQOut = pmsg; //将消息添加到消息队列容器
pq->OSQEntries++; //消息队列计数器加1
OS_EXIT_CRITICAL();//退出临界区(可以被中断打断)
return (OS_ERR_NONE);
}
5)、void *OSQAccept (OS_EVENT *pevent, INT8U *perr)
函数功能:若有消息,则将消息队列的数据块首地址返回,令perr =OS_ERR_NONE;若无消息,则令perr = OS_ERR_Q_EMPTY,且返回值为0
// pevent为消息队列指针
// perr =OS_ERR_NONE,且返回值不为0,表示读到消息
void *OSQAccept (OS_EVENT *pevent,
INT8U *perr)
{
void *pmsg;
OS_Q *pq;
#if OS_CRITICAL_METHOD == 3u
OS_CPU_SR cpu_sr = 0u;
#endif
#ifdef OS_SAFETY_CRITICAL
if (perr == (INT8U *)0)
{ //perr指针为0
OS_SAFETY_CRITICAL_EXCEPTION();
}
#endif
#if OS_ARG_CHK_EN > 0u
if (pevent == (OS_EVENT *)0)
{//pevent指针为0
*perr = OS_ERR_PEVENT_NULL;
return ((void *)0);
}
#endif
if (pevent->OSEventType != OS_EVENT_TYPE_Q)
{//事件类型不是消息队列类型
*perr = OS_ERR_EVENT_TYPE;
return ((void *)0);
}
OS_ENTER_CRITICAL();//进入临界区(无法被中断打断),需要定义cpu_sr变量
pq = (OS_Q *)pevent->OSEventPtr; //读取消息队列控制块指针
if (pq->OSQEntries > 0u)
{//“消息队列计数器”大于0,说明消息队列中有消息
pmsg = *pq->OSQOut++;//将”出列指针”保存到pmsg,然后将”出列指针”加1
pq->OSQEntries--;//“消息队列计数器”减1
if (pq->OSQOut == pq->OSQEnd)
{//若”出列指针”到达”消息队列容器的结束指针”
pq->OSQOut = pq->OSQStart;
//将出列指针设置为”消息队列容器的起始指针”
}
*perr = OS_ERR_NONE;
}
else
{//“消息队列计数器”大于0,说明消息队列中没有消息
*perr = OS_ERR_Q_EMPTY;
pmsg = (void *)0;
}
OS_EXIT_CRITICAL();//退出临界区(可以被中断打断)
return (pmsg);
}
6)、INT8U OSQFlush (OS_EVENT *pevent)
//函数功能:清除”消息对列”中的所有消息,返回值为OS_ERR_NONE,表示操作正确
// pevent为消息队列指针
INT8U OSQFlush (OS_EVENT *pevent)
{
OS_Q *pq;
#if OS_CRITICAL_METHOD == 3u
OS_CPU_SR cpu_sr = 0u;
#endif
#if OS_ARG_CHK_EN > 0u
if (pevent == (OS_EVENT *)0)
{//pevent指针为0
return (OS_ERR_PEVENT_NULL);
}
if (pevent->OSEventType != OS_EVENT_TYPE_Q)
{//事件类型为消息队列类型
return (OS_ERR_EVENT_TYPE);
}
#endif
OS_ENTER_CRITICAL();//进入临界区(无法被中断打断),需要定义cpu_sr变量
pq= (OS_Q *)pevent->OSEventPtr; //读取消息队列控制块指针
pq->OSQIn = pq->OSQStart; //设置”入列指针”为”消息队列容器的起始指针”
pq->OSQOut = pq->OSQStart; //设置”出列指针””消息队列容器的起始指针”
pq->OSQEntries = 0u; //设置”消息队列计数器”为0
OS_EXIT_CRITICAL();
return (OS_ERR_NONE);
}
7)、INT8U OSQPostOpt (OS_EVENT *pevent, void *pmsg, INT8U opt)
函数功能:若消息队列容器已满,则直接将消息发给因消息队列而等待的任务,否则,将消息按照指定的方式入列
若opt = OS_POST_OPT_BROADCAST=0x01,群发消息,不用入列,直接把当前的消息发给因消息队列而等待的任务
若“消息队列容器满了”,就不用添加消息入列,返回值为OS_ERR_Q_FULL
若“消息队列容器没有满”且opt=OS_POST_OPT_FRONT,按照后入先出添加消息到队列中
若“消息队列容器没有满”且opt!=OS_POST_OPT_FRONT,按照先入先出添加消息到队列中
//返回值为OS_ERR_Q_FULL,表示“入列因消息队列容器已满而失败”
//返回值为OS_ERR_NONE,表示入列成功
INT8U OSQPostOpt (OS_EVENT *pevent,
void *pmsg,
INT8U opt)
{
OS_Q *pq;
#if OS_CRITICAL_METHOD == 3u
OS_CPU_SR cpu_sr = 0u;
#endif
#if OS_ARG_CHK_EN > 0u
if (pevent == (OS_EVENT *)0)
{//pevent指针为0,不允许
return (OS_ERR_PEVENT_NULL);
}
#endif
if (pevent->OSEventType != OS_EVENT_TYPE_Q)
{//事件类型不是消息队列类型
return (OS_ERR_EVENT_TYPE);
}
OS_ENTER_CRITICAL();//进入临界区(无法被中断打断),需要定义cpu_sr变量
if (pevent->OSEventGrp != 0x00u)
{//发现因消息队列而挂起的任务,或者有更高优先级任务在等待消息队列
//说明当前的消息队列是空的,非空的消息队列不会引起任务挂起
if ((opt & OS_POST_OPT_BROADCAST) != 0x00u)
{//需要群发消息
while (pevent->OSEventGrp != 0u)
{//发现因消息队列而挂起的任务
(void)OS_EventTaskRdy(pevent, pmsg, OS_STAT_Q, OS_STAT_PEND_OK);
//消息不用”入列”,直接将pmsg所指向的数据块地址发送给等待任务
}
}
else//不需要群发消息
{
(void)OS_EventTaskRdy(pevent, pmsg, OS_STAT_Q, OS_STAT_PEND_OK);
//消息不用”入列”,直接将pmsg所指向的数据块地址发送给等待任务
}
OS_EXIT_CRITICAL();//退出临界区(可以被中断打断)
if ((opt & OS_POST_OPT_NO_SCHED) == 0u)
{
OS_Sched();//发现有更高优先级的任务在企图运行,执行任务调度
}
return (OS_ERR_NONE);
}
///没有发现因消息队列而挂起的任务///
pq = (OS_Q *)pevent->OSEventPtr; //读取消息队列控制块指针
if (pq->OSQEntries >= pq->OSQSize)
{//”消息队列计数器”超过”消息队列容器的最大值”, 消息队列容器满了
OS_EXIT_CRITICAL();//退出临界区(可以被中断打断)
return (OS_ERR_Q_FULL); //不添加消息,返回OS_ERR_Q_FULL
}
//消息队列容器没有满//
if ((opt & OS_POST_OPT_FRONT) != 0x00u)
{//按照后入先出方式添加消息
if (pq->OSQOut == pq->OSQStart)
{//若”出列指针”等于”消息队列容器的起始指针”
pq->OSQOut = pq->OSQEnd;
//将”出列指针”设置为”消息队列容器的结束指针”
}
pq->OSQOut--;//”出列指针”减1
*pq->OSQOut = pmsg; //将消息添加到消息队列容器
}
else
{//按照FIFO先入先出方式添加消息
*pq->OSQIn++ = pmsg; //将消息添加到消息队列容器,”入列指针”加1
if (pq->OSQIn == pq->OSQEnd)
{//若”入列指针”到达”消息队列容器的结束指针”
pq->OSQIn = pq->OSQStart;
//将”入列指针”设置为 ”消息队列容器的起始指针”
}
}
pq->OSQEntries++;//消息队列计数器加1
OS_EXIT_CRITICAL();//退出临界区(可以被中断打断)
return (OS_ERR_NONE);
}
6、FIFO程序举例
#include "FIFO_SendData_Task.h"
#include "delay.h"
#include "stdio.h" //getchar(),putchar(),scanf(),printf(),puts(),gets(),sprintf()
#include "key.h"
#include "My_Task_Priority.h"
//#include "ucos_ii.h"
const char FIFO_SendData_Task_rn_REG[]="\r\n";
const char FIFO_SendData_Task_REG[]="FIFO_SendData_Task:";
const char Queue_is_FULL_REG[]="Queue is FULL";
void FIFO_SendData_Task(void *pdata);
char SendMessageBuffer[10];//每条消息为一个字节
u8 NumberOfMessage;//存放发送消息数量
//FIFO_SendData_Task任务
void FIFO_SendData_Task(void *pdata)
{
u8 cnt,i;
u8 err;
strcpy(SendMessageBuffer,"123456789");
NumberOfMessage=strlen(SendMessageBuffer);//求"发送消息数量"
cnt=0;
while(1)
{
OSTimeDlyHMSM(0,0,0,1000);//延时1000ms
cnt++;
if(cnt>2)//时间到,则发送消息
{
for(i=0;i<NumberOfMessage;)//数据按照先进先出方式入列
{
printf("%s",FIFO_SendData_Task_rn_REG);
printf("%s",FIFO_SendData_Task_REG);
printf("%c",SendMessageBuffer[i]);//打印发送消息
err=OSQPost(MessageQueuePointer,&SendMessageBuffer[i]);
//按照先进先出方式,添加一条消息到“消息队列容器”;若消息队列容器为空,则不入列,直接将消息发送给”等待消息队列的任务”;
//返回值为OS_ERR_NONE,表示“入列正确”
if(err==OS_ERR_NONE)//入列正确
{
i++;
}
if(err==OS_ERR_Q_FULL)//消息队列已经满了
{
printf("%s",FIFO_SendData_Task_rn_REG);
printf("%s",Queue_is_FULL_REG);
}
}
cnt=0;
}
}
}
#include "FIFO_ReceiveData_Task.h"
#include "stdio.h" //getchar(),putchar(),scanf(),printf(),puts(),gets(),sprintf()
#include "LED.h"
#include "My_Task_Priority.h"
#include "FIFO_SendData_Task.h"
void FIFO_ReceiveData_Task(void *pdata);
const char FIFO_ReceiveData_Task_rn_REG[]="\r\n";
const char FIFO_ReceiveData_Task_REG[]="FIFO_ReceiveData_Task:";
//FIFO_ReceiveData_Task任务
void FIFO_ReceiveData_Task(void *pdata)
{
u8 err;
char *p;
u32 timeout;
while(1)
{
timeout=0;
p=OSQPend(MessageQueuePointer,timeout,&err);
//若有消息,则将消息队列的数据块首地址返回;若无消息,则挂起任务等待消息到来;
//err =OS_ERR_NONE,且返回值不为0,表示读到消息
//timeout=0任务会一直挂起,直到收到新消息
//timeout!=0任务在timeout个节拍内,若收到新消息,则退出,否则,超时退出消息消息接收
if(err==OS_ERR_NONE && p!=NULL)//读到消息
{
printf("%s",FIFO_ReceiveData_Task_rn_REG);
printf("%s",FIFO_ReceiveData_Task_REG);
printf("%c",*p);//打印接收到的消息
}
// OSTimeDlyHMSM(0,0,0,1);//延时1000ms
}
}
#include "Start_Task.h"
#include "stdio.h" //getchar(),putchar(),scanf(),printf(),puts(),gets(),sprintf()
#include "LED.h"
#include "My_Task_Priority.h"
#include "FIFO_ReceiveData_Task.h"
#include "FIFO_SendData_Task.h"
void Start_Task(void *pdata);
const char Start_Task_rn_REG[]="\r\n";
const char Start_Task_Initialise_REG[]="Start_Task Initialise";
//Start_Task任务
void Start_Task(void *pdata)
{
OS_CPU_SR cpu_sr=0;
pdata = pdata;
printf("%s",Start_Task_rn_REG);
printf("%s",Start_Task_Initialise_REG);
OS_ENTER_CRITICAL(); //进入临界区(无法被中断打断),需要定义cpu_sr变量
MessageQueuePointer=OSQCreate(&MessageContainerArray[0],MessageContainerArray_Size);
//建立一个消息队列,其指针为MessageQueuePointer
//MessageContainerArray[0],"消息容器数组"的首地址
//MessageContainerArray_Size,消息容器的大小
OSTaskCreate( FIFO_ReceiveData_Task,/* 函数指针*/
(void *)0,/* 建立任务时,传递的参数*/
(OS_STK*)&FIFO_ReceiveData_TASK_STACK[FIFO_ReceiveData_TASK_STACK_SIZE-1],/* 指向堆栈任务栈顶的指针*/
FIFO_ReceiveData_TASK_PRIORITY/* 任务优先级*/
);
OSTaskCreate( FIFO_SendData_Task,/* 函数指针*/
(void *)0,/* 建立任务时,传递的参数*/
(OS_STK*)&FIFO_SendData_TASK_STACK[FIFO_SendData_TASK_STACK_SIZE-1],/* 指向堆栈任务栈顶的指针*/
FIFO_SendData_TASK_PRIORITY/* 任务优先级*/
);
//OSTaskSuspend(START_TASK_PRIO); //挂起起始任务Start_Task(),但不删除
OSTaskDel(OS_PRIO_SELF); //删除自己
OS_EXIT_CRITICAL(); //退出临界区(可以被中断打断)
}
7、LIFO程序举例
#include "LIFO_SendData_Task.h"
#include "delay.h"
#include "stdio.h" //getchar(),putchar(),scanf(),printf(),puts(),gets(),sprintf()
#include "key.h"
#include "My_Task_Priority.h"
//#include "ucos_ii.h"
const char LIFO_SendData_Task_rn_REG[]="\r\n";
const char LIFO_SendData_Task_REG[]="LIFO_SendData_Task:";
const char Queue_is_FULL_REG[]="Queue is FULL";
void LIFO_SendData_Task(void *pdata);
char SendMessageBuffer[10];//每条消息为一个字节
u8 NumberOfMessage;//存放发送消息数量
//LIFO_SendData_Task任务
void LIFO_SendData_Task(void *pdata)
{
u8 cnt,i;
u8 err;
strcpy(SendMessageBuffer,"123456789");
NumberOfMessage=strlen(SendMessageBuffer);
cnt=0;
while(1)
{
OSTimeDlyHMSM(0,0,0,1000);//延时1000ms
if(MessageFlag==0)//消息没有发送完成
{
cnt++;
if(cnt>2)
{
for(i=0;i<NumberOfMessage;)//数据按照先进先出方式入列
{
printf("%s",LIFO_SendData_Task_rn_REG);
printf("%s",LIFO_SendData_Task_REG);
printf("%c",SendMessageBuffer[i]);//打印发送消息
err=OSQPostFront(MessageQueuePointer,(void*)&SendMessageBuffer[i]);
//按照后入先出方式,添加一条消息到“消息队列容器”;若消息队列容器为空,则不入列,直接将消息发送给”等待消息队列的任务”;
//返回值为OS_ERR_NONE,表示“入列正确”
if(err==OS_ERR_NONE)//入列正确
{
i++;
}
}
MessageFlag=1;//建立"消息发送完成"标志
cnt=0;
}
}
}
}
#include "LIFO_ReceiveData_Task.h"
#include "stdio.h" //getchar(),putchar(),scanf(),printf(),puts(),gets(),sprintf()
#include "My_Task_Priority.h"
void LIFO_ReceiveData_Task(void *pdata);
const char LIFO_ReceiveData_Task_rn_REG[]="\r\n";
const char LIFO_ReceiveData_Task_REG[]="LIFO_ReceiveData_Task:";
u8 Get_NumberOfMessage(OS_EVENT *pevent)
{
u8 x;
OS_Q *pq;
#if OS_CRITICAL_METHOD == 3u
OS_CPU_SR cpu_sr = 0u;
#endif
OS_ENTER_CRITICAL();
pq = (OS_Q *)pevent->OSEventPtr; /* Point to queue storage structure */
x=pq->OSQEntries;
OS_EXIT_CRITICAL();
return (x);
}
//LIFO_ReceiveData_Task任务
//由于使用OSQPend(),所以该任务不能使用OSTimeDlyHMSM()
void LIFO_ReceiveData_Task(void *pdata)
{
u8 err;
void *p;
while(1)
{
if(MessageFlag)//消息发送完成,允许接收
{
p=OSQAccept(MessageQueuePointer,&err);
//若有消息,则将消息队列的数据块首地址返回,令perr =OS_ERR_NONE;若无消息,则令err = OS_ERR_Q_EMPTY,且返回值为0
//pevent为消息队列指针
//perr =OS_ERR_NONE,且返回值不为0,表示读到消息
if(err==OS_ERR_NONE && p!=NULL)//读到消息
{
printf("%s",LIFO_ReceiveData_Task_rn_REG);
printf("%s",LIFO_ReceiveData_Task_REG);
printf("%c",*(char*)(p) );
}
if(err==OS_ERR_Q_EMPTY) MessageFlag=0;//清除"消息发送完成"标志
}
else OSTimeDlyHMSM(0,0,0,1000);//延时1000ms
}
}
#include "Start_Task.h"
#include "stdio.h" //getchar(),putchar(),scanf(),printf(),puts(),gets(),sprintf()
#include "LED.h"
#include "My_Task_Priority.h"
#include "LIFO_ReceiveData_Task.h"
#include "LIFO_SendData_Task.h"
void Start_Task(void *pdata);
const char Start_Task_rn_REG[]="\r\n";
const char Start_Task_Initialise_REG[]="Start_Task Initialise";
//Start_Task任务
void Start_Task(void *pdata)
{
OS_CPU_SR cpu_sr=0;
pdata = pdata;
printf("%s",Start_Task_rn_REG);
printf("%s",Start_Task_Initialise_REG);
OS_ENTER_CRITICAL(); //进入临界区(无法被中断打断),需要定义cpu_sr变量
MessageQueuePointer=OSQCreate(&MessageContainerArray[0],MessageContainerArray_Size);
//建立一个消息队列,其指针为MessageQueuePointer
//MessageContainerArray[0],"消息容器数组"的首地址
//MessageContainerArray_Size,消息容器的大小
MessageFlag=0;
OSTaskCreate( LIFO_ReceiveData_Task,/* 函数指针*/
(void *)0,/* 建立任务时,传递的参数*/
(OS_STK*)&LIFO_ReceiveData_TASK_STACK[LIFO_ReceiveData_TASK_STACK_SIZE-1],/* 指向堆栈任务栈顶的指针*/
LIFO_ReceiveData_TASK_PRIORITY/* 任务优先级*/
);
OSTaskCreate( LIFO_SendData_Task,/* 函数指针*/
(void *)0,/* 建立任务时,传递的参数*/
(OS_STK*)&LIFO_SendData_TASK_STACK[LIFO_SendData_TASK_STACK_SIZE-1],/* 指向堆栈任务栈顶的指针*/
LIFO_SendData_TASK_PRIORITY/* 任务优先级*/
);
//OSTaskSuspend(START_TASK_PRIO); //挂起起始任务Start_Task(),但不删除
OSTaskDel(OS_PRIO_SELF); //删除自己
OS_EXIT_CRITICAL(); //退出临界区(可以被中断打断)
}
8、任务优先级及相关变量
#include "My_Task_Priority.h"
OS_STK START_TASK_STACK[START_TASK_STACK_SIZE]; //START_TASK任务堆栈
OS_STK LIFO_ReceiveData_TASK_STACK[LIFO_ReceiveData_TASK_STACK_SIZE]; //LIFO_ReceiveData_TASK任务堆栈
__align(8) OS_STK LIFO_SendData_TASK_STACK[LIFO_SendData_TASK_STACK_SIZE];
//LIFO_SendData_TASK任务堆栈
//如果任务中使用printf来打印浮点数据的话一点要8字节对齐
OS_EVENT *MessageQueuePointer;//定义一个"消息队列事件指针"
void *MessageContainerArray[MessageContainerArray_Size];//"消息容器数组"用来存放"消息指针",和"栈"差不多
u8 MessageFlag;//"消息发送完成"标志
//My_Task_Priority.h
#ifndef __MY_TASK_PRIORITY_H
#define __MY_TASK_PRIORITY_H
#include "includes.h"
//任务的优先级资源由操作系统提供,以μC/OS-II为例,共有64个优先级,优先级的高低按编号从0(最高)到63(最低)排序;
/*
为了保证“启动任务”能够连续运行,必须将“启动任务”的优先级选择为最高。
否则,当“启动任务”创建一个优先级高于自己的任务时,刚刚创建的任务就
会立即进入运行状态,而与这个任务关联的其它任务可能还没有创建,它使
用的通信工具也还没有创建,系统必然出错。
*/
#define START_TASK_PRIORITY 4 //设置START_TASK任务优先级,开始任务的优先级设置为最高
#define START_TASK_STACK_SIZE 192 //设置START_TASK任务堆栈大小,为8的倍数
extern OS_STK START_TASK_STACK[START_TASK_STACK_SIZE]; //START_TASK任务堆栈
#define LIFO_ReceiveData_TASK_PRIORITY 5 //设置LIFO_ReceiveData_TASK任务优先级为5
#define LIFO_ReceiveData_TASK_STACK_SIZE 56 //设置LIFO_ReceiveData_TASK任务堆栈大小为56,为8的倍数
extern OS_STK LIFO_ReceiveData_TASK_STACK[LIFO_ReceiveData_TASK_STACK_SIZE]; //LIFO_ReceiveData_TASK任务堆栈
#define LIFO_SendData_TASK_PRIORITY 6 //设置LIFO_SendData_TASK任务优先级为7
#define LIFO_SendData_TASK_STACK_SIZE 56 //设置LIFO_SendData_TASK任务堆栈大小为56,为8的倍数
extern OS_STK LIFO_SendData_TASK_STACK[LIFO_SendData_TASK_STACK_SIZE]; //LIFO_SendData_TASK任务堆栈
extern OS_EVENT *MessageQueuePointer;//定义一个消息队列指针
#define MessageContainerArray_Size 12 //消息容器的大小
extern void *MessageContainerArray[MessageContainerArray_Size];//"消息容器数组"用来存放"消息指针"
extern u8 MessageFlag;//"消息发送完成"标志
#endif
9、测试结果