協議棧相關實驗之 SerialApp實驗 詳解

這份講解超級詳細,忍不住就轉了……


第一個功能:協調器的組網,終端設備和路由設備發現網絡以及加入網絡
//第一步:Z-Stack  由 main()函數開始執行,main()函數共做了 2 件事:一是系統初始化,另外一件是開始執行輪轉查詢式操作系統
int main( void )                          

{
 .......
// Initialize the operating system
osal_init_system(); //第二步,操作系統初始化
......
osal_start_system();//初始化完系統任務事件後,正式開始執行操作系統
 ......
}

//第二步,進入 osal_init_system()函數,執行操作系統初始化
uint8 osal_init_system( void )     //初始化操作系統,其中最重要的是,初始化操作系統的任務
{
// Initialize the Memory Allocation System
 osal_mem_init();

// Initialize the message queue
 osal_qHead = NULL;

// Initialize the timers
 osalTimerInit();

// Initialize the Power Management System
 osal_pwrmgr_init();

// Initialize the system tasks.
osalInitTasks(); //第三步,執行操作系統任務初始化函數

// Setup efficient search for the first free block of heap.
 osal_mem_kick();
 return ( SUCCESS );
}

//第三步,進入osalInitTasks()函數,執行操作系統任務初始化
void osalInitTasks( void )       //第三步,初始化操作系統任務
{
 uint8 taskID = 0;
 tasksEvents = (uint16 *)osal_mem_alloc( sizeof( uint16 ) * tasksCnt);
 osal_memset( tasksEvents, 0, (sizeof( uint16 ) * tasksCnt));

//任務優先級由高向低依次排列,高優先級對應 taskID 的值反而小
 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層,初始化,執行ZDApp_init函數後,如果是協調器將建立網絡,如果是終端設備將加入網絡。
#if defined ( ZIGBEE_FREQ_AGILITY ) || defined ( ZIGBEE_PANID_CONFLICT )  
 ZDNwkMgr_Init( taskID++ );
#endif
 SerialApp_Init( taskID );  //應用層SerialApp層初始化,需要用戶考慮     在此處設置了一個按鍵觸發事件,
                                        //當有按鍵按下的時候,產生一個系統消息
}                            

//第四步,進入ZDApp_init()函數,執行ZDApp層初始化
//The first step
void ZDApp_Init( uint8 task_id )     //The first step,ZDApp層初始化。
{
// 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 )        //devState 初值爲DEV_INIT , 所以在初始化ZDA層時,就執行該條件語句
 {
ZDOInitDevice( 0 );     //The second step, 接着轉到ZDOInitDevice()函數,執行The third step;
 }
 else
 {
// Blink LED to indicate HOLD_START
   HalLedBlink ( HAL_LED_4, 0, 50, 500 );
 }
 ZDApp_RegisterCBs();
} /* ZDApp_Init() */

//The third step,執行ZDOInitDevice()函數,執行設備初始化
uint8 ZDOInitDevice( uint16 startDelay )  //The third step, ZDO層初始化設備,
{
  .......
// Trigger the network start
ZDApp_NetworkInit( extendedDelay );   //網絡初始化,跳到相應的函數裏頭,執行The fourth step
  .......
}

//The fouth step,執行 ZDApp_NetWorkInit()函數
void ZDApp_NetworkInit( uint16 delay ) //The fourth step,網絡初始化
{
 if ( delay )
 {
// Wait awhile before starting the device
   osal_start_timerEx( ZDAppTaskID, ZDO_NETWORK_INIT, delay );    //發送ZDO_NETWORK_INIT(網絡初始化)消息到 ZDApp層,轉到                                                                                                                  //ZDApp層,執行The fifth step  , ZDApp_event_loop() 函數
 }                                                              
 else
 {
   osal_set_event( ZDAppTaskID, ZDO_NETWORK_INIT );
 }
}

//The fifth step,轉到ZDApp_event_loop()函數
UINT16 ZDApp_event_loop( uint8 task_id, UINT16 events )
{
if ( events & ZDO_NETWORK_INIT )   //The fivth step,網絡初始化事件處理
 {
// Initialize apps and start the network
   devState = DEV_INIT;

//設備邏輯類型,啓動模式,信標時間,超幀長度,接着轉到The sixth step,去啓動設備,接着執行The sixth step,轉到ZDO_StartDevice()
ZDO_StartDevice( (uint8)ZDO_Config_Node_Descriptor.LogicalType, devStartMode,  
                    DEFAULT_BEACON_ORDER, DEFAULT_SUPERFRAME_ORDER );
// Return unprocessed events
   return (events ^ ZDO_NETWORK_INIT);
 }
}

//The sixth step,執行ZDO_StartDevice()函數,啓動設備
void ZDO_StartDevice( byte logicalType, devStartModes_t startMode, byte beaconOrder, byte superframeOrder ) //The sixth step
{
......
if ( ZG_BUILD_COORDINATOR_TYPE && logicalType == NODETYPE_COORDINATOR )   //當設備作爲協調器時,執行這個條件語句。
 {
   if ( startMode == MODE_HARD )
   {
     devState = DEV_COORD_STARTING;  

//向網絡層發送網絡形成請求。當網絡層執行 NLME_NetworkFormationRequest()建立網絡後,將給予 ZDO層反饋信息。
      // 接着轉到The seventh step,去執行ZDApp層的  ZDO_NetworkFormationConfirmCB()函數
     ret = NLME_NetworkFormationRequest( zgConfigPANID, zgApsUseExtendedPANID, zgDefaultChannelList,
                                         zgDefaultStartingScanDuration, beaconOrder,
                                         superframeOrder, false );
   }
if ( ZG_BUILD_JOINING_TYPE && (logicalType == NODETYPE_ROUTER || logicalType == NODETYPE_DEVICE) ) //當爲終端設備或路由時
 {
   if ( (startMode == MODE_JOIN) || (startMode == MODE_REJOIN) )
   {
     devState = DEV_NWK_DISC;
// zgDefaultChannelList與協調器形成網絡的通道號匹配。 網絡發現請求。
     // 繼而轉到ZDO_NetworkDiscoveryConfirmCB()函數
     ret = NLME_NetworkDiscoveryRequest( zgDefaultChannelList, zgDefaultStartingScanDuration );
   }
 }
......
}

//The seventh step,分兩種情況,1.協調器   2.路由器或終端設備
1)協調器
void ZDO_NetworkFormationConfirmCB( ZStatus_t Status ) //The seventh step,給予ZDO層網絡形成反饋信息(協調器)
{
osal_set_event( ZDAppTaskID, ZDO_NETWORK_START ); //發送網絡啓動事件 到 ZDApp層,接着轉到ZDApp_event_loop()函數                            
......
}

