Zigbee网络设备启动流程—终端(非自启动模式)

使用的协议栈版本信息: ZigBee2006\ZStack-1.4.3-1.2.1
Zigbee网络设备启动流程—终端(非自启动模式HOLD_AUTO_START)
—以SimpleApp的开关点SimpleSwitchEB为例.


开关节点不作选择,无论按K1还是K2都作为终端启动.

1、开关节点预编译信息
通过project->options->c/c++compiler->extraOptions可以看到灯节点所带的配置文件为:
-f $PROJ_DIR$\..\..\..\Tools\CC2430DB\f8wEndev.cfg
-f $PROJ_DIR$\..\..\..\Tools\CC2430DB\f8wConfig.cfg

没有编译ZDO_COORDINATOR和RTR_NWK.

通过project->options->c/c++compiler->Preprocessor->Defined symbols可以看到灯节点预编译包含了:
CC2430EB; NWK_AUTO_POLL; HOLD_AUTO_START; REFLECTOR; POWER_SAVING; NV_INIT
xLCD_SUPPORTED; xNV_RESTORE; xZTOOL_P1; xMT_TASK; xMT_SAPI_FUNC; xMT_SAPI_CB_FUNC

可以看到编译了HOLD_AUTO_START但没有编译SOFT_START.
因而初始化:

