注册 登录
电子工程世界-论坛 返回首页 EEWORLD首页 频道 EE大学堂 下载中心 Datasheet 专题
王加辉的个人空间 https://home.eeworld.com.cn/space-uid-518156.html [收藏] [复制] [分享] [RSS]
日志

设备启动(转自天运科技)

已有 2551 次阅读2014-1-10 09:13 |个人分类:嵌入式物联网

设备启动准备
一、设备类型选择:
通过Workspace下拉框选择设备的类型:
file:///C:\Users\WANGJI~1\AppData\Local\Temp\ksohtml\wps_clip_image-28484.png
图1:协调器
file:///C:\Users\WANGJI~1\AppData\Local\Temp\ksohtml\wps_clip_image-1594.png
图2:路由器
file:///C:\Users\WANGJI~1\AppData\Local\Temp\ksohtml\wps_clip_image-30279.png
图3:终端节点
协议栈设备类型:
#define ZG_DEVICETYPE_COORDINATOR 0x00
#define ZG_DEVICETYPE_ROUTER 0x01
#define ZG_DEVICETYPE_ENDDEVICE 0x02
#define ZG_DEVICETYPE_SOFT 0x03
ZG_DEVICETYPE_SOFT说明:可选设备类型。可以成为路由器、也可以成为协调器,由后面的程序决定。
初始化:
对于DEVICE_LOGICAL_TYPE的值各逻辑类型设备初始化如下:
// Device Logical Type
//zgDeviceLogicalType = DEVICE_LOGICAL_TYPE
ZGlobals.h文件中设备逻辑类型进行了初始化:
#if defined ( SOFT_START )
#define DEVICE_LOGICAL_TYPE ZG_DEVICETYPE_SOFT //可选择类型
#elif defined( ZDO_COORDINATOR )
#define DEVICE_LOGICAL_TYPE ZG_DEVICETYPE_COORDINATOR //协调器
#elif defined (RTR_NWK)
#define DEVICE_LOGICAL_TYPE ZG_DEVICETYPE_ROUTER //路由器
#else
#define DEVICE_LOGICAL_TYPE ZG_DEVICETYPE_ENDDEVICE //终端
#endif
说明:如果编译了SOFT_START,则初始化设备逻辑类型(DEVICE_LOGICAL_TYPE)为可选择类型(ZG_DEVICETYPE_SOFT)即设备可以作为协调器启动创建一个网络或者作为路由器加入一个已经存在的网络。如果没有编译SOFT_START但编译了ZDO_COORDINATOR,则初始化设备逻辑类型(DEVICE_LOGICAL_TYPE)为协调器(ZG_DEVICETYPE_COORDINATOR),即设备作为路由器启动并创建一个网络。如果没有编译SOFT_STARTZDO_COORDINATOR,但编译了 RTR_NWK,则初始化设备的逻辑类型为路由器,即设备作为路由器启动并加入网络。其他情况则初始化为终端节点并加入网络。
SOFT-START说明:
SOFT_START is a compile option that allows the device to start as a coordinator if one isn't found.
设备启动模式(devStartMode)、设备状态(devState)
启动模式:表示了设备是以何种方式启动
typedef enum
{
MODE_JOIN, //加入
MODE_RESUME, //恢复
//MODE_SOFT, //暂不支持
MODE_HARD, //创建网络
MODE_REJOIN //重新加入
} devStartModes_t;
说明:MODE_JOINMODE_REJOIN 是路由器和终端使用的选项,用来加入或者重新加入网络。而MODE_HARD是协调器使用的选项。用来创建一个网络。而MODE_RESUME是恢复设备原来的状态。
设备状态标识了设备此时的状态:
typedef enum
{
DEV_HOLD, // Initialized - not started automatically
DEV_INIT, // Initialized - not connected to anything
DEV_NWK_DISC, // Discovering PAN's to join
DEV_NWK_JOINING, // Joining a PAN
DEV_NWK_REJOIN, // ReJoining a PAN, only for end devices
DEV_END_DEVICE_UNAUTH, // Joined but not yet authenticated by trust center
DEV_END_DEVICE, // Started as device after authentication
DEV_ROUTER, // Device joined, authenticated and is a router
DEV_COORD_STARTING, // Started as Zigbee Coordinator
DEV_ZB_COORD, // Started as Zigbee Coordinator
DEV_NWK_ORPHAN // Device has lost information about its parent..
} devStates_t;
初始化(以协调器为例)
devStartModedevState的初始化,ZDApp.c
启动模式(devStartModes_t)
#if defined( ZDO_COORDINATOR ) && !defined( SOFT_START )
// Set the default to coodinator
devStartModes_t devStartMode = MODE_HARD;
#else
// Assume joining
devStartModes_t devStartMode = MODE_JOIN;
#endif
说明:如果编译了ZDO_COORDINATOR并且没有编译SOFT_START,则初始化设备启动模式(devStartMode)为MODE_HARD,即协调器创建网络。其他情况初始化设备启动模式(devStartMode)为MODE_JOIN,即设备加入网络。.
以上可以看出,如果编译了 SOFT_START。首先并不是创建网络,而是先加入网络。如果网络加入失败,则考虑创建一个网络。
设备状态(devState):
#if defined( HOLD_AUTO_START )
devStates_t devState = DEV_HOLD;// Initialized - not started automatically
#else
devStates_t devState = DEV_INIT;// Initialized - not connected to anything
#endif
说明:如果编译了HOLD_AUTO_START,则设备状态(devState)为 DEV_HOLD;否则设备状态(devState)为 DEV_INIT
Hold Auto Start说明:
A device will automatically start trying to form or join a network . If the device should wait on a timer or other external event before joining, then HOLD_AUTO_START must be defined. In order to manually start the join process at a later time.
3、有两种方式来设置非自动启动模式:Hold Auto Start
(1)、手工方式:
ZDApp_Init()函数中有个ZDAppCheckForHoldKey();(// Check for manual(手工的) "Hold Auto Start").
void ZDAppCheckForHoldKey( void )
{
//如果检测到按键 SW_BYPASS_START被按下,则将设备的状态置为 DEV_HOLD
#if (defined HAL_KEY) && (HAL_KEY == TRUE)
if ( HalKeyRead () == SW_BYPASS_START)
{
  devState = DEV_HOLD;
}
#endif
}
(2)、预编译方式:
project->options->c/c++compiler->preprocessor->defined symbols下编译选项:HOLD_AUTO_START

ZDApp.c中:
#if defined( HOLD_AUTO_START )
devStates_t devState = DEV_HOLD;
#else
devStates_t devState = DEV_INIT;
#endif
devState初始化为DEV_HOLD.
以上两种方式最终都会设置devState = DEV_HOLD // Initialized - not started automatically
预编译选项:
什么是预编译选项:
Compile options are used to select between features that are provided in the source files. Most compile options act as on/off switches for specific sections within source programs. Some options are used to provide a user-defined
编译选项是将源程序里提供的特性选择应用。大多数编译选项是充当开关作用。直接通过编译选项来决定是否应用某一特性。
编译选项配置的两种方式:
ATOOL文件夹下的三个配置文件
Tools文件夹下查看f8wCoord.cfg、f8wRouter.cfg、f8wEdev.cfg三个配置文件信息。
协调器:f8wCoord.cfg配置文件中同时编译了路由功能RTR_NWK和协调器功能ZDO_COORDINATOR
/* Coordinator Settings */
-DZDO_COORDINATOR // Coordinator Functions
-DRTR_NWK // Router Functions
路由器:f8wRouter.cfg配置文件中编译了路由功能RTR_NWK
/* Router Settings */
-DRTR_NWK // Router Functions
终端:f8wEdev.cfg配置文件中没有编译这两个功能.
/* */
通过配置文件我们也可以看出协调器不仅具有协调器的作用还可以充当路由器,这就是我们所说的如果当协调器创建完网络后就可以认为协调器就变成了路由器了。路由器只有路由的功能,而终端设备没有路由的功能,更没有协调器的功能。
B: 通过Option选项下的配置条目进行配置
常用配置选项:
NV_RESTORE
SOFT_START
LCD_SUPPORTED=DEBUG
POWER_SAVING
REFLECTOR
RTR_NWK
ZDO_COORDINATOR
HOLD_AUTO_START
网络状态
// ZDOInitDevice return values
#define ZDO_INITDEV_RESTORED_NETWORK_STATE         0x00
#define ZDO_INITDEV_NEW_NETWORK_STATE                        0x01
#define ZDO_INITDEV_LEAVE_NOT_STARTED                         0x02
说明:ZDO_INITDEV_RESTORED_NETWORK_STATE 网络状态为恢复状态,即如果编译了NV-RESTORE,则设备将恢复为上次的状态。ZDO_INITDEV_RESTORED_NETWORK_STATE 网络状态为新状态或者没有可以恢复的状态,此时设备将直接创建或者加入一个网络。 ZDO_INITDEV_LEAVE_NOT_STARTED为离开下次启动
网络恢复 NV_RESTORE
Devices that have successfully joined a network can “restore the network” (instead of reforming by OTA messages) even after losing power or battery. This automatic restoration can be enabled by defining NV_RESTORE and/or NV_INIT.
地址结构体说明:
typedef struct
{
union
{
uint16 shortAddr;
ZLongAddr_t extAddr;
} addr;
byte addrMode;
} zAddrType_t;
包括有地址模式和地址。
地址模式有:
typedef enum
{
  afAddrNotPresent = AddrNotPresent,
  afAddr16Bit      = Addr16Bit,
  afAddrGroup      = AddrGroup,
  afAddrBroadcast  = AddrBroadcast
} afAddrMode_t
特殊地址:
0xFFFF-------向所有设备广播信息
0xFFFE-------绑定时使用该地址
0xFFFD------向所有非睡眠的节点广播
0xFFFC------向所有路由器广播(包括协调器)
实例:
ZDAppNwkAddr.addrMode = Addr16Bit;
ZDAppNwkAddr.addr.shortAddr = INVALID_NODE_ADDR;
注册机制说明:
为什么要注册?
协议栈三大注册机制:端点endpoint的注册、按键KEY的注册和ZDOMsg消息注册。这里我们看endpoint的注册。
Endpoint的注册最终调用了函数*afRegisterExtended();
epList_t *afRegisterExtended( endPointDesc_t *epDesc, pDescCB descFn )
{
if ( ep )//成功分配空间
{
// Fill in the new list entry
ep->epDesc = epDesc;
ep->flags = eEP_AllowMatch;
ep->pfnDescCB = descFn;
ep->nextDesc = NULL;
if ( epList == NULL )
epList = ep; // Make this the first entry
else
{
epSearch = epList;
while( epSearch->nextDesc != NULL )
epSearch = epSearch->nextDesc;
//循环到最后,就是依次“下一个”,直到最后一个,在最后把新的加上
epSearch->nextDesc = ep;
}
……
}
协调器创建网络规范说明
file:///C:\Users\WANGJI~1\AppData\Local\Temp\ksohtml\wps_clip_image-22916.png
在一个zigbee网络中,只有协调器(coordinator)才可以建立网络,建立网络的过程是通过原语实现的。
首先协调器的应用层调用NLME_NETWORK_FORMATION.request原语发出建立网络请求,网络层(NWK)收到这个原语,向MAC层发送MLME_SCAN.request原语执行信道能量扫描(energy scan)和活动情况扫描(active scan),(在IEEEE802.15.4协议中规定,在2.4G频段,共有16个信道,每个信道的带宽为5M)。
信道能量(energy scan)扫描是为了找到规定信道中那一个最安静,并标注为可用信道 。每扫描一个信道持续约半秒,如果配置为16个信道全部扫描共需约八秒。当MAC层执行完信道能量扫描(energy scan)后,由MAC层向NWK层发送MLME_SCAN.confirm确认原语。当信道能量扫描(energy scan)完成后下一步就在可用信道中执行活动情况扫描(active scan),
活动情况扫描(active scan)MAC层通过发送MAC帧,检测是否有回应判断信道是否有其他网络存在,活动情况扫描(active scan)的目的在于防止在同一个信道上建立两个具有相同PANID的网络。当MAC层执行活动情况扫描(active scan)后,由MAC层向NWK层发送MLME_SCAN.confirm确认原语。并将可用的可用的PANID和信道发送给上层。
当执行完上述扫描后,如果获得了可用的信道和PANID,则网络层(NWK)向MAC发送MLME_START.request原语请求启动创建网络,当网络创建成功MAC层向网络层(NWK)发送MLME_START.confirm原语告知结果。网络层(NWK)向应用层(APS)发送NLME_NETWORK_FORMATION.confirm原语告知结果。
协调器启动协议栈代码说明
1、协调器预编译信息
Tools文件夹下查看f8wCoord.cfg编译ZDO_COORDINATORRTR_NWK.
在Option选项卡编译:CC2430EB;ZTOOL_P1;MT_TASK;MANAGED_SCAN
2、具体流程
首先协议栈以主函数为入口点启动协议栈。在主函数中调用了osal_init_system()进行了OS的初始化
ZSEG int main( void )
{
  osal_int_disable( INTS_ALL );
  HAL_BOARD_INIT();
  zmain_vdd_check();
  ……
  osal_init_system()
  ……
}
OS 初始化系统,初始化了内存、定时器、电源管理以及系统任务等。
byte osal_init_system( void )
{
  osal_mem_init();
  osal_qHead = NULL;
  osalTimerInit();
  osal_pwrmgr_init();
  // Initialize the system tasks.初始化任务系统
  osalInitTasks();
}
在任务初始化osalInitTasks()函数中,OS为每一层分配一个任务ID,使得协议栈成为一个多任务的系统
void osalInitTasks( void )
{
  uint8 taskID = 0;
  tasksEvents = (uint16 *)osal_mem_alloc( sizeof( uint16 ) * tasksCnt);
  osal_memset( tasksEvents, 0, (sizeof( uint16 ) * tasksCnt));
  macTaskInit( taskID++ );
  nwk_init( taskID++ );
  Hal_Init( taskID++ );
#if defined( MT_TASK )
  MT_TaskInit( taskID++ );
#endif
  APS_Init( taskID++ );
  ZDApp_Init( taskID++ );
  SampleApp_Init( taskID );
}
函数说明:
1、 void *osal_mem_alloc( uint16 size )
说明:该函数在内存中开辟了size大小的一块内存
参数说明:size-----------------------要开辟内存的大小
返回值:指针
2、void *osal_memset( void *dest, byte value, int len )
说明:该函数将起始地址为dest,长度为len的一块内存的值设置为value
参数说明:*dest--------------------起始地址
                   value-------------------要设置成的值
                   len----------------------设置内存块的大小
返回值:指针
其他说明:
由编译选项可知MT_TASK编译了,可以知道函数中
#if defined( MT_TASK )
  MT_TaskInit( taskID++ );
#endif
将被执行。
即最后ZDApp_Init(byte task_id) 传递的任务id的值为5,而SampleApp_Init( taskID )传递的任务id为6.
void ZDApp_Init(byte task_id)
{
    uint8 capabilities;
//保存下了OS分配的任务id。即ZDAppTaskID=5
ZDAppTaskID=task_id;
……
//如果程序运行到这里正好SW_1被按下,则会设置设备的状态(devState)为DEV_HOLD,从而避开网络初始化
ZDAppCheckForHoldKey();
//Initialize ZDO items and setup the device – type of device to create.
ZDO_Init();
AfRegister( (endPointDesc_t *)&ZDApp_epDesc );
……
// Start the device
if ( devState != DEV_HOLD )
{
     ZDOInitDevice( 0 );
}
else
{
    // Blink LED to indicate HOLD_START
    HalLedBlink ( HAL_LED_4, 0, 50, 500 );
}
ZDApp_RegisterCBs();
}
函数说明:
1、 void ZDAppCheckForHoldKey( void )
说明:如果在设备启动的时候手动将SW_1按下,则将设备状体置为DEV_HOLD,从而避开网络初始化。
参数说明:无
返回值:无
2、void ZDO_Init( void )
说明:该函数是ZDObject 和 ZDProfile 初始化函数,在该函数中调用了ZDODeviceSetup()根据编译选项的不同进行了相关的初始化。
参数说明:无
返回值:无
3、afStatus_t afRegister( endPointDesc_t *epDesc )
说明:该函数为zigbee设备注册一个新的端口。应用程序的每一个端口都必须使用该函数进行注册。
参数说明:epDesc  -----------------------指向端口描述符的指针
返回值:afStatus_t-------------------------ZcomDef.hZstatus_t结构体中定义的状态值
4、uint8  ZDOInitDevice(uint16 startDelay)
说明:该函数为ZDO设备初始化函数,该函数会检查系统是否需要恢复,如果需要恢复则恢复,否则启动设备。
参数说明:startDelay----------------------------------------------------------------设备启动延时
返回值:ZDO_INITDEV_RESTORED_NETWORK_STATE-------------设备恢复成功
ZDO_INITDEV_NEW_NETWORK_STATE----------------------设备为新状态
ZDO_INITDEV_LEAVE_NOT_STARTED-------------------------离开下次启动

uint8 ZDOInitDevice( uint16 startDelay )
{
  //初始化设备网络状态为ZDO_INITDEV_NEW_NETWORK_STATE:新的网络状态.
  uint8 networkStateNV = ZDO_INITDEV_NEW_NETWORK_STATE;
  uint16 extendedDelay = 0;
  devState = DEV_INIT;    // Remove the Hold state/设备的状态为:设备初始化
  ……
#if defined ( NV_RESTORE )
  if ( HalKeyRead() == SW_BYPASS_NV )
  //如果SW_BYPASS_NV按键此时被按下则会避开NV-RSTORE,将网络状态置为新状态
  networkStateNV = ZDO_INITDEV_NEW_NETWORK_STATE;
  else//决定NV是否需要恢复
  {
    //函数返回的设备网络状态是新的网络状态或者是恢复的网络状态;以此
    //来确定要不要读取NV里相应条目来恢复网络先前状态
    networkStateNV = ZDApp_ReadNetworkRestoreState();
   //通过函数ZDApp_ReadNetworkRestoreState的返回值决定NV是否需要恢复。
  }
//函数ZDApp_ReadNetworkRestoreState()用来检测是否需要恢复,如果返回值为//ZDO_INITDEV_RESTORED_NETWORK_STATE则恢复网络先前的状态
if ( networkStateNV == ZDO_INITDEV_RESTORED_NETWORK_STATE )
{
//恢复设备先前的网络状态参数并且
  //设置devStartMode = MODE_RESUME
  networkStateNV = ZDApp_RestoreNetworkState();
    //通过函数ZDApp_RestoreNetworkState()恢复网络先前的状态
  }
  else  //如果没有可以恢复的则执行下面代码
  {
    // Wipe out the network state in NV
    NLME_InitNV();
    NLME_SetDefaultNV();  //设置默认NV条目
  }
#endif
//以下为没有定义NV_RESTORE,即不恢复网路状态或者恢复失败。如果网络状态为//ZDO_INITDEV_NEW_NETWORK_STATE则执行以下代码
if ( networkStateNV == ZDO_INITDEV_NEW_NETWORK_STATE )
{
   //根据编译选项决定设备的类型,但仅在编译了SOFT_START 才起作用
    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 、uint8 ZDApp_ReadNetworkRestoreState( void )
说明:通过该函数读取NV ZCD_NV_STARTUP_OPTION的值决定是否需要恢复先前的网络状态。
参数说明:无
返回值: ZDO_INITDEV_NEW_NETWORK_STATE-------------新网络状态,无需恢复
       ZDO_INITDEV_RESTORED_NETWORK_STATE-----------恢复先前网络状态
2、 uint8 ZDApp_RestoreNetworkState( void )
说明:通过该函数可以恢复先前的网络状态
参数说明:无
返回值: ZDO_INITDEV_RESTORED_NETWORK_STATE-------网络状态恢复成功
     ZDO_INITDEV_NEW_NETWORK_STATE----------------网络状态恢复失败
3、void ZDAppDetermineDeviceType( void )
说明:通过该函数决定启动时设备的逻辑类型及启动模式,但仅在编译了SOFT_START 才起作用
参数说明:无
返回值:无
4、void ZDApp_NetworkInit( uint16 delay )
说明:通过该函数触发设备启动函数,该函数触发了ZDO_NETWORK_INIT事件。
参数说明:delay---------------------------  触发ZDO_NETWORK_INIT的延时
返回值:无
其他说明:
在函数ZDApp_NetworkInit()的参数值的大小,这里extendedDelay的大小足够协议栈初始化其它层。因为该参数在传递给函数后定时触发事件ZDO_NETWORK_INIT,而溢出时间正是extendedDelay。在协议栈提供的SampleApp实例中,编译了SOFT_START,当执行到这里的时候,设备类型为可选类型(ZG_DEVICETYPE_SOFT)。延时extendedDelay确保了当设备开始创建网络的时候已经初始化了SampleApp的应用层,在应用层通过跳线决定了设备的类型是协调器或是路由器。
通过上面函数的分析我们可以看出设备在启动的时候可以利用NV恢复上次的网络状态,也可以不恢复直接开始创建或加入网络,恢复与否取决于是否编译了NV_RESTORE。ZDOInitDevice( uint16 startDelay )函数的最后触发了事件ZDO层的ZDO_NETWORK_INITZDO层事件处理函数为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 (events ^ ZDO_NETWORK_INIT);
  }
……
}
函数说明:
1 、void ZDO_StartDevice( byte logicalType, devStartModes_t startMode, byte beaconOrder, byte superframeOrder )
说明:通过该函数根据设备类型及设备启动方式的不同启动设备。
参数说明:logicalType-----------------------------设备的逻辑类型
                    startMode------------------------------设备的启动模式
          beaconOrder---------------------------信标时间
              superframeOrder---------------------超帧长度
返回值:无
ZDApp_event_loop()中处理ZDO_NETWORK_INIT前,即设备启动之前设备的状态设置为设备初始化(DEV_INIT)。然后调用了ZDO_StartDevice()启动设备。下面仔细分析设备的启动函数ZDO_StartDevice()
void ZDO_StartDevice( byte logicalType, devStartModes_t startMode, byte beaconOrder, byte superframeOrder )
{
  #if defined(ZDO_COORDINATOR)  //如果定义了协调器
  if ( logicalType == NODETYPE_COORDINATOR )  //node type coordinator  
  {
    //协调器的两种启动方式:
    if ( startMode == MODE_HARD )//普通的启动,直接创建网络
    {
      devState = DEV_COORD_STARTING;//设备状态为协调器正在启动
      // NLME_NetworkFormationRequest为系统函数,用于协调器创建网络
      ret = NLME_NetworkFormationRequest( zgConfigPANID, zgDefaultChannelList,
      gDefaultStartingScanDuration,beaconOrder,  superframeOrder, false );
    }
    else if ( startMode == MODE_RESUME )//网络恢复
   {
          // Just start the coordinator
      devState = DEV_COORD_STARTING; //设备状态为协调器正在启动
          //如果是协调器恢复则会以路由器启动
      ret = NLME_StartRouterRequest( beaconOrder, beaconOrder, false );
   }
    ……
}
#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; //此时设备状态为“发现网络”      
  #if defined( MANAGED_SCAN )  
      //如果编译了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 )//路由器的恢复
      {
        ……
        nwk_ScanJoiningOrphan(&scanCnf);//路由器以孤点方式加入网络
        ret = ZSuccess;
      }
      else//终端节点的恢复
      {
        devState = DEV_NWK_ORPHAN;
        ret = NLME_OrphanJoinRequest( zgDefaultChannelList,
                                      zgDefaultStartingScanDuration );
      }
    }
    ……
  }
#endif  //!ZDO COORDINATOR || SOFT_START
  if ( ret != ZSuccess )//如果没有启动,则重新再来一次!
   osal_start_timerEx(ZDAppTaskID,ZDO_NETWORK_INIT, NWK_RETRY_DELAY );
}
函数说明:
1 、ZStatus_t NLME_NetworkFormationRequest( uint16 PanId, uint32 ScanChannels,
                            byte ScanDuration, byte BeaconOrder,  
byte SuperframeOrder, byte BatteryLifeExtension  )
说明:通过该函数协调器可以创建一个网络。该函数会触发其对应的回调函数
void ZDO_NetworkFormationConfirmCB( ZStatus_t Status )。
2、ZStatus_t NLME_StartRouterRequest( byte BeaconOrder, byte SuperframeOrder,
                                                byte BatteryLifeExtension  )
