首页 > 代码库 > Zigbee网络设备启动—基本问题说明

Zigbee网络设备启动—基本问题说明

记录几个问题:

***********************************
1、
有关设备的启动模式选项:(有待完善):

非自动启动模式 HOLD_AUTO_START:HOLD_AUTO_START is a compile option that will surpress ZDApp from starting the device and wait for the application to start the device.不通过ZDApp(具体讲是ZDOInitDevice())而是等待应用程序来开启设备并初始化建立/加入网络.

软启动模式 SOFT_START:SOFT_START is a compile option that allows the device to start as a coordinator if one isn‘t found. Otherwise, the device will start as a router.如果没有协调器则作为协调器启动,有则作为路由器启动.

自动启动模式:个人认为就是自动通过ZDApp(具体讲是ZDOInitDevice())来开启设备并初始化建立/加入网络,设备的逻辑类型由所携带的配置文件来决定.
***********************************


***********************************
2、三种逻辑类型节点的配置文件
协调器:f8wCoord.cfg配置文件中同时编译了路由功能RTR_NWK和协调器功能ZDO_COORDINATOR
/* Common To All Applications */
-DCPU32MHZ                         // CC2430s Run at 32MHz
-DFORCE_MAC_NEAR                   // MAC code in NEAR
-DROOT=__near_func                 // MAC/ZMAC code in NEAR

-DMAC_CFG_APP_PENDING_QUEUE=TRUE

/* Coordinator Settings */
-DZDO_COORDINATOR                  // Coordinator Functions
-DRTR_NWK                          // Router Functions

/* Optional Settings */
-DBLINK_LEDS                       // LED Blink Functions

/* Compiler keywords */
-DCONST="const __code"
-DGENERIC=__generic                // Ptr declaration

路由器:f8wRouter.cfg配置文件中编译了路由功能RTR_NWK
/* Common To All Applications */
-DCPU32MHZ                         // CC2430s Run at 32MHz
-DFORCE_MAC_NEAR                   // MAC code in NEAR
-DROOT=__near_func                 // MAC/ZMAC code in NEAR

-DMAC_CFG_APP_PENDING_QUEUE=TRUE

/* Router Settings */
-DRTR_NWK                          // Router Functions

/* Optional Settings */
-DBLINK_LEDS                       // LED Blink Functions

/* Compiler keywords */
-DCONST="const __code"
-DGENERIC=__generic                // Ptr declaration

终端:f8wEdev.cfg配置文件中没有编译这两个功能.
/* Common To All Applications */
-DCPU32MHZ                         // CC2430s Run at 32MHz
-DFORCE_MAC_NEAR                   // MAC code in NEAR
-DROOT=__near_func                 // MAC/ZMAC code in NEAR

/* Optional Settings */
-DMAC_OPT_FFD=0                    // ZigBee RFD
-DBLINK_LEDS                       // LED Blink Functions

/* Compiler keywords */
-DCONST="const __code"
-DGENERIC=__generic                // Ptr declaration

协调器的配置文件只比路由器配置文件多编译了个-DZDO_COORDINATOR  // Coordinator Functions
如果同时编译非自动启动模式HOLD_AUTO_START和软启动模式SOFT_START,设备可以通过应用程序来选择
成为路由器或协调器.当然这个设备所带的配置文件应为f8wCoord.cfg.对于设备携带的是何种配置文件,可以打开project->options->c/c++compiler->extraOptions选项查看.
比如SampleApp中的DemoEB,同时编译了SOFT_START和HOLD_AUTO_START,貌似下载进去后通过外部跳线来选择
是当协调器还是路由器,不过这部分程序已经被注销了.当然的,DemoEB所带的配置文件应该是同时编译了路由功能和协调器功能的f8wCoord.cfg,即编译了RTR_NWK和ZDO_COORDINATOR.再比如SimpleApp中两个实验中的灯与开关实验.开关设备SimpleSwitchEB无论按K1还是K2都是作为终端设备的,其预编译选项只有HOLD_AUTO_START而没有SOFT_START,配置文件为f8wEndev.cfg,没有协调器和路由功能;而灯设备SimpleControllerEB按键K1则作为协调器,按K2则作为路由器,其预编译选项同时编译了SOFT_START和HOLD_AUTO_START,配置文件为f8wCoord.cfg,即编译了RTR_NWK和ZDO_COORDINATOR. SimpleApp中传感器实验也一样,只能作为终端设备的SimpleSensorEB预编译了HOLD_AUTO_START,配置文件为f8wEndev.cfg;而SimpleCollectorEB按K1作为协调器按K2作为路由器,预编译了SOFT_START和HOLD_AUTO_START,配置文件f8wCoord.cfg. 可以通过project->options->c/c++compiler->extraOptions选项查看,也可由workspace看出,如下:

作为终端的开关节点与作为协调器/路由器的灯节点:空白的文件表示没有包含在这个workspace中.

                      Zigbee网络设备启动—基本问题说明 - 小峰 - happy~   Zigbee网络设备启动—基本问题说明 - 小峰 - happy~

 作为终端的传感器节点与作为协调器、路由器的中心收集节点:空白的文件表示没有包含在这个workspace中.

                     Zigbee网络设备启动—基本问题说明 - 小峰 - happy~Zigbee网络设备启动—基本问题说明 - 小峰 - happy~

***********************************

***********************************
3、有两种方式来设置非自动启动模式:Hold Auto Start
(1)、手工方式:
在ZDApp_Init()函数中有个ZDAppCheckForHoldKey();(// Check for manual(手工的) "Hold Auto Start").

来看下这个函数:
********************
void ZDAppCheckForHoldKey( void )
{
#if (defined HAL_KEY) && (HAL_KEY == TRUE)
  //直接通过读取按键来判断是否需要采用HOLD_AUTO_START模式,如果发现
  //SW_1处于按下(普通按键?)/向上(Joystick up)状态,则设置devState为DEV_HOLD;
  // Get Keypad directly to see if a HOLD_START is needed.
  // Hold down the SW_BYPASS_START key (see OnBoard.h)
  // while booting to avoid starting up the device.
  if ( HalKeyRead () == SW_BYPASS_START) //HAL_KEY_SW_1
  {
    // Change the device state to HOLD on start up
    devState = DEV_HOLD;
  }
#endif // HAL_KEY
}
*********************
可以看到如果手工使HAL_KEY_SW_1置位状态,则会设置devState = DEV_HOLD.
这里HAL_KEY的初始化见hal_board_cfg.h:
 #ifndef HAL_KEY
 #define HAL_KEY TRUE
 #endif

而对SW_BYPASS_START的初始化见OnBoard.h:
// These Key definitions are unique to this development system.
// They are used to bypass functions when starting up the device.
//这些键的定义仅适用于本应用例子,可以在设备启动时避开一些功能:
//避开网络层的NV存储和避开网络初始化

#define SW_BYPASS_NV    HAL_KEY_SW_5     // Bypass Network layer NV restore
#define SW_BYPASS_START HAL_KEY_SW_1 
// Bypass Network initialization
因此避开网络层NV存储也可以通过手工方式来完成.

(2)、预编译方式:
project->options->c/c++compiler->preprocessor->defined symbols下编译选项:HOLD_AUTO_START
SimpleApp例子里四种节点都预编译了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

****************************************

****************************************
4、devStartMode和devState的初始化,ZDApp.c中:

devStartMode:
#if defined( ZDO_COORDINATOR ) && !defined( SOFT_START )
  // Set the default to coodinator
  devStartModes_t devStartMode = MODE_HARD;
#else
  devStartModes_t devStartMode = MODE_JOIN;   
  // Assume joining
  //devStartModes_t devStartMode = MODE_RESUME; // if already "directly joined"
                        // to parent. Set to make the device do an Orphan scan.

#endif
编译了ZDO_COORDINATOR并且没有编译SOFT_START,则初始化devStartMode = MODE_HARD;其他情况初始化
devStartMode = MODE_RESUME;比如SampleApp三种逻辑类型节点.

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;
****************************************

****************************************
5、存储设备逻辑类型的NV条目ZCD_NV_LOGICAL_TYPE:

(1)在ZGlobals.c下的NV条目表ZGlobal Item Table有这么一个条目:
static CONST zgItem_t zgItemTable[ ] =
{
#if defined ( NV_INIT )
  {
    ZCD_NV_LOGICAL_TYPE, sizeof(zgDeviceLogicalType), &zgDeviceLogicalType
  },
  …………
}

zgItem_t结构体如下:
typedef struct zgItem
{
  uint16 id;
  uint16 len;
  void *buf;
} zgItem_t;

ZDO全局变量zgDeviceLogicalType被初始化为uint8 zgDeviceLogicalType = DEVICE_LOGICAL_TYPE;
因此这里全局变量zgDeviceLogicalType的值为NV条目ZCD_NV_LOGICAL_TYPE的值,buf指向
zgDeviceLogicalType.如果在应用程序中改变了设备逻辑类型并写入NV条目ZCD_NV_LOGICAL_TYPE中,这时即全局变量zgDeviceLogicalType的值改变了(卡在这步上近两天,因为找不着改变的逻辑状态与设备开启时选择的逻辑状态之间的关系).

(2)NV条目ZCD_NV_LOGICAL_TYPE的值有哪些?
// Device Logical Type
//NV条目ZCD_NV_LOGICAL_TYPE的值包括:(即zgDeviceLogicalType的值)
// Values for ZCD_NV_LOGICAL_TYPE (zgDeviceLogicalType)

#define ZG_DEVICETYPE_COORDINATOR       0x00
#define ZG_DEVICETYPE_ROUTER                   0x01
#define ZG_DEVICETYPE_ENDDEVICE             0x02
#define ZG_DEVICETYPE_SOFT                        0x03

(3)对于DEVICE_LOGICAL_TYPE的值各逻辑类型设备初始化如下:
// Device Logical Type
//zgDeviceLogicalType = DEVICE_LOGICAL_TYPE

#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,即zgDeviceLogicalType = ZG_DEVICETYPE_SOFT;如果没有编译SOFT_START但编译了ZDO_COORDINATOR,则初始DEVICE_LOGICAL_TYPE = ZG_DEVICETYPE_COORDINATOR,即zgDeviceLogicalType=ZG_DEVICETYPE_COORDINATOR;
路由器和终端类似.
****************************************

****************************************
6、启动设备都要通过ZDApp_event_loop()函数调用ZDO_StartDevice()对ZDO_NETWORK_INIT事件的处理:
ZDO_StartDevice( (uint8)ZDO_Config_Node_Descriptor.LogicalType, devStartMode,
                     DEFAULT_BEACON_ORDER, DEFAULT_SUPERFRAME_ORDER );
这里对于设备逻辑类型的传递参数ZDO_Config_Node_Descriptor.LogicalType,ZDConfig.c中初始化是这样的

NodeDescriptorFormat_t ZDO_Config_Node_Descriptor =
{
#if defined( ZDO_COORDINATOR ) && !defined( SOFT_START )
  NODETYPE_COORDINATOR, //协调器
#elif defined (RTR_NWK)
 NODETYPE_ROUTER,             //路由器
#else
  NODETYPE_DEVICE,             //终端设备    // Logical Type
#endif
…………
}

如果编译了ZDO_COORDINATOR且没有编译SOFT_START,则ZDO_Config_Node_Descriptor.LogicalType的值为NODETYPE_COORDINATOR; 如果没有编译ZDO_COORDINATOR或者编译了SOFT_START,且编译了了RTR_NWK,则ZDO_Config_Node_Descriptor.LogicalType的值为NODETYPE_ROUTER(比如simpleApp中灯节点和中心收集节点就是这种情况,因此它俩的ZDO_Config_Node_Descriptor.LogicalType被初始化为NODETYPE_ROUTER,当然当通过外部按键选择设备逻辑类型为协调器时,会通过内部程序把这个值改为NODETYPE_COORDINATOR,这点后面再作记录);剩下情况为NODETYPE_DEVICE.
****************************************

****************************************
7、
simpleApp中的按键完成两个功能:
设置设备的逻辑类型(ZC/ZR/ED) 和 设置设备的启动方式(ZCD_STARTOPT_AUTO_START),然后写入相应的NV条
目ZCD_NV_LOGICAL_TYPE和ZCD_NV_STARTUP_OPTION.
ZDApp_Init()是先于应用任务如SampleApp_Init()初始化的.

ZCD_NV_STARTUP_OPTION值包括:
//   These are bit weighted - you can OR these together.
//   Setting one of these bits will set their associated NV items
//   to code initialized values.

#define ZCD_STARTOPT_DEFAULT_CONFIG_STATE  0x01    //默认配置
#define ZCD_STARTOPT_DEFAULT_NETWORK_STATE 0x02 //默认网络状态
#define ZCD_STARTOPT_AUTO_START            0x04               //自动启动
#define ZCD_STARTOPT_CLEAR_CONFIG   ZCD_STARTOPT_DEFAULT_CONFIG_STATE //清除配置=默认配置
#define ZCD_STARTOPT_CLEAR_STATE  ZCD_STARTOPT_DEFAULT_NETWORK_STATE
//清除状态=默认网络状态

ZCD_NV_LOGICAL_TYPE值参见4.
**************************************

****************************************
8、网络状态的类型:

//设备的网络状态为恢复的网络状态
#define ZDO_INITDEV_RESTORED_NETWORK_STATE      0x00
//网络状态初始化,即设备的网络状态为新的网络状态.可能意味着ZCD_NV_STARTUP_OPTION不能恢复,或没有任何网络状态恢复
#define ZDO_INITDEV_NEW_NETWORK_STATE           0x01
//复位前,网络重返选项为TRUE,因此该设备在网络中没有启动(仅一次),下次调用该函数将启动.
#define ZDO_INITDEV_LEAVE_NOT_STARTED           0x02

****************************************

****************************************
9、NV条目ZCD_NV_STARTUP_OPTION初始化

********************************************************************************
********************************************************************************
********************************************************************************

分析一:(错误,见分析二;暂不删除)
NV条目的初始化在zgInit()中.main()函数中:
***************
  // Initialize basic NV items
  /*初始化NV条目*/
  zgInit();
****************************************
 * @fn          zgInit
 *
 * @brief
 *
 *   Initialize the Z-Stack Globals. If an item doesn‘t exist in
 *   NV memory, write the system default into NV memory. But if
 *   it exists, set the item to the value stored in NV memory.
 *
 * NOTE: The Startup Options (ZCD_NV_STARTUP_OPTION) indicate
 *       that the Config state items (zgItemTable) need to be
 *       set to defaults (ZCD_STARTOPT_DEFAULT_CONFIG_STATE). The
 *
 *
 * @param       none
 *
 * @return      ZSUCCESS if successful, NV_ITEM_UNINIT if item did not
 *              exist in NV, NV_OPER_FAILED if failure.
 */

uint8 zgInit( void )
{
  uint8  i = 0;
  uint8  setDefault = FALSE;

  // Do we want to default the Config state values
  if ( zgReadStartupOptions() & ZCD_STARTOPT_DEFAULT_CONFIG_STATE )// 0x01
  {
    setDefault = TRUE;
  }

#if 0 //正常情况下被禁止来节省NV空间
  // Enable this section if you need to track the number of resets
  // This section is normally disabled to minimize "wear" on NV memory

  …………
#endif

  // Initialize the Extended PAN ID as my own extended address
  //扩展地址

  ZMacGetReq( ZMacExtAddr, zgExtendedPANID );

#ifndef NONWK
  // Initialize the Pre-Configured Key to the default key
  osal_memcpy( zgPreConfigKey, defaultKey, SEC_KEY_LEN );  // Do NOT Change!!!
#endif
// NONWK

//----------------------------------初始化各NV配置状态条目值
//初始化NV条目表zgItemTable[ ].如果setDefault=TRUE,则设置为默认
//值;否则读取原先存储在里面的值.

  while ( zgItemTable[i].id != 0x00 )
  {
    // Initialize the item
    zgItemInit( zgItemTable[i].id, zgItemTable[i].len, zgItemTable[i].buf, setDefault  );

    // Move on to the next item
    i++;
  }
//----------------------------------
 
  // Clear the Config State default
  //如果NV条目ZCD_NV_STARTUP_OPTION的ZCD_STARTOPT_DEFAULT_CONFIG_STATE标致位为1
  if ( setDefault )
  { //把ZCD_STARTOPT_DEFAULT_CONFIG_STATE标致位清0
    zgWriteStartupOptions( ZG_STARTUP_CLEAR, ZCD_STARTOPT_DEFAULT_CONFIG_STATE );
  }

  return ( ZSUCCESS );
}
****************************************
NOTE: The Startup Options (ZCD_NV_STARTUP_OPTION) indicate
that the Config state items (zgItemTable) need to be
set to defaults (ZCD_STARTOPT_DEFAULT_CONFIG_STATE).
开启选项表明了各NV配置状态条目(zgItemTable表中的即为NV配置状态条目)需要被设置为默认值.
setDefault的值取决于zgReadStartupOptions(),而这个函数记取的正是ZCD_NV_STARTUP_OPTION的值.
****************************************
// Reads the ZCD_NV_STARTUP_OPTION NV Item.
uint8 zgReadStartupOptions( void )
{
  // Default to Use Config State and Use Network State
  uint8 startupOption = 0;

  // This should have been done in ZMain.c, but just in case.
  if ( osal_nv_item_init( ZCD_NV_STARTUP_OPTION,
                              sizeof(startupOption),
                              &startupOption ) == ZSUCCESS )
  {
    // Read saved startup control
    //NV条目ZCD_NV_STARTUP_OPTION值读到startupOption

    osal_nv_read( ZCD_NV_STARTUP_OPTION,
                  0,
                  sizeof( startupOption ),
                  &startupOption);
  }
  return ( startupOption );
}
****************************************

****************************************
 * @fn       zgItemInit()
 *
 *   Initialize a global item. If the item doesn‘t exist in NV memory,
 *   write the system default (value passed in) into NV memory. But if
 *   it exists, set the item to the value stored in NV memory.
 *
 *   Also, if setDefault is TRUE and the item exists, we will write
 *   the default value to NV space.
 *
 * @param   id - item id
 * @param   len - item len
 * @param   buf - pointer to the item
 * @param   setDefault - TRUE to set default, not read
 *                       TRUE则设置为默认值而不读取原先的值
 * @return  ZSUCCESS if successful, NV_ITEM_UNINIT if item did not
 *          exist in NV, NV_OPER_FAILED if failure.
 */

static uint8 zgItemInit( uint16 id, uint16 len, void *buf, uint8 setDefault )
{

  uint8 status;

  // If the item doesn‘t exist in NV memory, create and initialize
  // it with the value passed in.

  status = osal_nv_item_init( id, len, buf );  //NV条目读写前要进行条目的初始化
  if ( status == ZSUCCESS )
  {
    if ( setDefault )  //条目存在,setDefault=TRUE,设置为默认值
    {
      // Write the default value back to NV
      status =  osal_nv_write( id, 0, len, buf );
    }
    else //条目存在,setDefault=FALSE,读取原先存储的值
    {
      // The item exists in NV memory, read it from NV memory
      status = osal_nv_read( id, 0, len, buf );
    }
  }

  return (status);
}
****************************************
zgReadStartupOptions()中:Default to Use Config State and Use Network State说明采用默认的配置和网络状态,那设备初始启动时ZCD_NV_STARTUP_OPTION值应该是默认配置标志位和默认网络标志位都置1,两者相或0x03(见7),但还没能找到究竟哪里进行过初始化.
回到zgInit():(1)setDefault = FALSE;(2)读取ZCD_NV_STARTUP_OPTION值如果ZCD_STARTOPT_DEFAULT_CONFIG_STATE标志位为1,设置setDefault = TRUE;(3)初始化NV配置状态条目表zgItemTable[ ]中各条目为默认值(4)清除ZCD_STARTOPT_DEFAULT_CONFIG_STATE标志位.
至于清除ZCD_STARTOPT_DEFAULT_CONFIG_STATE标志位,我想如果用户不改变ZCD_NV_STARTUP_OPTION条目的值,设备每次重启都会将NV条目表中各项初始化为默认值:比如SampleApp例子,每次设备重启配置状态都为默认值.而如果用户改变了ZCD_NV_STARTUP_OPTION的值,比如SimpleApp中:
        zb_ReadConfiguration( ZCD_NV_STARTUP_OPTION, sizeof(uint8), &startOptions );
        startOptions = ZCD_STARTOPT_AUTO_START;
        zb_WriteConfiguration( ZCD_NV_STARTUP_OPTION, sizeof(uint8), &startOptions );
        zb_SystemReset();

每次按键后都会先把设备逻辑状态写入NV中(准备下次启动设备时以保存在这个NV条目里的逻辑类型来启动),然后把ZCD_NV_STARTUP_OPTION设为ZCD_STARTOPT_AUTO_START 0x04,则下面两个标志位都为0.
ZCD_STARTOPT_DEFAULT_CONFIG_STATE  0x01 //默认配置状态:存储配置信息
ZCD_STARTOPT_DEFAULT_NETWORK_STATE 0x02 //默认网络状态:存储网络信息

这样系统重启进入zgInit()后:
if ( zgReadStartupOptions() & ZCD_STARTOPT_DEFAULT_CONFIG_STATE )// 0x01
  {
    setDefault = TRUE;
  }
ZCD_STARTOPT_DEFAULT_CONFIG_STATE标志位不为1,setDefault保持FALSE,因而进入NV配置状态条目初始化zgItemInit()时,执行的是:
    else //条目存在,setDefault=FALSE,读取原先存储的值
    {
      // The item exists in NV memory, read it from NV memory
      status = osal_nv_read( id, 0, len, buf );
    }
这样把上次存储在NV条目ZCD_NV_LOGICAL_TYPE中的设备逻辑类型值读出作为此次NV条目ZCD_NV_LOGICAL_TYPE的值.
问题是,还未确定开启选项ZCD_NV_STARTUP_OPTION的值初始化的地方.纠结中,待解决……

 ********************************************************************************
********************************************************************************
********************************************************************************


分析二:
@@@ ZCD_NV_STARTUP_OPTION最开始在osal_nv_item_init()中初始化,初始化为0

****************************************
 * @fn          zgInit
 *
 * @brief
 *
 *   Initialize the Z-Stack Globals. If an item doesn‘t exist in
 *   NV memory, write the system default into NV memory. But if
 *   it exists, set the item to the value stored in NV memory.
 *
 * NOTE: The Startup Options (ZCD_NV_STARTUP_OPTION) indicate
 *       that the Config state items (zgItemTable) need to be
 *       set to defaults (ZCD_STARTOPT_DEFAULT_CONFIG_STATE). The
 *
 *
 * @param       none
 *
 * @return      ZSUCCESS if successful, NV_ITEM_UNINIT if item did not
 *              exist in NV, NV_OPER_FAILED if failure.
 */
uint8 zgInit( void )
{
  uint8  i = 0;
  uint8  setDefault = FALSE;

  // Do we want to default the Config state values
  if ( zgReadStartupOptions() & ZCD_STARTOPT_DEFAULT_CONFIG_STATE )  // 0x01
  {
    setDefault = TRUE;
  }

#if 0 //正常情况下被禁止来节省NV空间
  // Enable this section if you need to track the number of resets
  // This section is normally disabled to minimize "wear" on NV memory
  …………
#endif

  // Initialize the Extended PAN ID as my own extended address
  //扩展地址

  ZMacGetReq( ZMacExtAddr, zgExtendedPANID );

#ifndef NONWK
  // Initialize the Pre-Configured Key to the default key
  osal_memcpy( zgPreConfigKey, defaultKey, SEC_KEY_LEN );  // Do NOT Change!!!
#endif
// NONWK

//----------------------------------初始化各NV配置状态条目值
//初始化NV条目表zgItemTable[ ].如果setDefault=TRUE,则设置为系统默认
//值;否则设置为原先存储在里面的值.

  while ( zgItemTable[i].id != 0x00 )
  {
    // Initialize the item
    zgItemInit( zgItemTable[i].id, zgItemTable[i].len, zgItemTable[i].buf, setDefault  );

    // Move on to the next item
    i++;
  }
//----------------------------------
 
  // Clear the Config State default
  //如果NV条目ZCD_NV_STARTUP_OPTION的ZCD_STARTOPT_DEFAULT_CONFIG_STATE标致位为1
  if ( setDefault )
  { //把ZCD_STARTOPT_DEFAULT_CONFIG_STATE标致位清0
    zgWriteStartupOptions( ZG_STARTUP_CLEAR, ZCD_STARTOPT_DEFAULT_CONFIG_STATE );
  }

  return ( ZSUCCESS );
}
****************************************

首先来看下zgReadStartupOptions():
****************************************
// Reads the ZCD_NV_STARTUP_OPTION NV Item.
uint8 zgReadStartupOptions( void )
{
  // Default to Use Config State and Use Network State
  uint8 startupOption = 0;

  // This should have been done in ZMain.c, but just in case.
  //能返回ZSUCCESS,说明条目已经存在;当然,如果不存在,还是在osal_nv_item_init
  //中创建条目并设置为startupOption的值.
  if ( osal_nv_item_init( ZCD_NV_STARTUP_OPTION,
                              sizeof(startupOption),
                              &startupOption ) == ZSUCCESS )
  {
    // Read saved startup control
    //NV条目ZCD_NV_STARTUP_OPTION值读到startupOption

    osal_nv_read( ZCD_NV_STARTUP_OPTION,
                  0,
                  sizeof( startupOption ),
                  &startupOption);
  }
  return ( startupOption );
}
****************************************
这里读取ZCD_NV_STARTUP_OPTION NV值,再来看下osal_nv_item_init():
****************************************
 * @fn      osal_nv_item_init
 *
 * @brief   If the NV item does not already exist, it is created and
 *          initialized with the data passed to the function, if any.
 *          This function must be called before calling osal_nv_read() or
 *          osal_nv_write().
            如果条目不存在,则创建并初始化为传递过来的值.如果存在,
            直接返回ZSUCCESS.这个必须在read和write之前调用.

 *
 * @param   id  - Valid NV item Id.
 * @param   len - Item length.
 * @param  *buf - Pointer to item initalization data. Set to NULL if none.
 *                条目初始化时的值.
 * @return  NV_ITEM_UNINIT - Id did not exist and was created successfully.
 *          ZSUCCESS       - Id already existed, no action taken.
 *          NV_OPER_FAILED - Failure to find or create Id.
 */
uint8 osal_nv_item_init( uint16 id, uint16 len, void *buf )
{
  /* Global fail flag for fail due to low bus voltage has less impact on code
   * size than passing back a return value all the way from the lowest level.
   */
  failF = FALSE;

  // ZCD_NV_EXTADDR is maintained without an osalNvHdr_t, so it is always already

initialized.
  //条目已经存在,则直接返回ZSUCCESS

  if ( (id == ZCD_NV_EXTADDR) || (findItem( id ) != OSAL_NV_ITEM_NULL) )
  {
    return ZSUCCESS;  //只有条目已经存在了,才会返回ZSUCCESS
  }
  //条目不存在,则通过initItem创建条目并初始化为buf传递过来的值
  else if ( initItem( TRUE, id, len, buf ) )
  {
    if ( failF )
    {
      (void)initNV();  // See comment at the declaration of failF.
      return NV_OPER_FAILED;
    }
    else
    {
      return NV_ITEM_UNINIT;
    }
  }
  else
  {
    return NV_OPER_FAILED;
  }
}
****************************************
在zgReadStartupOptions()中调用了osal_nv_item_init( ZCD_NV_STARTUP_OPTION,sizeof(startupOption),&startupOption );其中startupOption = 0;一开始并不存在ZCD_NV_STARTUP_OPTION这个NV条目,因而这里会创建NV条目ZCD_NV_STARTUP_OPTION并初始化为startupOption传递过来的值0.成功则返回NV_ITEM_UNINIT,因而不会调用zgReadStartupOptions()的osal_nv_read( ZCD_NV_STARTUP_OPTION,0,sizeof( startupOption ),&startupOption),最后返回startupOption=0.回到zgInit(),setDefault = FALSE保持不变,然后进入NV配置状态条目初始化 zgItemInit( zgItemTable[i].id, zgItemTable[i].len, zgItemTable[i].buf, setDefault  ),注意此时传递进去的值zgItemTable[i].buf已经定义为系统默认值(可以查看ZGlobals.c的NWK GLOBAL VARIABLES栏).看下zgItemInit():
****************************************
 * @fn       zgItemInit()
 *
 * @brief
 *
 *   Initialize a global item. If the item doesn‘t exist in NV memory,
 *   write the system default (value passed in) into NV memory. But if
 *   it exists, set the item to the value stored in NV memory.
 *   条目不存在,创建并设置为传递过来的值;存在则设置为原存储的值.
 *   Also, if setDefault is TRUE and the item exists, we will write
 *   the default value to NV space.
 *
 * @param   id - item id
 * @param   len - item len
 * @param   buf - pointer to the item
 * @param   setDefault - TRUE to set default, not read
 *                     
 * @return  ZSUCCESS if successful, NV_ITEM_UNINIT if item did not
 *          exist in NV, NV_OPER_FAILED if failure.
 */
static uint8 zgItemInit( uint16 id, uint16 len, void *buf, uint8 setDefault )
{

  uint8 status;

  // If the item doesn‘t exist in NV memory, create and initialize
  // it with the value passed in.
  status = osal_nv_item_init( id, len, buf );//NV条目读写前都要进行条目的初始化
  //这里是要status == ZSUCCESS才会执行里面的内容,说明
  //此NV条目是已经存在的,这样才会返回ZSUCCESS
  if ( status == ZSUCCESS )
  {
    if ( setDefault )  //条目存在,设置为默认值
    {
      // Write the default value back to NV
      status =  osal_nv_write( id, 0, len, buf );
    }
    else //条目存在,读取原先存储的值
    {
      // The item exists in NV memory, read it from NV memory
      status = osal_nv_read( id, 0, len, buf );
    }
  }

  return (status);
}
****************************************
可以看到同样调用了osal_nv_item_init( id, len, buf ),如果条目不存在则创建并设置为buf传递过来的值,如果存在则直接返回ZSUCCESS.一开始各NV配置状态条目不存在,因而这里创建各条目并设置为buf传递过来的值(系统定义的默认值).回到zgInit(),各NV配置状态条目初始化完成后,返回ZSUCCESS.因为此时setDefault保持FALSE不变,因而不会执行zgWriteStartupOptions( ZG_STARTUP_CLEAR, ZCD_STARTOPT_DEFAULT_CONFIG_STATE );

以上为NV条目的初始化流程.初始化时ZCD_NV_STARTUP_OPTION=0,即把各NV条目初始化为系统默认的值.
想到zgReadStartupOptions()开头那句话:
  // Default to Use Config State and Use Network State
  uint8 startupOption = 0;
startupOption即为ZCD_NV_STARTUP_OPTION的初始化值.

(###)如果,这样初始化完成后,又在应用程序中改变ZCD_NV_STARTUP_OPTION的值,改为ZCD_STARTOPT_DEFAULT_CONFIG_STATE.然后zb_SystemReset()重启系统.进入zgInit(),首先设置setDefault = FALSE,然后同样进入判断:
  if ( zgReadStartupOptions() & ZCD_STARTOPT_DEFAULT_CONFIG_STATE )// 0x01
  {
    setDefault = TRUE;
  }
调用:zgReadStartupOptions()->osal_nv_item_init(),因为此时NV条目ZCD_NV_STARTUP_OPTION已经存在,所以osal_nv_item_init()不会再创建条目并设置为传递过来的值startupOption = 0,而是直接返回ZSUCCESS,因而这时在zgReadStartupOptions()里会调用    osal_nv_read( ZCD_NV_STARTUP_OPTION,0,sizeof( startupOption ),&startupOption)读取先前已经存储在条目ZCD_NV_STARTUP_OPTION里的值:ZCD_STARTOPT_DEFAULT_CONFIG_STATE!所以这里设置setDefault = TRUE.然后进入各NV配置状态条目初始化:zgItemInit( zgItemTable[i].id, zgItemTable[i].len, zgItemTable[i].buf, setDefault  );进入zgItemInit(),同样先调用了osal_nv_item_init( id, len, buf ),因为各NV配置状态条目已经存在,所以不再创建而是直接返回ZSUCCESS,所以这次会调用:
  if ( status == ZSUCCESS )
  {
    if ( setDefault )  //条目存在,设置为默认值
    {
      // Write the default value back to NV
      status =  osal_nv_write( id, 0, len, buf );
    }
    else //条目存在,设置为原先存储的值
    {
      // The item exists in NV memory, read it from NV memory
      status = osal_nv_read( id, 0, len, buf );
    }
  }
因为setDefault = TRUE,所以重新设置各NV配置状态条目为系统默认值.然后回到zgInit(),最终调用
  if ( setDefault )
  {  //把ZCD_STARTOPT_DEFAULT_CONFIG_STATE这位清0
    zgWriteStartupOptions( ZG_STARTUP_CLEAR, ZCD_STARTOPT_DEFAULT_CONFIG_STATE );
  }
这里把ZCD_NV_STARTUP_OPTION条目值重新清0(我上面只假设ZCD_NV_STARTUP_OPTION的值为ZCD_STARTOPT_DEFAULT_CONFIG_STATE 0x01).

  (###)当然,如果像SampleApp例子没有在应用程序中修改过ZCD_NV_STARTUP_OPTION,最开始启动设备时会创建条目并初始化为0(startupOption = 0),然后创建各NV配置状态条目并初始化为系统默认值.以后的每次设备启动,ZCD_NV_STARTUP_OPTION值为0,setDefault = FALSE,各NV配置状态条目初始化执行的是:
    else //条目存在,设置为原先存储的值
    {
      // The item exists in NV memory, read it from NV memory
      status = osal_nv_read( id, 0, len, buf );
    }
读取此前的配置状态.

  (###)再比如SimpleApp例子,应用程序通过按键改变NV条目ZCD_NV_STARTUP_OPTION以及NV配置状态条目ZCD_NV_LOGICAL_TYPE的值,通过zb_WriteConfiguration()写入NV中,然后zb_SystemReset()重启系统.进入zgInit(),通过zgReadStartupOptions()读出的ZCD_NV_STARTUP_OPTION值不为ZCD_STARTOPT_DEFAULT_CONFIG_STATE,因而setDefault = FALSE.再进入zgItemInit(),因为条目存在且setDefault = FALSE,所以执行     
// The item exists in NV memory, read it from NV memory
      status = osal_nv_read( id, 0, len, buf );

把条目的值设置为先前存储的值.即在SampleApp例子中,把上次存储在NV条目ZCD_NV_LOGICAL_TYPE中
的设备逻辑类型值读出作为此次NV条目ZCD_NV_LOGICAL_TYPE的值.

(1)初始ZCD_NV_STARTUP_OPTION=0.如果设备是首次启动,则调用status = osal_nv_item_init( id, len, buf )创建各NV配置状态条目并初始化为系统默认值;如果设备不是首次启动,则调用osal_nv_read( id, 0, len, buf )读取各NV配置状态条目先前存储的值作为本次的值.
(2)设置ZCD_NV_STARTUP_OPTION=ZCD_STARTOPT_DEFAULT_CONFIG_STATE.NV条目ZCD_NV_STARTUP_OPTION已经存在,调用osal_nv_write( id, 0, len, buf )设置各NV配置状态条目为系统默认值.最后把这位清0.
(3)设置ZCD_NV_STARTUP_OPTION=ZCD_STARTOPT_AUTO_START.NV条目ZCD_NV_STARTUP_OPTION已经存在,调用osal_nv_read( id, 0, len, buf )读取各NV配置状态条目先前存储的值作为本次的值.