devState = DEV_HOLD(见基本问题说明3.编译了HOLD_AUTO_START)
devStartMode = MODE_JOIN(见基本问题说明4.)
zgDeviceLogicalType = DEVICE_LOGICAL_TYPE=ZG_DEVICETYPE_ENDDEVICE(见基本问题说明5.)
ZDO_Config_Node_Descriptor.LogicalType=NODETYPE_DEVICE(见基本问题说明6.)
2、具体流程
main()->osal_init_system()->osalInitTasks()->ZDApp_Init()
进入ZDApp_Init()函数:
**************************************
void ZDApp_Init( byte task_id )
{
uint8 capabilities;

// Save the task ID
ZDAppTaskID = task_id;

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

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

ZDAppCheckForHoldKey();

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


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

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

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

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

ZDApp_RegisterCBs();
}
**************************************
ZDApp_Init()中一些函数可以参见主要函数说明1.根据编译情况ZDO_Init()里开启的功能函数是APS_ReflectorInit( APS_REFLECTOR_PRIVATE )和NLME_DeviceJoiningInit().
开关点编译了HOLD_AUTO_START,则devState = DEV_HOLD,也就可以不用管有没有手工置位SW1了.因此不会进入ZDOInitDevice()来初始化设备(建网或入网).而是通过LED4的闪烁来指示这是一个非自动启动模式,等待应用程序来初始化设备.ZDApp_Init()初始化结束后再进入SAPI_Init():
**************************************
void osalInitTasks( void )
{
…………
macTaskInit( taskID++ );
nwk_init( taskID++ );
Hal_Init( taskID++ );
#if defined( MT_TASK )
MT_TaskInit( taskID++ );
#endif
APS_Init( taskID++ );
ZDApp_Init( taskID++ );
SAPI_Init( taskID );
}
**************************************
看下SAPI_Init():
**************************************
void SAPI_Init( byte task_id )
{
…………
if ( HalKeyRead () == HAL_KEY_SW_1)
{
//期间如果按下SW1键,则禁止自动启动和NV存储功能,更新配置重启
// If SW5 is pressed and held while powerup, force auto-start and nv-restore off and reset
startOptions = ZCD_STARTOPT_CLEAR_STATE | ZCD_STARTOPT_CLEAR_CONFIG; //默认启动状态和配置
//startOptions作为默认启动项配置,存到NV条目ZCD_NV_STARTUP_OPTION中
zb_WriteConfiguration( ZCD_NV_STARTUP_OPTION, sizeof(uint8), &startOptions );
zb_SystemReset(); //复位系统
}

#endif // HAL_KEY
#ifdef LCD_SUPPORTED
Print8(HAL_LCD_LINE_2 ,20,"Simple",1);
#endif
// Set an event to start the application
osal_set_event(task_id, ZB_ENTRY_EVENT);
}
**************************************
按键SW1(个人记录中按键有点乱,具体查看映射函数)功能和SW_BYPASS_NV、SW_BYPASS_START类似,置位可以避开一些功能.
可以看到触发sapi_TaskID的ZB_ENTRY_EVENT事件,看下对ZB_ENTRY_EVENT的处理:
**************************************
if ( events & ZB_ENTRY_EVENT ) /*sapi启动事件*/
{
uint8 startOptions;
// Give indication to application of device startup
zb_HandleOsalEvent( ZB_ENTRY_EVENT );

// LED off cancels HOLD_AUTO_START blink set in the stack
HalLedSet (HAL_LED_4, HAL_LED_MODE_OFF);
//关闭指示HOLD_AUTO_START的LED4

//读配置
zb_ReadConfiguration( ZCD_NV_STARTUP_OPTION, sizeof(uint8), &startOptions );
if ( startOptions & ZCD_STARTOPT_AUTO_START ) //自动启动
{
zb_StartRequest();
}
else
{
//LED2闪烁指示需要外部输入来配置信息再重启设备
// blink leds and wait for external input to config and restart
HalLedBlink(HAL_LED_2, 0, 50, 500);
#if defined SENSOR //!!! 如果是传感器实验的sensor节点则不需要外界按键,在这里直接设置终端类型和自启动模式,然后重启!
UINT8 logicalType;
logicalType = ZG_DEVICETYPE_ENDDEVICE; //终端写入NV
zb_WriteConfiguration(ZCD_NV_LOGICAL_TYPE, sizeof(uint8), &logicalType);

// Do more configuration if necessary and then restart device with auto-start bit set

zb_ReadConfiguration( ZCD_NV_STARTUP_OPTION, sizeof(uint8), &startOptions );
startOptions = ZCD_STARTOPT_AUTO_START; //自启动写入NV
zb_WriteConfiguration( ZCD_NV_STARTUP_OPTION, sizeof(uint8), &startOptions );
zb_SystemReset();
#endif }

return (events ^ ZB_ENTRY_EVENT );
}
**************************************
从程序中可以看到首先读NV条目ZCD_NV_STARTUP_OPTION所存储的值,如果是ZCD_STARTOPT_AUTO_START则调用zb_StartRequest()来请求启动设备.NV条目ZCD_NV_STARTUP_OPTION的值在zgInit()中初始化为默认值(暂时估计为0,见基本问题说明9),因此这里不会调用zb_StartRequest()启动设备,而是LED2闪烁来指示需要外部输入来控制(当然这里sensor节点除外,因为sensor节点预编译了SENSOR,所以会直接把ZG_DEVICETYPE_ENDDEVICEZCD_STARTOPT_AUTO_START写入NV然后重启,不需要外界按键)。
作为一个单一设备,main()函数中各项功能已经初始化完成并进入了系统主循环函数osal_start_system(),要不也不会处理ZB_ENTRY_EVENT事件,只是这个时候还没有启动作为一个网络设备的功能.接下来就通过按键来启动,开关点按下K1或K2,按键处理函数处理如下:
**************************************
if ( keys & HAL_KEY_SW_1 )
{
if ( myAppState == APP_INIT )
{
// In the init state, keys are used to indicate the logical mode.
// The Switch device is always an end-device
logicalType = ZG_DEVICETYPE_ENDDEVICE; //终端设备
zb_WriteConfiguration(ZCD_NV_LOGICAL_TYPE, sizeof(uint8), &logicalType);

// Do more configuration if necessary and then restart device with auto-start bit set

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();

}
else
{
// Initiate a binding with null destination
zb_BindDevice(TRUE, TOGGLE_LIGHT_CMD_ID, NULL);
//zb_SendDataRequest( 0xFFFE, TOGGLE_LIGHT_CMD_ID, 0,
// (uint8 *)NULL, myAppSeqNumber, 0, 0 );//按K2
}
}
**************************************
按K1或K2选择逻辑类型logicalType = ZG_DEVICETYPE_ENDDEVICE,写入NV条目ZCD_NV_LOGICAL_TYPE,不过开关节点中zgDeviceLogicalType本身就被初始化为zgDeviceLogicalType = DEVICE_LOGICAL_TYPE=ZG_DEVICETYPE_ENDDEVICE,因而按K1/2没有改变zgDeviceLogicalType的值.然后把启动选项startOptions = ZCD_STARTOPT_AUTO_START的值写入NV条目ZCD_NV_STARTUP_OPTION中.下次读取NV条目ZCD_NV_STARTUP_OPTION的值就为ZCD_STARTOPT_AUTO_START(因此会在对ZB_ENTRY_EVENT的处理中触发进入zb_StartRequest()).
最后重启系统.重新经过流程main()->osal_init_system()->osalInitTasks()->ZDApp_Init()->SAPI_Init()->触发sapi_TaskID的ZB_ENTRY_EVENT事件
此时
zb_ReadConfiguration( ZCD_NV_STARTUP_OPTION, sizeof(uint8), &startOptions );
if ( startOptions & ZCD_STARTOPT_AUTO_START ) //自动启动
{
zb_StartRequest();
}
再读取NV条目ZCD_NV_STARTUP_OPTION值时为ZCD_STARTOPT_AUTO_START,因而进入zb_StartRequest():
**************************************
* @fn zb_StartRequest
*
* @brief The zb_StartRequest function starts the ZigBee stack. When the
* ZigBee stack starts, the device reads configuration parameters
* from Nonvolatile memory and the device joins its network. The
* ZigBee stack calls the zb_StartConrifm callback function when
* the startup process completes.
*
* @param none
*
* @return none
*/
void zb_StartRequest()
{
uint8 logicalType;

// Start the device
// start delay = min(NWK_START_DELAY, zgStartDelay) + rand() - only for fresh start, not restore
if ( zgStartDelay < NWK_START_DELAY )
zgStartDelay = 0;
else
zgStartDelay -= NWK_START_DELAY;

// check that bad combinations of compile flag definitions and device type
/*
// 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(可选择类型)
*/
//读取存储在NV里的设备ZCD_NV_LOGICAL_TYPE
zb_ReadConfiguration( ZCD_NV_LOGICAL_TYPE, sizeof(uint8), &logicalType );

//检查错误的参数.
//如:logicalType>0x02(可选择类型选择后不是协调器就是路由器);定义
//了协调器,但logicalType为终端设备;
//只可能是路由器时logicalType不为路由器;未编译RTR_NWK功能却定义了
//logicalType为协调器;只可能为终端时logicalType却未定义为终端;
if ( ( logicalType > ZG_DEVICETYPE_ENDDEVICE ) ||
#if defined( RTR_NWK )
#if defined( ZDO_COORDINATOR )
// Only RTR or Coord possible
( logicalType == ZG_DEVICETYPE_ENDDEVICE ) ||
#else
// Only RTR possible
( logicalType != ZG_DEVICETYPE_ROUTER ) ||
#endif
#else
#if defined( ZDO_COORDINATOR )
// Error
( 1 ) ||
#else
// only ED possible
( logicalType != ZG_DEVICETYPE_ENDDEVICE ) ||
#endif
#endif
( 0 ) )
{
// error configuration
SAPI_SendCback( SAPICB_START_CNF, ZInvalidParameter, 0 ); //发送错误报告
}
else //设备逻辑类型参数都正确则调用ZDOInitDevice()
{
ZDOInitDevice(zgStartDelay);
}

return;
}

**************************************
读取设备存储在NV条目ZCD_NV_LOGICAL_TYPE中的逻辑类型参数(这里开关节点按K1/2逻辑类型参数为ZG_DEVICETYPE_ENDDEVICE),并检验是否有错误,正确则调用ZDOInitDevice(zgStartDelay).看下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
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();
}