说明:通过该函数可以启动一个路由器或者完成协调器的恢复。该函数会触发其对应的回调函数void ZDO_StartRouterConfirmCB( ZStatus_t Status )。
3、ZStatus_t NLME_NetworkDiscoveryRequest( uint32 ScanChannels,
byte scanDuration)
说明:通过该函数请求网络层发现邻居路由器节点。该函数会触发其对应的回调函数
ZStatus_t ZDO_NetworkDiscoveryConfirmCB( byte ResultCount,
                                                    networkDesc_t *NetworkList )
4、void nwk_ScanJoiningOrphan( ZMacScanCnf_t *param )
说明:通过该函数路由器会以孤点的方式加入网络。该函数会触发其回调函数
void ZDO_JoinConfirmCB( uint16 PanId, ZStatus_t Status )
5、ZStatus_t NLME_OrphanJoinRequest( uint32 ScanChannels, byte ScanDuration )
说明:通过该函数终端节点以孤点的方式加入网络。该函数会触发其回调函数
void ZDO_JoinConfirmCB( uint16 PanId, ZStatus_t Status )
我们这里是协调器的启动,并且没有用到网络恢复,所以在该函数中会调用
NLME_NetworkFormationRequest( zgConfigPANID, zgDefaultChannelList,
                                          zgDefaultStartingScanDuration, beaconOrder, superframeOrder, false )