UINT16 ZDApp_event_loop( uint8 task_id, UINT16 events )
{
......
if ( events & ZDO_NETWORK_START )  // 網絡啓動事件
   {
ZDApp_NetworkStartEvt();    //網絡啓動事件,接着跳轉到The ninth step, 執行ZDApp_NetworkStartEvt()函數
 ......
   }
}

void ZDApp_NetworkStartEvt( void )     //處理網絡啓動事件
{
......
osal_pwrmgr_device( PWRMGR_ALWAYS_ON );                           //電源總是上電
osal_set_event( ZDAppTaskID, ZDO_STATE_CHANGE_EVT ); //設置網絡狀態改變事件,發送到ZDApp層,轉到The tenth step,去
......                                                                                                 // ZDApp_event_loop()函數,找到相對應的網絡改變事件。
}


2)路由器或終端設備
//The seventh step(終端設備), 當發現有網絡存在時,網絡層將給予 ZDO 層發現網絡反饋信息
ZStatus_t ZDO_NetworkDiscoveryConfirmCB( uint8 ResultCount, networkDesc_t *NetworkList )  
{
  .......
//把網絡發現這個反饋消息,發送到ZDA層,轉到 ZDApp_ProcessOSALMsg(),執行
 ZDApp_SendMsg( ZDAppTaskID, ZDO_NWK_DISC_CNF, sizeof(ZDO_NetworkDiscoveryCfm_t), (uint8 *)&msg );
}

void ZDApp_ProcessOSALMsg( osal_event_hdr_t *msgPtr )
{
  ......
  case ZDO_NWK_DISC_CNF:      // (終端設備),網絡發現響應。
  ......
//當發現有網絡存在時,網絡層將給予 ZDO 層發現網絡反饋信息。然後由網絡層發起加入網絡請求,
         //如加入網絡成功,則網絡層將給予 ZDO 層加入網絡反饋,執行NLME_JoinRequest()函數。然後轉到
         //The ninth step,執行 ZDO_JoinConfirmCB()函數
           if ( NLME_JoinRequest( ((ZDO_NetworkDiscoveryCfm_t *)msgPtr)->extendedPANID,
                BUILD_UINT16( ((ZDO_NetworkDiscoveryCfm_t *)msgPtr)->panIdLSB, ((ZDO_NetworkDiscoveryCfm_t *)msgPtr)->panIdMSB ),
                ((ZDO_NetworkDiscoveryCfm_t *)msgPtr)->logicalChannel,
                ZDO_Config_Node_Descriptor.CapabilityFlags ) != ZSuccess )
           {
             ZDApp_NetworkInit( (uint16)(NWK_START_DELAY
                 + ((uint16)(osal_rand()& EXTENDED_JOINING_RANDOM_MASK))) );
           }
         ......
  }

void ZDO_JoinConfirmCB( uint16 PanId, ZStatus_t Status )  //The ninth step(終端設備), 終端設備加入網絡響應。
{
......
//將ZDO_NWK_JOIN_IND事件發送到ZDA層,執行 ZDApp_ProcessOSALMsg()函數。
 ZDApp_SendMsg( ZDAppTaskID, ZDO_NWK_JOIN_IND, sizeof(osal_event_hdr_t), (byte*)NULL );
}

void ZDApp_ProcessOSALMsg( osal_event_hdr_t *msgPtr )
{
......
case ZDO_NWK_JOIN_IND:           //終端設備,加入網絡反饋信息事件。
     if ( ZG_BUILD_JOINING_TYPE && ZG_DEVICE_JOINING_TYPE )
     {
ZDApp_ProcessNetworkJoin(); //轉到ZDApp_ProcessNetworkJoin(),執行ZDApp_ProcessNetworkJoin()函數。
     }
     break;
......
}


在執行ZDApp_ProcessNetworkJoin()函數的時候,要分兩種情況,一種是終端設備,一種是路由器:
3)終端設備:
void ZDApp_ProcessNetworkJoin( void )  //處理網絡加入事件。
{
......
if ( nwkStatus == ZSuccess )
   {
//設置 ZDO_STATE_CHANGE_EVT ,發送到ZDA層,執行 ZDApp_event_loop()函數。
     osal_set_event( ZDAppTaskID, ZDO_STATE_CHANGE_EVT );
   }
......
}

4)路由器:
void ZDApp_ProcessNetworkJoin( void )  
{
......
 if ( ZSTACK_ROUTER_BUILD )
       {
// NOTE: first two parameters are not used, see NLMEDE.h for details
         if ( ZDO_Config_Node_Descriptor.LogicalType != NODETYPE_DEVICE )
         {
NLME_StartRouterRequest( 0, 0, false );               //路由啓動請求
         }
       }
......
}

void ZDO_StartRouterConfirmCB( ZStatus_t Status )
{
 nwkStatus = (byte)Status;
......
 osal_set_event( ZDAppTaskID, ZDO_ROUTER_START );
}


UINT16 ZDApp_event_loop( uint8 task_id, UINT16 events )
{
 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 );     //設置ZDO狀態改變事件
     // Return unprocessed events
     return (events ^ ZDO_ROUTER_START);
   }
}


