【转自小峰博客】协调器的启动【自动模式】

首先请让我向大家介绍下强人小峰,看完他写的多篇博客感悟颇多哦!

使用的协议栈版本信息: ZigBee2006\ZStack-1.4.3-1.2.1
Zigbee网络设备启动流程—协调器(自启动模式)—以SampleApp的协调器为例.



1、协调器预编译信息
通过project->options->c/c++compiler->extraOptions可以看到协调器所带的配置文件为:
-f $PROJ_DIR$\..\..\..\Tools\CC2430DB\f8wCoord.cfg
-f $PROJ_DIR$\..\..\..\Tools\CC2430DB\f8wConfig.cfg
即编译了ZDO_COORDINATOR和RTR_NWK.

通过project->options->c/c++compiler->Preprocessor->Defined symbols可以看到协调器预编译包含了:
CC2430EB; ZTOOL_P1; MT_TASK; LCD_SUPPORTED=DEBUG; MANAGED_SCAN
没有编译HOLD_AUTO_START和SOFT_START.

2、具体流程
main()->osal_init_system()->osalInitTasks()->ZDApp_Init()
进入ZDApp_Init()函数:
**************************************
void ZDApp_Init( byte task_id )
{
  uint8 capabilities;

  // Save the task ID
  ZDAppTaskID = task_id;

  // Initialize the ZDO global device short address storage
  ZDAppNwkAddr.addrMode = Addr16Bit;
  ZDAppNwkAddr.addr.shortAddr = INVALID_NODE_ADDR; //0xFFFE
  (void)NLME_GetExtAddr();  // Load the saveExtAddr pointer.

  // Check for manual"Hold Auto Start"
  //检测到有手工设置SW_1则会设置devState = DEV_HOLD,从而避开网络初始化
  ZDAppCheckForHoldKey();

  // Initialize ZDO items and setup the device - type of device to create.
  ZDO_Init(); //通过判断预编译来开启一些函数功能


  // Register the endpoint description with the AF
  // This task doesn't have a Simple description, but we still need
  // to register the endpoint.
  afRegister( (endPointDesc_t *)&ZDApp_epDesc );

#if defined( ZDO_USERDESC_RESPONSE )
  ZDApp_InitUserDesc();
#endif // ZDO_USERDESC_RESPONSE

  // set broadcast address mask to support broadcast filtering
  NLME_GetRequest(nwkCapabilityInfo, 0, &capabilities);
  NLME_SetBroadcastFilter( capabilities );

  // Start the device?
  if ( devState != DEV_HOLD )
  {
    ZDOInitDevice( 0 );
  }
  /*如果devState=DEV_HOLD,则不会调用ZDOInitDevice()来初始化网络
    即不组网也不进网.LED4闪烁等待应用程序来开启设备并初始化网络
  */
  else
  {
    // Blink LED to indicate HOLD_START
    HalLedBlink ( HAL_LED_4, 0, 50, 500 );
  }

  ZDApp_RegisterCBs();
}
**************************************
协调器没有编译HOLD_AUTO_START,也没有手工设置SW_1,初始化devState = DEV_INIT(参见基本问题说明3).因此直接在ZDApp_Init()中进入ZDOInitDevice( 0 )开启设备.

