Zigbee网络设备启动—主要函数说明

使用的协议栈版本信息: ZigBee2006\ZStack-1.4.3-1.2.1
1、ZDApp_Init()及其中几个函数的说明.
(1)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();
}
****************************************
这里说明三个函数:
ZDAppCheckForHoldKey()
ZDO_Init()
ZDOInitDevice( 0 )

(1)ZDAppCheckForHoldKey()
**************************
void ZDAppCheckForHoldKey( void )
{
#if (defined HAL_KEY) && (HAL_KEY == TRUE)
//个人认为:可以直接通过读取按键来看是否需要采用HOLD_START模式
//如果发现SW_1按下(普通按键吧)/向上(Joystick up),则设置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
}
参见基本问题说明3.
*************************
(2)、ZDO_Init()
*************************
void ZDO_Init( void )
{
/*
//REFLECTOR如果定义了这个编译选项则使用“源绑定”,
In the Zigbee 2006 release,the binding mechanism is implemented in
all devices and is called source binding.绑定机制可以在所有设备中实现,
04版的只能在协调器中实现.*/
// Initialize ZD items
#if defined ( REFLECTOR )
ZDO_EDBind = NULL;
#endif

// Setup the device - type of device to create.
ZDODeviceSetup();
}
*************************
看下ZDODeviceSetup(),具体见各节点启动流程记录.
*************************
//Call set functions depending on the type of device compiled.
//根据编译选项来设置; 比如simpleApp中的灯节点,预编译了ZDO_COORDINATOR和
//REFLECTOR和SOFT_START,因此会根据这些来选择开启一些函数功能.

static void ZDODeviceSetup( void )
{
#if defined( ZDO_COORDINATOR ) //编译了ZDO_COORDINATOR
NLME_CoordinatorInit();
#endif

#if defined ( REFLECTOR ) //编译了REFLECTOR
#if defined ( ZDO_COORDINATOR ) //编译了REFLECTOR且编译了ZDO_COORDINATOR
APS_ReflectorInit( APS_REFLECTOR_PUBLIC );
#else //编译了REFLECTOR且编译了路由器或终端
APS_ReflectorInit( APS_REFLECTOR_PRIVATE ); //路由器/终端
#endif
#endif

//没有编译ZDO_COORDINATOR或者编译了SOFT_START
#if !defined( ZDO_COORDINATOR ) || defined( SOFT_START )
NLME_DeviceJoiningInit();
#endif
}
*************************
(3)、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

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 );
}
*************************
看下五个函数:
ZDApp_LeaveCtrlInit()
ZDApp_ReadNetworkRestoreState()
ZDApp_RestoreNetworkState()
ZDAppDetermineDeviceType()
ZDApp_NetworkInit( extendedDelay )

