Chinaunix首页 | 论坛 | 博客
  • 博客访问: 43268
  • 博文数量: 59
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 10
  • 用 户 组: 普通用户
  • 注册时间: 2015-05-31 10:42
文章分类
文章存档

2015年(59)

我的朋友

分类: 嵌入式

2015-05-31 10:48:54

    在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);这条语句来进行解析,以下不在叙述。
----------------------------------------------来自飞比论坛
阅读(280) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~