**************************************
uint8 ZDOInitDevice( uint16 startDelay )
{
    //初始化设备网络状态为ZDO_INITDEV_NEW_NETWORK_STATE:新的网络状态.
    //可能意味着ZCD_NV_STARTUP_OPTION不能恢复,或没有任何网络状态恢复
  uint8 networkStateNV = ZDO_INITDEV_NEW_NETWORK_STATE;
  uint16 extendedDelay = 0;

  devState = DEV_INIT;    // Remove the Hold state

  // Initialize leave control logic
  //函数读取NV项目ZCD_NV_LEAVE_CTRL的值,ZDApp_LeaveCtrl指向这个值
  ZDApp_LeaveCtrlInit();

  // Check leave control reset settings
  ZDApp_LeaveCtrlStartup( &devState, &startDelay );

  // Leave may make the hold state come back
  //以上两个函数设置了对设备离开时的控制,如果有延时则延时,没有则
  //把设备状态设为DEV_HOLD
  if ( devState == DEV_HOLD )
    //ZDO_INITDEV_LEAVE_NOT_STARTED:该设备没有在网络中,下次调用才启用.
    return ( ZDO_INITDEV_LEAVE_NOT_STARTED );   // Don't join - (one time).

#if defined ( NV_RESTORE )
  // Get Keypad directly to see if a reset nv is needed.
  // Hold down the SW_BYPASS_NV key (defined in OnBoard.h)
  // while booting(引导) to skip past NV Restore.
  if ( HalKeyRead() == SW_BYPASS_NV )
    //SW_BYPASS_NV按键处于按下状态时,则避开网络层的NV存储
    networkStateNV = ZDO_INITDEV_NEW_NETWORK_STATE;  //设备网络状态为新的网络状态
  
  else
  {
    // Determine if NV should be restored
    //函数返回的设备网络状态要么是新的网络状态;要么是恢复的网络状态;以此
    //来确定要不要读取NV里相应条目来恢复网络先前状态
    networkStateNV = ZDApp_ReadNetworkRestoreState();
  }

   //如果设备的网络状态为恢复的网络状态
  if ( networkStateNV == ZDO_INITDEV_RESTORED_NETWORK_STATE )
  {
    //恢复设备先前的网络状态参数并且
    //设置devStartMode = MODE_RESUME!!!!
    networkStateNV = ZDApp_RestoreNetworkState();
  }
  else  //如果设备的网络状态为新的网络状态,在下面进行处理
  {
    // Wipe out(清除) the network state in NV
    NLME_InitNV();
    NLME_SetDefaultNV();  //设置默认NV条目
  }
#endif

  //如果设备的网络状态为新的网络状态
  if ( networkStateNV == ZDO_INITDEV_NEW_NETWORK_STATE )
  {
    //根据预编译来设置设备新的网络状态参数
    ZDAppDetermineDeviceType();/*!!!!*/

    // Only delay if joining network - not restoring network state
    extendedDelay = (uint16)((NWK_START_DELAY + startDelay)
              + (osal_rand() & EXTENDED_JOINING_RANDOM_MASK));
  }

  // Initialize device security
  ZDApp_SecInit( networkStateNV );

  // Trigger the network start
  ZDApp_NetworkInit( extendedDelay );

  return ( networkStateNV );
}

**************************************
分两种情况:
(1)如果协调器预编译了NV_RESTORE,且函数ZDApp_ReadNetworkRestoreState()返回值为ZDO_INITDEV_RESTORED_NETWORK_STATE,则进入ZDApp_RestoreNetworkState()里设置ZDO_Config_Node_Descriptor.LogicalType = NODETYPE_COORDINATOR和devStartMode = MODE_RESUME.
****************
uint8 ZDApp_RestoreNetworkState( void )
{
  …………
    // Are we a coordinator
    //设备的网络状态为恢复的网络状态.则进入这个函数进行恢复
    //先判断如果短地址为0则设置设备逻辑类型为协调器且devStartMode = MODE_RESUME
    //否则设置devStartMode = MODE_RESUME
      ZDAppNwkAddr.addr.shortAddr = NLME_GetShortAddr();
      if ( ZDAppNwkAddr.addr.shortAddr == 0 ) //如果短地址是0,即协调器
      {
        ZDO_Config_Node_Descriptor.LogicalType = NODETYPE_COORDINATOR; //!!!!!
      }
      devStartMode = MODE_RESUME; //MODE_RESUME!!!!!!!!
  …………
}
****************

(2)如果协调器没有预编译NV_RESTORE,networkStateNV ==ZDO_INITDEV_NEW_NETWORK_STATE,但由于协调器编译了ZDO_COORDINATOR而没有编译SOFT_START,因此ZDAppDetermineDeviceType()不起作用.因此ZDO_Config_Node_Descriptor.LogicalType和devStartMode这两个关键参数保持初始化时的值:
ZDO_Config_Node_Descriptor.LogicalType = NODETYPE_COORDINATOR(见基本问题说明6)
devStartMode = MODE_HARD(见基本问题说明4)