//The eighth step,執行ZDO狀態改變事件
UINT16 ZDApp_event_loop( uint8 task_id, UINT16 events )
{
.......
if ( events & ZDO_STATE_CHANGE_EVT )  //The eighth step, 網絡改變事件,這個事件就是在設備加入網絡成功後,
                                                                    //並在網絡中的身份確定後產生的一個事件
 {
ZDO_UpdateNwkStatus( devState );  //更新網絡狀態,轉到The eleventh step,執行 ZDO_UpdateNwkStatus()函數。
......
 }
}

//The ninth step,執行ZDO_UpdateNwkStatus()函數,完成網絡狀態更新
void ZDO_UpdateNwkStatus(devStates_t state)  //The ninth step, 更新網絡狀態
{
......
zdoSendStateChangeMsg(state, *(pItem->epDesc->task_id));  //發送狀態改變消息到zdo層,這是The tenth step,轉到
                                                                                                       //zdoSendStateChangeMsg()函數
.......
 ZDAppNwkAddr.addr.shortAddr = NLME_GetShortAddr(); //調用NLME_GetShortAddr()函數,獲得16位短地址
 (void)NLME_GetExtAddr();  // Load the saveExtAddr pointer.  //獲得64位的IEEE地址。

}

//The tenth step,執行zdoSendStateChangeMsg()函數
static void zdoSendStateChangeMsg(uint8 state, uint8 taskId) //The tenth step,
{
 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);  //如果ZDO狀態沒有任何改變,再一次,跳到
                                                                                                       //ZDO_STATE_CHANGE_EVT事件處理函數。
   }
   else
   {
     pMsg->event = ZDO_STATE_CHANGE;     //如果ZDO狀態改變了 了,把ZDO_STATE_CHANGE這個消息保存到pMsg
     pMsg->status = state;
     (void)osal_msg_send(taskId, (uint8 *)pMsg);    //轉到MT_TASK.C,去執行The eleven step, MT_ProcessIncomingCommand()函數
   }
 }
 ......
}
//The eleventh step,去執行MT_ProcessIncomingCommand()函數
void MT_ProcessIncomingCommand( mtOSALSerialData_t *msg )
{
......
case ZDO_STATE_CHANGE:       //The thirteenth step, 接着跳到MT_ZdoStateChangeCB()函數。
                                                     //自此,協調器組網形成(終端設備成功加入網絡)
MT_ZdoStateChangeCB((osal_event_hdr_t *)msg);
        break;
......
}

//第五步,//初始化玩系統任務事件後,正是開始執行操作系統,此時操作系統不斷的檢測有沒有任務事件發生,一旦檢測到有事件發生,就轉 //到相應的處理函數,進行處理。
void osal_start_system( void )  //第五步,正式執行操作系統
{
#if !defined ( ZBIT ) && !defined ( UBIT )
 for(;;)  // Forever Loop     //死循環
#endif
 {
   uint8 idx = 0;
   osalTimeUpdate();
   Hal_ProcessPoll();  // This replaces MT_SerialPoll() and osal_check_timer().
   do {
     if (tasksEvents[idx])  // Task is highest priority that is ready.
     {
       break;        // 得到待處理的最高優先級任務索引號idx
     }
   } while (++idx < tasksCnt);
   if (idx < tasksCnt)
   {
     uint16 events;
     halIntState_t intState;
     HAL_ENTER_CRITICAL_SECTION(intState);  //進入臨界區
     events = tasksEvents[idx];             //提取需要處理的任務中的事件
     tasksEvents[idx] = 0;  // Clear the Events for this task.   // 清除本次任務的事件
     HAL_EXIT_CRITICAL_SECTION(intState);   //退出臨界區
     events = (tasksArr[idx])( idx, events ); //通過指針調用任務處理函數  , 緊接着跳到相應的函數去處理,此爲第五步
     HAL_ENTER_CRITICAL_SECTION(intState);  //進入臨界區
     tasksEvents[idx] |= events;  // Add back unprocessed events to the current task.  // 保存未處理的事件
     HAL_EXIT_CRITICAL_SECTION(intState);   //退出臨界區
   }
#if defined( POWER_SAVING )    
   else // Complete pass through all task events with no activity?
   {
     osal_pwrmgr_powerconserve();  // Put the processor/system into sleep
   }
#endif
 }
}

第二個功能:設備間的綁定
                                                                                                                                                                                                                           -----------引用自藍天白雲        
/*當我們按下sw2,即JoyStick控杆的右鍵時,節點發出終端設備綁定請求,因爲我們在SerialApp層,註冊過了鍵盤響應事件,所以,當我們按  下右鍵時,我們會在SerialApp_ProcessEvent()函數裏找到對應的鍵盤相應事件*/
UINT16 SerialApp_ProcessEvent( uint8 task_id, UINT16 events )  //當有事件傳遞到應用層的時候,執行此處
{
if ( events & SYS_EVENT_MSG )   // 有事件傳遞過來,故通過這個條件語句
 {
......
case KEY_CHANGE:         //鍵盤觸發事件
       SerialApp_HandleKeys( ((keyChange_t *)MSGpkt)->state, ((keyChange_t *)MSGpkt)->keys ); //接着跳到相應的按鍵處理函數去執行
       break;
.......
 }
}