来创建一个新的网络。调用NLME_NetworkFormationRequest()触发了其对应的回调函数ZDO_NetworkFormationConfirmCB()
void ZDO_NetworkFormationConfirmCB( ZStatus_t Status )
{
#if defined(ZDO_COORDINATOR)
  nwkStatus = (byte)Status;      //将状态保存到nwkStatus中
  if ( Status == ZSUCCESS )          //网络创建成功
  {
    //点亮LED_3标识协调器已经创建网络成功
HalLedSet ( HAL_LED_3, HAL_LED_MODE_ON );
// LED off forgets HOLD_AUTO_START
    HalLedSet (HAL_LED_4, HAL_LED_MODE_OFF);
……
  }
#if defined(BLINK_LEDS)
  else //没有成功创建网络将闪烁LED提示用户
   HalLedSet ( HAL_LED_3, HAL_LED_MODE_FLASH );  
#endif
//触发网络已经启动事件 ZDO_NETWORK_START
  osal_set_event( ZDAppTaskID, ZDO_NETWORK_START );
#endif  //ZDO_COORDINATOR
}
不论是否创建了网络,函数最后触发事件ZDO_NETWORK_START,同样在ZDApp_event_loop()中处理ZDO_NETWORK_START事件。注意:每一层处理每一层的事件,不允许越权处理也不允许不处理,两种情况系统都是无法运行的。
UINT16 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()ZDO_NETWORK_START进行处理
void ZDApp_NetworkStartEvt( void )
{
  if ( nwkStatus == ZSuccess )//如果创建网络成功
  {
    // Successfully started a ZigBee network
    if ( devState == DEV_COORD_STARTING )
    {
      devState = DEV_ZB_COORD;//将设备的状态修改为DEV_ZB_COORD
}
//设置电源,因为是协调器所以需要PWRMGR_ALWAYS_ON 而不能电池供电
osal_pwrmgr_device( PWRMGR_ALWAYS_ON );
//触发ZDAppTaskID的事件ZDO_STATE_CHANGE_EVT
    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//如果能量最大也没有成功进入dormant状态
    {
      // Failed to start network. Enter a dormant state (until user intervenes)
      devState = DEV_INIT;
      osal_set_event( ZDAppTaskID, ZDO_STATE_CHANGE_EVT );
    }
  }
}
函数说明:
1 、void osal_pwrmgr_device( uint8 pwrmgr_device )
说明:通过该函数对设备的电源进行管理,如果设备是终端可以设置为电池供电,如果设备具有路由功能则不能设置为
参数:pwrmgr_device ------------------------------------------供电模式
启动供电模式协议栈里提供了两种:
PWRMGR_ALWAYS_ON和 PWRMGR_BATTERY分别对应全功能设备和半功能设备。
返回值:无
2 、uint8 NLME_GetEnergyThreshold( void )
说明:该函数获取设备启动的能量值大小。
参数:无
返回值:设备启动能量大小
3 、void NLME_SetEnergyThreshold( uint8 value )
说明:该函数设定设备启动时能量值的大小
参数:value---------------------------------------------------启动能量值
返回值:无
如果设备成功加入创建网络,则会触发事件ZDO_STATE_CHANGE_EVT,此事件同样在ZDApp_event_loop中处理
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 )ZDO_STATE_CHANGE_EVT进行处理,注意函数ZDO_UpdateNwkStatus()的参数是devState,所以虽然不管协调器是否成功创建网络都调用了该函数,但是通过devState可以感受到两者受到的待遇是不一样的,胜者为王。
void ZDO_UpdateNwkStatus( devStates_t state )
{
  // Endpoint/Interface descriptor list.
  epList_t *epDesc = epList;              //endpoint列表
  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;
        //上面获取了endpoint列表,这里将信息msgPtr发送给所有层
        osal_msg_send( *(epDesc->epDesc->task_id), (byte *)msgPtr );
      }
    }
    epDesc = epDesc->nextDesc;
  }
}
利用函数osal_msg_send向除ZDO以外的所以endpoint都发送了事件ZDO_STATE_CHANGE。含有endpoint的对应层将会接收到ZDO_STATE_CHANGE事件。自此协调器的启动全部完成了。
终端、路由器加入网络规范说明
1、允许加入网络
通过NLME-PERMIT-JOINING.request原语来允许设备与网络连接。只有设备为ZigBee协调器或者路由器时,才能企图允许其他设备与网络连接。具体流程图如下:
file:///C:\Users\WANGJI~1\AppData\Local\Temp\ksohtml\wps_clip_image-24338.png
当此过程开始时,若设置PermitDuration参数为0x00则启动该过程,并且网络层管理实体把在MAC层的macAssociationPermit PIB属性设置为FALSEMAC层的属性设置通过MLME-SET. Request原语来完成。
当此过程开始时,若设置PermitDuration参数为一个0x010xFE之间的值时,则网络层管理实体将把在MAC层中的macAssociationPermit PIB属性设置为TRUE,并且网络层管理实体将启动一个定时器,用来对一个特定的时间进行计时,达到该时间时,定时器停止计时,在该定时器停止时,网络层管理实体将把MAC层中的macAssociationPermit PIB属性设置为FALSE
当此过程开始时,若设置PermitDuration参数设置为0xFF,则网络层管理实体将把在MAC层中的macAssociationPermit PIB属性设置为TRUE,以表示无限定时间,除非发送另一个NLME-PERMITJOINING.request原语。
2、 加入网络的方式
在一个网络中具有从属关系的设备允许一个新设备连接时,它就与新连接的设备形成了一个父子关系。新设备成为子设备,而第一个设备为父设备。一个子设备通过一下两个方法加入到网络中:
① 子设备用MAC连接程序来加入网络;
② 在设备直接同一个预先所指定的父设备连接来加入网络。
(1) 联合方式加入网络
▶子设备流程
这里详细介绍了一个子设备同一个网络连接的过程,以及一个ZigBee协调器或路由器(父设备)在接收到连接请求命令后所采取的措施。子设备具体流程图如下
file:///C:\Users\WANGJI~1\AppData\Local\Temp\ksohtml\wps_clip_image-9287.png
子设备连接网络的流程为:首先,应用层发送NLME-NETWORK-DISCOVERY.request原语,其中扫描参数(ScanChannels)设置为网络将要扫描的信道,扫描持续时间参数(ScanDuration)设置为扫描每个信道所需要的时间。网络层接收到该原语后,将发送MLME-SCAN.request原语请求MAC层执行一个主动扫描。
扫描设备的MAC层在扫描过程中一旦接收到有效长度不为零的信标帧时,将向其网络层发送MLME-BEACON-NOTIFY.indication原语。该原语中包括的信息为信标设备地址、是否允许连接和信标载荷。扫描设备的网络层将检查信标载荷中的协议标识符域的值,并验证它是否与ZigBee协议识别符匹配。如果不匹配,则忽略该信标。反之,设备将从接收到的信标中,将相关的信息复制到的的邻居表中。
一旦MAC层完成对信道的扫描,在向网络层管理实体发送MLME-SCAN.confirm原语后,网络层将发送NLME-NETWORK-DISCOVERY.confirm原语,其参数包括扫描得到的网络描述参数。这些描述参数为ZigBee版本号、堆栈结构、扩展个域网网标识符(PANId)、个域网网标识符(PANId)、逻辑信道和是否允许连接的信息。
其上层收到NLME-NETWORK-DISCOVERY.confirm原语,就可得到目前邻居网络的信息。以便发现更多的网络,上层可以选择重新执行网络发现命令。如果不重新执行,它将从所发现的网络中选择一个网络进行连接,即通过发送NLME-JOIN.request原语进行连接,其中RejoinNetwork参数设置为0x00,且JoinAsRoute参数设置为设备是否同网络连接。
只有那些还没有同网络连接的设备才能执行该连接流程。如果任何其他设备执行这个流程,则网络管理实体将终止这个流程,并且向上层发送状态参数为INVALID_REQUEST的NLME-JOIN.confirm原语。
对于一个还没有同网络连接的设备,NLME-JOIN.request原语将使得网络层在邻居表中搜索一个合适的父设备。一个合适的父设备必须具备2个条件:允许连接;链路成本最大为3。如果在邻居表中存在潜在的父设备子域,则该子域设置为1
如果邻居表中不包括合适的父设备,网络层管理实体将发送参数状态为NOT_PERMITTED的NLME-JOIN.confirm原语。如果邻居表中包括不只一个合适的父设备,则选择具有到ZIgBee协调器最小深度的设备。如果存在多个到ZigBee协调器最小深度的设备,则可在他们之间任意地选择一个。
一旦选择了一个合适的父设备,网络层管理实体将向MAC层发送MLMEASSOCIATE. Request原语,其原语的地址参数为在邻居表中所选择的设备地址,并通过LMEASSOCIATE. confirm原语将连接的状态返回到网络层管理实体。
如果试图连接网络没有成功,网络层将收到从MAC层发送来的MLMEASSOCIATE. Confirm原语,其状态参数为错误代码。如果状态参数表明拒绝与邻居设备连接(即PAN容量或者PAN接入拒绝),则尝试连接的设备将把邻居表中潜在的父设备子域设置为0,以表示尝试连接失败。潜在的父设备子域为0使得网络层将不会发送另一个连接请求原语去尝试连接该邻居设备。每次发送MLMESCAN.request原语,将邻居表中的潜在的父设备子域设置为1
如果潜在的父设备不允许连接新的路由器(路由器的最大数,已经连接设备的最大路由器),并且要连接的设备将JoinAsRouter参数设置为TRUE,则连接请求也可能不成功。在这种情况下,NLMEJOIN.confirm原语将给出NOT_PERMITTED的状态,子设备应用层将希望再次尝试连接,但只能作为一个终端设备,将发送另一个NLME-JOIN.request原语,且原语的JoinAsRouter参数设置为FALSE
如果尝试连接网络失败,网络层管理实体将试图从邻居表中找寻一个合适的父设备。如果不存在这样的设备,网络管理实体将发出NLME-JOIN.confirm原语,其状态参数值为MLME-ASSOCIATE.confirm原语所返回的值。
如果尝试连接失败,并且存在第二个邻居的设备,该设备可以作为合适的父设备,则网络层启动连接第二个设备的MAC层连接程序。网络层将不断重复这个过程,知道直到成功的与网络连接或者已尝试所有可能连接的网络。
如果设备不能成功的连接由上层所指定的网络,网络管理实体将通过NLME-JOIN. confirm原语来终止该过程,其原语的状态参数为最后接收到的MLME-ASSOCIATE.confirm原语所返回的值。在这种情况下,设备将不接收有效的逻辑地址,也不允许在网络中通信。
如果尝试连接网络成功,网络层收到MLME-ASSOCIATE.confirm原语,该原语中将包括16位的逻辑地址,该逻辑地址在网络中是唯一的,并且该子设备在未来的通信中将使用这个逻辑地址。然后,网络层将设置相对应的邻居表的关系域,以表示邻居设备为它的父设备。此时,父设备将把新连接的设备增加到它的邻居表中。而且网络层将更新NIBnwkShortAddress的值。
如果设备试图同一个安全网络连接且它是路由器,则在发送信标前必须等待父设备对它进行验证,验证之后就可以进行连接。因而,该设备将等待上层发送来的NLME-START-ROUTER.request原语。如果设备为一个路由器,当它的网络层管理实体接收到该原语,就发送MLME-START.request原语。如果NLME-STARTROUTER.request原语由一个终端设备发出,则网络层将发出NLME-START-ROUTER.confirm原语,其原语状态参数设置为INVALID_REQUEST。
当设备成功的同网络连接,如果设备是路由器且上层将发出NLME-START-ROUTER. request原语,则网络层将向MACMLME-START.request原语。PANIdLogicalChannel、BeaconOrder和SuperframeOrder参数设置将设置为它所对应的父设备在邻居表中的所对应的参数值。而PANCoordinator和CoordRealignment参数都会设置为FALSE,网络层接收到MLME-START.confirm原语后,将发送具有相同状态的NLME-START-ROUTER.confirm原语。
▶父设备流程
ZigBee协调器或路由器(父设备)在接收到连接请求命令后所采取的措施。父设备具体流程图如下
file:///C:\Users\WANGJI~1\AppData\Local\Temp\ksohtml\wps_clip_image-26616.png
ZigBee协调器或者路由器使用MAC层将一个设备同它所在的网络进行连接,其流程由来自于MAC层的MLMEASSOCIATE.indication原语来进行初始化。仅仅当这些设备为协调器或者路由器,并且允许同网络连接的设备时,才能执行这个流程。如果设备为其他设备,网络层管理实体将终止这个流程。
当这个流程开始后,潜在父设备的网络层管理实体首先将要确定设备是否愿意同已经存在的网络连接。为了确定这一点,网络层管理实体将会搜索的邻居表以确定是否能找到一个匹配的64位扩展地址。如果搜索到相匹配的地址,则网络层管理实体将检查在邻居表中给定的设备能力是否匹配设备类型。如果设备类型也匹配则网络层管理实体将得到一个相应的16位网络地址,并且向MAC层发送连接响应。如果设备类型不匹配,网络管理实体将移除邻居表中设备的所有记录且重新启动MLME-ASSOCIATION.indication。如果搜索不到相匹配的地址,如果可能,网络管理实体将分配一个16位的网络地址给这个新设备。
如果潜在的父设备没有能力接受更多的子设备(用完了它的分配地址空间),则网络管理实体将终止该流程,然后向MAC层发出MLME-ASSOCIATE.response原语对其响应。该原语的状态参数将表明PAN的能力。
如果同意连接请求,则父设备的网络管理实体将使用设备所提供的信息在它的邻居表中为子设备创建一个新的入口。并且随后向MAC层发送表明连接成功的MLME-ASSOCIATE. response原语。MLME-COMMSTATUS.indication原语将传送给子设备的响应状态回到网络层。
如果传送不成功(即MLME-COMM-STATUS.indication原语状态参数不为SUCCESS),则网络层管理实体将终止程序。如果传送成功,网络层管理实体将通过向上层发送NLME-JOIN.indication原语,表明子设备已经成功地同网络连接。
(2) 孤点或者重新连接网络
下面介绍了一个已经直接同网络连接的设备(通过孤点方式)或者一个以前同网络连接的设备,但目前没有和它的父设备失去联系,它将(通过孤点方式重新连接)如何执行孤点连接流程同网络连接。
一个已经同网络连接的设备为了完成建立它与其父设备的关系,应开始执行孤点流程,设备的应用层将决定是否开始该流程,如果开始,则应用层将通过网络层打开电源。
如果一个以前已经同网络连接的设备,其网络层管理实体将不断地接收到来自于MAC层发送的通信失败通知,则它将开始执行孤点流程。
▶子设备流程
这里详细介绍了一个子设备孤点或者重新连接网络的过程,以及一个ZigBee协调器或路由器(父设备)在接收到请求命令后所采取的措施。子设备具体流程图如下
file:///C:\Users\WANGJI~1\AppData\Local\Temp\ksohtml\wps_clip_image-25641.png
子设备通过发送NLME-JOIN.request原语来开始执行孤点方式同网络连接,其原语的RejoinNetwork参数设置为0x01.
当开始执行流程时,首先,网络层管理实体请求MAC层对ScanChannels参数给定的信道进行孤点扫描。通过向MAC层发送MLMESCAN.request原语开始进行孤点扫描,其扫描的结果通过MLME-SCAN.confirm原语返回到网络层管理实体。
如果孤点扫描成功(即子设备扫描到父设备),网络层管理实体将通过发送NLME-JOIN.confirm原语向其上层通告请求连接或者重新连接网络已成功执行,其原语状态参数设置为SUCCESS。
注意如果子设备是第一次连接或者以前已经同网络连接但是保持树形深度信息失败(树形深度在3.4.6.1中规定),它有可能在网络中不能正确操作,对于消息的恢复超出了规定的范围。
如果孤点扫描不成功(即没有扫描到父设备),网络层管理实体将终止该流程,并通过发送NLME-JOIN.confirm原语向其上层通告没有扫描到网络,其原语的状态参数设置为NO_NETWORKS。
▶父设备流程
ZigBee协调器或路由器(父设备)在接收到请求命令后所采取的措施。父设备连接或者重新连接孤点设备的流程图如下
file:///C:\Users\WANGJI~1\AppData\Local\Temp\ksohtml\wps_clip_image-22072.png
一个父设备收到来自于MAC层发送来的MLME-ORPHAN.indication原语时,就可得知存在一个孤点设备。仅仅当设备为Zigee协调器或者路由器(也就是具有父设备能力)时,才能执行该连接流程;否这其他设备执行该流程时,网络层管理实体将终止该流程的执行。
该流程开始执行时,网络层管理实体首先判断该孤点是否是它的子设备。为了对其进行判断,需要将孤点设备的扩展地址和邻居表中所记录的子设备地址向比较,如果存在向匹配的地址(即古典设备是它的子设备),则网络层管理实体将得到其相对应的16位网络地址以及它随后对MAC层的孤点响应状态。网络层管理实体通过向MAC层发送MLME-ORPHAN.response原语对其孤点进行响应,并且通过MLME-COMM-STATUS.indication原语得到其传输状态。
如果不存在相匹配的地址(即孤点设备不是它的子设备),流程终止且不通知上层。
路由器和终端加入网络基本过程
1NLME_NetworkDiscoveryRequest() 发现网络
2ZDO_NetworkDiscoveryConfirmCB()对找到的网络进行检测
这个函数如果检测到有个合适的网络可以加入,则触发事件ZDO_NWK_DISC_CNF
注意:即使没有找到一个合适的网络,也触发了ZDO_NWK_DISC_CNF,但是状态值不一样,在ZDO_NWK_DISC_CNF的处理代码段进行了分别对待。
3ZDO_NWK_DISC_CNF的处理
a)、如果没有找到一个合适的可以加入的网络
如果编译了SOFT_START则会以协调器的方式启动并创建一个网络。
b)、如果找到一个合适的可以加入的网络
根据设备启动方式的不同,选择加入或者重新加入网络---如果失败则重新加入初始化
加入网络函数NLME_JoinRequest()将触发回调函数
4、加入网络回调函数ZDO_JoinConfirmCB( uint16 PanId, ZStatus_t Status )
这个函数触发了事件ZDO_NWK_JOIN_IND。并且在函数开始的时候保存了网络状态值
nwkStatus = (byte)Status,将当前的状态值赋给了全局变量nwkStatus
5、事件ZDO_NWK_JOIN_IND的处理调用了函数ZDApp_ProcessNetworkJoin( void )
6ZDApp_ProcessNetworkJoin( void )说明:
a)、如果设备加入网络成功(由全局变量nwkStatus判断),ZDApp_ProcessNetworkJoin 函数触发了 ZDO_STATE_CHANGE_EVT 事件。---------这里终端设备的启动完成了。
b)、如果加入网络后设备为路由器,则以路由器启动,调用路由器启动函数NLME_StartRouterRequest( 0, 0, false )
7、路由器启动函数NLME_StartRouterRequest()触发其对应的回调函数
void ZDO_StartRouterConfirmCB  ( ZStatus_t Status )
说明:该回调函数触发了事件ZDO_ROUTER_START
8ZDO_ROUTER_START事件的处理
说明:处理分为两块
a)、电源控制
b)、触发事件 ZDO_STATE_CHANGE_EVT---------这里终端路由的启动完成了。
可以看出路由器的启动是设备加入网络后启动为路由器…
路由器启动协议栈代码说明
1、路由器预编译信息
Tools文件夹下查看f8wCoord.cfg编译RTR_NWK.
在Option选项卡编译:CC2430EB;ZTOOL_P1;MT_TASK;MANAGED_SCAN
2、具体流程
首先协议栈以主函数为入口点启动协议栈。在主函数中调用了osal_init_system()进行了OS的初始化
ZSEG int main( void )
{
  osal_int_disable( INTS_ALL );
  HAL_BOARD_INIT();
  zmain_vdd_check();
  ……
  osal_init_system()
  ……
}
OS 初始化系统,初始化了内存、定时器、电源管理以及系统任务等。
byte osal_init_system( void )
{
  osal_mem_init();
  osal_qHead = NULL;
  osalTimerInit();
  osal_pwrmgr_init();
  // Initialize the system tasks.初始化任务系统
  osalInitTasks();
}
在任务初始化osalInitTasks()函数中,OS为每一层分配一个任务ID,使得协议栈成为一个多任务的系统
void osalInitTasks( void )
{
  uint8 taskID = 0;
  tasksEvents = (uint16 *)osal_mem_alloc( sizeof( uint16 ) * tasksCnt);
  osal_memset( tasksEvents, 0, (sizeof( uint16 ) * tasksCnt));
  macTaskInit( taskID++ );
  nwk_init( taskID++ );
  Hal_Init( taskID++ );
#if defined( MT_TASK )
  MT_TaskInit( taskID++ );
#endif
  APS_Init( taskID++ );
  ZDApp_Init( taskID++ );
  SampleApp_Init( taskID );
}
函数说明:
2、 void *osal_mem_alloc( uint16 size )
说明:该函数在内存中开辟了size大小的一块内存
参数说明:size-----------------------要开辟内存的大小
返回值:指针
2、void *osal_memset( void *dest, byte value, int len )
说明:该函数将起始地址为dest,长度为len的一块内存的值设置为value
参数说明:*dest--------------------起始地址
                   value-------------------要设置成的值
                   len----------------------设置内存块的大小