对于协调器,这两种情况最终都是确定两个关键设备网络状态参数的值.对本例的SampleApp的协调器,没有编译NV_RESTORE,因此属于情况(2).
然后调用ZDApp_NetworkInit()启动网络:
****************
void ZDApp_NetworkInit( uint16 delay )
{
  if ( delay )
  {
    // Wait awhile before starting the device
    osal_start_timerEx( ZDAppTaskID, ZDO_NETWORK_INIT, delay );
  }
  else
  {
    osal_set_event( ZDAppTaskID, ZDO_NETWORK_INIT );
  }
}
****************
通过触发ZDAppTaskID的ZDO_NETWORK_INIT事件.来看下对ZDO_NETWORK_INIT 事件的处理:
****************
UINT16 ZDApp_event_loop( byte task_id, UINT16 events )
{
  …………
  if ( events & ZDO_NETWORK_INIT )
  {
    // Initialize apps and start the network
    devState = DEV_INIT;
    ZDO_StartDevice( (uint8)ZDO_Config_Node_Descriptor.LogicalType, devStartMode,
                     DEFAULT_BEACON_ORDER, DEFAULT_SUPERFRAME_ORDER );

    // Return unprocessed events
    return (events ^ ZDO_NETWORK_INIT);
  }
  …………
}
****************
可以看到调用了ZDO_StartDevice( (uint8)ZDO_Config_Node_Descriptor.LogicalType,                  devStartMode,DEFAULT_BEACON_ORDER, DEFAULT_SUPERFRAME_ORDER );
这里设备网络状态参数:
ZDO_Config_Node_Descriptor.LogicalType = NODETYPE_COORDINATOR
devStartMode = MODE_HARD
且协调器编译了ZDO_COORDINATOR
****************
void ZDO_StartDevice( byte logicalType, devStartModes_t startMode, byte beaconOrder, byte superframeOrder )
{
  ZStatus_t ret;

  ret = ZUnsupportedMode;

#if defined(ZDO_COORDINATOR)
  if ( logicalType == NODETYPE_COORDINATOR )
  {
    if ( startMode == MODE_HARD )  //MODE_HARD
    {
      devState = DEV_COORD_STARTING;  //Started as Zigbee Coordinator
      //建网
      ret = NLME_NetworkFormationRequest( zgConfigPANID, zgDefaultChannelList,
                                          zgDefaultStartingScanDuration, beaconOrder,
                                          superframeOrder, false );
    }
    else if ( startMode == MODE_RESUME )  //MODE_RESUME
    {
      // Just start the coordinator
      devState = DEV_COORD_STARTING;
      ret = NLME_StartRouterRequest( beaconOrder, beaconOrder, false );
    }
    else  //错误,未知启动模式
    {
#if defined( LCD_SUPPORTED )
      //HalLcdWriteScreen( "StartDevice ERR", "MODE unknown" );
      ClearScreen();
      Print8(HAL_LCD_LINE_1,10,"StartDevice ERR",1);
      Print8(HAL_LCD_LINE_2,10,"MODE unknown",1);
#endif
    }
  }
#endif  // !ZDO_COORDINATOR

//#if !defined ( ZDO_COORDINATOR ) || defined( SOFT_START )
  
  if ( logicalType == NODETYPE_ROUTER || logicalType == NODETYPE_DEVICE )
  {
    if ( (startMode == MODE_JOIN) || (startMode == MODE_REJOIN) )
    {
      devState = DEV_NWK_DISC;  //Discovering PAN's to join

  #if defined( MANAGED_SCAN )
      ZDOManagedScan_Next();
      ret = NLME_NetworkDiscoveryRequest( managedScanChannelMask, BEACON_ORDER_15_MSEC );
  #else
      ret = NLME_NetworkDiscoveryRequest( zgDefaultChannelList, zgDefaultStartingScanDuration );
  #endif
    }
    else if ( startMode == MODE_RESUME )  //MODE_RESUME 恢复
    {
      if ( logicalType == NODETYPE_ROUTER )
      {
        ZMacScanCnf_t scanCnf;
        devState = DEV_NWK_ORPHAN;

        /* if router and nvram is available, fake successful orphan scan */
        scanCnf.hdr.Status = ZSUCCESS;
        scanCnf.ScanType = ZMAC_ORPHAN_SCAN;
        scanCnf.UnscannedChannels = 0;
        scanCnf.ResultListSize = 0;
        nwk_ScanJoiningOrphan(&scanCnf);

        ret = ZSuccess;
      }
      else
      {
        devState = DEV_NWK_ORPHAN; //孤儿
        ret = NLME_OrphanJoinRequest( zgDefaultChannelList,
                                      zgDefaultStartingScanDuration );
      }
    }
    else
    {
#if defined( LCD_SUPPORTED )
     // HalLcdWriteScreen( "StartDevice ERR", "MODE unknown" );
      Print8(HAL_LCD_LINE_1,10,"StartDevice ERR",1);
      Print8(HAL_LCD_LINE_2,10,"MODE unknown",1);
#endif
    }
  }
//#endif  //!ZDO COORDINATOR || SOFT_START

  if ( ret != ZSuccess )
    osal_start_timerEx(ZDAppTaskID, ZDO_NETWORK_INIT, NWK_RETRY_DELAY );
}
****************
通过参数可知协调器调用NLME_NetworkFormationRequest( zgConfigPANID, zgDefaultChannelList,
                                          zgDefaultStartingScanDuration, beaconOrder,
                                          superframeOrder, false )
