ZigBee协议栈探究—-(三)网络创建
关于设备的链接,还需要从头说起。
在(一)中我提到过一个函数osalInitTasks
,这个函数位于OSAL_Smart_home.c
中。我们再把这个函数拿出来看一下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
|
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++ ); #if defined ( ZIGBEE_FRAGMENTATION ) APSF_Init( taskID++ ); #endif ZDApp_Init( taskID++ ); #if defined ( ZIGBEE_FREQ_AGILITY ) || defined ( ZIGBEE_PANID_CONFLICT ) ZDNwkMgr_Init( taskID++ ); #endif Smart_home_Init( taskID ); }
|
在最开始的流程分析中,我们已经分析了Smart_home_Init( taskID );
的作用:初始化用户进程,为用户创建Task。在分析的时候也提过ZDO层,也就是ZigBee Device Object
,这个层掌管着ZigBee设备的终端节点具体提供以下功能
- 初始化应用支持子层,网络层。
- 发现节点和节点功能。在无信标的网络中,加入的节点只对其父节点可见。而其他节点可以通过ZDO的功能来确定网络的整体拓扑结构已经节点所能提供的功能。
- 安全加密管理:主要包括安全key的建立和发送,已经安全授权。
- 网络的维护功能。
- 绑定管理:绑定的功能由应用支持子层提供,但是绑定功能的管理却是由ZDO提供,它确定了绑定表的大小,绑定的发起和绑定的解除等功能。
- 节点管理:对于网络协调器和路由器,ZDO提供网络监测、获取路由和绑定信息、发起脱离网络过程等一系列节点管理功能。
也就是说,我们想要探究的设备发现与绑定,ZigBee组网的过程都在ZDO层进行实现。那就进入ZDApp_Init()
这个函数来看看吧。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
|
void ZDApp_Init( uint8 task_id ) { ZDAppTaskID = task_id;
ZDAppNwkAddr.addrMode = Addr16Bit; ZDAppNwkAddr.addr.shortAddr = INVALID_NODE_ADDR; (void)NLME_GetExtAddr();
ZDAppCheckForHoldKey();
ZDO_Init();
afRegister( (endPointDesc_t *)&ZDApp_epDesc );
#if defined( ZDO_USERDESC_RESPONSE ) ZDApp_InitUserDesc(); #endif
if ( devState != DEV_HOLD ) { ZDOInitDevice( 0 ); } else { ZDOInitDevice( ZDO_INIT_HOLD_NWK_START ); HalLedBlink ( HAL_LED_4, 0, 50, 500 ); }
ZDApp_InitZdoCBFunc();
ZDApp_RegisterCBs(); }
|
在这个函数中可以看到,最重要的一步就是ZDOInitDevice()
设备在这里开始启动,并让LED闪烁。那么我们从这里来看看设备是如何启动的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107
|
uint8 ZDOInitDevice( uint16 startDelay ) { uint8 networkStateNV = ZDO_INITDEV_NEW_NETWORK_STATE; uint16 extendedDelay = 0;
if ( devState == DEV_HOLD ) { zgInitItems( FALSE ); }
ZDConfig_InitDescriptors(); _NIB.CapabilityFlags = ZDO_Config_Node_Descriptor.CapabilityFlags;
#if defined ( NV_RESTORE ) if ( HalKeyRead() == SW_BYPASS_NV ) networkStateNV = ZDO_INITDEV_NEW_NETWORK_STATE; else { networkStateNV = ZDApp_ReadNetworkRestoreState(); }
if ( networkStateNV == ZDO_INITDEV_RESTORED_NETWORK_STATE ) { networkStateNV = ZDApp_RestoreNetworkState(); } else { NLME_InitNV(); NLME_SetDefaultNV(); ZDSecMgrClearNVKeyValues(); } #endif
if ( networkStateNV == ZDO_INITDEV_NEW_NETWORK_STATE ) { ZDAppDetermineDeviceType();
extendedDelay = (uint16)((NWK_START_DELAY + startDelay) + (osal_rand() & EXTENDED_JOINING_RANDOM_MASK)); }
ZDApp_SecInit( networkStateNV );
if( ZDO_INIT_HOLD_NWK_START != startDelay ) { devState = DEV_INIT;
ZDApp_LeaveCtrlInit();
ZDApp_LeaveCtrlStartup( &devState, &startDelay );
if ( devState == DEV_HOLD ) { zgWriteStartupOptions( ZG_STARTUP_SET, ZCD_STARTOPT_DEFAULT_NETWORK_STATE );
osal_set_event( ZDAppTaskID, ZDO_STATE_CHANGE_EVT );
return ( ZDO_INITDEV_LEAVE_NOT_STARTED ); }
ZDApp_NetworkInit( extendedDelay ); }
NLME_SetBroadcastFilter( ZDO_Config_Node_Descriptor.CapabilityFlags );
return ( networkStateNV ); }
|
通过阅读可以看到,这个函数的主要目的就是计算启动延迟,并启动网络设备,主要启动设备的语句为ZDApp_NetworkInit( extendedDelay );
那我们就来看看这个函数又做了什么
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
|
void ZDApp_NetworkInit( uint16 delay ) { if ( delay ) { osal_start_timerEx( ZDAppTaskID, ZDO_NETWORK_INIT, delay ); } else { osal_set_event( ZDAppTaskID, ZDO_NETWORK_INIT ); } }
|
从这两个函数名称和形参来看,都是要将ZDAppTaskID
的标识设置为ZDO_NETWORK_INIT
。而被设置了flag的任务在这个时候就会被处理。接收这个处理的函数就是ZDApp_event_loop()
,由于这个函数过长,所以只看与ZDO_NETWORK_INIT
处理相关的部分。
1 2 3 4 5 6 7 8 9 10 11 12
| if ( events & ZDO_NETWORK_INIT ) { devState = DEV_INIT; osal_set_event( ZDAppTaskID, ZDO_STATE_CHANGE_EVT );
ZDO_StartDevice( (uint8)ZDO_Config_Node_Descriptor.LogicalType, devStartMode, DEFAULT_BEACON_ORDER, DEFAULT_SUPERFRAME_ORDER );
return (events ^ ZDO_NETWORK_INIT); }
|
在这里又调用了ZDO_StartDevice()函数,其中
ZDO_Config_Node_Descriptor.LogicalType = NODETYPE_COORDINATOR
devStartMode = MODE_HARD
且协调器编译了ZDO_COORDINATOR
也就是说,在这里就要分为协调器的开始组建网络,路由器的辅助转发网络消息,以及终端节点的加入网络进行区别,对于我们现在来说,主要的部分就来看协调器的组建网络:
我们把这一部分建网的代码拿来看一下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| void ZDO_StartDevice( byte logicalType, devStartModes_t startMode, byte beaconOrder, byte superframeOrder ) { ...... if ( ZG_BUILD_COORDINATOR_TYPE && logicalType == NODETYPE_COORDINATOR ) { if ( startMode == MODE_HARD ) { devState = DEV_COORD_STARTING; ret = NLME_NetworkFormationRequest( zgConfigPANID, zgApsUseExtendedPANID, zgDefaultChannelList, zgDefaultStartingScanDuration, beaconOrder, superframeOrder, false ); } ...... }
|
也就是说,组建网络的重要一环原来是在NLME_NetworkFormationRequest()
这个函数中,很遗憾,这部分函数并不开源,但从网络查询中得知,这个函数是用来判断网络状态的,主要要分为两种状态
- nwkStatus为ZSuccess时,也就是网络创建成功了,将设备状态
decSAtatue
设置为对应的DEV_ZB_COORD
,然后设置事件ZDO_STATE_CHANGE_EVENT
也就是网络状态发生转变的事件,返回到ZD_App_event_loop()
中继续执行
- nwkStatus为非ZSuccess时,也就是网络创建不成功,那么就将增大功率返回到
ZD_App_event_loop()
中加大功率,如果功率已经是最大了,那么就将devState
设置为DEV_INIT
(创建网络失败)再设置事件ZDO_STATE_CHANGE_EVENT
,返回到ZD_App_event_loop()
所以,无论如何,返回值都是在上面提到的语句中执行,也就是这一条
1
| osal_set_event( ZDAppTaskID, ZDO_STATE_CHANGE_EVT );
|
那再回过来看ZD_App_event_loop()
是怎么处理这个状态的呢?
1 2 3 4 5 6 7 8 9 10
| if ( events & ZDO_STATE_CHANGE_EVT ) { ZDO_UpdateNwkStatus( devState );
if ( zgConcentratorEnable == TRUE ) { osal_start_timerEx( NWK_TaskID, NWK_MTO_RTG_REQ_EVT, 100 ); }
|
调用了ZDO_UpdateNwkStatus( devState )
,网络状态改变,这个函数会更新和发送信息通知每个注册登记过的应用终端.我们再接着来看这个函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| void ZDO_UpdateNwkStatus( devStates_t state ) { epList_t *epDesc = epList; byte bufLen = sizeof(osal_event_hdr_t); osal_event_hdr_t *msgPtr;
ZDAppNwkAddr.addr.shortAddr = NLME_GetShortAddr(); (void)NLME_GetExtAddr();
while ( epDesc ) { if ( epDesc->epDesc->endPoint != ZDO_EP ) { msgPtr = (osal_event_hdr_t *)osal_msg_allocate( bufLen ); if ( msgPtr ) { msgPtr->event = ZDO_STATE_CHANGE; msgPtr->status = (byte)state;
osal_msg_send( *(epDesc->epDesc->task_id), (byte *)msgPtr ); } } epDesc = epDesc->nextDesc; } }
|
对SampleApp的协调器来说,这里触发应用任务SampleApp_TaskID的ZDO_STATE_CHANGE事件,看下对ZDO_STATE_CHANGE的处理:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| Ccase ZDO_STATE_CHANGE: SampleApp_NwkState = (devStates_t)(MSGpkt->hdr.status); if ( (SampleApp_NwkState == DEV_ZB_COORD) || (SampleApp_NwkState == DEV_ROUTER) || (SampleApp_NwkState == DEV_END_DEVICE) ) { osal_start_timerEx( SampleApp_TaskID, SAMPLEAPP_SEND_PERIODIC_MSG_EVT, SAMPLEAPP_SEND_PERIODIC_MSG_TIMEOUT ); } else { } break;
|
可以看到当协调器建立网络成功,通过回调函数触发应用任务的ZDO_STATE_CHANGE事件,最终开启定时器发送周期信息.
路由器也可以按这个思路进行分析。
最后来给一下大致的流程
main() -> osal_init_system() -> osalInitTasks() -> ZDApp_Init() -> ZDOInitDevice() -> ZDApp_NetworkInit -> 触发ZDAppTaskID的ZDO_NETWORK_INIT -> ZDO_StartDevice()-> NLME_NetworkFormationRequest() -> 网络建立成功ZDO_NetworkFormationConfirmCB -> 触发ZDAppTaskID的ZDO_NETWORK_START -> ZDApp_NetworkStartEvt()->触发ZDAppTaskID的ZDO_STATE_CHANGE_EVT->ZDO_UpdateNwkStatus() -> 触发SampleApp_TaskID的ZDO_STATE_CHANGE ->开户周期信息发送的定时器.