找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 6372|回复: 0
收起左侧

z-stack协议栈-数据包接收处理流程

[复制链接]
ID:71477 发表于 2015-1-1 19:30 | 显示全部楼层 |阅读模式
有人对osal处理消息的机制及如果有数据包接收,协议栈到底是怎么来处理得到所需要的数据的有点不清楚,以下我简单的按照我的理解写一下。
其实只要触发SYS_EVENT_MSG事件,首先都会有这样一个语句:
MSGpkt = (afIncomingMSGPacket_t *)osal_msg_receive( SampleApp_TaskID );
让我们一步一步看下函数的意思,首先是osal_msg_receive()这个函数,来看一下原型:
@fn      osal_msg_receive
*
* @brief
*
*    This function is called by a task to retrieve a received command
*    message. The calling task must deallocate the message buffer after
*    processing the message using the osal_msg_deallocate() call.
*函数描述:
该函数通过一个任务调用来检索收到的命令信息。调用的任务必须在利用osal_msg_deallocate()函数处理信息之后必须释放消息占用的内存。
* @param   byte task_id - receiving tasks ID
*收到的任务ID
* @return  *byte - message information or NULL if no message
*消息信息或者空指针
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
在队列中查找属于该任务的消息。
  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 );
}
这个函数看来主要是提取属于该任务的消息,osal在后台维护一个消息队列,对应的任务可以在该消息队列中提取到属于自己的消息,从而判断是哪个时间需要处理,但这又有一个问题,到底osal怎么把消息送到osal的消息队列中去呢,看来还有一些问题没搞清楚,没事,咱在继续研究,让我们在程序中好好找找,这就涉及到osal运行方式了,我们好好看看,一定要弄明白:
我们都知道在开发自己的应用程序时,对于我们开发者来说,一个任务包括最主要的两个部分:首先要做的是做的是写任务初始化函数,然后是相应的事件处理函数。当然,最后一步是将自己编写的任务添加到osal任务队列中去,这个主要由osalAddTasks()函数来完成。
而对于整个osal运行来说,主要的过程如下:
首先主要是进行一系列的初始化,然后就是进入一个死循环函数osal_start_system()
ZSEG int main( void )
{

  // Turn off interrupts
  osal_int_disable( INTS_ALL );

  // Make sure supply voltage is high enough to run
  zmain_vdd_check();
  // Initialize stack memory
  zmain_ram_init();
  // Initialize board I/O
  InitBoard( OB_COLD );
  // Initialze HAL drivers
  HalDriverInit();
  // Initialize NV System
  osal_nv_init( NULL );
  // Determine the extended address
  zmain_ext_addr();
  // Initialize basic NV items
  zgInit();
  // Initialize the MAC
  ZMacInit();
#ifndef NONWK
  // Since the AF isn't a task, call it's initialization routine
  afInit();
#endif
  // Initialize the operating system
  osal_init_system();
  // Allow interrupts
  osal_int_enable( INTS_ALL );
  // Final board initialization
  InitBoard( OB_READY );
  // Display information about this device
  zmain_dev_info();

#ifdef LCD_SUPPORTED
  zmain_lcd_init();
#endif
  osal_start_system(); // No Return from here
} // main()
osal_start_system()这个函数的原型如下:其主要功能是执行任务队列中的任务。主要是通过不断循环来实现。
void osal_start_system( void )
{
  uint16 events;
  uint16 retEvents;
  byte activity;
  halIntState_t intState;
  // Forever Loop
#if !defined ( ZBIT )
  for(;;)
#endif
  {
   
    Hal_ProcessPoll();//
    activity = false;
    activeTask = osalNextActiveTask();
    if ( activeTask )
    {
如果有任务的话就暂缓中断。
      HAL_ENTER_CRITICAL_SECTION(intState);
      events = activeTask->events;//进入事件
      // Clear the Events for this task
      activeTask->events = 0;//对应事件清0
      HAL_EXIT_CRITICAL_SECTION(intState);
开放总断。
      if ( events != 0 )
      {
        // Call the task to process the event(s)
        if ( activeTask->pfnEventProcessor )// 调用相应的任务事件处理函数
        {
          retEvents = (activeTask->pfnEventProcessor)( activeTask->taskID, events );
          // Add back unprocessed events to the current task
          HAL_ENTER_CRITICAL_SECTION(intState);
          activeTask->events |= retEvents;
          HAL_EXIT_CRITICAL_SECTION(intState);
          activity = true;
        }
      }
    }
    // Complete pass through all task events with no activity?
    if ( activity == false )//没有相应的任务需要处理后就进入休眠模式。
    {
#if defined( POWER_SAVING )
      // Put the processor/system into sleep
      osal_pwrmgr_powerconserve();//进入休眠
#endif
    }
  }
整个流程下来,你可能并不知道任务里的事件是怎么触发的,消息是怎么进行传递的,所以得继续往下看(有点乱是吧,没办法,哥们,没经过整理的东西往往是这么无力,O(∩_∩)O哈哈~)来,继续:
这个函数里面一个关键函数是Hal_ProcessPoll(),不要小看这个函数,这个函数为系统设置了一个"心跳",称之为心跳的原因主要是因为这个设置了一个定时器作为osal运行的时钟,现在的关键是搞清楚这个时钟在哪设置,怎样引导osal工作(好像扯得远了,没事,把这些先慢慢搞清楚,然后就知道消息的传送过程)这个函数调用的是这个函数 HalTimerTick(),这个函数原型如下:
void HalTimerTick (void)
{
  if (!halTimerRecord[HW_TIMER_1].intEnable)
  {
    halProcessTimer1 ();
  }
  if (!halTimerRecord[HW_TIMER_3].intEnable)
  {
    halProcessTimer3 ();
  }
  if (!halTimerRecord[HW_TIMER_4].intEnable)
  {
    halProcessTimer4 ();
  }
}
妈的,出了点问题,我不知道这些计时器的配置函数在哪,到底是哪个定时器作为osal运行的时钟呢,halTimerRecord[HW_TIMER_1].intEnable到底是在哪配置的我没找到相应的函数,没事,等等,继续找。。。。
按照我的感觉如果在osal任务初始化函数中(初始化我会拿另一节来讲,这节就只讲讲这个)肯定可以找到一些端倪,于是按照思路找下去,有了一个不错的发现:这个是板级初始化函数,我们来找,
void InitBoard( byte level )
{
  if ( level == OB_COLD )
  {
    // Initialize HAL
    HAL_BOARD_INIT();
    // Interrupts off
    osal_int_disable( INTS_ALL );
    // Turn all LEDs off
    HalLedSet( HAL_LED_ALL, HAL_LED_MODE_OFF );
    // Check for Brown-Out reset
    ChkReset();

   OnboardTimerIntEnable = FALSE;
   HalTimerConfig (OSAL_TIMER,                        // 8bit timer2
                  HAL_TIMER_MODE_CTC,                 // Clear Timer on Compare
                  HAL_TIMER_CHANNEL_SINGLE,           // Channel 1 - default
                  HAL_TIMER_CH_MODE_OUTPUT_COMPARE,   // Output Compare mode
                  OnboardTimerIntEnable,              // Use interrupt
                  Onboard_TimerCallBack);             // Channel Mode
  }
最主要的是这个函数 HalTimerConfig()设置了时钟。
  else  // !OB_COLD
  {
#ifdef ZTOOL_PORT
    MT_IndReset();
#endif
   
    OnboardKeyIntEnable = HAL_KEY_INTERRUPT_DISABLE;
    HalKeyConfig( OnboardKeyIntEnable, OnBoard_KeyCallback);
  }
来看下HalTimerConfig()的原型:
uint8 HalTimerConfig (uint8 timerId, uint8 opMode, uint8 channel, uint8 channelMode,
                      bool intEnable, halTimerCBack_t cBack)
{
  uint8 hwtimerid;
  hwtimerid = halTimerRemap (timerId);
  if ((opMode & HAL_TIMER_MODE_MASK) && (timerId < HAL_TIMER_MAX) &&
      (channelMode & HAL_TIMER_CHANNEL_MASK) && (channel & HAL_TIMER_CHANNEL_MASK))
  {
    halTimerRecord[hwtimerid].configured    = TRUE;
    halTimerRecord[hwtimerid].opMode        = opMode;
    halTimerRecord[hwtimerid].channel       = channel;
    halTimerRecord[hwtimerid].channelMode   = channelMode;
    halTimerRecord[hwtimerid].intEnable     = intEnable;
    halTimerRecord[hwtimerid].callBackFunc  = cBack;//设置回调函数的指针。
  }
  else
  {
    return HAL_TIMER_PARAMS_ERROR;
  }
  return HAL_TIMER_OK;
}
层层调用我最后找到了这个函数(层层调用的我就不写出来了,只写最主要的)
void osal_update_timers( void )
{
  osalTimerUpdate( tmr_decr_time )
;//tmr_decr_time在void osalTimerInit( void )赋值,为:    tmr_decr_time = TIMER_DECR_TIME;define TIMER_DECR_TIME    1  // 1ms - has to be matched with TC_OCC 即是一毫秒即是每隔一毫秒更新一次定时器。但是有一个问题,我在例子中看到这个定时器没有被打开。
}
osalTimerUpdate( tmr_decr_time )原型:
*********************************************************************
* @fn      osalTimerUpdate
*
* @brief   Update the timer structures for a timer tick.
*更新定时器的数据结构
* @param   none
*
* @return  none
*********************************************************************/
static void osalTimerUpdate( uint16 updateTime )//这个函数真有点让我有点摸不着头脑,不知道为啥多出来一个定时器链表。
{
  halIntState_t intState;
  osalTimerRec_t *srchTimer;
  osalTimerRec_t *prevTimer;
  osalTimerRec_t *saveTimer;
  HAL_ENTER_CRITICAL_SECTION( intState );  // Hold off interrupts.
  // Update the system time
  osal_systemClock += updateTime;
  // Look for open timer slot
  if ( timerHead != NULL )
  {
    // Add it to the end of the timer list
    srchTimer = timerHead;
    prevTimer = (void *)NULL;
    // Look for open timer slot
    while ( srchTimer )
    {
      // Decrease the correct amount of time
      if (srchTimer->timeout <= updateTime)
        srchTimer->timeout = 0;
      else
        srchTimer->timeout = srchTimer->timeout - updateTime;
      // When timeout, execute the task
      if ( srchTimer->timeout == 0 )
      {
        osal_set_event( srchTimer->task_id, srchTimer->event_flag );//这个函数又是关键。
        // Take out of list
        if ( prevTimer == NULL )
          timerHead = srchTimer->next;
        else
          prevTimer->next = srchTimer->next;
        // Next
        saveTimer = srchTimer->next;
        // Free memory
        osal_mem_free( srchTimer );
        srchTimer = saveTimer;
      }
      else
      {
        // Get next
        prevTimer = srchTimer;
        srchTimer = srchTimer->next;
      }
    }
#ifdef POWER_SAVING
    osal_retune_timers();这个函数以后再说。
#endif
  }
  HAL_EXIT_CRITICAL_SECTION( intState );   // Re-enable interrupts.
}
还是先上一个图说明定时器数据链表吧,该图来自奥特曼的笔记:
其实就是一个数据结构,没啥复杂的,不要想复杂了,
typedef struct
{
  void *next;
  UINT16 timeout;
  UINT16 event_flag;
  byte task_id;
} osalTimerRec_t;。

有点晕,先不管上面的了,我不知道上面这个链表是怎么更新的,即是上述的链表的值是怎么赋值的,为什么要搞个这样的表出来?(妈的,为了搞清楚开头的那玩意跑到现在这么远的地方了,越说越远,没办法了,打破砂锅问到底吧)先把这个高清楚之后在来看上面函数的具体作用,我看了奥特曼写的一些东西,感觉有些不是很对,但是他说的一个函数引起了我的注意,这就是

byte osal_start_timerEx( byte taskID, UINT16 event_id, UINT16 timeout_value )
{
  halIntState_t intState;
  osalTimerRec_t *newTimer;
  HAL_ENTER_CRITICAL_SECTION( intState );  // Hold off interrupts.
  // Add timer
  newTimer = osalAddTimer( taskID, event_id, timeout_value );
添加一个新的任务到定时器链表中。
  if ( newTimer )
  {
#ifdef POWER_SAVING
    // Update timer registers
    osal_retune_timers();
    (void)timerActive;
#endif
    // Does the timer need to be started?
  //现在如果定时器没有启动,那么启动定时器
    if ( timerActive == FALSE )
    {
      osal_timer_activate( TRUE
);//妈的,原来是在这里启动的,难怪在osal初始化中可以开始不启动。搞的我心里惴惴不安,还以为自己看错了,
    }
  }
  HAL_EXIT_CRITICAL_SECTION( intState );   // Re-enable interrupts.
  return ( (newTimer != NULL) ? ZSUCCESS : NO_TIMER_AVAIL );
}
搞清楚了这个函数,看来这个函数主要的任务是将任务的一个对应事件送到上述的定时器链表中,那上面那个函数怎么执行呢,问题又来了,什么样的情况下才去运行osalTimerUpdate()这个函数呢,在初始化的过程中运行了一次,但是,后来又是怎么运行的呢,如果是靠定时器溢出来运行的,那么这个机制又是怎样的呢?
继续看程序,继续思考,别急。。。。。
查了有关资料,如果定时器溢出会调用这个函数halTimerSendCallBack(),看一下这个函数的原型,
void halTimerSendCallBack (uint8 timerId, uint8 channel, uint8 channelMode)
{
  uint8 hwtimerid;
  hwtimerid = halTimerRemap (timerId);
  if (halTimerRecord[hwtimerid].callBackFunc)
    (halTimerRecord[hwtimerid].callBackFunc) (timerId, channel, channelMode);
}
即如果系统定时器溢出后将调用这个函数(调用机制等下再来看)并找出对应的定时器回调函数,系统定时器的回调函数是这个void Onboard_TimerCallBack ( uint8 timerId, uint8 channel, uint8 channelMode)
{
  if ((timerId == OSAL_TIMER) && (channelMode == HAL_TIMER_CH_MODE_OUTPUT_COMPARE))
  {
    osal_update_timers();
  }
}
这样就知道了如果定时器每溢出一次将会调用这个osal_update_timers();函数一次,现在我们再回来看看到底他妈的这个函数是干嘛用的:。。。。。。。。
osalTimerUpdate( tmr_decr_time )原型:
*********************************************************************
* @fn      osalTimerUpdate
*
* @brief   Update the timer structures for a timer tick.
*更新定时器的数据结构
* @param   none
*
* @return  none
*********************************************************************/
static void osalTimerUpdate( uint16 updateTime )
{
  halIntState_t intState;
  osalTimerRec_t *srchTimer;
  osalTimerRec_t *prevTimer;
  osalTimerRec_t *saveTimer;//
这些是链表的相应指针,不要怕,知道吗,都学过了,呵呵
  HAL_ENTER_CRITICAL_SECTION( intState );  // Hold off interrupts.
  // Update the system time
  osal_systemClock += updateTime;//
更新系统时间,这个osal_systemClock 是一个非常大的数,static uint32 osal_systemClock;
  // Look for open timer slot
  if ( timerHead != NULL )
  {
    // Add it to the end of the timer list
    srchTimer = timerHead;
    prevTimer = (void *)NULL;
    // Look for open timer slot
    while ( srchTimer )
    {
      // Decrease the correct amount of time
      if (srchTimer->timeout <= updateTime)
        srchTimer->timeout = 0;
      else
        srchTimer->timeout = srchTimer->timeout - updateTime;
      // When timeout, execute the task
      if ( srchTimer->timeout == 0 )
      {
        osal_set_event( srchTimer->task_id, srchTimer->event_flag );//这个函数又是关键。
        // Take out of list
        if ( prevTimer == NULL )
          timerHead = srchTimer->next;
        else
          prevTimer->next = srchTimer->next;
        // Next
        saveTimer = srchTimer->next;
        // Free memory
        osal_mem_free( srchTimer );
        srchTimer = saveTimer;
      }
      else
      {
        // Get next
        prevTimer = srchTimer;
        srchTimer = srchTimer->next;
      }
    }
#ifdef POWER_SAVING
    osal_retune_timers();
这个函数以后再说。
#endif
  }
  HAL_EXIT_CRITICAL_SECTION( intState );   // Re-enable interrupts.
}
这下应该看懂了吧,主要是在timeout的时间后执行
osal_set_event( srchTimer->task_id, srchTimer->event_flag );好的先,把大体流程总结一下在来看,如果假设利用osal_start_timerEx()函数设置了一个事件,并将其加入到定时器链表中,这时候定时器启动,并且如果溢出的时候就会调用osalTimerUpdate()函数,在每次执行后都会检查时间对应timeout是否等于0,如果等于零的时候就会调用这个函数osal_set_event( srchTimer->task_id, srchTimer->event_flag),现在我们来仔细看一下这个函数,

byte osal_set_event( byte task_id, UINT16 event_flag )
{
  osalTaskRec_t *srchTask;
  halIntState_t   intState;
  srchTask = osalFindTask( task_id );//找到这个任务
  if ( srchTask ) {
    // Hold off interrupts
    HAL_ENTER_CRITICAL_SECTION(intState);
    // Stuff the event bit(s)
    srchTask->events |= event_flag;//
设定相应事件的标志。
**************************************
注:这里的events标志可以设定为一个非零值,而这个非零值就是对应的事件,在执行的时候就是根据相应的值来执行相应的事件。
**************************************
    // Release interrupts
    HAL_EXIT_CRITICAL_SECTION(intState);
  }
   else
    return ( INVALID_TASK );
  return ( ZSUCCESS );
}
这下知道了这个函数主要是置位相应的事件,然后传给系统的处理,系统处理这个主要是经过轮询
主要靠这个函数实现:
osalTaskRec_t *osalNextActiveTask( void )
{
  osalTaskRec_t *srchTask;
  // Start at the beginning
  srchTask = tasksHead;
  // When found or not
  while ( srchTask )  {
      if (srchTask->events)  {
    // task is highest priority that is ready
          return srchTask;
      }
      srchTask = srchTask->next;
  }
  return NULL;
}
它返回一个指向typedef struct osalTaskRec
{
  struct osalTaskRec  *next;
  pTaskInitFn          pfnInit;
  pTaskEventHandlerFn  pfnEventProcessor;
  byte                 taskID;
  byte                 taskPriority;
  uint16               events;
} osalTaskRec_t;
的指针。
然后将任务交给retEvents = (activeTask->pfnEventProcessor)( activeTask->taskID, events );这个处理, 这还没有结束,我们在开头提到的这个还没有解决;
MSGpkt = (afIncomingMSGPacket_t *)osal_msg_receive( SampleApp_TaskID );(终于回来了,累啊)
这里涉及到两个数据结构:
typedef struct
{
  void   *next;
  uint16 len;
  byte   dest_id;
} osal_msg_hdr_t;
typedef struct
{
  uint8  event;
  uint8  status;
} osal_event_hdr_t;
我查了一下有这么一个函数和消息有关的,如下
*********************************************************************
* @fn      osal_msg_send
*
* @brief
*
*    This function is called by a task to send a command message to
*    another task or processing element.  The sending_task field must
*    refer to a valid task, since the task ID will be used
*    for the response message.  This function will also set a message
*    ready event in the destination tasks event list.
*这个函数主要由任务调用发送一个命令消息到另一个任务或是处理元素,同时这个函数将会设定一个消息事件到目的任务的事件列表中
*
* @param   byte destination task - Send msg to?  Task ID
* @param   byte *msg_ptr - pointer to new message buffer
* @param   byte len - length of data in message
*
* @return  ZSUCCESS, INVALID_SENDING_TASK, INVALID_DESTINATION_TASK,
*          INVALID_MSG_POINTER, INVALID_LEN
*/
byte osal_msg_send( byte destination_task, byte *msg_ptr )
{
  if ( msg_ptr == NULL )
    return ( INVALID_MSG_POINTER );
  if ( osalFindTask( destination_task ) == NULL )
  {
    osal_msg_deallocate( msg_ptr );
    return ( INVALID_TASK );
  }
  // Check the message header
  if ( OSAL_MSG_NEXT( msg_ptr ) != NULL ||
       OSAL_MSG_ID( msg_ptr ) != TASK_NO_TASK )
  {
    osal_msg_deallocate( msg_ptr );
    return ( INVALID_MSG_POINTER );
  }
  OSAL_MSG_ID( msg_ptr ) = destination_task;
  // queue message
  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 );
}
看来,这个osal_msg_send()函数只是发送一个系统事件,而和用户自定义的事件无关(自己猜想的,以后验证。)。
现在大家应该豁然开朗了吧,我们终于走到了新的一步了。

回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表