返回值:指针
其他说明:
由编译选项可知MT_TASK编译了,可以知道函数中
#if defined( MT_TASK )
  MT_TaskInit( taskID++ );
#endif
将被执行。
即最后ZDApp_Init(byte task_id) 传递的任务id的值为5,而SampleApp_Init( taskID )传递的任务id为6.
void ZDApp_Init(byte task_id)
{
    uint8 capabilities;
//保存下了OS分配的任务id。即ZDAppTaskID=5
ZDAppTaskID=task_id;
……
//如果程序运行到这里正好SW_1被按下,则会设置设备的状态(devState)为DEV_HOLD,从而避开网络初始化
ZDAppCheckForHoldKey();
//Initialize ZDO items and setup the device – type of device to create.
ZDO_Init();
AfRegister( (endPointDesc_t *)&ZDApp_epDesc );
……
// Start the device
if ( devState != DEV_HOLD )
{
     ZDOInitDevice( 0 );
}
else
{
    // Blink LED to indicate HOLD_START
    HalLedBlink ( HAL_LED_4, 0, 50, 500 );
}
ZDApp_RegisterCBs();
}
函数说明:
2、 void ZDAppCheckForHoldKey( void )
说明:如果在设备启动的时候手动将SW_1按下,则将设备状体置为DEV_HOLD,从而避开网络初始化。
参数说明:无
返回值:无
2、void ZDO_Init( void )
说明:该函数是ZDObject 和 ZDProfile 初始化函数,在该函数中调用了ZDODeviceSetup()根据编译选项的不同进行了相关的初始化。
参数说明:无
返回值:无
3、afStatus_t afRegister( endPointDesc_t *epDesc )
说明:该函数为zigbee设备注册一个新的端口。应用程序的每一个端口都必须使用该函数进行注册。
参数说明:epDesc  -----------------------指向端口描述符的指针
返回值:afStatus_t-------------------------ZcomDef.hZstatus_t结构体中定义的状态值
4、uint8  ZDOInitDevice(uint16 startDelay)
说明:该函数为ZDO设备初始化函数,该函数会检查系统是否需要恢复,如果需要恢复则恢复,否则启动设备。
参数说明:startDelay----------------------------------------------------------------设备启动延时
返回值:ZDO_INITDEV_RESTORED_NETWORK_STATE-------------设备恢复成功
ZDO_INITDEV_NEW_NETWORK_STATE----------------------设备为新状态
ZDO_INITDEV_LEAVE_NOT_STARTED-------------------------离开下次启动