(3.1)ZDApp_LeaveCtrlInit()
*************************
void ZDApp_LeaveCtrlInit( void )
{
uint8 status;


// Initialize control state
ZDApp_LeaveCtrl = ZDAPP_LEAVE_CTRL_INIT;
//0
//初始化一个NV条目.这个函数检查一个NV条目的存在与否.
//如果不存在,它将被建立和初始化随着数据一起传给函数,
//这个函数必须在调用osal_nv_read() or osal_nv_write()之前被调用
status = osal_nv_item_init( ZCD_NV_LEAVE_CTRL,//用户定义的条目ID
sizeof(ZDApp_LeaveCtrl),//条目的大小
&ZDApp_LeaveCtrl );
//指向条目初始化的数据
if ( status == ZSUCCESS ) //这个NV条目存在,则把这个条目值读出来,ZDApp_LeaveCtrl指向它
{
// Read saved control
osal_nv_read( ZCD_NV_LEAVE_CTRL,//用户定义的项目ID
0,//Memory offset into item in bytes.
sizeof( uint8 ),//条目长度
&ZDApp_LeaveCtrl); //数据保存缓冲区指针
}
}
*************************
(3.2)ZDApp_ReadNetworkRestoreState()
如果预编译了NV_RESTORE并且没有手工设置避开NV存储机制,则通过这个函数来判断是否需要恢复设备原先的网络状态.
*************************
//返回要么是新的网络状态; 要么是恢复的网络状态
//返回默认为恢复的状态,但如果从读取ZCD_NV_STARTUP_OPTION这个NV条目中
//读取出来的设备网络状态是ZCD_STARTOPT_DEFAULT_NETWORK_STATE,则返回
//新的网络状态ZDO_INITDEV_NEW_NETWORK_STATE
uint8 ZDApp_ReadNetworkRestoreState( void )
{ //设备的网络状态为恢复的网络状态
uint8 networkStateNV = ZDO_INITDEV_RESTORED_NETWORK_STATE;

// Look for the New Network State option.默认的网络状态
if ( zgReadStartupOptions() & ZCD_STARTOPT_DEFAULT_NETWORK_STATE )
{
//网络状态初始化,即新的网络状态,可能意味着没有任何网络状态恢复
networkStateNV = ZDO_INITDEV_NEW_NETWORK_STATE;
}

return ( networkStateNV );
}
*************************
(3.3)ZDApp_RestoreNetworkState()
如果3.2的函数得到的networkStateNV为恢复的网络状态,则通过这个函数来读取存储在NV里的先前的网络状态.
*************************
uint8 ZDApp_RestoreNetworkState( void )
{
…………

// Initialize NWK NV items
nvStat = NLME_InitNV();

if ( nvStat != NV_OPER_FAILED )
{
if ( NLME_RestoreFromNV() )
{
// 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!!!!!!!!
}
else
nvStat = NV_ITEM_UNINIT;

…………
if ( nvStat == ZSUCCESS )
return ( ZDO_INITDEV_RESTORED_NETWORK_STATE ); //说明设备的网络状态已经恢复
else
return ( ZDO_INITDEV_NEW_NETWORK_STATE );
}
*************************
(3.4)ZDAppDetermineDeviceType()
如果设备的网络状态为新的网络状态(第一种情况:未编译NV_RESTORE则在ZDOInitDevice()开头初始化为ZDO_INITDEV_NEW_NETWORK_STATE; 第二种情况:编译了NV_RESTORE但ZDApp_ReadNetworkRestoreState()得到的设备网络状态为ZDO_INITDEV_NEW_NETWORK_STATE,即不能恢复或无状态恢复.).这个函数仅适用于编译了SOFT_START的设备,不支持终端设备.比如对于自动启动模式的SampleApp,没有编译SOFT_START.因此本函数对其没作用.具体见各类型设备启动流程.
*************************
* @fnZDAppDetermineDeviceType()
*
* @briefDetermines the type of device to start.Right now
*this only works with the SOFT_START feature.So it doesn't
*support the end device type.
*//终端逻辑类型只能作为终端
*/

//像simpleApp这个例子中,灯节点和中心收集节点都同时预编译了
//SOFT_START和HOLD_AUTO_START; 而开关节点和传感器节点都只预编译了
//HOLD_AUTO_START;
//开关节点和传感器节点无论按哪个键都是作为终端设备启动的
//而灯节点和中心收集节点可以选择作为协调器或路由器.
void ZDAppDetermineDeviceType( void )
{
/*
像simpleApp中灯节点和中心收集节点都编译了SOFT_START,
zgDeviceLogicalType在ZGlobals.h中被初始化为ZG_DEVICETYPE_SOFT.而像
开关节点和传感器节点没有编译SOFT_START,则zgDeviceLogicalType被初始化为
ZG_DEVICETYPE_ENDDEVICE.之前卡在这里很长时间,因为像灯节点和中心收集节点
编译过SOFT_START,那zgDeviceLogicalType不就等于ZG_DEVICETYPE_SOFT,最终在
这个函数里也只能执行devStartMode = MODE_JOIN这一句,这与灯节点和中心收集
节点可以通过按键来选择作为协调器或路由器不符,纠结了很久,最后才发现,在按键
选择设备的逻辑类型后,会把这个逻辑类型值写入NV条目ZCD_NV_LOGICAL_TYPE,而
ZDO全局变量zgDeviceLogicalType等于NV条目ZCD_NV_LOGICAL_TYPE的值,通过按键
选择逻辑类型会改变zgDeviceLogicalType的值.比如灯节点按K1选择设备的逻辑类型
为协调器ZG_DEVICETYPE_COORDINATOR,写入NV条目ZCD_NV_LOGICAL_TYPE存储,则此时
zgDeviceLogicalType的值即为ZG_DEVICETYPE_COORDINATOR!!!见基本问题说明5.
*/
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
}
*************************
(3.5)ZDApp_NetworkInit( extendedDelay )
函数设置触发事件ZDO_NETWORK_INIT.
*************************
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 ); //不需延时
}
}
*************************

