zigbee一般可以分爲協調器,路由和終端幾個類型。首先由協調器建立網絡,然後路由和終端加入網絡。
現在根據TI的zigbee工程簡單分析的建網入網的代碼流程。
協調器建網:
1.
int main( void )
{
...........
// Initialize the operating system 系統初始化
osal_init_system();
...........
return 0; // Shouldn't get here.
} // main()
2.
uint8 osal_init_system( void )
{
.............
// Initialize the system tasks. 任務初始化
osalInitTasks();
..........
return ( SUCCESS );
}
3.
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++ );//ZDAPP層初始化
#if defined ( ZIGBEE_FREQ_AGILITY ) || defined ( ZIGBEE_PANID_CONFLICT )
ZDNwkMgr_Init( taskID++ );
#endif
SerialApp_Init( taskID );
}
4.
void ZDApp_Init( uint8 task_id )
{
// Save the task ID
ZDAppTaskID = task_id;
// Initialize the ZDO global device short address storage
ZDAppNwkAddr.addrMode = Addr16Bit;
ZDAppNwkAddr.addr.shortAddr = INVALID_NODE_ADDR;
(void)NLME_GetExtAddr(); // Load the saveExtAddr pointer.
// Check for manual "Hold Auto Start"
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
// Start the device?
if ( devState != DEV_HOLD )//初始值爲DEV_INIT
{
ZDOInitDevice( 0 );
}
else
{
ZDOInitDevice( ZDO_INIT_HOLD_NWK_START );
// Blink LED to indicate HOLD_START
HalLedBlink ( HAL_LED_4, 0, 50, 500 );
}
// Initialize the ZDO callback function pointers zdoCBFunc[]
ZDApp_InitZdoCBFunc();
ZDApp_RegisterCBs();
} /* ZDApp_Init() */
5.
uint8 ZDOInitDevice( uint16 startDelay )
{
..........
if( ZDO_INIT_HOLD_NWK_START != startDelay )
{
..........
// Trigger the network start 網路初始化
ZDApp_NetworkInit( extendedDelay );
}
// set broadcast address mask to support broadcast filtering
NLME_SetBroadcastFilter( ZDO_Config_Node_Descriptor.CapabilityFlags );
return ( networkStateNV );
}
6.
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 );//發送ZDO_NETWORK_INIT消息到ZDAPP層
}
}
7.
UINT16 ZDApp_event_loop( uint8 task_id, UINT16 events )
{
uint8 *msg_ptr;
........
if ( events & ZDO_NETWORK_INIT )
{
// Initialize apps and start the network
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 unprocessed events
return (events ^ ZDO_NETWORK_INIT);
}
.........
}
8.
void ZDO_StartDevice( byte logicalType, devStartModes_t startMode, byte beaconOrder, byte superframeOrder )
{
......
ret = ZUnsupportedMode;
if ( ZG_BUILD_COORDINATOR_TYPE && logicalType == NODETYPE_COORDINATOR )//協調器部分
{
if ( startMode == MODE_HARD )
{
devState = DEV_COORD_STARTING;
//利用NLME_NetworkFormationRequest()向網絡層發送建網請求,執行後會返回信道到ZDO_NetworkFormationConfirmCB()
ret = NLME_NetworkFormationRequest( zgConfigPANID, zgApsUseExtendedPANID, zgDefaultChannelList,
zgDefaultStartingScanDuration, beaconOrder,
superframeOrder, false );
}
else if ( startMode == MODE_RESUME )
{
// Just start the coordinator
devState = DEV_COORD_STARTING;
ret = NLME_StartRouterRequest( beaconOrder, beaconOrder, false );
}
else
{
#if defined( LCD_SUPPORTED )
HalLcdWriteScreen( "StartDevice ERR", "MODE unknown" );
#endif
}
}
......
}
if ( ret != ZSuccess )
{
osal_start_timerEx(ZDAppTaskID, ZDO_NETWORK_INIT, NWK_RETRY_DELAY );
}
}
9.
void ZDO_NetworkFormationConfirmCB( ZStatus_t Status )
{
nwkStatus = (byte)Status;
......
osal_set_event( ZDAppTaskID, ZDO_NETWORK_START );//發送ZDO_NETWORK_START消息到ZDAPP層
}
10.
UINT16 ZDApp_event_loop( uint8 task_id, UINT16 events )
{
uint8 *msg_ptr;
......
if ( ZSTACK_ROUTER_BUILD )
{
if ( events & ZDO_NETWORK_START )
{
ZDApp_NetworkStartEvt();//網絡啓動事件
// Return unprocessed events
return (events ^ ZDO_NETWORK_START);
}
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);
}
}
......
}
11.
void ZDApp_NetworkStartEvt( void )
{
if ( nwkStatus == ZSuccess )
{
// Successfully started a ZigBee network
if ( devState == DEV_COORD_STARTING )
{
devState = DEV_ZB_COORD;
}
osal_pwrmgr_device( PWRMGR_ALWAYS_ON );
osal_set_event( ZDAppTaskID, ZDO_STATE_CHANGE_EVT );//發送ZDO_STATE_CHANGE_EVT消息到ZDAPP層
}
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
{
// Failed to start network. Enter a dormant state (until user intervenes)
devState = DEV_INIT;
osal_set_event( ZDAppTaskID, ZDO_STATE_CHANGE_EVT );
}
}
}
至此協調器網絡建立完成
路由和終端入網:
前1-7步和協調器建網相同
8.
void ZDO_StartDevice( byte logicalType, devStartModes_t startMode, byte beaconOrder, byte superframeOrder )
{
......
if ( ZG_BUILD_JOINING_TYPE && (logicalType == NODETYPE_ROUTER || logicalType == NODETYPE_DEVICE) )//路由和終端部分
{
if ( (startMode == MODE_JOIN) || (startMode == MODE_REJOIN) )
{
devState = DEV_NWK_DISC;
#if defined( MANAGED_SCAN )
ZDOManagedScan_Next();
ret = NLME_NetworkDiscoveryRequest( managedScanChannelMask, BEACON_ORDER_15_MSEC );
#else
//利用NLME_NetworkDiscoveryRequest()向網絡層發送發現網絡請求,執行後會返回信道到ZDO_NetworkDiscoveryConfirmCB()
//zgDefaultChannelList在f8wConfig.cfg中定義
ret = NLME_NetworkDiscoveryRequest( zgDefaultChannelList, zgDefaultStartingScanDuration );
#if defined ( ZIGBEE_FREQ_AGILITY )
if ( !( ZDO_Config_Node_Descriptor.CapabilityFlags & CAPINFO_RCVR_ON_IDLE ) &&
( ret == ZSuccess ) && ( ++discRetries == 4 ) )
{
// For devices with RxOnWhenIdle equals to FALSE, any network channel
// change will not be recieved. On these devices or routers that have
// lost the network, an active scan shall be conducted on the Default
// Channel list using the extended PANID to find the network. If the
// extended PANID isn't found using the Default Channel list, an scan
// should be completed using all channels.
zgDefaultChannelList = MAX_CHANNELS_24GHZ;
}
#endif // ZIGBEE_FREQ_AGILITY
#if defined ( ZIGBEE_COMMISSIONING )
if (startMode == MODE_REJOIN && scanCnt++ >= 5 )
{
// When ApsUseExtendedPanID is commissioned to a non zero value via
// application specific means, the device shall conduct an active scan
// on the Default Channel list and join the PAN with the same
// ExtendedPanID. If the PAN is not found, an scan should be completed
// on all channels.
// When devices rejoin the network and the PAN is not found from
zgDefaultChannelList = MAX_CHANNELS_24GHZ;
}
#endif // ZIGBEE_COMMISSIONING
#endif
}
else if ( startMode == MODE_RESUME )
{
if ( logicalType == NODETYPE_ROUTER )
{
ZMacScanCnf_t scanCnf;
devState = DEV_NWK_ORPHAN;
/* if router and nvram is available, fake successful orphan scan */
scanCnf.hdr.Status = ZSUCCESS;
scanCnf.ScanType = ZMAC_ORPHAN_SCAN;
scanCnf.UnscannedChannels = 0;
scanCnf.ResultListSize = 0;
nwk_ScanJoiningOrphan(&scanCnf);
ret = ZSuccess;
}
else
{
devState = DEV_NWK_ORPHAN;
ret = NLME_OrphanJoinRequest( zgDefaultChannelList,
zgDefaultStartingScanDuration );
}
}
else
{
#if defined( LCD_SUPPORTED )
HalLcdWriteScreen( "StartDevice ERR", "MODE unknown" );
#endif
}
}
if ( ret != ZSuccess )
{
osal_start_timerEx(ZDAppTaskID, ZDO_NETWORK_INIT, NWK_RETRY_DELAY );
}
}
9.
ZStatus_t ZDO_NetworkDiscoveryConfirmCB(uint8 status)
{
osal_event_hdr_t msg;
// If Scan is initiated by ZDO_MGMT_NWK_DISC_REQ
// Send ZDO_MGMT_NWK_DISC_RSP back
#if defined ( ZDO_MGMT_NWKDISC_RESPONSE )
if ( zdappMgmtNwkDiscReqInProgress )
{
zdappMgmtNwkDiscReqInProgress = false;
ZDO_FinishProcessingMgmtNwkDiscReq();
}
else
#endif
{
// Pass the confirm to another task if it registers the callback
// Otherwise, pass the confirm to ZDApp.
if (zdoCBFunc[ZDO_NWK_DISCOVERY_CNF_CBID] != NULL )
{
zdoCBFunc[ZDO_NWK_DISCOVERY_CNF_CBID]( (void*)&status );
}
else
{
// Otherwise, send scan confirm to ZDApp task to proceed
msg.status = ZDO_SUCCESS;
//將ZDO_NWK_DISC_CNF消息發送到ZDAPP層,由ZDApp_ProcessOSALMsg()處理
ZDApp_SendMsg( ZDAppTaskID, ZDO_NWK_DISC_CNF, sizeof(osal_event_hdr_t), (uint8 *)&msg );
}
}
return (ZSuccess);
} // ZDO_NetworkDiscoveryConfirmCB
10.
void ZDApp_ProcessOSALMsg( osal_event_hdr_t *msgPtr )
{
// Data Confirmation message fields
uint8 sentEP; // This should always be 0
uint8 sentStatus;
afDataConfirm_t *afDataConfirm;
uint8 tmp;
switch ( msgPtr->event )
{
......
case ZDO_NWK_DISC_CNF:
if (devState != DEV_NWK_DISC)
break;
if ( ZG_BUILD_JOINING_TYPE && ZG_DEVICE_JOINING_TYPE )
{
// Process the network discovery scan results and choose a parent
// device to join/rejoin itself
networkDesc_t *pChosenNwk;
if ( ( (pChosenNwk = ZDApp_NwkDescListProcessing()) != NULL ) && (zdoDiscCounter > NUM_DISC_ATTEMPTS) )
{
if ( devStartMode == MODE_JOIN )
{
devState = DEV_NWK_JOINING;
ZDApp_NodeProfileSync( pChosenNwk->stackProfile);
//請求加入網絡NLME_JoinRequest(),執行結果返回到ZDO_JoinConfirmCB()
if ( NLME_JoinRequest( pChosenNwk->extendedPANID, pChosenNwk->panId,
pChosenNwk->logicalChannel,
ZDO_Config_Node_Descriptor.CapabilityFlags,
pChosenNwk->chosenRouter, pChosenNwk->chosenRouterDepth ) != ZSuccess )
{
ZDApp_NetworkInit( (uint16)(NWK_START_DELAY
+ ((uint16)(osal_rand()& EXTENDED_JOINING_RANDOM_MASK))) );
}
} // if ( devStartMode == MODE_JOIN )
else if ( devStartMode == MODE_REJOIN )
{
devState = DEV_NWK_REJOIN;
// Before trying to do rejoin, check if the device has a valid short address
// If not, generate a random short address for itself
if ( _NIB.nwkDevAddress == INVALID_NODE_ADDR )
{
_NIB.nwkDevAddress = osal_rand();
ZMacSetReq( ZMacShortAddress, (byte*)&_NIB.nwkDevAddress );
}
if ( ZG_SECURE_ENABLED )
{
ZDApp_RestoreNwkKey();
}
// Check if the device has a valid PanID, if not, set it to the discovered Pan
if ( _NIB.nwkPanId == INVALID_PAN_ID )
{
_NIB.nwkPanId = pChosenNwk->panId;
ZMacSetReq( ZMacPanId, (byte*)&(_NIB.nwkPanId) );
}
tmp = true;
ZMacSetReq( ZMacRxOnIdle, &tmp ); // Set receiver always on during rejoin
if ( NLME_ReJoinRequest( ZDO_UseExtendedPANID, pChosenNwk->logicalChannel) != ZSuccess )
{
ZDApp_NetworkInit( (uint16)(NWK_START_DELAY
+ ((uint16)(osal_rand()& EXTENDED_JOINING_RANDOM_MASK))) );
}
} // else if ( devStartMode == MODE_REJOIN )
if ( ZDO_Config_Node_Descriptor.CapabilityFlags & CAPINFO_RCVR_ON_IDLE )
{
// The receiver is on, turn network layer polling off.
NLME_SetPollRate( 0 );
NLME_SetQueuedPollRate( 0 );
NLME_SetResponseRate( 0 );
}
else
{
if ( (ZG_SECURE_ENABLED) && (devStartMode == MODE_JOIN) )
{
ZDApp_SavedPollRate = zgPollRate;
NLME_SetPollRate( zgRejoinPollRate );
}
}
}
else
{
if ( continueJoining )
{
#if defined ( MANAGED_SCAN )
ZDApp_NetworkInit( MANAGEDSCAN_DELAY_BETWEEN_SCANS );
#else
zdoDiscCounter++;
ZDApp_NetworkInit( (uint16)(BEACON_REQUEST_DELAY
+ ((uint16)(osal_rand()& BEACON_REQ_DELAY_MASK))) );
#endif
}
}
}
break;
......
}
11.
void ZDO_JoinConfirmCB( uint16 PanId, ZStatus_t Status )
{
(void)PanId; // remove if this parameter is used.
......
// Notify ZDApp 發送消息給ZDAPP層,在ZDApp_ProcessOSALMsg()中處理
ZDApp_SendMsg( ZDAppTaskID, ZDO_NWK_JOIN_IND, sizeof(osal_event_hdr_t), (byte*)NULL );
}
12.
void ZDApp_ProcessOSALMsg( osal_event_hdr_t *msgPtr )
{
// Data Confirmation message fields
uint8 sentEP; // This should always be 0
uint8 sentStatus;
afDataConfirm_t *afDataConfirm;
uint8 tmp;
switch ( msgPtr->event )
{
......
case ZDO_NWK_JOIN_IND:
if ( ZG_BUILD_JOINING_TYPE && ZG_DEVICE_JOINING_TYPE )
{
//調用ZDApp_ProcessNetworkJoin
ZDApp_ProcessNetworkJoin();
}
break;
......
}
}
13.
此處分爲路由和終端兩種情況
終端:
void ZDApp_ProcessNetworkJoin( void )
{
if ( (devState == DEV_NWK_JOINING) ||
((devState == DEV_NWK_ORPHAN) &&
(ZDO_Config_Node_Descriptor.LogicalType == NODETYPE_ROUTER)) )
{
// Result of a Join attempt by this device.
if ( nwkStatus == ZSuccess )
{
//發送ZDO_STATE_CHANGE_EVT消息給ZDAPP層
osal_set_event( ZDAppTaskID, ZDO_STATE_CHANGE_EVT );
......
}
}
......
}
路由:
void ZDApp_ProcessNetworkJoin( void )
{
......
else if ( devState == DEV_NWK_ORPHAN || devState == DEV_NWK_REJOIN )
{
......
if ( ZSTACK_ROUTER_BUILD )
{
// NOTE: first two parameters are not used, see NLMEDE.h for details
if ( ZDO_Config_Node_Descriptor.LogicalType != NODETYPE_DEVICE )
{
//路由啓動請求,執行結果返回到ZDO_StartRouterConfirmCB()
NLME_StartRouterRequest( 0, 0, false );
}
}
ZDApp_AnnounceNewAddress();
}
......
}
void ZDO_StartRouterConfirmCB( ZStatus_t Status )
{
......
//發送ZDO_ROUTER_START消息給ZDAPP層
osal_set_event( ZDAppTaskID, ZDO_ROUTER_START );
}
UINT16 ZDApp_event_loop( uint8 task_id, UINT16 events )
{
uint8 *msg_ptr;
......
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!!
}
//發送ZDO_STATE_CHANGE_EVT消息給ZDAPP層
osal_set_event( ZDAppTaskID, ZDO_STATE_CHANGE_EVT );
// Return unprocessed events
return (events ^ ZDO_ROUTER_START);
}
......
}
14.
UINT16 ZDApp_event_loop( uint8 task_id, UINT16 events )
{
uint8 *msg_ptr;
......
if ( events & ZDO_STATE_CHANGE_EVT )//設備加入網絡後,身份確定產生一個網絡狀態改變事件
{
ZDO_UpdateNwkStatus( devState );//更新網絡狀態
// At start up, do one MTO route discovery if the device is a concentrator
if ( zgConcentratorEnable == TRUE )
{
// Start next event
osal_start_timerEx( NWK_TaskID, NWK_MTO_RTG_REQ_EVT, 100 );
}
// Return unprocessed events
return (events ^ ZDO_STATE_CHANGE_EVT);
}
......
}
15.
void ZDO_UpdateNwkStatus(devStates_t state)
{
epList_t *pItem = epList;
while (pItem != NULL)
{
if (pItem->epDesc->endPoint != ZDO_EP)
{
zdoSendStateChangeMsg(state, *(pItem->epDesc->task_id));
}
pItem = pItem->nextDesc;
}
#if defined MT_ZDO_CB_FUNC
zdoSendStateChangeMsg(state, MT_TaskID);//發送狀態改變信息到ZDO層
#endif
ZDAppNwkAddr.addr.shortAddr = NLME_GetShortAddr();//獲取16位短地址
(void)NLME_GetExtAddr(); // Load the saveExtAddr pointer.
}
16.
static void zdoSendStateChangeMsg(uint8 state, uint8 taskId)
{
osal_event_hdr_t *pMsg = (osal_event_hdr_t *)osal_msg_find(taskId, ZDO_STATE_CHANGE);
if (NULL == pMsg)
{
if (NULL == (pMsg = (osal_event_hdr_t *)osal_msg_allocate(sizeof(osal_event_hdr_t))))
{
// Upon failure to notify any EndPoint of the state change, re-set the ZDO event to
// try again later when more Heap may be available.
osal_set_event(ZDAppTaskID, ZDO_STATE_CHANGE_EVT);//狀態未改變則重新執行一次
}
else
{
pMsg->event = ZDO_STATE_CHANGE;
pMsg->status = state;
(void)osal_msg_send(taskId, (uint8 *)pMsg);//狀態改變後到MT_TASK.C中執行MT_ProcessIncomingCommand
}
}
else
{
// Modify in place the status of an existing ZDO_STATE_CHANGE message to the EndPoint.
pMsg->status = state;
}
}
17.
static void MT_ProcessIncomingCommand( mtOSALSerialData_t *msg )
{
uint8 len, *msg_ptr = msg->msg;
......
#ifdef MT_ZDO_CB_FUNC
case ZDO_STATE_CHANGE:
MT_ZdoStateChangeCB((osal_event_hdr_t *)msg);
break;
#endif
......
}
至此入網完成
注:此協議版本爲ZStack-CC2530-2.5.1a,若有不正確的地方歡迎大俠指正