uint8 ZDOInitDevice( uint16 startDelay )
{
  //初始化设备网络状态为ZDO_INITDEV_NEW_NETWORK_STATE:新的网络状态.
  uint8 networkStateNV = ZDO_INITDEV_NEW_NETWORK_STATE;
  uint16 extendedDelay = 0;
  devState = DEV_INIT;    // Remove the Hold state/设备的状态为:设备初始化
  ……
#if defined ( NV_RESTORE )
  if ( HalKeyRead() == SW_BYPASS_NV )
  //如果SW_BYPASS_NV按键此时被按下则会避开NV-RSTORE,将网络状态置为新状态
  networkStateNV = ZDO_INITDEV_NEW_NETWORK_STATE;
  else//决定NV是否需要恢复
  {
    //函数返回的设备网络状态是新的网络状态或者是恢复的网络状态;以此
    //来确定要不要读取NV里相应条目来恢复网络先前状态
    networkStateNV = ZDApp_ReadNetworkRestoreState();
   //通过函数ZDApp_ReadNetworkRestoreState的返回值决定NV是否需要恢复。
  }
//函数ZDApp_ReadNetworkRestoreState()用来检测是否需要恢复,如果返回值为//ZDO_INITDEV_RESTORED_NETWORK_STATE则恢复网络先前的状态
if ( networkStateNV == ZDO_INITDEV_RESTORED_NETWORK_STATE )
{
//恢复设备先前的网络状态参数并且
  //设置devStartMode = MODE_RESUME
  networkStateNV = ZDApp_RestoreNetworkState();
    //通过函数ZDApp_RestoreNetworkState()恢复网络先前的状态
  }
  else  //如果没有可以恢复的则执行下面代码
  {
    // Wipe out the network state in NV
    NLME_InitNV();
    NLME_SetDefaultNV();  //设置默认NV条目
  }
#endif
//以下为没有定义NV_RESTORE,即不恢复网路状态或者恢复失败。如果网络状态为//ZDO_INITDEV_NEW_NETWORK_STATE则执行以下代码
if ( networkStateNV == ZDO_INITDEV_NEW_NETWORK_STATE )
{
   //根据编译选项决定设备的类型,但仅在编译了SOFT_START 才起作用
    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 、uint8 ZDApp_ReadNetworkRestoreState( void )
说明:通过该函数读取NV ZCD_NV_STARTUP_OPTION的值决定是否需要恢复先前的网络状态。
参数说明:无
返回值: ZDO_INITDEV_NEW_NETWORK_STATE-------------新网络状态,无需恢复
       ZDO_INITDEV_RESTORED_NETWORK_STATE-----------恢复先前网络状态
6、 uint8 ZDApp_RestoreNetworkState( void )
说明:通过该函数可以恢复先前的网络状态
参数说明:无
返回值: ZDO_INITDEV_RESTORED_NETWORK_STATE-------网络状态恢复成功
     ZDO_INITDEV_NEW_NETWORK_STATE----------------网络状态恢复失败
7、void ZDAppDetermineDeviceType( void )
说明:通过该函数决定启动时设备的逻辑类型及启动模式,但仅在编译了SOFT_START 才起作用
参数说明:无
返回值:无
8、void ZDApp_NetworkInit( uint16 delay )
说明:通过该函数触发设备启动函数,该函数触发了ZDO_NETWORK_INIT事件。
参数说明:delay---------------------------  触发ZDO_NETWORK_INIT的延时
返回值:无
其他说明:
通过上面函数的分析我们可以看出设备在启动的时候可以利用NV恢复上次的网络状态,也可以不恢复直接开始创建或加入网络,恢复与否取决于是否编译了NV_RESTORE。ZDOInitDevice( uint16 startDelay )函数的最后触发了事件ZDO层的ZDO_NETWORK_INITZDO层事件处理函数为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 (events ^ ZDO_NETWORK_INIT);
  }
……
}
函数说明:
1 、void ZDO_StartDevice( byte logicalType, devStartModes_t startMode, byte beaconOrder, byte superframeOrder )
说明:通过该函数根据设备类型及设备启动方式的不同启动设备。
参数说明:logicalType-----------------------------设备的逻辑类型
                    startMode------------------------------设备的启动模式
          beaconOrder---------------------------信标时间
              superframeOrder---------------------超帧长度
