Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1919673
  • 博文数量: 376
  • 博客积分: 2147
  • 博客等级: 大尉
  • 技术积分: 3642
  • 用 户 组: 普通用户
  • 注册时间: 2012-02-06 10:47
文章分类

全部博文(376)

文章存档

2019年(3)

2017年(28)

2016年(15)

2015年(17)

2014年(182)

2013年(16)

2012年(115)

我的朋友

分类: 嵌入式

2014-06-28 18:18:22

    在上一篇文章中,分析了一下定义了HOLD_AUTO_START编译选项后,程序的流程,这里分析一下在没有定义这个编译选项后,程序的启动流程,也就是自动启动模式的流程。SampleApp例程为例。
ZDApp.c文件中,可以看到下面的定义:
#if defined( HOLD_AUTO_START )
devStates_t devState = DEV_HOLD;  // 初始化-不会自动启动
#else
  devStates_t devState = DEV_INIT;  //初始化-没有连接到任何东西
#endif
    不管什么样的启动方式,都会在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;
  (void)NLME_GetExtAddr();  // Load the saveExtAddr pointer. 加载IEEE地址
 
  // 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(); //初始化ZDO条目,并设置设备的启动方式是协调器,还是别的
 
  // 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? 是否启动设备?如果devState不是DEV_HOLD时,则启动设备,在上面的代码分析中,也可以看到,如果定义了HOLD_AUTO_START宏,则devState等于DEV_HOLD,不会启动设备。如果按下了SW_1devState也等于DEV_HOLD,也不会启动网络。也就是说有两种方式可以设置非自动启动模式,一种是通过按键,一种通过宏定义
  if ( devState != DEV_HOLD )
  {
    ZDOInitDevice( 0 );  //在本例程中没有定义HOLD_AUTO_START所以这个会成功执行
  }
  else
  {
//如果定义了HOLD_AUTO_START,则等待延时或外部事件启动网络,并且LED4灯,也就是蓝色的灯闪烁
    // Blink LED to indicate HOLD_START
    HalLedBlink ( HAL_LED_4, 0, 50, 500 );
  }
 
  ZDApp_RegisterCBs();
} /* ZDO_Init() */
   其中ZDOInitDevice()函数是最重要的,也是设备的初始化。下面是ZDOInitDevice()函数的源代码。
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
//设备的断开会造成DEV_HOLD状态,这里面设置的.
  ZDApp_LeaveCtrlStartup( &devState, &startDelay );
 
  // Leave may make the hold state come back
//以上两个函数设置了对设备离开时的控制,如果有延时则延时,没有则
//把设备状态设为DEV_HOLD
 //ZDO_INITDEV_LEAVE_NOT_STARTED:该设备没有在网络中,下次调用才启用.
  if ( devState == DEV_HOLD )
    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 )
  {
    networkStateNV = ZDApp_RestoreNetworkState();
  }
  else
  {
// Wipe out the network state in NV
 //恢复设备先前的网络状态参数
    //设置devStartMode = MODE_RESUME
    NLME_InitNV();
    NLME_SetDefaultNV();
  }
#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 );
}
   在这里又分了两种情况,一种是定义了编译选项NV_RESTORE,另一种就是没有定义该编译选项。
下面首先看一下没有定义该选项的情况:
(1)       NV_RESTORE编译选项定义
    首先,判断按键HAL_KEY_SW_5是否按下,如果HAL_KEY_SW_5按键按下,则设置networkStateNV = ZDO_INITDEV_NEW_NETWORK_STATE; 如果没有按下,通过调用ZDApp_ReadNetworkRestoreState()函数设置networkStateNV的值,通过读取ZCD_NV_STARTUP_OPTION项的值,来决定networkStateNV选项的值。如果这个函数的返回值为ZDO_INITDEV_RESTORED_NETWORK_STATE。则调用ZDApp_RestoreNetworkState();函数,该函数的源代码如下:
uint8 ZDApp_RestoreNetworkState( void )
{
  byte nvStat;
#if ( SECURE != 0 )
  nwkActiveKeyItems keyItems;
#endif
 
  // Initialize NWK NV items
  nvStat = NLME_InitNV();
//如果设备的状态为恢复的网络状态,即ZDO_INITDEV_RESTORED_NETWORK_STATE状态,则进行这个函数进行恢复。如果短地址为0x0000则为协调器结点,然后设备逻辑设备的类型为NODETYPE_COORDINATOR,并设置devStartMode = MODE_RESUME;说明设备为恢复状态。
  if ( nvStat != NV_OPER_FAILED )
  {
    if ( NLME_RestoreFromNV() )
    {
      // Are we a coordinator
      ZDAppNwkAddr.addr.shortAddr = NLME_GetShortAddr();
      if ( ZDAppNwkAddr.addr.shortAddr == 0 )
      {
        ZDO_Config_Node_Descriptor.LogicalType = NODETYPE_COORDINATOR;
      }
      devStartMode = MODE_RESUME;
    }
    else
      nvStat = NV_ITEM_UNINIT;
 
#if   ( SECURE != 0  )
    nwkFrameCounterChanges = 0;
    osal_memset( &keyItems, 0, sizeof( nwkActiveKeyItems ) );
    osal_nv_item_init( ZCD_NV_NWKKEY, sizeof(nwkActiveKeyItems), (void *)&keyItems );
 
  #if defined ( ZDO_COORDINATOR )
    ZDApp_RestoreNwkKey();
  #endif // ZDO_COORDINATOR
#endif // SECURE
 
    // The default for RxOnWhenIdle is true for RTR_NWK and false for end devices
    // [setup in the NLME_RestoreFromNV()].  Change it here if you want something
    // other than default.
  }
 
  if ( nvStat == ZSUCCESS )
    return ( ZDO_INITDEV_RESTORED_NETWORK_STATE );
  else
    return ( ZDO_INITDEV_NEW_NETWORK_STATE );
}
//如果networkStateNV=ZDO_INITDEV_NEW_NETWORK_STATE,则会调用下面的函数
void ZDAppDetermineDeviceType( void )
{
  if ( zgDeviceLogicalType == ZG_DEVICETYPE_ENDDEVICE )
    return;
 
#if defined ( SOFT_START )
  if ( zgDeviceLogicalType == ZG_DEVICETYPE_COORDINATOR )
  {
    devStartMode = MODE_HARD;     // Start as a coordinator
    ZDO_Config_Node_Descriptor.LogicalType = NODETYPE_COORDINATOR;
  }
  else
  {
    if ( zgDeviceLogicalType == ZG_DEVICETYPE_ROUTER )
    {
      softStartAllowCoord = FALSE;  // Don't allow coord to start 如果是路由器则不充许作为协调器启动
      continueJoining = TRUE;
    }
    devStartMode = MODE_JOIN;     // Assume joining
  }
#endif // SOFT_START
}
    可以看到,如果编译等式SOFT_START也有定义的话,设备的类型为协调器,则会设置devStartMode = MODE_HARD;也就是说设备为开始状态。如果是不是协调器而是路由器的话,devStartMode = MODE_JOIN; 设备为重新加入的状态。
ZDOInitDevice()函数的最后,通过调用ZDApp_NetworkInit( extendedDelay );进行网络的初始化操作。
2void 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 );
  }
}
3)在这里设置了ZDO_NETWORK_INIT事件,在下次循环中,也就是在ZDApp_event_loop()函数中,进行处理。
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);
  }
.....................
}
4. ZDO_StartDevice()函数根据传递过来的值,也就是
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) //如果定义了编译选项ZDO_COORDINATOR
  if ( logicalType == NODETYPE_COORDINATOR ) //如果逻辑设备是协调器
  {
    if ( startMode == MODE_HARD ) //开始启动设备
    {
      devState = DEV_COORD_STARTING; //建立网络
      ret = NLME_NetworkFormationRequest( zgConfigPANID, zgDefaultChannelList,
                                          zgDefaultStartingScanDuration, beaconOrder,
                                          superframeOrder, false );
    }
    else if ( startMode == 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" );
#endif
    }
  }
#endif  // !ZDO_COORDINATOR
 