//恢复设备先前的网络状态参数
//设置devStartMode = MODE_RESUME!!!!
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 );
}
**************************************
两种情况,编译了NV_RESTORE和未编译.见自启动模式记录的一些说明.开关节点没有编译NV_RESTORE因此networkStateNV == ZDO_INITDEV_NEW_NETWORK_STATE,但开关节点没有编译过SOFT_START,因而ZDAppDetermineDeviceType()不起作用.开关节点各项参数保持初始值:
devStartMode = MODE_JOIN
ZDO_Config_Node_Descriptor.LogicalType=NODETYPE_DEVICE
然后跳回ZDOInitDevice()进入ZDApp_NetworkInit( extendedDelay )触发ZDAppTaskID的ZDO_NETWORK_INIT事件.看下对ZDO_NETWORK_INIT处理:
**************************************
UINT16 ZDApp_event_loop( byte task_id, UINT16 events )
{
…………
if ( events & ZDO_NETWORK_INIT )
{
// Initialize apps and start the network
devState = DEV_INIT;
ZDO_StartDevice( (uint8)ZDO_Config_Node_Descriptor.LogicalType, devStartMode,
DEFAULT_BEACON_ORDER, DEFAULT_SUPERFRAME_ORDER );

// Return unprocessed events
return (events ^ ZDO_NETWORK_INIT);
}
…………
}
**************************************
这里两个参数:
devStartMode = MODE_JOIN
ZDO_Config_Node_Descriptor.LogicalType=NODETYPE_DEVICE
进入ZDO_StartDevice()
**************************************
void ZDO_StartDevice( byte logicalType, devStartModes_t startMode, byte beaconOrder, byte superframeOrder )
{
ZStatus_t ret;

ret = ZUnsupportedMode;

#if defined(ZDO_COORDINATOR) //预编译了协调器
…………
#endif
// !ZDO_COORDINATOR

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

#if defined( MANAGED_SCAN ) //管理扫描,由自己设定扫描参数.
ZDOManagedScan_Next();
ret = NLME_NetworkDiscoveryRequest( managedScanChannelMask, BEACON_ORDER_15_MSEC );
#else
ret = NLME_NetworkDiscoveryRequest( zgDefaultChannelList, zgDefaultStartingScanDuration );
#endif
}
…………
}
**************************************