返回值:无
ZDApp_event_loop()中处理ZDO_NETWORK_INIT前,即设备启动之前设备的状态设置为设备初始化(DEV_INIT)。然后调用了ZDO_StartDevice()启动设备。下面仔细分析设备的启动函数ZDO_StartDevice()
void ZDO_StartDevice( byte logicalType, devStartModes_t startMode, byte beaconOrder, byte superframeOrder )
{
  #if defined(ZDO_COORDINATOR)  //如果定义了协调器
  if ( logicalType == NODETYPE_COORDINATOR )  //node type coordinator  
  {
    //协调器的两种启动方式:
    if ( startMode == MODE_HARD )//普通的启动,直接创建网络
    {
      devState = DEV_COORD_STARTING;//设备状态为协调器正在启动
      // NLME_NetworkFormationRequest为系统函数,用于协调器创建网络
      ret = NLME_NetworkFormationRequest( zgConfigPANID, zgDefaultChannelList,
      gDefaultStartingScanDuration,beaconOrder,  superframeOrder, false );
    }
    else if ( startMode == MODE_RESUME )//网络恢复
   {
          // Just start the coordinator
      devState = DEV_COORD_STARTING; //设备状态为协调器正在启动
          //如果是协调器恢复则会以路由器启动
      ret = NLME_StartRouterRequest( beaconOrder, beaconOrder, false );
   }
    ……
}
#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; //此时设备状态为“发现网络”      
  #if defined( MANAGED_SCAN )  
      //如果编译了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 )//路由器的恢复
      {
        ……
        nwk_ScanJoiningOrphan(&scanCnf);//路由器以孤点方式加入网络
        ret = ZSuccess;
      }
      else//终端节点的恢复
      {
        devState = DEV_NWK_ORPHAN;
        ret = NLME_OrphanJoinRequest( zgDefaultChannelList,
                                      zgDefaultStartingScanDuration );
      }
    }
    ……
  }