进行网络的组建.
而对NLME_NetworkFormationRequest()的调用会产生一个回调函数ZDO_NetworkFormationConfirmCB()(见主要函数说明3),来看下:
****************
void ZDO_NetworkFormationConfirmCB( ZStatus_t Status )
{
#if defined(ZDO_COORDINATOR)
  nwkStatus = (byte)Status;

  if ( Status == ZSUCCESS )
  {
    // LED on shows Coordinator started
    HalLedSet ( HAL_LED_3, HAL_LED_MODE_ON );

    // LED off forgets HOLD_AUTO_START
    HalLedSet (HAL_LED_4, HAL_LED_MODE_OFF);

#if defined ( ZBIT )
    SIM_SetColor(0xd0ffd0);
#endif

    if ( devState == DEV_HOLD )
    {
      // Began with HOLD_AUTO_START
      devState = DEV_COORD_STARTING;
    }
  }
#if defined(BLINK_LEDS)
  else
    HalLedSet ( HAL_LED_3, HAL_LED_MODE_FLASH );  // Flash LED to show failure
#endif

  osal_set_event( ZDAppTaskID, ZDO_NETWORK_START );
#endif  //ZDO_COORDINATOR
}
****************
如果Status返回ZSUCCESS,建立网络成功,通过一些灯亮来来指示;不成功则通过闪烁灯来指示.最后触发任务ZDAppTaskID的ZDO_NETWORK_START事件,看下对ZDO_NETWORK_START的处理:
****************
#if defined (RTR_NWK)
  if ( events & ZDO_NETWORK_START )
  {
    ZDApp_NetworkStartEvt();

    // Return unprocessed events
    return (events ^ ZDO_NETWORK_START);
  }
****************
调用了ZDApp_NetworkStartEvt()
****************
void ZDApp_NetworkStartEvt( void )
{
  if ( nwkStatus == ZSuccess )//网络建立成功
  {
    // Successfully started a ZigBee network
    if ( devState == DEV_COORD_STARTING )
    {
      devState = DEV_ZB_COORD;
    }

    osal_pwrmgr_device( PWRMGR_ALWAYS_ON );
    osal_set_event( ZDAppTaskID, ZDO_STATE_CHANGE_EVT );
  }
  else //网络建立不成功,则增加能量阀值重新建网.
  {
    // Try again with a higher energy threshold !!
    if ( ( NLME_GetEnergyThreshold() + ENERGY_SCAN_INCREMENT ) < 0xff )
    {
      NLME_SetEnergyThreshold( (uint8)(NLME_GetEnergyThreshold() + ENERGY_SCAN_INCREMENT) );
      osal_set_event( ZDAppTaskID, ZDO_NETWORK_INIT ); //重新初始化建立网络
    }
    else
    {
      // Failed to start network. Enter a dormant state (until user intervenes)
      devState = DEV_INIT;
      osal_set_event( ZDAppTaskID, ZDO_STATE_CHANGE_EVT );
    }
  }
}
****************
如果协调器建立网络成功,则触发ZDAppTaskID的ZDO_STATE_CHANGE_EVT事件.看下对 ZDO_STATE_CHANGE_EVT的处理:
****************
  if ( events & ZDO_STATE_CHANGE_EVT )
  {
    ZDO_UpdateNwkStatus( devState );

    // Return unprocessed events
    return (events ^ ZDO_STATE_CHANGE_EVT);
  }
