在OSAL中每个任务都有一个任务初始化函数和任务的事件处理回调函数,而每一层中都是一个任务在处理,而每一层中也都对应着一个事件的处理函数,具体这个OSAL中支持多少个任务?好像程序中也没有定义。在uC/OS中支持64个任务。例如SampleApp_Init(taskID) SampleApp_ProcessEvent(byte task_id,UNINT16 events),在Z-Stack1.4.3-1.2.1中可以通过向
const pTaskEventHandlerFn tasksArr[] = {
macEventLoop,
nwk_event_loop,
Hal_ProcessEvent,
#if defined( MT_TASK )
MT_ProcessEvent,
#endif
APS_event_loop,
ZDApp_event_loop,
SampleApp_ProcessEvent
};
数组中添加一个任务的事件处理函数,和在void osalInitTasks( void )函数中添加一个SampleApp_Init( taskID );初始化函数来添加一个任务,这在前面的文章中也有具体讲到,我们知道在一个任务中可以有16个事件,其中SYS_EVENT_MSG是系统的事件,是协议栈中定义好的,也是一个强制事件,在它的下面还有子集,也就是我们消息,那么一个事件可以有256个消息,从0x00—0xFF,我们在Sample_App中看到的,KEY_CHANGE应该是属于SYS_EVENT_MSG事件下的消息,其实这样说不是很准确,它只是包括在传递的消息中,表现的形式还是一个事件,但是它不属于一个任务中16个事件之一,本质上还是SYS_EVENT_MSG下的子事件。在ZcomDef.h文件中这样定义了KEY_CHANGE。
#define KEY_CHANGE 0xC0 // Key Events
在osal.h文件中,定义了下面两个数据结构
typedef struct
{
void *next;
uint16 len;
byte dest_id;
} osal_msg_hdr_t;
typedef struct
{
uint8 event;
uint8 status;
} osal_event_hdr_t;
第一个是关于消息的数据结构,另一个是关于事件的数据结构。接收消息和发送消息主要通过下面两个函数实现。
函数osal_msg_send( byte destination_task, byte *msg_ptr )给task_id的任务发送消息,在这里消息放进了消息队列中,然后调用了 osal_set_event( destination_task, SYS_EVENT_MSG );通知系统有任务的事件没有处理,这里我们可以看到传递的事件是SYS_EVENT_MSG,那是不是意味着这个 osal_msg_send( byte destination_task, byte *msg_ptr )函数触发事件时,只能是SYS_EVENT_MSG类型的,也就是系统的,比如我们自己定义的SAMPLEAPP_SEND_PERIOCDIC_MSG_EVT事件,必须要用osal_start_timerEx()函数来触发,从止前看到有例子程序中,都是这种处理行为。
byte osal_msg_send( byte destination_task, byte *msg_ptr )
{
................................
OSAL_MSG_ID( msg_ptr ) = destination_task;
// queue message当一个新消息封装好了以后,就要加入消息队列 osal_qHead是消息队列的头指针
osal_msg_enqueue( &osal_qHead, msg_ptr );
// Signal the task that a message is waiting
osal_set_event( destination_task, SYS_EVENT_MSG );
return ( ZSUCCESS );
}
其中#define OSAL_MSG_ID(msg_ptr) ((osal_msg_hdr_t *) (msg_ptr) - 1)->dest_id
这里我们看到了有一个减一的操作,其中osal_msg_hdr_t 就是前面提到的那个消息的数据结构。其实,消息结构:消息头结构+消息数据结构(Key),在* osal_msg_allocate( uint16 len )函数中,我们可以看到,分配的内存空间实际上是消息头+数据其长度。但在hdr并没有指向消息头,而是了指向了消息数据。而(osal_msg_hdr_t *) (msg_ptr)这条语句的含义是将该指针强制转换为消息头结构的指针,这样的话,-1后指针将向上移动消息头结构大小的偏移量,也就是恰好指向消息头结构处,而且因为是强制为消息头结构指针,自然就找到消息头中的dest_id成员,并将destination_task赋值给它。
byte * osal_msg_allocate( uint16 len )
{
osal_msg_hdr_t *hdr;
if ( len == 0 )
return ( NULL );
hdr = (osal_msg_hdr_t *) osal_mem_alloc( (short)(len + sizeof( osal_msg_hdr_t )) );
if ( hdr )
{
hdr->next = NULL;
hdr->len = len;
hdr->dest_id = TASK_NO_TASK;
#if defined( OSAL_TOTAL_MEM )
osal_msg_cnt++;
#endif
return ( (byte *) (hdr + 1) );
}
else
return ( NULL );
}
这是消息的接收函数,
byte *osal_msg_receive( byte task_id )
{
osal_msg_hdr_t *listHdr;
osal_msg_hdr_t *prevHdr=0;
halIntState_t intState;
// Hold off interrupts
HAL_ENTER_CRITICAL_SECTION(intState);
// Point to the top of the queue
listHdr = osal_qHead;
// Look through the queue for a message that belongs to the asking task 以任务id号遍历消息队列,如果消息队列不为空的话
while ( listHdr != NULL )
{
if ( (listHdr - 1)->dest_id == task_id ) //这里的减一操作,还是和前面的分析相一致
{
break;
}
prevHdr = listHdr;
listHdr = OSAL_MSG_NEXT( listHdr );
}
// Did we find a message?
if ( listHdr == NULL )
{
// Release interrupts
HAL_EXIT_CRITICAL_SECTION(intState);
return NULL;
}
// Take out of the link list 把消息从消息队列中去除
osal_msg_extract( &osal_qHead, listHdr, prevHdr );
// Release interrupts
HAL_EXIT_CRITICAL_SECTION(intState);
return ( (byte*) listHdr ); //如果找到相对应的taskid的消息,返回其指针
}
在例子中有MSGpkt = (afIncomingMSGPacket_t *)osal_msg_receive( SampleApp_TaskID );这个函数是接收消息的数据,根据接收到的消息判断其中消息包括的SYS_EVENT_MSG子事件。
补充:
在这里有从网上找到了几个关于消息和事件的比喻,感觉还是挺准确,现在摘抄一下。
(1)消息与事件的联系。事件是驱动任务去执行某些操作的条件,当系统产生了一个事件,将这个传递给相应的任务后,任务才能执行一个相应的操作。但是某些事件在 它发生的同时,又伴随着一些附加信息的产生。任务的事件处理函数在处理这个事件的时候,还需要参考其附加信息。最典型的一类便是按键消息,它同时产生了一 个哪个按键被按下了附加信息。所以在OnBoard_SendKeys这个函数中,不仅向GenericApp发送了事件,还通过调用 osal_msg_send函数向GenericApp发送了一个消息,这个消息记录了这个事件的附加信息。在 GenericApp_ProcessEvent中,通过
{
MSGpkt = (afIncomingMSGPacket_t *)osal_msg_receive( GenericApp_TaskID );
}
获取了这样一个消息,然后再进一步处理。
OSAL在后台维护了一个消息队列,每一个消息都会被放到这个消息队列中去,当任务接收到事件以后,从消息队列中获取属于自己的消息,然后进行处理。
--------------------------------------------------来自于outman的深入浅出OSAL多任务的分配
(2)事件数组:消息操作完成后,还需要向系统发个通知,也就是说通知系统某某任务有消息来了,你去处理一下。事件数组和任务是一一对应的,就是说每个任务都对 应事件数组中的一个单元。对于Key来说,就是往事件数组中对应与Hal任务的单元中放入SYS_EVENT_MSG。
可以这样理解:一个小区100户,每户一个邮箱,那么小区就有100个任务,100个邮箱构成事件数组。消息是包裹,事件就是包裹通知单,包裹通知单送达 某户的邮箱,这户人家查看邮箱,发现有包裹通知单,就去领出包裹,领出包裹后拆开其实就是解析消息后再决定是吃掉它,用掉它,回覆它还是丢掉它等等。
消息的解析:正如上面包裹的例子一样,收到包裹后你一定要打开包裹看看是什么东西再决定下步的行动,反应到消息上,就是消息的解析,实际上是封装的反过程。l
还是以Key为例:Hal任务的事件处理函数是Hal_ProcessEvent(),在这个函数中收到SYS_EVENT_MSG后会调用osal_msg_receive(Hal_TaskID);这条语句来进行解析,以下不在叙述。
----------------------------------------------来自飞比论坛
阅读(1012) | 评论(0) | 转发(0) |