ZDO終端設備綁定請求:設備能告訴協調器他們想建立綁定表格報告。該協調器將使協調並在這兩個設備上創建綁定表格條目。在這裏是以SerialApp例子爲例。
void SerialApp_HandleKeys( uint8 shift, uint8 keys )
{
.......
   if ( keys & HAL_KEY_SW_2 )       // Joystick right
   {

    HalLedSet ( HAL_LED_4, HAL_LED_MODE_OFF );

//終端設備綁定請求

     // Initiate an End Device Bind Request for the mandatory endpoint

     dstAddr.addrMode = Addr16Bit;

     dstAddr.addr.shortAddr = 0x0000;     // Coordinator 地址

ZDP_EndDeviceBindReq( &dstAddr, NLME_GetShortAddr(),               //終端設備綁定請求

                           SerialApp_epDesc.endPoint,

                           SERIALAPP_PROFID,

                           SERIALAPP_MAX_CLUSTERS,

                          (cId_t *)SerialApp_ClusterList,

                           SERIALAPP_MAX_CLUSTERS,

                          (cId_t *)SerialApp_ClusterList,
                           FALSE );
   }
......
   if ( keys & HAL_KEY_SW_4 )
   {

     HalLedSet ( HAL_LED_4, HAL_LED_MODE_OFF );

     // Initiate a Match Description Request (Service Discovery)

     dstAddr.addrMode = AddrBroadcast; //廣播地址

     dstAddr.addr.shortAddr = NWK_BROADCAST_SHORTADDR;

ZDP_MatchDescReq( &dstAddr, NWK_BROADCAST_SHORTADDR,                     //描述符匹配請求 這也是兩不同匹配方式,使用的按鍵不同

                       SERIALAPP_PROFID,
                       SERIALAPP_MAX_CLUSTERS,
                      (cId_t *)SerialApp_ClusterList,
                       SERIALAPP_MAX_CLUSTERS,
                      (cId_t *)SerialApp_ClusterList,
                       FALSE );
   }
 }
}
說明:從上面可以看到,SW2是發送終端設備綁定請求方式,SW4是發送描述符匹配請求方式。如果按下SW2的話,使用終端設備綁定請求方式,這裏是要通過終端告訴協調器他們想要建立綁定表格,協調器將協調這兩個請求的設備,在兩個設備上建立綁定表格條目。
(1)終端設備向協調器發送終端設備綁定請求
 調用ZDP_EndDeviceBindReq()函數發送綁定請求。

ZDP_EndDeviceBindReq( &dstAddr,    //目的地址設爲0x0000;

                       NLME_GetShortAddr(),
                       SerialApp_epDesc.endPoint, //EP號

                       SERIALAPP_PROFID,//Profile ID

                    SERIALAPP_MAX_CLUSTERS,  //輸入簇的數目

               (cId_t *)SerialApp_ClusterList, //輸入簇列表

                   SERIALAPP_MAX_CLUSTERS, //輸出簇數目

                (cId_t *)SerialApp_ClusterList,//輸出簇列表

                           FALSE );
該函數實際調用無線發送函數將綁定請求發送給協調器節點:默認clusterID爲End_Device_Bind_req,最後通過AF_DataRequest()發送出去.

fillAndSend( &ZDP_TransID, dstAddr, End_Device_Bind_req, len );

最後通過AF_DataRequest()發送出去,這裏的&afAddr,是目的地址; &ZDApp_epDesc ,是端口號; clusterID,是簇號; len+1,是數據的長度;

//ZDP_TmpBuf-1,是數據的內容; transSeq,是數據的順序號; ZDP_TxOptions,是發射的一個選項 ; AF_DEFAULT_RADIUS,是一個默認的半徑(跳數)。
AF_DataRequest( &afAddr, &ZDApp_epDesc, clusterID,


              (uint16)(len+1), (uint8*)(ZDP_TmpBuf-1),

           transSeq, ZDP_TxOptions,  AF_DEFAULT_RADIUS );

(2) 協調器收到終端設備綁定請求End_Device_Bind_req

這個信息會傳送到ZDO層,在ZDO層的事件處理函數中,調用ZDApp_ProcessOSALMsg( (osal_event_hdr_t *)msg_ptr );

UINT16 ZDApp_event_loop( byte task_id, UINT16 events )