#endif  //!ZDO COORDINATOR || SOFT_START
  if ( ret != ZSuccess )//如果没有启动,则重新再来一次!
   osal_start_timerEx(ZDAppTaskID,ZDO_NETWORK_INIT, NWK_RETRY_DELAY );
}
函数说明:
1 、ZStatus_t NLME_NetworkFormationRequest( uint16 PanId, uint32 ScanChannels,
                            byte ScanDuration, byte BeaconOrder,  
byte SuperframeOrder, byte BatteryLifeExtension  )
说明:通过该函数协调器可以创建一个网络。该函数会触发其对应的回调函数
void ZDO_NetworkFormationConfirmCB( ZStatus_t Status )。
2、ZStatus_t NLME_StartRouterRequest( byte BeaconOrder, byte SuperframeOrder,
                                                byte BatteryLifeExtension  )
说明:通过该函数可以启动一个路由器或者完成协调器的恢复。该函数会触发其对应的回调函数void ZDO_StartRouterConfirmCB( ZStatus_t Status )。
3、ZStatus_t NLME_NetworkDiscoveryRequest( uint32 ScanChannels,
byte scanDuration)
说明:通过该函数请求网络层发现邻居路由器节点。该函数会触发其对应的回调函数
ZStatus_t ZDO_NetworkDiscoveryConfirmCB( byte ResultCount,
                                                    networkDesc_t *NetworkList )
