1. 程式人生 > >Zigbee學習(二)之Zstack協議棧執行原理分析

Zigbee學習(二)之Zstack協議棧執行原理分析

Zigbee協議棧的實現方式採用的是分層的思想,分別有物理層、資料鏈路層(介質訪問控制層)、網路層和應用層。每一層都實現了不同的功能,但是每一層實現的功能對於其它層來說又是封閉的,如果要進行資料互通,需要呼叫一些API函式。這是一些淺顯的基本概念,百度一下都可以知道的啦!那麼整個協議棧是如何執行的呢?我們直接來看程式碼吧!開啟Zmain.c檔案,之前是一些巨集定義,暫時先不用管,看到主函式:

int main( void )
{
  // Turn off interrupts
  osal_int_disable( INTS_ALL );

  // Initialization for board related stuff such as LEDs


  HAL_BOARD_INIT();

  // Make sure supply voltage is high enough to run
  zmain_vdd_check();

  // Initialize board I/O
  InitBoard( OB_COLD );

  // Initialze HAL drivers
  HalDriverInit();

  // Initialize NV System
  osal_nv_init( NULL );

  // Initialize the MAC
  ZMacInit();

  // Determine the extended address


  zmain_ext_addr();

#if defined ZCL_KEY_ESTABLISH
  // Initialize the Certicom certificate information.

  zmain_cert_init();
#endif

  // Initialize basic NV items
  zgInit();

#ifndef NONWK
  // Since the AF isn't a task, call it's initialization routine

  afInit();
#endif

  // Initialize the operating system

  osal_init_system();

  // Allow interrupts
  osal_int_enable( INTS_ALL );

  // Final board initialization
  InitBoard( OB_READY );

  // Display information about this device
  zmain_dev_info();

  /* Display the device info on the LCD */
#ifdef LCD_SUPPORTED
 
zmain_lcd_init();
#endif

#ifdef WDT_IN_PM1
  /* If WDT is used, this is a good place to enable it. */

  WatchDogEnable( WDTIMX );
#endif

  osal_start_system(); // No Return from here

  return 0;  // Shouldn't get here.
} // main()

 上述程式碼藍色部分都是一些巨集定義和註釋,只要看黑色字型部分就好了,不難發現都是一些初始化操作,直到osal_start_system();這個函式才是真正系統執行的開始,那麼我來分析一下這個函式的具體執行機理吧,自己也是學來的,說的不好請見諒哦!

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;
      }
    } 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
  }
}

首先,我們可以發現上述程式碼是個無限迴圈FOR函式,緊接著的兩個函式osaltimeupdate()應該也是初始化定時器吧,HAL_ProcessPoll()是對一些硬體輪詢,檢查是否發生引數變化之類的,接下來就是一個do...while的迴圈函式,意思是如果tasksEvents[idx]不為0那麼就跳出迴圈,否則idx自增1,與tasksCnt比較,如果沒有超過tasksCnt就繼續迴圈do...while,如果超過了就跳出迴圈,如果再摸一個idx的迴圈過程中tasksEvent[idx]非空,則執行下面的程式碼,將tasksEvents[idx]賦值給events,然後清零,對(tasksArr[idx])( idx, events )求解並將值賦值給events,最後再和tasksEvents[idx]做或運算。那麼這整個過程是什麼意思呢?我們要來看一下tasksArr[idx]和tasksEvents是如何定義的。首先看tasksArr[idx]:

const pTaskEventHandlerFn tasksArr[] = {
  macEventLoop,
  nwk_event_loop,
  Hal_ProcessEvent,
#if defined( MT_TASK )
  MT_ProcessEvent,
#endif
  APS_event_loop,
#if defined ( ZIGBEE_FRAGMENTATION )
  APSF_ProcessEvent,
#endif
  ZDApp_event_loop,
#if defined ( ZIGBEE_FREQ_AGILITY ) || defined ( ZIGBEE_PANID_CONFLICT )
  ZDNwkMgr_event_loop,
#endif
  PuppyApp_ProcessEvent
};

不難發現,這是一個數組,陣列的每個元素都是一個函式指標,指向了不同的事件處理函式,函式名即函式首地址。我們再找tasksEvents在哪裡定義的,發現osalInitTasks( void )這個osal任務初始化函式中有定義,看一下這個函式的程式碼:

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
  PuppyApp_Init( taskID );
}

在這個函式中,先定義了一個任務ID號,tasksEvents所指向的地址長度是兩個位元組,然後使tasksEvents指向一個為任務總數*2個位元組大小的空間的首地址,並將空間內容初始化為0,這裡就可以知道tasksEvents其實就是指向每個任務事件的指標了。而且不難發現,這個函式中的任務排序和tasksArr[]陣列定義的排序是一樣的。事實上,當摸個tasksEvents[idx]非空時,就表明有對應該任務的事件要處理,可能是一件,也可能是很多件。然後通過idx在taskArr[idx]中找到相應的事件處理函式進行處理,處理完了之後有這樣一句指令return(events^SYS_EVENT_MSG),當然後面的巨集定義可能不一樣,這是一個異或處理,1^1=0,1^0=1,也就是說SYS_EVENT_MSG這個事件處理完了清零了,剩下的events繼續反饋上去,進行下一輪的迴圈然後處理。

整個協議棧輪詢的過程就是這樣,是不是很清楚了呢!沒清楚也沒關係,我會再寫一篇按鍵的小例子幫助理解~!