****************
调用了ZDO_UpdateNwkStatus( devState ),网络状态改变,这个函数会更新和发送信息通知每个注册登记过的应用终端.
****************
void ZDO_UpdateNwkStatus( devStates_t state )
{
  // Endpoint/Interface descriptor list.
  epList_t *epDesc = epList;
  byte bufLen = sizeof(osal_event_hdr_t);
  osal_event_hdr_t *msgPtr;

  ZDAppNwkAddr.addr.shortAddr = NLME_GetShortAddr();
  (void)NLME_GetExtAddr();  // Load the saveExtAddr pointer.

  while ( epDesc )
  {
    if ( epDesc->epDesc->endPoint != ZDO_EP )
    {
      msgPtr = (osal_event_hdr_t *)osal_msg_allocate( bufLen );
      if ( msgPtr )
      {
        msgPtr->event = ZDO_STATE_CHANGE; // Command ID
        msgPtr->status = (byte)state;

        osal_msg_send( *(epDesc->epDesc->task_id), (byte *)msgPtr ); //发往应用任务
      }
    }
    epDesc = epDesc->nextDesc;
  }
}
****************
对SampleApp的协调器来说,这里触发应用任务SampleApp_TaskID的ZDO_STATE_CHANGE事件,看下对ZDO_STATE_CHANGE的处理:
****************
        case ZDO_STATE_CHANGE:   
          SampleApp_NwkState = (devStates_t)(MSGpkt->hdr.status); //获取设备当前状态
          if ( (SampleApp_NwkState == DEV_ZB_COORD)
              || (SampleApp_NwkState == DEV_ROUTER)
              || (SampleApp_NwkState == DEV_END_DEVICE) )
          {
            // Start sending the periodic message in a regular interval.
            osal_start_timerEx( SampleApp_TaskID,
                              SAMPLEAPP_SEND_PERIODIC_MSG_EVT,
                              SAMPLEAPP_SEND_PERIODIC_MSG_TIMEOUT );
          }
          else
          {
            // Device is no longer in the network
          }
          break;
****************
可以看到当协调器建立网络成功,通过回调函数触发应用任务的ZDO_STATE_CHANGE事件,最终开启定时器发送周期信息.

3、
协调器(自启动模式)—以SampleApp的协调器为例,并假设初始化成功,网络建立成功.
程序大致流程:

main()->osal_init_system()->osalInitTasks()->ZDApp_Init()->ZDOInitDevice()->ZDApp_NetworkInit->触发ZDAppTaskID的ZDO_NETWORK_INIT->ZDO_StartDevice()->NLME_NetworkFormationRequest()->网络建立成功ZDO_NetworkFormationConfirmCB->触发ZDAppTaskID的ZDO_NETWORK_START->ZDApp_NetworkStartEvt()->触发ZDAppTaskID的ZDO_STATE_CHANGE_EVT->ZDO_UpdateNwkStatus()->触发SampleApp_TaskID的ZDO_STATE_CHANGE->开户周期信息发送的定时器.

4、注:
(1)自启动模式下SampleApp的终端和路由器总体流程基本一致.

(2)
以SampleApp为例,ZDO_StartDevice()函数的两个重要参数比较:
终端:
ZDO_Config_Node_Descriptor.LogicalType = NODETYPE_DEVICE
devStartMode = MODE_JOIN
路由器:
ZDO_Config_Node_Descriptor.LogicalType = NODETYPE_ROUTER
devStartMode = MODE_JOIN
协调器:
ZDO_Config_Node_Descriptor.LogicalType = NODETYPE_COORDINATOR
devStartMode = MODE_HARD

***************************************
***************************************
看完了自己也添加了一些自己的感想,希望大家多给点意见。
posted @ 2011-04-27 10:02  雪帕  阅读(3024)  评论(0编辑  收藏  举报