调用了NLME_NetworkDiscoveryRequest()来发现网络,对NLME_NetworkDiscoveryRequest()的调用会产生一个回调函数ZDO_NetworkDiscoveryConfirmCB()(参见主要函数说明3).接下来的回调函数流程与[Zigbee网络设备启动流程—路由器(自启动模式)]记录中的一样.最终应用层任务sapi_TaskID对ZDO_STATE_CHANGE事件的处理为设置myAppState = APP_START通知应用层网络设备开启成功.

3、终端(非自启动模式HOLD_AUTO_START)—以SimpleApp的开关节点SimpleSwitchEB按K1或K2作为终端启动为例.假设初始化成功,网络发现成功,网络加入成功.
程序大致流程:
main()->osal_init_system()->osalInitTasks()->ZDApp_Init()->SAPI_Init()->触发sapi_TaskID的ZB_ENTRY_EVENT事件->等待按键->按K1/2写设备逻辑类型和启动选项到NV->zb_SystemReset()->main()->osal_init_system()->osalInitTasks()->ZDApp_Init()->SAPI_Init()->触发sapi_TaskID的ZB_ENTRY_EVENT事件->zb_StartRequest()->ZDOInitDevice()->ZDApp_NetworkInit()->触发ZDAppTaskID的ZDO_NETWORK_INIT->ZDO_StartDevice()->NLME_NetworkDiscoveryRequest()->网络发现成功ZDO_NetworkDiscoveryConfirmCB()->触发ZDAppTaskID的ZDO_NWK_DISC_CNF->NLME_JoinRequest()->网络加入成功ZDO_JoinConfirmCB->触发ZDAppTaskID的ZDO_NWK_JOIN_IND->ZDApp_ProcessNetworkJoin()->触发ZDAppTaskID的ZDO_STATE_CHANGE_EVT-> ZDO_UpdateNwkStatus->触发sapi_TaskID的ZDO_STATE_CHANGE事件->SAPI_StartConfirm()->zb_StartConfirm()->设置myAppState = APP_START通知应用层网络设备开启成功.

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

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

更新2010.7.12 2010.7.09 ~XF

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值