{

 uint8 *msg_ptr;

 if ( events & SYS_EVENT_MSG )

 {

   while ( (msg_ptr = osal_msg_receive( ZDAppTaskID )) )

   {

ZDApp_ProcessOSALMsg( (osal_event_hdr_t *)msg_ptr );

     // Release the memory

     osal_msg_deallocate( msg_ptr );

   }

   // Return unprocessed events

return (events ^ SYS_EVENT_MSG);
.....................
 }

void ZDApp_ProcessOSALMsg( osal_event_hdr_t *msgPtr )

{

 // Data Confirmation message fields

 byte sentEP;       // This should always be 0

 byte sentStatus;

 afDataConfirm_t *afDataConfirm;

 switch ( msgPtr->event )

 {
   // Incoming ZDO Message

   case AF_INCOMING_MSG_CMD:

ZDP_IncomingData( (afIncomingMSGPacket_t *)msgPtr );

     break;
................................
}

在ZDP_IncomingData( (afIncomingMSGPacket_t *)msgPtr );函數中

void ZDP_IncomingData( afIncomingMSGPacket_t *pData )

{
 uint8 x = 0;

 uint8 handled;

 zdoIncomingMsg_t inMsg;

//解析clusterID這個消息

 inMsg.srcAddr.addrMode = Addr16Bit;

 inMsg.srcAddr.addr.shortAddr = pData->srcAddr.addr.shortAddr;

 inMsg.wasBroadcast = pData->wasBroadcast;

inMsg.clusterID = pData->clusterId;                      //這個clusterID,在這裏指的是,終端設備發送過來的End_Device_Bind_req這個消息

 inMsg.SecurityUse = pData->SecurityUse;

 inMsg.asduLen = pData->cmd.DataLength-1;

 inMsg.asdu = pData->cmd.Data+1;

 inMsg.TransSeq = pData->cmd.Data[0];

 handled = ZDO_SendMsgCBs( &inMsg );

#if defined( MT_ZDO_FUNC )

 MT_ZdoRsp( &inMsg );

#endif

 while ( zdpMsgProcs[x].clusterID != 0xFFFF )

 {

   if ( zdpMsgProcs[x].clusterID == inMsg.clusterID )   //在zdpMsgProcs[]中,查找,看看有沒有跟End_Device_Bind_req相匹配的描述符。

   {

     zdpMsgProcs[x].pFn( &inMsg );

     return;
   }
   x++;
 }

 // Handle unhandled messages

 if ( !handled )

   ZDApp_InMsgCB( &inMsg );

}

因爲ZDO信息處理表zdpMsgProcs[ ]沒有對應的End_Device_Bind_req簇,因此沒有調用ZDO信息處理表中的處理函數,但是前面的ZDO_SendMsgCBs()會把這個終端設備綁定請求發送到登記過這個ZDO信息的任務中去。那這個登記註冊的程序在哪裏呢?

  對於協調器來說,由於在void ZDApp_Init( byte task_id )函數中調用了ZDApp_RegisterCBs();面的函數。進行註冊了終端綁定請求信息。

void ZDApp_RegisterCBs( void )
{

#if defined ( ZDO_IEEEADDR_REQUEST ) || defined ( REFLECTOR )

 ZDO_RegisterForZDOMsg( ZDAppTaskID, IEEE_addr_rsp );

#endif

#if defined ( ZDO_NWKADDR_REQUEST ) || defined ( REFLECTOR )

 ZDO_RegisterForZDOMsg( ZDAppTaskID, NWK_addr_rsp );

#endif
#if defined ( ZDO_COORDINATOR )

 ZDO_RegisterForZDOMsg( ZDAppTaskID, Bind_rsp );

 ZDO_RegisterForZDOMsg( ZDAppTaskID, Unbind_rsp );

 ZDO_RegisterForZDOMsg( ZDAppTaskID, End_Device_Bind_req );

#endif
#if defined ( REFLECTOR )

 ZDO_RegisterForZDOMsg( ZDAppTaskID, Bind_req );

 ZDO_RegisterForZDOMsg( ZDAppTaskID, Unbind_req );

#endif
}
因此,協調器節點的 ZDApp 接收到外界輸入的數據後,由於註冊了 ZDO 反饋消息,即ZDO_CB_MSG,ZDApp 層任務事件處理函數將進行處理:也就是調用下面的程序。

UINT16 ZDApp_event_loop( byte task_id, UINT16 events )

{

 uint8 *msg_ptr;

 if ( events & SYS_EVENT_MSG )

 {

   while ( (msg_ptr = osal_msg_receive( ZDAppTaskID )) )

   {

ZDApp_ProcessOSALMsg( (osal_event_hdr_t *)msg_ptr );

     // Release the memory

     osal_msg_deallocate( msg_ptr );

   }

   // Return unprocessed events

return (events ^ SYS_EVENT_MSG);
..............................
 }

在這裏調用函數ZDApp_ProcessOSALMsg( (osal_event_hdr_t *)msg_ptr );在這個函數中我們可以看到對ZDO_CB_MSG事件的處理

void ZDApp_ProcessOSALMsg( osal_event_hdr_t *msgPtr )

{

 // Data Confirmation message fields

 byte sentEP;       // This should always be 0

 byte sentStatus;

 afDataConfirm_t *afDataConfirm;

 switch ( msgPtr->event )

 {

   // Incoming ZDO Message

   case AF_INCOMING_MSG_CMD:

     ZDP_IncomingData( (afIncomingMSGPacket_t *)msgPtr );

     break;

   case ZDO_CB_MSG:

ZDApp_ProcessMsgCBs( (zdoIncomingMsg_t *)msgPtr );

     break;
....................................
}

調用ZDApp_ProcessMsgCBs()函數。在這個函數中根據ClusterID(這裏是 End_Device_Bind_req)選擇相對應的匹配描述符處理函數

void ZDApp_ProcessMsgCBs( zdoIncomingMsg_t *inMsg )
{
.......
   case End_Device_Bind_req:
     {

       ZDEndDeviceBind_t bindReq;

       ZDO_ParseEndDeviceBindReq( inMsg, &bindReq );  //解析綁定請求信息

ZDO_MatchEndDeviceBind( &bindReq );                   //然後向發送綁定請求的節點發送綁定響應消息:

       // Freeing the cluster lists - if allocated.

       if ( bindReq.numInClusters )

         osal_mem_free( bindReq.inClusters );

       if ( bindReq.numOutClusters )

         osal_mem_free( bindReq.outClusters );

     }
     break;
#endif    
 }
}

下面是ZDO_MatchEndDeviceBind()函數的源代碼

void ZDO_MatchEndDeviceBind( ZDEndDeviceBind_t *bindReq )

{

 zAddrType_t dstAddr;

 uint8 sendRsp = FALSE;

 uint8 status;

// Is this the first request? 接收到的是第一個綁定請求

 if ( matchED == NULL )

 {

// Create match info structure 創建匹配信息結構體

   matchED = (ZDMatchEndDeviceBind_t *)osal_mem_alloc( sizeof ( ZDMatchEndDeviceBind_t ) ); //分配空間

   if ( matchED )

   {

// Clear the structure 先進行清除操作

     osal_memset( (uint8 *)matchED, 0, sizeof ( ZDMatchEndDeviceBind_t ) );

// Copy the first request's information 複製第一個請求信息

   if ( !ZDO_CopyMatchInfo( &(matchED->ed1), bindReq ) ) //複製不成功後

     {

       status = ZDP_NO_ENTRY;

       sendRsp = TRUE;

     }
   }
   else //分配空間不成功
   {

     status = ZDP_NO_ENTRY;

     sendRsp = TRUE;

   }

   if ( !sendRsp ) //分配空間成功 ,複製數據結構成功

   {

// Set into the correct state 設置正確的設備狀態

     matchED->state = ZDMATCH_WAIT_REQ;

// Setup the timeout     設置計時時間APS_SetEndDeviceBindTimeout(AIB_MaxBindingTime,

                       ZDO_EndDeviceBindMatchTimeoutCB );

   }
 }
 else //接收到的不是第一個綁定請求
 {

     matchED->state = ZDMATCH_SENDING_BINDS; //狀態爲綁定中

// Copy the 2nd request's information 拷貝第2個請求信息結構

     if ( !ZDO_CopyMatchInfo( &(matchED->ed2), bindReq ) ) //拷貝不成功

     {

       status = ZDP_NO_ENTRY;

       sendRsp = TRUE;

     }

// Make a source match for ed1

   //對ed1的輸出簇ID與ed2的輸入簇ID進行比較,如果有符合的則會返回,相匹配的簇的數目

     matchED->ed1numMatched = ZDO_CompareClusterLists(

                 matchED->ed1.numOutClusters, matchED->ed1.outClusters,

                 matchED->ed2.numInClusters, matchED->ed2.inClusters,  ZDOBuildBuf );

     if ( matchED->ed1numMatched )      //如果有返回ed1相匹配的簇

     {

// Save the match list 申請空間保存相匹配的簇列表

       matchED->ed1Matched= osal_mem_alloc( (short)(matchED->ed1numMatched * sizeof ( uint16 )) );

       if ( matchED->ed1Matched )         //分配成功

       {
//保存相匹配的簇列表

         osal_memcpy(matchED->ed1Matched,ZDOBuildBuf, (matchED->ed1numMatched * sizeof ( uint16 )) );

       }

       else //內存空間分配不成功

       {
         // Allocation error, stop

         status = ZDP_NO_ENTRY;

         sendRsp = TRUE;

       }
     }

// Make a source match for ed2 以ed2爲源

   //對ed2的終端匹配請求和ed1的簇列表相比較,返回相相匹配的簇的數目

     matchED->ed2numMatched = ZDO_CompareClusterLists(

                 matchED->ed2.numOutClusters, matchED->ed2.outClusters,

                 matchED->ed1.numInClusters, matchED->ed1.inClusters, ZDOBuildBuf );

     if ( matchED->ed2numMatched )        //如果匹配成功

     {

// Save the match list 保存匹配的簇列表

       matchED->ed2Matched = osal_mem_alloc( (short)(matchED->ed2numMatched * sizeof ( uint16 )) );

       if ( matchED->ed2Matched )

       {

         osal_memcpy( matchED->ed2Matched, ZDOBuildBuf, (matchED->ed2numMatched * sizeof ( uint16 )) );

       }
       else
       {

         // Allocation error, stop

         status = ZDP_NO_ENTRY;

         sendRsp = TRUE;

       }
     }
//如果兩個相請求的終端設備,有相匹配的簇,並且保存成功

     if ( (sendRsp == FALSE) && (matchED->ed1numMatched || matchED->ed2numMatched) )

     {

// Do the first unbind/bind state 發送響應信息給兩個設備

       ZDMatchSendState( ZDMATCH_REASON_START, ZDP_SUCCESS, 0 );

     }
     else
     {

       status = ZDP_NO_MATCH;

       sendRsp = TRUE;

     }
 }

 if ( sendRsp ) //如果沒有相匹配的或匹配不成功

 {

// send response to this requester 發送匹配請求響應

   dstAddr.addrMode = Addr16Bit;     //設置目的地址是16位的短地址

dstAddr.addr.shortAddr = bindReq->srcAddr;

//發送綁定終端響應函數status = ZDP_NO_MATCH;

   ZDP_EndDeviceBindRsp( bindReq->TransSeq, &dstAddr, status, bindReq->SecurityUse );

   if ( matchED->state == ZDMATCH_SENDING_BINDS )

   {

// send response to first requester

     dstAddr.addrMode = Addr16Bit;

     dstAddr.addr.shortAddr = matchED->ed1.srcAddr;

     ZDP_EndDeviceBindRsp( matchED->ed1.TransSeq, &dstAddr, status, matchED->ed1.SecurityUse );

   }

// Process ended - release memory used

   ZDO_RemoveMatchMemory();

 }
}
ZDO_MatchEndDeviceBind()函數,如果協調器接收到接收到第一個綁定請求,則分配內存空間進行保存並計時,如果不是第一個綁定請求,則分別以第一個和第二個綁定請求爲源綁定,進行比較匹配,如果比較匹配成功則發送匹配成功的信息End_Device_Bind_rsp給兩個請求終端。因爲在ZDMatchSendState()函數中也是調用了ZDP_EndDeviceBindRsp()函數,對匹配請求響應進行了發送。如果匹配不成功則發送匹配失敗的信息給兩個終端。

uint8 ZDMatchSendState( uint8 reason, uint8 status, uint8 TransSeq )

{
..............................
else
 {

// Send the response messages to requesting devices

   // send response to first requester 發送響應信息給第一個請求終端,

   dstAddr.addr.shortAddr = matchED->ed1.srcAddr;

   ZDP_EndDeviceBindRsp( matchED->ed1.TransSeq, &dstAddr, rspStatus, matchED->ed1.SecurityUse );

// send response to second requester 發送響應信息給第二請求終端

   if ( matchED->state == ZDMATCH_SENDING_BINDS )

   {

     dstAddr.addr.shortAddr = matchED->ed2.srcAddr;

     ZDP_EndDeviceBindRsp( matchED->ed2.TransSeq, &dstAddr, rspStatus, matchED->ed2.SecurityUse );

   }

// Process ended - release memory used

   ZDO_RemoveMatchMemory();

 }

 return ( TRUE );

}
(3)終端結點的響應

    由於終端節點在 SerialApp.c 中層註冊過 End_Device_Bind_rsp 消息,因此當接收到協調器節點發來的綁定響應消息將交由 SerialApp 任務事件處理函數處理:

UINT16 SerialApp_ProcessEvent( uint8 task_id, UINT16 events )

{

 if ( events & SYS_EVENT_MSG )

 {

   afIncomingMSGPacket_t *MSGpkt;

   while ( (MSGpkt = (afIncomingMSGPacket_t *)osal_msg_receive(

                                           SerialApp_TaskID )) )

   {

     switch ( MSGpkt->hdr.event )

     {
       case ZDO_CB_MSG:

SerialApp_ProcessZDOMsgs( (zdoIncomingMsg_t *)MSGpkt );

         break;

...................................
}

然後,調用 SerialApp_ProcessZDOMsgs()函數。進行事件處理。

static void SerialApp_ProcessZDOMsgs( zdoIncomingMsg_t *inMsg )

{

 switch ( inMsg->clusterID )

 {

   case End_Device_Bind_rsp:

     if ( ZDO_ParseBindRsp( inMsg ) == ZSuccess )

     {

       // Light LED

       HalLedSet( HAL_LED_4, HAL_LED_MODE_ON );

     }
#if defined(BLINK_LEDS)
     else
     {

       // Flash LED to show failure

       HalLedSet ( HAL_LED_4, HAL_LED_MODE_FLASH );

     }
#endif
     break;
................................
}


第三個功能:實現兩個節點間的串口通信
“串口終端1”的數據,如何被“節點 1”所接收,並且發送出去的?
串口數據是由哪層來負責的呢?--HAL。 。 。恩,猜對了。但這個肯定不是靠猜的,其中的過程
就不講了。 讓我們從主循環 (osal_start_system) 的Hal_ProcessPoll函數找下去 (用source  insight
的同學可以用 ctrl +) ,Hal_ProcessPoll ==> HalUARTPoll ==> HalUARTPollDMA
這個 HalUARTPollDMA 函數裏最後有這樣一句話:dmaCfg.uartCB(HAL_UART_DMA-1,  evt); 對dmaCfg.uartCB 這個函數進行了調用,ctrl  / 搜索這個 dmaCfg.uartCB,發現 SerialApp_Init 函數有兩句話:

uartConfig.callBackFunc         = SerialApp_CallBack;
   HalUARTOpen (SERIAL_APP_PORT, &uartConfig);

此處將 dmaCfg.uartCB 這個函數註冊成爲 SerialApp_CallBack, 也就是說 SerialApp_CallBack函數每次循環中被調用一次,對串口的內容進行查詢,如果 DMA 中接收到了數據,則調用HalUARTRead,將 DMA 數據讀至數據 buffer 並通過 AF_DataRequest 函數發送出去,注意:出去的信息的 CLUSTERID(信息簇ID)號爲 SERIALAPP_CLUSTERID1。 總結一下這個過程:
    串口數據==>DMA接收==>主循環中通過SerialApp_CallBack 查詢==>從 DMA獲取併發送到空中。

具體流程如下:
void SerialApp_Init( uint8 task_id )
{
......
 uartConfig.configured           = TRUE;              // 2x30 don't care - see uart driver.
 uartConfig.baudRate             = SERIAL_APP_BAUD;
 uartConfig.flowControl          = TRUE;
 uartConfig.flowControlThreshold = SERIAL_APP_THRESH; // 2x30 don't care - see uart driver.
 uartConfig.rx.maxBufSize        = SERIAL_APP_RX_SZ;  // 2x30 don't care - see uart driver.
 uartConfig.tx.maxBufSize        = SERIAL_APP_TX_SZ;  // 2x30 don't care - see uart driver.
 uartConfig.idleTimeout          = SERIAL_APP_IDLE;   // 2x30 don't care - see uart driver.
 uartConfig.intEnable            = TRUE;              // 2x30 don't care - see uart driver.
 uartConfig.callBackFunc         = SerialApp_CallBack;     //調用SerialApp_CallBack函數,對串口內容進行查詢
 HalUARTOpen (SERIAL_APP_PORT, &uartConfig);
......
}

static void SerialApp_CallBack(uint8 port, uint8 event)
{
 (void)port;
//如果 DMA 中接收到了數據
 if ((event & (HAL_UART_RX_FULL | HAL_UART_RX_ABOUT_FULL | HAL_UART_RX_TIMEOUT)) &&
#if SERIAL_APP_LOOPBACK
     (SerialApp_TxLen < SERIAL_APP_TX_MAX))
#else
     !SerialApp_TxLen)
#endif
 {
SerialApp_Send();   //調用串口發送函數,將從串口接受到的數據,發送出去
 }
}

static void SerialApp_Send(void)
{
#if SERIAL_APP_LOOPBACK         //初始化時,SERIAL_APP_LOOPBACK=false ,所以不執行if這個預編譯,轉到else去執行
 if (SerialApp_TxLen < SERIAL_APP_TX_MAX)
 {
   SerialApp_TxLen += HalUARTRead(SERIAL_APP_PORT, SerialApp_TxBuf+SerialApp_TxLen+1,
                                                   SERIAL_APP_TX_MAX-SerialApp_TxLen);
 }
 if (SerialApp_TxLen)
 {
   (void)SerialApp_TxAddr;
   if (HalUARTWrite(SERIAL_APP_PORT, SerialApp_TxBuf+1, SerialApp_TxLen))
   {
     SerialApp_TxLen = 0;
   }
   else
   {
     osal_set_event(SerialApp_TaskID, SERIALAPP_SEND_EVT);
   }
 }
#else
 if (!SerialApp_TxLen &&
     (SerialApp_TxLen = HalUARTRead(SERIAL_APP_PORT, SerialApp_TxBuf+1, SERIAL_APP_TX_MAX)))
 {
   // Pre-pend sequence number to the Tx message.
   SerialApp_TxBuf[0] = ++SerialApp_TxSeq;
 }
 if (SerialApp_TxLen)
 {
   if (afStatus_SUCCESS != AF_DataRequest(&SerialApp_TxAddr,        //通過AF_DataRequest()函數,將數據從空中發送出去
                                          (endPointDesc_t *)&SerialApp_epDesc,
                                           SERIALAPP_CLUSTERID1,
                                           SerialApp_TxLen+1, SerialApp_TxBuf,
                                           &SerialApp_MsgID, 0, AF_DEFAULT_RADIUS))
   {
     osal_set_event(SerialApp_TaskID, SERIALAPP_SEND_EVT);   //如果數據沒有發送成功,重新發送
   }
 }
#endif
}

UINT16 SerialApp_ProcessEvent( uint8 task_id, UINT16 events )  
{
   if ( events & SERIALAPP_SEND_EVT )       //當數據沒有發送成功時
     {
SerialApp_Send();
        return ( events ^ SERIALAPP_SEND_EVT );
     }
}

節點2 在收到空中的信號後,如何傳遞給與其相連的串口終端?
節點 2 從空中捕獲到信號後, 在應用層上首先收到信息的就是 SerialApp_ProcessEvent 這個函數了,它收到一個 AF_INCOMING_MSG_CMD 的事件,並通知 SerialApp_ProcessMSGCmd,執行以下代碼 :
UINT16 SerialApp_ProcessEvent( uint8 task_id, UINT16 events )  //當有事件傳遞到應用層的時候,執行此處
{
 ......
  while ( (MSGpkt = (afIncomingMSGPacket_t *)osal_msg_receive( SerialApp_TaskID )) )
   {
     switch ( MSGpkt->hdr.event )
     {
        ......
        case AF_INCOMING_MSG_CMD:      //在這個實驗中,使用串口通訊時,觸發的事件,從空中捕獲到信號。
SerialApp_ProcessMSGCmd( MSGpkt );  //處理這個消息
           break;
      ......
     }
   }
}

void SerialApp_ProcessMSGCmd( afIncomingMSGPacket_t *pkt ) //對從空中捕獲到的信號進行處理
{
 uint8 stat;
 uint8 seqnb;
 uint8 delay;
 switch ( pkt->clusterId )
 {
// A message with a serial data block to be transmitted on the serial port.
 case SERIALAPP_CLUSTERID1:    //節點一發送過來的信息的 CLUSTERID(信息簇ID)號爲 SERIALAPP_CLUSTERID1

// Store the address for sending and retrying.
   osal_memcpy(&SerialApp_RxAddr, &(pkt->srcAddr), sizeof( afAddrType_t ));
   seqnb = pkt->cmd.Data[0];
 // Keep message if not a repeat packet
   if ( (seqnb > SerialApp_RxSeq) ||                    // Normal
       ((seqnb < 0x80 ) && ( SerialApp_RxSeq > 0x80)) ) // Wrap-around
   {
     // Transmit the data on the serial port.
     if ( HalUARTWrite( SERIAL_APP_PORT, pkt->cmd.Data+1, (pkt->cmd.DataLength-1) ) )  //通過串口發送數據到PC機
     {
       // Save for next incoming message
       SerialApp_RxSeq = seqnb;
       stat = OTA_SUCCESS;
     }
     else
     {
       stat = OTA_SER_BUSY;
     }
   }
   else
   {
     stat = OTA_DUP_MSG;
   }
   // Select approproiate OTA flow-control delay.
   delay = (stat == OTA_SER_BUSY) ? SERIALAPP_NAK_DELAY : SERIALAPP_ACK_DELAY;
   // Build & send OTA response message.
   SerialApp_RspBuf[0] = stat;
   SerialApp_RspBuf[1] = seqnb;
   SerialApp_RspBuf[2] = LO_UINT16( delay );
   SerialApp_RspBuf[3] = HI_UINT16( delay );
   osal_set_event( SerialApp_TaskID, SERIALAPP_RESP_EVT );  //受到數據後,向節點1發送一個響應事件,跳到SerialApp_ProcessEvent()
   osal_stop_timerEx(SerialApp_TaskID, SERIALAPP_RESP_EVT);  
   break;
......
 }
}

UINT16 SerialApp_ProcessEvent( uint8 task_id, UINT16 events )  
{
......
  if ( events & SERIALAPP_RESP_EVT )   //串口響應事件,表示成功接受來自節點1的數據,
 {
SerialApp_Resp();      //向節點1發送  成功接受的response
   return ( events ^ SERIALAPP_RESP_EVT );
 }
......
}

static void SerialApp_Resp(void)
{
 if (afStatus_SUCCESS != AF_DataRequest(&SerialApp_RxAddr,         //通過AF_DataRequest函數,講接收成功響應從空中發送出去
                                        (endPointDesc_t *)&SerialApp_epDesc,
                                         SERIALAPP_CLUSTERID2,
                                         SERIAL_APP_RSP_CNT, SerialApp_RspBuf,
                                        &SerialApp_MsgID, 0, AF_DEFAULT_RADIUS))
 {
   osal_set_event(SerialApp_TaskID, SERIALAPP_RESP_EVT);   //如果發送失敗,重新發送
 }
}


節點1,接收到來自節點2的response。
UINT16 SerialApp_ProcessEvent( uint8 task_id, UINT16 events )
{
 ......
  while ( (MSGpkt = (afIncomingMSGPacket_t *)osal_msg_receive( SerialApp_TaskID )) )
   {
     switch ( MSGpkt->hdr.event )
     {
        ......
        case AF_INCOMING_MSG_CMD:      //在這個實驗中,使用串口通訊時,觸發的事件,從空中捕獲到信號。
SerialApp_ProcessMSGCmd( MSGpkt );  //處理這個消息
           break;
      ......
     }
   }
}

SERIALAPP_CLUSTERID2代表接收到發送成功的response,取消自動重發,如果不,自動重發。
void SerialApp_ProcessMSGCmd( afIncomingMSGPacket_t *pkt )
{
 ......
// A response to a received serial data block.
 case SERIALAPP_CLUSTERID2:        //SerialWsn_CLUSTERID2代表接收到發送成功的response
   if ((pkt->cmd.Data[1] == SerialApp_TxSeq) &&
      ((pkt->cmd.Data[0] == OTA_SUCCESS) || (pkt->cmd.Data[0] == OTA_DUP_MSG)))
   {
     SerialApp_TxLen = 0;
     osal_stop_timerEx(SerialApp_TaskID, SERIALAPP_SEND_EVT);  //當收到發送成功的response,停止自動從發
   }
   else
   {
// Re-start timeout according to delay sent from other device.
     delay = BUILD_UINT16( pkt->cmd.Data[2], pkt->cmd.Data[3] );
     osal_start_timerEx( SerialApp_TaskID, SERIALAPP_SEND_EVT, delay ); //沒有收到成功的response,自動重發
   }
   break;
   default:
     break;
}



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章