文章目录
- 一.事件控制块及事件处理函数
- 1.等待任务列表
- 2.事件控制块的结构
- 3.操作事件控制块的函数
- 4.空事件控制块列表
- 二.消息邮箱
-
- 三.消息队列
- 1.消息指针数组
- 2.队列控制块
- 3.消息队列的操作流程
- 四.总结
消息邮箱是特殊的消息队列,是大小为1的消息队列!!!
一.事件控制块及事件处理函数
1.等待任务列表
对于事件来说,当其被占用时,会导致其他请求该事件的任务因暂时的得不到该事件的服务而处于等待状态。作为功能完善的事件,应该对等待任务具有两方面的管理功能:
一是要对等待事件的所有任务进行记录并排序;
二是应该允许等待任务有一个等待时限。
UCOSII定义了一个INT8U类型的数组OSEventTb1[]作为等待事件的任务的记录表,即等待任务表。位为1则表示为等待任务,0则不是。
为了加快对该表的访问速度,也定义了一个INT8U类型的变量OSEventGrp来表示等待任务表中的等待组。OSEventTb1[]与变量OSEventGrp示意图如下:
2.事件控制块的结构
为了把事件统一起来,UCOSII用事件控制块ECB的数据结构来描述信号量、消息邮箱、和消息队列等事件。结构如下:
#if (OS_EVENT_EN) && (OS_MAX_EVENTS > 0u)
typedef struct os_event {
INT8U OSEventType;
INT16U OSEventCnt;
void *OSEventPtr;
OS_PRIO OSEventGrp;
OS_PRIO OSEventTbl[OS_EVENT_TBL_SIZE];
} OS_EVENT;
3.操作事件控制块的函数
1)事件控制块初始化函数
void OS_EventWaitListInit(
OS_EVENT *pevent
)
2)使一个任务进入等待状态的函数
void OS_EventTaskWait(
OS_EVENT *pevent
)
3)使一个正在等待的任务进入就绪状态的函数
INT8U OS_EventTaskRdy(
OS_EVENT *pevent
void *msg,
INT8U msk
)
4)使一个等待超时的任务进入就绪状态的函数
void OS_EventTO(
OS_EVENT *pevent
)
4.空事件控制块列表
UCOSII初始化时,系统会在初始化函数OSInit()中按照程序使用事件的总数OS_MAX_EVENTS(在文件OS_CFG.H中定义)创建OS_MAX_EVENTS个空事件控制块并借用成员OSEventPtr作为链接指针。由于链表中所有控制块尚未与具体事件想关联,因此叫空事件控制块链表。以后,每当应用程序创建一个事件时,系统会从链表中取出一个空事件控制块,并对它进行初始化以描述该事件。当删除一个事件时,系统会将该事件的控制块归还给空事件控制块链表。
二.消息邮箱
1.消息邮箱介绍
如果要在任务与与任务之间传递一个数据,那么为了适应不同数据的需要最好在存储器中建立一个数据缓存区,然后以这个缓存区为中介来实现任务间的数据传递。
把数据缓存区指针赋给控制块的成员OSEventPtr,同时使事件控制块成员OSEventType为常数OS_EVENT_TYPE_MBOX,则事件控制块就叫做消息邮箱。消息邮箱通过在两个需要通信的任务之间传递数据缓冲区指针进行通信。消息邮箱数据结构如下图:
2.消息邮箱操作步骤
1.定义消息邮箱
OS_EVENT * msg_key;
2.创建消息邮箱
msg_key=OSMboxCreate((void*)0);
3请求消息邮箱
void main_task(void *pdata)
{
u32 key=0;
u8 err;
u8 semmask=0;
u8 tcnt=0;
while(1)
{
key=(u32)OSMboxPend(msg_key,10,&err);
switch(key)
{
case 1://控制DS1
LED1=!LED1;
break;
case 2://发送信号量
semmask=1;
OSSemPost(sem_beep);
OSMutexPost(sem_mutex);
break;
case 3://清除
LCD_Fill(0,121,lcddev.width,lcddev.height,WHITE);
break;
case 4://校准
OSTaskSuspend(TOUCH_TASK_PRIO);
if((tp_dev.touchtype&0X80)==0)TP_Adjust();
OSTaskResume(TOUCH_TASK_PRIO);
ucos_load_main_ui();
break;
}
if(semmask||sem_beep->OSEventCnt)
{
LCD_ShowxNum(192,50,sem_beep->OSEventCnt,3,16,0X80);
if(sem_beep->OSEventCnt==0)semmask=0;
}
if(tcnt==50)
{
tcnt=0;
POINT_COLOR=BLUE;
LCD_ShowxNum(192,30,OSCPUUsage,3,16,0);
}
tcnt++;
delay_ms(10);
}
}
4.发生消息邮箱
void key_task(void *pdata)
{
u8 key;
while(1)
{
key=KEY_Scan(0);
if(key)OSMboxPost(msg_key,(void*)key);
delay_ms(10);
}
}
5.查询邮箱的状态
INT8U OSMboxQuery(
OS_EVENT *pevent,
SO_MBOX_DATA *pdata
)
SO_MBOX_DATA 结构如下:
typedef struct
{
void *OSMsg;
INT8U OSEventTb1[OS_EVENT_TBL_SIZE];
INTU8 OSEventGrp;
}SO_MBOX_DATA
6.删除邮箱
OS_EVENT *OSboxDel(
OS_EVENT *pevent,
INT8U opt,
INT8U *err
)
三.消息队列
使用消息队列可以在任务之间传递多条消息,消息队列由三部分组成:事件控制块、消息队列、消息。
当事件控制块成员OSEventType值为OS_EVENT_TYPE_Q时,该事件控制块代表一个消息队列。
消息队列相当于一个共用一个任务等待列表的消息邮箱数组,事件控制块成员OSEventPtr指向一个叫做队列控制块(OS_Q)的结构,该结构管理着一个数组MsgTb1[],该数组中的元素都是指向消息的指针。
1.消息指针数组
消息队列的核心部件为消息指针数组。
可以采用2种方式向指针数组插入消息:先进先出的FIFO方式和后进先出的LIFO方式。当采用FIFO方式时,消息队列将在指针OSQIn指向的位置插入消息指针,而OSQOut指向的消息指针为输出;当采用LIFO方式时,则只使用指针OSQOut。
2.队列控制块
UCOSII把消息指针数组的基本参数都记录在一个叫做队列控制块的结构中。队列控制块的结构如下:
#if OS_Q_EN > 0u
typedef struct os_q {
struct os_q *OSQPtr;
void **OSQStart;
void **OSQEnd;
void **OSQIn;
void **OSQOut;
INT16U OSQSize;
INT16U OSQEntries;
} OS_Q;
在UCOSII系统初始化时,系统将按文件OS_CFG.H中配置OS_MAX_QS个队列控制块,并用队列控制块中的指针OSQPtr将所有队列控制块链接为链表。由于这时还没有使用它们,因此这个链表叫做空队列控制块列表。
3.消息队列的操作流程
1)定义消息队列
OS_EVENT * q_msg;
2)创建消息队列
q_msg=OSQCreate(&MsgGrp[0],256);
3)请求消息队列
void qmsgshow_task(void *pdata)
{
u8 *p;
u8 err;
while(1)
{
p=OSQPend(q_msg,0,&err);
LCD_ShowString(5,170,240,16,16,p);
myfree(SRAMIN,p);
delay_ms(500);
}
}
4)向消息队列发生消息
void tmr3_callback(OS_TMR *ptmr,void *p_arg)
{
u8* p;
u8 err;
static u8 msg_cnt=0;
p=mymalloc(SRAMIN,13);
if(p)
{
sprintf((char*)p,"ALIENTEK %03d",msg_cnt);
msg_cnt++;
err=OSQPost(q_msg,p);
if(err!=OS_ERR_NONE)
{
myfree(SRAMIN,p);
OSTmrStop(tmr3,OS_TMR_OPT_NONE,0,&err);
}
}
}
5)清空消息队列
INT8U OSQFlush(
OS_EVENT *pevent
)
6)删除消息队列
INT8U OSQDel(
OS_EVENT *pevent
)
7)查询消息队列
INT8U OSQQuery(
OS_EVENT *pevent
OS_Q_DATA*pdata
)
函数中的参数pdata是OS_Q_DATApdata类型的指针。OS_Q_DATApdata的机构如下:
typedef struct struct{
void *OSMsg;
INT16U OSNMsgs;
INT16U OSQSize;
OS_PRIO OSEventTbl[OS_EVENT_TBL_SIZE];
OS_PRIO OSEventGrp;
} OS_Q_DATA;
四.总结
1.消息邮箱是特殊的消息队列,是大小为1的消息队列。
2.消息邮箱是能够在任务之间传递消息指针的数据结构。
3.消息队列是能够在任务之间传递一组消息指针的数据结构。
4.信号量、消息邮箱、消息队列都叫做“事件”,每个事件都有一个用来记录等待事件的任务的表----等待任务表,而任务的等待时限则记录在OSTCBDly中。
5.UCOSII统一用事件控制块来描述各种事件。
6.操作系统中各任务之间的同步与通信是通过各种各样的事件来完成的。
7.重要总结:消息队列的应用有点类似于信号量。
函数需要通过访问事件控制块的成员OSEventPtr指向的队列控制块OS_Q的成员OSQEntries来判断是否有消息可用(有点类似于信号量计数值cnt)。如果有消息可用,则返回OS_Q成员OSQOut指向的消息,同时调整指针OSQOut,使之指向下一条消息并把有效消息数的变量OSQEntries减1,如果无消息可用(也就是OSQEntries=0),则使用调用函数OSQPend()的任务挂起,使之处于等待状态并引发一次任务调度。同样,向队列发送消息后OSQEntries+1.如果希望任务无等待的请求一个消息队列,则可调用函数OSQAccept();函数原型为:void OSQAccept(OS_EVENT *pevent);
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)