****************************************
2、ZDO_StartDevice()
ZDApp_event_loop()对事件ZDO_NETWORK_INIT处理如下:
*************************
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_Config_Node_Descriptor.LogicalType和devStartMode,
ZDO_Config_Node_Descriptor.LogicalType的初始化见基本问题说明6.devStartMode的初始化见4.但两个参数的值可以通过函数(3.3)ZDApp_RestoreNetworkState()[如果编译了NV_RESTORE]和函数(3.4)ZDAppDetermineDeviceType()来改变.
*************************
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//错误,启动模式未知
{
…………
}
}
#endif
// !ZDO_COORDINATOR

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 )
{
…………
nwk_ScanJoiningOrphan(&scanCnf);

ret = ZSuccess;
}
else
{
devState = DEV_NWK_ORPHAN; //孤儿
ret = NLME_OrphanJoinRequest( zgDefaultChannelList,
zgDefaultStartingScanDuration );
}
}
else
{
…………
}
}
…………
}
*************************

3、协调器建网,路由器/终端入网的回调函数
(1)ZDO_NetworkFormationConfirmCB():协调器建立网络请求的回调函数
* @briefThis function reports the results of the request to
*initialize a coordinator in a network.
*
* @paramStatus - Result of NLME_NetworkFormationRequest()

(2)ZDO_StartRouterConfirmCB():作为路由器启动请求的回调函数
* @briefThis function reports the results of the request to
*start functioning as a router in a network.
*
* @paramStatus - Result of NLME_StartRouterRequest()

(3)ZDO_JoinConfirmCB():路由器/终端加入网络请求的回调函数
* @briefThis function allows the next hight layer to be notified
*of the results of its request to join itself or another
*device to a network.
*
* @paramStatus - Result of NLME_JoinRequest()

【Zigbee网络设备启动—主要函数说明】(4) ZDO_NetworkDiscoveryConfirmCB():路由器/终端发现网络请求的回调函数
* @fnZDO_NetworkDiscoveryConfirmCB
*
* @briefThis function returns a choice of PAN to join.
*
* @paramResultCount - Number of routers discovered
* @paramNetworkList - Pointer to list of network descriptors


四种回调函数会分别触发ZDAppTaskID相应事件:
ZDO_NetworkFormationConfirmCB(): osal_set_event( ZDAppTaskID, ZDO_NETWORK_START );
ZDO_StartRouterConfirmCB():osal_set_event( ZDAppTaskID, ZDO_ROUTER_START );
ZDO_JoinConfirmCB():ZDApp_SendMsg( ZDAppTaskID, ZDO_NWK_JOIN_IND, sizeof(osal_event_hdr_t), (byte*)NULL );
ZDO_NetworkDiscoveryConfirmCB(): ZDApp_SendMsg( ZDAppTaskID, ZDO_NWK_DISC_CNF, sizeof(ZDO_NetworkDiscoveryCfm_t), (byte *)&msg );

参照上面ZDO_StartDevice()函数,可以看到相应功能的请求函数.
回调函数具体调用流程参见种类型设备的启动流程.


说明:
1、本文为个人学习笔记,纯属个人理解,错误不可避免,仅供参考.随时更新!
2、细节基本不管,遇到问题再作分析,程序注释为个人原始注释内容,记录有些仓促!
3、欢迎交流,转载请注明出处,谢谢!

2010.7.09~XF

    推荐阅读