#if !defined ( ZDO_COORDINATOR ) || defined( SOFT_START )//如果没有定义ZDO_COORDINATOR编译选项,而是定义了SOFT_START选项的话
  if ( logicalType == NODETYPE_ROUTER || logicalType == NODETYPE_DEVICE )
  { //如果不是协调器,而是终端或者路由器,并且开始状态为加入或者重新加入,
    if ( (startMode == MODE_JOIN) || (startMode == MODE_REJOIN) )
    {
      devState = DEV_NWK_DISC; //发现网络加入
 
  #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 ) //如果开始的状态为恢复的话,
    {
      if ( logicalType == NODETYPE_ROUTER )//如果是路由器结点
      {
        ZMacScanCnf_t scanCnf;
        devState = DEV_NWK_ORPHAN; //以孤儿结点方式加入,在ZigBee中有两种加入网络的方式还有一种是联合的方式
 
        /* 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" );
#endif
    }
  }
#endif  //!ZDO COORDINATOR || SOFT_START
 
  if ( ret != ZSuccess )
    osal_start_timerEx(ZDAppTaskID, ZDO_NETWORK_INIT, NWK_RETRY_DELAY );
}
(5)协调器调用NLME_NetworkFormationRequest(  
                           zgConfigPANID,  
                           zgDefaultChannelList,
                           zgDefaultStartingScanDuration,                           
beaconOrder,
                           superframeOrder, false )
   进行网络的组建.这个函数也是不开源的,在文件中没有找这个函数的源代码
而对NLME_NetworkFormationRequest()的调用:对申请的回应在程序中没对应的回调处理,猜想回调处理在lib库中处理。在程序找到了两个对申请结果作处理的函数,void ZDO_NetworkFormationConfirmCB( ZStatus_t Status )void nwk_Status( uint16 statusCode,uint16 statusValue );在两个函数中都有申请的结果。如果不成功则nwk_Status先执行如果成功则ZDO_NetworkFormationConfirmCB先执行。这个函数也是在系统的lib库文件中调用的。
6void ZDO_NetworkFormationConfirmCB( ZStatus_t Status )
{//参数就是NLME_NetworkFormationRequest()返回状态
#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
}
    这个函数主要是对建立网络后的一些指示,一般建立网络成功的话,绿色的灯会点亮。并对nwkStatus变量进行赋值。在这个函数中执行了  osal_set_event( ZDAppTaskID, ZDO_NETWORK_START );设置了事件。在任务IDZDAppTaskID的事件处理函数中。下面我们看一下对ZDO_NETWORK_START事件的处理过程。在ZDApp_event_loop()函数中。
7UINT16 ZDApp_event_loop( byte task_id, UINT16 events )
{
......................
#if defined (RTR_NWK)
  if ( events & ZDO_NETWORK_START )
  {
    ZDApp_NetworkStartEvt();
 
    // Return unprocessed events
    return (events ^ ZDO_NETWORK_START);
  }
#endif  //RTR_NWK
...........................
}
可以看到在这里调用了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 );//触发事件ZDO_STATE_CHANGE_EVT
  }
  else
  {
    // Try again with a higher energy threshold !! 如果不成功则能量阀值增加16,原来也是16
    if ( ( NLME_GetEnergyThreshold() + ENERGY_SCAN_INCREMENT ) < 0xff )
    {
      NLME_SetEnergyThreshold( (uint8)(NLME_GetEnergyThreshold() + ENERGY_SCAN_INCREMENT) );
      osal_set_event( ZDAppTaskID, ZDO_NETWORK_INIT );//重新启动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 );
    }
  }
}
8)下面是ZDO_STATE_CHANGE_EVT事件的处理。
UINT16 ZDApp_event_loop( byte task_id, UINT16 events )
{
  ....................................
if ( events & ZDO_STATE_CHANGE_EVT )
  {
    ZDO_UpdateNwkStatus( devState );
 
    // Return unprocessed events
    return (events ^ ZDO_STATE_CHANGE_EVT);
  }
...........................................
}
在这里调用了ZDO_UpdateNwkStatus( devState );函数。
9void 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;
  }
}
   在这个函数中设置了事件ZDO_STATE_CHANGE也就是状态变化事件,这样整个的流程就到了用户的事件处理函数中,在很多的例子中我们都可以看到这个事件的处理,也是网络建立成功后,或者加入网络成功后,首先要做的事件处理过程。
 
2. 如果协调器没有预编译NV_RESTORE,networkStateNV ==ZDO_INITDEV_NEW_NETWORK_STATE,但由于协调器编译了ZDO_COORDINATOR而没有编译SOFT_START,因此ZDAppDetermineDeviceType()不起作用.因此ZDO_Config_Node_Descriptor.LogicalTypedevStartMode这两个关键参数保持初始化时的值:
ZDO_Config_Node_Descriptor.LogicalType = NODETYPE_COORDINATORdevStartMode = MODE_HARD
uint8 ZDOInitDevice( uint16 startDelay )
{
  uint8 networkStateNV = ZDO_INITDEV_NEW_NETWORK_STATE;
  uint16 extendedDelay = 0;
 
  devState = DEV_INIT;    // Remove the Hold state
 
  // Initialize leave control logic
  ZDApp_LeaveCtrlInit();
 
  // Check leave control reset settings
  ZDApp_LeaveCtrlStartup( &devState, &startDelay );
 
  // Leave may make the hold state come back
  if ( devState == DEV_HOLD )
    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 )
    networkStateNV = ZDO_INITDEV_NEW_NETWORK_STATE;
  else
  {
    // Determine if NV should be restored
    networkStateNV = ZDApp_ReadNetworkRestoreState();
  }
 
  if ( networkStateNV == ZDO_INITDEV_RESTORED_NETWORK_STATE )
  {
    networkStateNV = ZDApp_RestoreNetworkState();
  }
  else
  {
    // Wipe out the network state in NV
    NLME_InitNV();
    NLME_SetDefaultNV();
  }
#endif
 
  if ( networkStateNV == ZDO_INITDEV_NEW_NETWORK_STATE )
  {
    ZDAppDetermineDeviceType();//当没有定义NV_RESTORESOFT_START,时这个不起作用
 
    // 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 );
}
其余部分和定义了NV_RESTORE选项类似.
阅读(941) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~