4、void nwk_ScanJoiningOrphan( ZMacScanCnf_t *param )
说明:通过该函数路由器会以孤点的方式加入网络。该函数会触发其回调函数
void ZDO_JoinConfirmCB( uint16 PanId, ZStatus_t Status )
9、ZStatus_t NLME_OrphanJoinRequest( uint32 ScanChannels, byte ScanDuration )
说明:通过该函数终端节点以孤点的方式加入网络。该函数会触发其回调函数
void ZDO_JoinConfirmCB( uint16 PanId, ZStatus_t Status )
我们这里是路由器的启动,并且没有用到网络恢复而是直接加入网络。所以在该函数中会调用
ZStatus_t NLME_NetworkDiscoveryRequest( uint32 ScanChannels,   byte scanDuration)
去发现路由节点。调用NLME_NetworkDiscoveryRequest()触发了其对应的回调函数ZDO_NetworkDiscoveryConfirmCB()
ZStatus_t ZDO_NetworkDiscoveryConfirmCB( byte ResultCount,
networkDesc_t *NetworkList )
{
……
for ( stackProfile = 0; stackProfile < STACK_PROFILE_MAX; stackProfile++ )
{
pNwkDesc = NetworkList;
for ( i = 0; i < ResultCount; i++, pNwkDesc = pNwkDesc->nextDesc )
{
if ( zgConfigPANID != 0xFFFF )
{
if ( pNwkDesc->panId != ( zgConfigPANID & 0x3FFF ) )
continue;
}
关于PANID的说明,如果设备配置的PANID为0xFFFF则不进行检测,即不论网络的PANID是多少,设备都会选择其,并加入。然而如果PANID配置为非0xFFFF则对获取来的网络的PANID进行检测,如果获取的PANID与配置的一样则会接着检测其他选项,否则则会用 continue跳出本循环,放弃获取的这个网络,检测下一个网络。这里注意continue的使用。
……
break;//如果所有检测项都通过,就跳出循环,此时 i < ResultCount
}
if ( i == ResultCount )
{
msg.hdr.status = ZDO_FAIL; // 没有发现可加入的网络
}
else//如果发现了一个可以加入的网络,将网络信息存放到msg中
{
msg.hdr.status = ZDO_SUCCESS;
msg.panIdLSB = LO_UINT16( pNwkDesc->panId );
msg.panIdMSB = HI_UINT16( pNwkDesc->panId );
msg.logicalChannel = pNwkDesc->logicalChannel;
msg.version = pNwkDesc->version;
osal_cpyExtAddr( msg.extendedPANID, pNwkDesc->extendedPANID );
}
ZDApp_SendMsg( ZDAppTaskID, ZDO_NWK_DISC_CNF, sizeof(ZDO_NetworkDiscoveryCfm_t), (byte *)&msg );
//这里虽然都触发了事件ZDO_NWK_DISC_CNF,但是有无可加入的网络都包含于 msg中了
return (ZSuccess);
} // ZDO_NetworkDiscoveryConfirmCB
通过这个回调函数检测是否发现了可以加入的网络,如果找到一个可以加入的        网络,将其相关信息保存到msg,并最后触发了事件ZDO层的ZDO_NWK_DISC_CNF。ZDO层事件处理函数为ZDApp_event_loop()。
UINT16 ZDApp_event_loop( byte task_id, UINT16 events )
{
   ……
case ZDO_NWK_DISC_CNF:
if (devState != DEV_NWK_DISC)
{
//注意这里的层次结构,如果设备状态不是 DEV_NWK_DISC, 设备将什么都不会做了
}
#if !defined ( ZDO_COORDINATOR ) || defined ( SOFT_START )
#if defined ( MANAGED_SCAN )
//下面这两个都是寻找到了一个可以加入的合适网络
else if ( (((ZDO_NetworkDiscoveryCfm_t *)msgPtr)->hdr.status == ZDO_SUCCESS) && (zdoDiscCounter > NUM_DISC_ATTEMPTS) )
#else
else if ( (((ZDO_NetworkDiscoveryCfm_t *)msgPtr)->hdr.status == ZDO_SUCCESS) && (zdoDiscCounter++ > NUM_DISC_ATTEMPTS) )
#endif
{
if ( devStartMode == MODE_JOIN )//设备启动模式为“加入”
{
devState = DEV_NWK_JOINING;
ZDApp_NodeProfileSync((ZDO_NetworkDiscoveryCfm_t *)msgPtr);
if ( NLME_JoinRequest( ((ZDO_NetworkDiscoveryCfm_t *)msgPtr)->extendedPANID,
BUILD_UINT16( ((ZDO_NetworkDiscoveryCfm_t *)msgPtr)->panIdLSB, ((ZDO_NetworkDiscoveryCfm_t *)msgPtr)->panIdMSB ),
((ZDO_NetworkDiscoveryCfm_t *)msgPtr)->logicalChannel,
ZDO_Config_Node_Descriptor.CapabilityFlags ) != ZSuccess )
{
ZDApp_NetworkInit( (uint16)(NWK_START_DELAY
+ ((uint16)(osal_rand()& EXTENDED_JOINING_RANDOM_MASK))) );
//如果没有加入成功,则会再重新来一次的
}
}
else if ( devStartMode == MODE_REJOIN )//设备启动模式为“再次加入”
{
devState = DEV_NWK_REJOIN;
if ( NLME_ReJoinRequest() != ZSuccess )
{
ZDApp_NetworkInit( (uint16)(NWK_START_DELAY
+ ((uint16)(osal_rand()& EXTENDED_JOINING_RANDOM_MASK))) );
//如果没有加入成功,则会再重新来一次的
}
}
……
}
else //这里是没有找到一个可以加入的网络
{
#if defined ( SOFT_START ) && !defined ( VIRTKEY_SOFT_START )
#if defined ( MANAGED_SCAN )
if ( (softStartAllowCoord)
&& (((ZDO_NetworkDiscoveryCfm_t *)msgPtr)->hdr.status != ZDO_SUCCESS )
&& (zdoDiscCounter > NUM_DISC_ATTEMPTS) )
#else
if ( (softStartAllowCoord)
&& (((ZDO_NetworkDiscoveryCfm_t *)msgPtr)->hdr.status != ZDO_SUCCESS )
&& (zdoDiscCounter++ > NUM_DISC_ATTEMPTS) )
#endif
{
//满足以上条件则会将设备启动为协调器,并创建一个网络
ZDO_Config_Node_Descriptor.LogicalType = NODETYPE_COORDINATOR;
devStartMode = MODE_HARD;
}
else if ( continueJoining == FALSE )//这个FALSE我的理解是其它函数所致
{
//不满足上述条件,并 continueJoining = FALSE,将设备状态置为 DEV_HOLD
devState = DEV_HOLD;
osal_stop_timerEx( ZDAppTaskID, ZDO_NETWORK_INIT );
break; // Don't init
}
#endif
#if defined ( MANAGED_SCAN )
ZDApp_NetworkInit( MANAGEDSCAN_DELAY_BETWEEN_SCANS );
#else
if ( continueJoining )
{
ZDApp_NetworkInit( (uint16)(BEACON_REQUEST_DELAY
+ ((uint16)(osal_rand()& BEACON_REQ_DELAY_MASK))) );
}
#endif
}
#endif // !ZDO_COORDINATOR
……
}
函数说明:
1 、NLME_JoinRequest( uint8 *extendedPANID, uint16 PanId,
byte Channel,byte CapabilityInfo);
说明:通过该函数可以使一个设备加入特定的一个网络。该函数会触发其对应的回调函数
void ZDO_JoinConfirmCB( uint16 PanId, ZStatus_t Status )。
2 、ZStatus_t NLME_ReJoinRequest( void )
说明:通过该函数可以使一个设备重新加入原来的网络。该函数会触发其对应的回调函数
void ZDO_JoinConfirmCB( uint16 PanId, ZStatus_t Status )。
我们这里是路由器的启动,所以当找到一个合适的网络的时候在该函数中会调用
NLME_JoinRequest( uint8 *extendedPANID, uint16 PanId,
byte Channel,byte CapabilityInfo);
来创建一个新的网络。调用NLME_JoinRequest()触发了其对应的回调函数ZDO_JoinConfirmCB ()
void ZDO_JoinConfirmCB( uint16 PanId, ZStatus_t Status )
{
nwkStatus = (byte)Status; //将状态保存到nwkStatus中
   if ( Status == ZSUCCESS )
   {
  //点亮LED_3标识设备已经成功加入网络
    HalLedSet ( HAL_LED_3, HAL_LED_MODE_ON );
    // LED off forgets HOLD_AUTO_START
    HalLedSet ( HAL_LED_4, HAL_LED_MODE_OFF);
    ……
  }
#if defined(BLINK_LEDS)
  else//没有成功加入网络将闪烁LED提示用户
            HalLedSet ( HAL_LED_3, HAL_LED_MODE_FLASH );  // Flash LED to show failure
#endif
//触发网络已经启动事件ZDO_NWK_JOIN_IND
    ZDApp_SendMsg( ZDAppTaskID, ZDO_NWK_JOIN_IND, sizeof(osal_event_hdr_t), (byte*)NULL );
}
不论是否加入网络,函数最后触发事件ZDO_NWK_JOIN_IND,同样在ZDApp_event_loop()中处理ZDO_NWK_JOIN_IND事件。
UINT16 ZDApp_event_loop( byte task_id, UINT16 events )
{
   ……
    case ZDO_NWK_JOIN_IND:
      ZDApp_ProcessNetworkJoin();
      break;
   ……
}
函数中调用了函数ZDApp_ProcessNetworkJoin()对ZDO_NWK_JOIN_IND进行处理
#if !defined( ZDO_COORDINATOR ) || defined( SOFT_START )
void ZDApp_ProcessNetworkJoin( void )
{
//加入网络或者路由器以孤点的方式加入网络
if ( (devState == DEV_NWK_JOINING) ||((devState == DEV_NWK_ORPHAN) &&
(ZDO_Config_Node_Descriptor.LogicalType == NODETYPE_ROUTER)) )
{
if ( nwkStatus == ZSuccess ) //如果加入成功
{
osal_set_event( ZDAppTaskID, ZDO_STATE_CHANGE_EVT );//触发ZDO事件
……
//通过下面的if条件语句可以得知设备是以路由器、孤点方式加入网络
if ( devState == DEV_NWK_ORPHAN
&& ZDO_Config_Node_Descriptor.LogicalType != NODETYPE_DEVICE )
{
// Change NIB state to router for restore 修改NIB信息
_NIB.nwkState = NWK_ROUTER;
}
devState = DEV_END_DEVICE;//标示设备以加入网络
if ( ZDO_Config_Node_Descriptor.LogicalType != NODETYPE_DEVICE )
{
//以路由器启动设备
NLME_StartRouterRequest( 0, 0, false );
}
……
}
else // -------这里是没有成功加入网络
{
if ( (devStartMode == MODE_RESUME) && (++retryCnt >= MAX_RESUME_RETRY) )
{
//恢复失败需加入或者重新加入网络
if ( _NIB.nwkPanId == 0xFFFF || _NIB.nwkPanId == INVALID_PAN_ID )
devStartMode = MODE_JOIN; //加入网络
else
{
devStartMode = MODE_REJOIN;//重新加入网络
_tmpRejoinState = true;
}
}
……
//重新初始化,准备再次加入网络
ZDApp_NetworkInit( (uint16)(NWK_START_DELAY
+ ((uint16)(osal_rand()& EXTENDED_JOINING_RANDOM_MASK))) );
}
}
//孤点加入或者重新加入
else if ( devState == DEV_NWK_ORPHAN || devState == DEV_NWK_REJOIN )
{
// results of an orphaning attempt by this device
if (nwkStatus == ZSuccess)
{
devState = DEV_END_DEVICE;//标示设备加入网络
osal_set_event( ZDAppTaskID, ZDO_STATE_CHANGE_EVT );
}
else
{
if ( (devStartMode == MODE_RESUME) && (++retryCnt >= MAX_RESUME_RETRY) )
{
//恢复失败需加入或者重新加入网络
if ( _NIB.nwkPanId == 0xFFFF || _NIB.nwkPanId == INVALID_PAN_ID )
devStartMode = MODE_JOIN; //加入网络
else
{
devStartMode = MODE_REJOIN;//重新加入网络
_tmpRejoinState = true;
}
}
//重新初始化,准备再次加入网络
ZDApp_NetworkInit( (uint16)(NWK_START_DELAY+ (osal_rand()&EXTENDED_JOINING_RANDOM_MASK)) );
}
}
}
#endif // !ZDO_COORDINATOR
函数说明:
1 、ZStatus_t NLME_StartRouterRequest( byte BeaconOrder,                                        byte SuperframeOrder,byte BatteryLifeExtension )
说明:通过该函数可以使设备成为路由器。该函数会触发其对应的回调函数
void ZDO_StartRouterConfirmCB( ZStatus_t Status )。
当设备加入网络后,会触发事件ZDO_STATE_CHANGE_EVT,如果是该设备是路由器,则会调用函数NLME_StartRouterRequest()使设备成为路由器。当调用NLME_StartRouterRequest时会触发其对应的回调函数ZDO_StartRouterConfirmCB().其中如果设备只是终端设备那么剩余的和协调器启动类似,这里不再赘述。我们这里是路由器的启动,所以会调用函数NLME_StartRouterRequest()。
void ZDO_StartRouterConfirmCB( ZStatus_t Status )
{
nwkStatus = (byte)Status;  //将状态保存到nwkStatus中
  if ( Status == ZSUCCESS )   //成功成为路由器
  {
//点亮LED_3标识成功成为 路由器
HalLedSet ( HAL_LED_3, HAL_LED_MODE_ON );
    // LED off forgets HOLD_AUTO_START
    HalLedSet ( HAL_LED_4, HAL_LED_MODE_OFF);
    ……
  }
#if defined(BLINK_LEDS)
  else //没有成功创建网络将闪烁LED提示用户
    HalLedSet( HAL_LED_3, HAL_LED_MODE_FLASH );  // Flash LED to show failure
#endif
//触发网络已经启动事件ZDO_ROUTER_START
  osal_set_event( ZDAppTaskID, ZDO_ROUTER_START );
}
不论是否成为路由,最后触发事件ZDO_ROUTER_START,同样在ZDApp_event_loop()中处理ZDO_ROUTER_START事件。
UINT16 ZDApp_event_loop( byte task_id, UINT16 events )
{
   ……
#if defined ( RTR_NWK )
  if ( events & ZDO_ROUTER_START )
  {
    if ( nwkStatus == ZSuccess )
    {
      if ( devState == DEV_END_DEVICE )
        devState = DEV_ROUTER;//将设备的状态标示为路由器
      osal_pwrmgr_device( PWRMGR_ALWAYS_ON );
    }
    else
    {
      // remain as end device!!
    }
    osal_set_event( ZDAppTaskID, ZDO_STATE_CHANGE_EVT );
    // Return unprocessed events
    return (events ^ ZDO_ROUTER_START);
  }
#endif  // RTR   ……
}
在ZDO_ROUTER_START处理过程中,协议栈做了三件事,首先是将设备的状态设置为路由器,其次是通过电源管理函数设置该设备供电模式。最后触发了事件ZDO_STATE_CHANGE_EVT。关于ZDO_STATE_CHANGE_EVT在协调器启动和上面的代码中都已提及,这里不再赘述,到目前为止,路由器启动步骤全部完成。

本文来自论坛,点击查看完整帖子内容。

评论 (0 个评论)

facelist doodle 涂鸦板

您需要登录后才可以评论 登录 | 注册

热门文章