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
// 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繼續反饋上去,進行下一輪的迴圈然後處理。
整個協議棧輪詢的過程就是這樣,是不是很清楚了呢!沒清楚也沒關係,我會再寫一篇按鍵的小例子幫助理解~!