天天看點

Zstack OSAL資料彙編

Zstack OSAL詳解 2010-08-16 13:33

Zstack OSAL詳解

 1. void osal_start_system( void )

所有應用程式,無論是自己寫的最簡單的測試程式還是複雜的OSAL作業系統,都必須從main( )來入口。所謂的OS作業系統,我們不妨這樣想像:自己寫一個最簡單的main( ),裡面就一句列印“Hello, World”.如果需要加入Key, LED這樣的輸入輸出功能,那麼就需要擴充main( ),加入Key, LED的驅動,如果要實作多線程排程,就要加入Timer驅動等等。其實作業系統就是這麼來的,簡單吧:)。

一般在OS中都會有個死循環,在這個循環中去處理各種各樣的事件。在Zstack OSAL中,這個死循環就存在于osal_start_system()這個函數中。下面來詳細分析在這個死循環中到底在做哪些事情。

 2. OSAL的“心跳”

在OSAL的死循環中,各個事件隻是在某些特定的情況下發生,如果OSAL一刻不停去輪詢去處理這些應用程式,遲早會累死(熱量,功耗,壽命…),這樣做是完全沒有必要的。是以這裡就引入了心跳的概念,也就是OS的時鐘節奏。在Zstack OSAL中這個節奏定義為1ms, 由8 bits HW_TIMER4來控制,當然這些都可以由程式員來修改,後面就以系統的預設值來講述。在void InitBoard( byte level )這個函數中有下面這段代碼就是在定義系統的心跳Timer。

HalTimerConfig (OSAL_TIMER,

HAL_TIMER_MODE_CTC,                  HAL_TIMER_CHANNEL_SINGLE,

HAL_TIMER_CH_MODE_OUTPUT_COMPARE,

                  OnboardTimerIntEnable,

                  Onboard_TimerCallBack);

在OSAL的Timer定義好了以後,就要啟動Timer, 至于如何啟動Timer, 請自行查閱2430 Spec, 我這裡想說的是,在一步步跟蹤源碼到死循環開始,都沒有發現啟動OSAL Timer的代碼,最後通過觀察Timer相關的控制寄存器,發現,在網絡層初始化函數nwk_init( taskID++ )執行完畢後Timer啟動了,也就是說在網絡層初始化函數中有啟動Timer的語句,因為網絡層初始化是不開源的,無從去看源代碼驗證,總之,Timer啟動了就好。

每當1ms心跳來臨時,Timer4的中斷标志置位,這樣在OSAL的死循環中檢測到這個标志置位後,就去輪詢處理各事件。沒有檢測到這個标志位則繼續死循環。在死循環的開始有調用Hal_ProcessPoll()這條語句,實際上就是在查詢中斷标志并作相應的處理。

 3. OSAL的“心跳”來臨後的處理

上節提到Hal_ProcessPoll()這條語句,實際上就是在查詢中斷标志并作相應的處理。那麼當1ms心跳來臨時,我們跟蹤進這個函數看看它到底幹了些什麼。

當判斷到中斷标志,表明1ms心跳來臨了,就去調用Timer4相應的回調函數。這個回調函數由HalTimerConfig()的最後一個參數來定義,請回看上節,OSAL Timer的回調函數就是Onboard_TimerCallBack(),一步步跟進,最終調用osalTimerUpdate()這個函數。在這個函數中會去輪詢Timer事件連結清單。

Timer事件連結清單是下面這樣一個結構,next指向下一個Timer事件,timeout值表明本Timer事件還需要timeout個心跳才需要被處理,因為此處心跳是1ms,是以也就是說還需要timeout個ms才處理。所謂的處理也就是檢測timeout是否小于1ms,如果小于1ms, 則發出event_flag這個消息到消息隊列,這個消息隸屬于task_id這個任務。如果大于1ms,說明該Timer事件還不到處理的時候,則Timeout = Timeout-1,然後繼續耐心等待下一次心跳。注:Timer事件連結清單的維護是通過osal_start_timerEx()這個函數來實作的。

typedef struct

{

void *next;

UINT16 timeout;

UINT16 event_flag;

byte task_id;

} osalTimerRec_t;

 4. 消息發出後的處理

上節講到在心跳中發出任務的事件消息到消息隊列。那麼這個消息由誰來處理?回頭再看osal_start_system( )中的死循環,有檢測消息隊列的語句,當發現有消息時,判斷該消息隸屬于哪個任務就去調用對應于該任務的消息處理函數。各任務的消息處理函數是在tasksArr[]這個常量數組中定義。這個數組中定義的消息處理函數和任務初始化函數中的任務必須一一對應。

 5. 節電模式

細心的同學會發現在死循環體的後面有調用osal_pwrmgr_powerconserve()這樣一條語句。從名字及注釋來看,屬于節電模式的調用。此處不詳細列舉代碼,隻講其工作原理。

上面章節講到1ms心跳來臨時去輪詢各事件Timer是否需要處理。這裡心跳很快(1ms),各事件的Timeout很慢(往往成百上千)。譬如Key檢測的Timer事件的Timeout是100,意思是說100ms才去檢測一次是否有Key按下。假如說Key檢測的Timout在各Timer事件中Timeout最小,那麼也就是說有99次心跳都不會有事件需要處理,但是死循環依然在跑,在做無用功,為了解決這個問題,就加入了節電模式。

在osal_pwrmgr_powerconserve()這個函數中會檢測Timer時間連結清單中Timeout最小的值,假設為next, 然後設定CPU進入休眠模式next個毫秒。休眠時間到了蘇醒過來立即就會有Timer事件需要處理,這樣就可以達到省電的目的。

 6. 小結

到此為止,OSAL神秘面紗已完全揭開,為了鞏固知識,下面以Key為例講述從Key按下到Key消息被處理的整個過程。

首先在Key的初始化過程中會調用下面這條語句:osal_start_timerEx (Hal_TaskID, HAL_KEY_EVENT, HAL_KEY_POLLING_VALUE),這條語句的功能就是将檢測Key的這個事件放入Timer事件連結清單。這個事件隸屬于Hal_Task, Timeout是HAL_KEY_POLLING_VALUE。

當1ms心跳來臨時,判斷timeout是否小于1,如果不小于,則timeout=timeout-1并等待下一次心跳。如果小于1,則發出HAL_KEY_EVENT這個消息到消息隊列,然後調用Hal_Task的事件處理函數Hal_ProcessEvent()處理HAL_KEY_EVENT消息,在處理這個消息的過程中調用函數HalKeyPoll()。這個函數檢測目前有無按鍵,如果有按鍵并且和上次按鍵值不同則認為有新的按鍵按下并發出相應的按鍵消息。

在上面這個過程完成後,必須通過osal_start_timerEx()這個函數将Key檢測事件繼續放入Timer事件連結清單,以便後面心跳時能檢測到該事件,也就是說每100ms都會掃描看有無按鍵按下。

OSAL排程機制

摘要:

1、任務排程:osal采用一個連結清單結構來管理協定棧各層相應任務。相關操作函數有,添加任務到連結清單中;擷取下一個活動任務;根據taskID查找下一個任務。osal采用輪詢任務排程隊列(任務連結清單),通過兩個函數:排程程式主循環函數和設定事件發生标志函數。

2、時間管理:通過為事件設定逾時等待時間,一旦等待時間結束,便為對應任務設定事件發生标志,進而達到對事件進行延時處理目的。

3、原語通信:請求響應原語操作:一旦調用了下層相關函數後,就立即傳回。下層處理函數在操作結束後,将結果以消息的形式發送到上層并産生一個系統事件,排程程式發現這個事件後就會調用相應的事件處理函數對它進行處理。兩個相關函數:向目标任務發送消息的函數;消息提取函數。

一、作業系統介紹

現有的嵌入式作業系統可以分為兩類,即通用的多任務作業系統(General—purpose Multi-tasking OS)和事件驅動的作業系統(Event-driven OS)。前者能夠很好地支援多任務或者多線程,但是會随着内部任務切換頻率的增加而産生很大的開銷,這類作業系統有:uC/OS-II、嵌入式Linux、WinCE等。後者支援資料流的高效并發,并且考慮了系統的低功耗要求,在功耗、運作開銷等方面具有優勢。典型的代表如TinyOSl291。

目前TinyOS作業系統支援的平台有ATMEL公司的AVR系列、TI公司的MSP430系列。由于TinyOS作業系統還沒有對Chipcon公司(才知道TI把它收購了

Zstack OSAL資料彙編

)提供CC2430開發平台提供支援,是以,要在CC2430開發平台上使用TinyOS系統來開發Zigbee協定棧軟體,就必須首先對TinyOS進行移植。灰常麻煩

Zstack OSAL資料彙編

……

是以Chipcon公司為自己設計的ZStack協定棧中提供了一個名為作業系統抽象層OSAL的協定棧排程程式。

//-------------------------------------------------------------------------------------

二、下面分析下這個協定棧排程程式(OSAL)的排程機制。

三部分:1、任務排程

2、時間管理

3、原語通信

(一)任務排程

//每層任務=對應事件處理函數

//任務連結清單,任務按優先級插入

ZigBee協定棧中的每一層都有很多原語操作要執行,是以對于整個協定棧來說,就會有很多并發操作要執行。協定棧的每一層都設計了一個事件處理函數,用來處理與這一層操作相關的各種事件。這些事件處理函數可以看成是與協定棧每一層相對應的任務,由ZigBee協定棧中排程程式OSAL來進行管理。這樣,對于協定棧來說,無論何時發生了何種事件,我們都可以通過排程協定棧相應層的任務,即事件處理函數來進行處理。這樣,整個協定棧便會按照時間順序有條不紊的運作。

ZigBee協定棧的實時性要求并不高,是以在設計任務排程程式時,OSAL隻采用了輪詢任務排程隊列的方法來進行任務排程管理。

OSAL采用一個連結清單結構來管理協定棧各層相應的任務。連結清單中的每一項是一個結構體,用來記錄連結清單中相關任務的基本資訊。連結清單的建立是按照任務優先級從高到低的順序進行插入的。優先級高的任務将被插入到優先級低的任務前面。如果倆任務優先級相同,則按照時間順序加入到連結清單中。那麼這個任務連結清單在系統啟動的時候建立,一旦建立後便一直存在于事個系統運作的過程中,直到系統關閉或硬體複位才被銷毀。

連結清單中的每一項資料結構聲明:

typedef void (*pTaskInitFn)(unsigned char task_id) ; //指向任務初始化函數

typedef void (*pTaskEventHandlerFn)(usigned char task_id unsigned short event_flag); //指向事件處理函數

typedef struct osalTaskRec

{

struct osalTaskRec *next; //指向連結清單中下一個結構體

pTaskInitFn pfnInit; //指向相關層任務初始化函數

pTaskEventHandlerFn pfnEventProcessor; //指向相關層事件處理函數

byte taskID; //對應目前任務ID

byte taskPriority; //目前任務優先級

uint16 events; //需要被處理的事件,0表示沒有要被處理事件

} osalTaskRec_t; //連結清單中的每一項資料結構

//-------------------------------------------------------------------------------------

對于pfnInit,是指向相關層任務初始化函數的指針,比如:

網絡層中,任務初始化函數為nwkInit(),用來對網絡層相關資料進行初始化操作。函數聲明為

extern void nwkInit(byte task_id);

//task_id表示為網絡層任務配置設定的唯一任務号

//-------------------------------------------------------------------------------------

對于 pfnEventProcessor是指向協定棧相關層的事件處理函數指針。比如:

網絡層中,事件處理函數nwk_event_loop(),用來處理與網絡層相關的各種事件。函數聲明為

extern void nwk_event_loop(byte task_id, uint16 event_flag);

//event_flag标志需要在網絡層處理的事件

//-------------------------------------------------------------------------------------

上面記錄的是連結清單中的每一項資料結構,與任務連結清單有關的主要操作有:添加任務到清單中;擷取下一個活動任務;根據taskID值查找相應的任務。

(1)在任務管理清單中添加任務

這個函數周遊整個任務隊列連結清單,并按照優先級的高低将優先級高的任務插入到優先級低的任務前面;否則,就将任務插入到連結清單的尾部。在這個過程中,将為每個任務配置設定一個唯一的任務号。函數聲明為:

Extern osalTaskAdd(pTaskInitFn pfnInit,pTaskEventHandleFn pfnEventProcessor,byte taskPriorty);

(2)擷取下一個活動任務

這個函數将根據osalTaskRec_t結構中的events标記來擷取任務隊列中下一個要執行的任務。函數聲明:

Extern osalTaskRec_t *osalNextActiveTask(void) ;

(3)根據taskID查找任務

這個函數将根據任務清單在建立過程中為協定棧中每個任務配置設定的任務号,來查找對應任務。函數聲明:

Extern osalTaskRec_t *osalFindTask(byte taskID);

當任務連結清單建立成功後,系統便開始運作。如果在系統運作的過程中有事件發生,系統就會通過調用相應的任務,即事件處理函數,對所發生的事件進行相應處理。在整個運作過程中,排程程式(OSAL)始終不停地輪詢任務隊列連結清單,以發現需要處理的事件。這個過程涉及兩個函數操作:

1、排程程式主循環函數

2、設定事件發生标志函數

(1)系統主循環

這個函數始終不停地輪詢隊列連結清單,來處理系統發生的各種事件。函數聲明和部分實作如下:

extern void osal_start_system(void);

//無限循環

for(; ; )

{

activeTask=osalNextActiveTask();

if(activeTask)

{

StoreDisableInts;

events=activeTask—>events;

activeTask—>events=0;

if(events!=0)

{

(activeTask—>pfnEventProcessor)(activeTask—>taskID,events);

RestoreInts;

}

}

}

(2)設定事件發生标志

當協定棧中有任何事件發生時,我們可以通過設定osalTaskRec_t結構中的events來标記有事件發生,以便主循環函數能夠及時加以處理。函數聲明如下:

extern byte osal_set_event(byte task_id,uint16 event_flag);

(二)時間管理

協定棧中的每層都會有很多不同的事件發生,這些事件發生的時間順序各不相同。很多時候,事件并不要求立即得到處理,而是經過一定的時間後再進行處理。OSAL排程程式設計了與時間管理相關的函數,用來各種不同的要被處理的事件。

對事件進行時間管理,OSAL也采用了連結清單的方式進行,有時發生一個要被處理的事件,就啟動一個邏輯上的定時器,并将此定時器添加到連結清單當中。利用硬體定時器作為時間操作的基本單元。設定時間操作的最小精度為1ms,每1ms硬體定時器便産生一個時間中斷,在時間中斷處理程式中去更新定時器連結清單。每次更新,就将連結清單中的每一項時間計數減1,如果發現定時器連結清單中有某一表項時間計數已減到0,則将這個定時器從連結清單中删除,并設定相應的事件标志。這樣任務排程程式便可以根據事件标志進行相應的事件處理。具體參見關于“系統時鐘”的記錄。

時間管理函數:

extern byte osal_start_timer(byte task_id, uint16 event_id, uint16 timeout_value);

這個函數為事件event_id設定逾時等待時間timeout_value。一旦等待結束,便為task_id所對應的任務設定相應的事件發生标記,再對事件進行相應處理。

(三)原語通信

(原語隻是一個理論層面上的術語,描述了服務層次的關系,以及兩個通信的N使用者和它們相連的N層(子層)對待協定實體之間的關系。初學時總是想不通原語跟協定棧的代碼有什麼關系,後來才了解了原語隻是規範裡面的一個術語,反映到協定棧代碼裡就是一個個具體的函數了!例如我們可以看到很多原語是以request,confirm等為字尾的,到了程式裡面就是相應的request請求函數,confirm确認函數了。)

對請求(request)、響應(response)原語可以直接使用函數調用來實作

對确認(confirm)、訓示(indication)原語則需采用間接處理機制來完成

一個原語的操作往往需要逐層調用下層函數并根據下層傳回的結果來進行進一步的操作。在這種情況下,一個原主的操作從發起到完成需要很長時間。是以,如果讓程式一直等待下層傳回的結果再進一步處理,會使微處理器大部分時間處于循環等待之中,無法及時處理其它請求。

是以,與請求、響應原語操作相對應的函數,一旦調用了下層相關函數後,就立即傳回。下層處理函數在操作結束後,将結果以消息的形式發送到上層并産生一個系統事件,排程程式發現這個事件後就會調用相應的事件處理函數對它進行處理。(調用就傳回,而不管函數有沒有處理完成。當函數處理完成後将結果以消息的形式發送到上層産生一個系統事件)。

OSAL排程程式用兩個相關的函數來完成這個過程:

1、向目标任務發送消息的函數

這個函數主要用來将原語操作結果以消息的形式往上層任務發送,并産生一個系統事件來通知排程程式。函數聲明如下:

extern byte osal_msg_send(byte destination_task,byte *msg_ptr,byte len);

參數destination_task是目标任務的任務号,參數指針msg_ptr指向要被發送的消息,參數len為消息長度

2、消息提取函數

這個消息用來從記憶體空間中提取相應的消息。其中消息結構和函數聲明如下:

typedef struct

{

byte task_id;

byte dst_task_id;

byte send_len;

}osal_msg_rec_header_t;

typedef struct

{

osal_msg_rec_header_t hdr;

byte *msg_ptr;

}osal_msg_received_t; //消息結構(上面結構體包含在裡面)

extern osal_msg_received_t osal_rcvd; //全局變量

extern osal_msg_received_t *osal_msg_receive(byte task_id); //接收任務

這個函數傳回一個指向osal_msg_received結構的指針,通過msg_ptr這個指針就可以提取出所需要的資訊。(有一個例子,是以MAC層傳回原語MCPS_DATA.confirm所對應的函數為例,下次再說明)

說明:本文作者所記錄,錯誤處還請高手指點,本人随時更新,轉載請注明出處。

本文《OSAL排程機制》主要内容參考廈門大學闫沫的《zigbee協定棧分析與設計》

參考資料:《zigbee技術基礎及案例分析》;《zigbee協定棧分析與設計》;

TI Z-stack協定棧開發環境和工作流程

       系統軟體設計是在硬體設計的基礎上進行的,良好的軟體設計是實作系統功能的重要環節,也是提高系統性能的關鍵所在。節點設計基于通用性及便于開發的考慮,移植了TI公司的Z-Stack協定棧,其主要特點就是其相容性,完全支援IEEE 802. 15. 4/ZigBee的CC2430片上系統解決方案。Z-Stack還支援豐富的新特性,如無線下載下傳,可通過ZigBee網狀網絡(Mesh Network)下載下傳節點更新。

Zstack OSAL資料彙編

圖 ZigBee節點開發環境

     TI的Z-Stack裝載在一個基于IAR開發環境的工程裡。強大的IAR Embedded Workbench除了提供編譯下載下傳功能外,還可以結合程式設計器進行單步跟蹤調試和監測片上寄存器、Flash資料等。Z-Stack根據IEEE 802. 15.4和ZigBee标準分為以下幾層:API(Application Programming Interface),HAL (Hardware Abstract Layer),MAC(Media Access Control), NWK(Zigbee Network Layer),OSAL(Operating System Abstract System),Security,Service,ZDO(Zigbee Device Objects)。使用IAR打開工程檔案SampleApp.eww後,即可檢視到整個協定棧從HAL層到APP層的檔案夾分布。該協定棧可以實作複雜的網絡連結,在協調器節點中實作對路由表和綁定表的非易失性存儲,是以網絡具有一定的記憶功能。

      Z-Stack采用作業系統的思想來建構,采用事件輪循機制,當各層初始化之後,系統進入低功耗模式,當事件發生時,喚醒系統,開始進入中斷處理事件,結束後繼續進入低功耗模式。如果同時有幾個事件發生,判斷優先級,逐次處理事件。這種軟體構架可以極大地降級系統的功耗。

    整個Z-stack的主要工作流程,大緻分為系統啟動,驅動初始化,OSAL初始化和啟動,進入任務輪循幾個階段,下面将逐一詳細分析。

Zstack OSAL資料彙編

圖 Z-Stack系統運作流程圖

Figure . The Flow Chart of Z-Stack

        系統初始化

系統上電後,通過執行ZMain檔案夾中ZMain.c的ZSEG int main( )函數實作硬體的初始化,其中包括關總中斷osal_int_disable( INTS_ALL )、初始化闆上硬體設定HAL_BOARD_INIT( )、初始化I/O口InitBoard( OB_COLD )、初始化HAL層驅動HalDriverInit( )、初始化非易失性存儲器sal_nv_init( NULL )、初始化MAC層ZMacInit( )、配置設定64位位址zmain_ext_addr( )、初始化作業系統osal_init_system( )等。

硬體初始化需要根據HAL檔案夾中的hal_board_cfg.h檔案配置寄存器8051的寄存器。TI官方釋出Z-stack的配置針對的是TI官方的開發闆CC2430DB、CC2430EMK等,如采用其他開發闆,則需根據原理圖設計改變hal_board_cfg.h檔案配置,例如本方案制作的實驗闆與TI官方的I/O口配置略有不同,其中狀态訓示LED2的需要重新設定LED2控制引腳口、通用I/O口方向和控制函數定義等。

當順利完成上述初始化時,執行osal_start_system( )函數開始運作OSAL系統。該任務排程函數按照優先級檢測各個任務是否就緒。如果存在就緒的任務則調用tasksArr[ ]中相對應的任務處理函數去處理該事件,直到執行完所有就緒的任務。如果任務清單中沒有就緒的任務,則可以使處理器進入睡眠狀态實作低功耗。程式流程如圖3-13所示。osal_start_system( )一旦執行,則不再傳回Main( )函數。

Zstack OSAL資料彙編

OSAL任務排程流程圖

Figure . The Flow Chart of OSAL Scheduler

        OSAL任務

OSAL是協定棧的核心,Z-stack的任何一個子系統都作為OSAL的一個任務,是以在開發應用層的時候,必須通過建立OSAL任務來運作應用程式。通過osalInitTasks( )函數建立OSAL任務,其中TaskID為每個任務的唯一辨別号。任何OSAL任務必須分為兩步:一是進行任務初始化;二是處理任務事件。任務初始化主要步驟如下:

(1)    初始化應用服務變量。

const pTaskEventHandlerFn tasksArr[ ]數組定義系統提供的應用服務和使用者服務變量,如MAC層服務macEventLoop、使用者服務SampleApp_ProcessEvent等

(2)    配置設定任務ID和配置設定堆棧記憶體

void osalInitTasks( void )主要功能是通過調用osal_mem_alloc( )函數給各個任務配置設定記憶體空間,和給各個已定義任務指定唯一的辨別号。

(3)    在AF層注冊應用對象

    通過填入endPointDesc_t資料格式的EndPoint變量,調用 afRegister( )在AF層注冊EndPoint應用對象。

通過在AF層注冊應用對象的資訊,告知系統afAddrType_t位址類型資料包的路由端點,例如用于發送周期資訊的SampleApp_Periodic_DstAddr和發送LED閃爍指令的SampleApp_Flash_DstAddr。

(4)注冊相應的OSAL或則HAL系統服務

    在協定棧中,Z-stack提供鍵盤響應和序列槽活動響應兩種系統服務,但是任何Z-Stask任務均不自行注冊系統服務,兩者均需要由使用者應用程式注冊。值得注意的是,有且僅有一個OSAL Task可以注冊服務。例如注冊鍵盤活動響應可調用RegisterForKeys( )函數。

(5)處理任務事件

處理任務事件通過建立“ApplicationName”_ProcessEvent( )函數處理。一個OSAL任務除了強制事件(Mandatory Events)之外還可以定義15個事件。

SYS_EVENT_MSG(0x8000)是強制事件。該事件主要用來發送全局的系統資訊,包括以下資訊:

AF_DATA_CONFIRM_CMD:該資訊用來訓示通過喚醒AF DataRequest( )函數發送的資料請求資訊的情況。ZSuccess确認資料請求成功的發送。如果資料請求是通過AF_ACK_REQUEST置位實作的,那麼ZSussess可以确認資料正确的到達目的地。否則,ZSucess僅僅能确認資料成功的傳輸到了下一個路由。

AF_INCOMING_MSG_CMD:用來訓示接收到的AF資訊。

KEY_ CHANGE:用來确認按鍵動作。

ZDO_ NEW_ DSTADDR:用來訓示自動比對請求。

ZDO_STATE_CHANGE:用來訓示網絡狀态的變化。

   網絡層資訊

Zigbee裝置有兩種網絡位址:1個是64位的IEEE位址,通常也叫作MAC位址或者擴充位址(Extended address),另一個是16位的網絡位址,也叫做邏輯位址(Logical address)或者短位址。64位長位址是全球唯一的位址,并且終身配置設定給裝置。這個位址可由制造商設定或者在安裝的時候設定,是由IEEE來提供。當裝置加入Zigbee網絡被配置設定一個短位址,在其所在的網絡中是唯一的。這個位址主要用來在網絡中辨識裝置,傳遞資訊等。

協調器(Coordinator)首先在某個頻段發起一個網絡,網絡頻段的定義放在DEFAULT_ CHANLIST配置檔案裡。如果ZDAPP_ CONFIG_ PANID定義的PAN ID是0xFFFF(代表所有的PAN ID),則協調器根據它的IEEE位址随機确定一個PAN ID。否則,根據ZDAPP_ CONFIG_ PANID的定義建立PAN ID。當節點為Router或者End Device時,裝置将會試圖加入DEFAULT_ CHANLIST所指定的工作頻段。如果ZDAPP_ CONFIG_ PANID沒有設為0xFFFF,則Router或者End Device會加入ZDAPP_ CONFIG_ PANID所定義的PAN ID。

裝置上電之後會自動的形成或加入網絡,如果想裝置上電之後不馬上加入網絡或者在加入網絡之前先處理其他事件,可以通過定義HOLD_AUTO_START來實作。通過調用ZDApp_StartUpFromApp( )來手動定義多久時間之後開始加入網絡。

裝置如果成功的加入網絡,會将網絡資訊存儲在非易失性存儲器(NV Flash)裡,掉電後仍然儲存,這樣當再次上電後,裝置會自動讀取網絡資訊,這樣裝置對網絡就有一定的記憶功能。對NV Flash的動作,通過NV_RESTORE( )和NV_ITNT( )函數來執行。

有關網絡參數的設定大多儲存在協定棧Tools檔案夾的f8wConfig.cfg裡。

        路由

Z-Stack采用無線自組網按需平面距離矢量路由協定AODV,建立一個Hoc網絡,支援移動節點,連結失敗和資料丢失,能夠自組織和自修複。當一個Router接受到一個資訊包之後,NMK層将會進行以下的工作:首先确認目的地,如果目的地就是這個Router的鄰居,資訊包将會直接傳輸給目的裝置;否則,Router将會确認和目的位址相應的路由表條目,如果對于目的位址能找到有效的路由表條目,資訊包将會被傳遞到該條目中所存儲的下一個hop位址;如果找不到有效的路由表條目,路由探測功能将會被啟動,資訊包将會被緩存直到發現一個新的路由資訊。

ZigBee End Device不會執行任何路由函數,它隻是簡單的将資訊傳送給前面的可以執行路由功能的父裝置。是以,如果End Device想發送資訊給另外一個End Device,在發送資訊之間将會啟動路由探測功能,找到相應的父路由節點。

轉自:http://blog.ednchina.com/Jam/247711/message.aspx

PS:百度部落格發文章有字數限制,太煩人了,一篇文章分成好幾段,看起來也很不爽。終于找到解決方法了:在記事本裡把文字寫好再複制過來,圖表單獨插入,即可解決問題。

Z-stack1.4.3及以後的版本裡包含了一個作業系統,名義上為實時作業系統,可是在應用上,它的實時性能并不理想。至少不少人這樣認為,我也是,呵。。。先說下幾個概念。

任務:協定棧是多任務,但它們不是同時執行的,是準并行的,輪流使用CPU,就像很多人一起讀一本書,每個人隻能拿到書時才可以讀。同進,在輪流過程中處理器要為每個任務維護其狀态資訊,也就是任務的場景,就跟書簽一樣,每個讀者都有必要有一根自己的書簽(taskID),以便知道自己讀到上回讀到哪了,也就是讀者上下文環境資訊。

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++ );

  ZDApp_Init( taskID++ );

  OuhsApp_Init( taskID );

}

任務排程機制:osal_start_system()将查詢所有的任務事件,并使每個任務事件調用對應的事件處理函數。如果某個特定的任務有事件發生,那麼該函數就将調用該任務事件的處理函數,當做完相應的處理後,再次傳回到主循環函數,繼續查找其它任務事件。如果所有的任務都沒有事件發生,那麼該函數将使處理器進入睡眠模式。該函數定義在osal.c中

void osal_start_system( void )

{

#if !defined ( ZBIT )

  for(;;)     //主大循環

#endif

  {

    uint8 idx = 0;

    Hal_ProcessPoll();  // 詢uart、timer

    do {

      if (tasksEvents[idx])  // 當有任務事件發生時,跳出任務查詢

      {

        break;

      }

    } while (++idx < tasksCnt);

    if (idx < tasksCnt)

    {

      uint16 events;

      halIntState_t intState;

      HAL_ENTER_CRITICAL_SECTION(intState); //臨界區,先儲存EA然後禁止所有中斷

      events = tasksEvents[idx];         

      tasksEvents[idx] = 0;                 // 清除該任務的事件

      HAL_EXIT_CRITICAL_SECTION(intState);  //臨界區,恢複前面儲存EA狀态

      events = (tasksArr[idx])( idx, events ); //調用處理函數

          HAL_ENTER_CRITICAL_SECTION(intState);

      tasksEvents[idx] |= events;  // 傳回沒有處理的事件到目前任務中

          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

    }

}

這裡的任務事件處理的函數的順序必須要和osalInitTasks裡的順序一緻。

const pTaskEventHandlerFn tasksArr[] = {

  macEventLoop,

  nwk_event_loop,

  Hal_ProcessEvent,

#if defined( MT_TASK )

  MT_ProcessEvent,

#endif

  APS_event_loop,

  ZDApp_event_loop,

  OuhsApp_ProcessEvent

};

排程程式:排程程式是作業系統的核心和靈魂,它決定在給定的時間内哪一個任務有權使用處理器。普通的排程算法:先進先出、短任務優先、循環法。這些是不太适合嵌入式的簡單的排程算法。

先進先出(FIFO):每一個任務一直運作到它結束,至結束時下一個任務才被啟動。非多任務作業系統。

短任務優先:每一次運作的任務在完成或挂起的時候,下一個被選擇的任務是需要最小處理器完成時間的任務。

循環法:運作中的任務可以被占先,也就是在運作過程中可以被中斷。在這種情況下,每個任務都運作一個預先決定的時間,那個時間間隔過後,運作的程度被作業系統占先,然後下一個任務有機會運作,被占先的任務直到其它所有的任務都有機會運作一輪之後再次開始運作。

但這些簡單的排程算法不太适合嵌入式系統,大部分嵌入式作業系統使用一個支援占先的基于優先級的的排程算法,也就是在任何一個給定的時刻,正在使用處理器的任務保證就是就緒任務中優先級最高的,低優先級的任務必須等待高優先級的任務運作完成才能使用處理器。“占先”就是如果一個高優先級的任務就緒之後,任何任務都能被作業系統中斷。

在z-stack1.4.3以前不含作業系統,z-stack1.4.3的作業系統,有定義了任務優先級(共三個),可是1.4.3-1.2.1裡沒有再定義優先級了,它使用基于數組索引的序号順序排程,是以并不是真正意義上的實時系統,它使用了一個定時器來完成排程。它的排程方式決定了使用者的應用并不實時。

任務同步:把一個任務當做完全獨立的實體來讨論是不完全準确的,必須偶爾和其它的任務通信使得它們的活動同步。實作任務同步的機制有:互斥體、信号燈、消息隊列以及螢幕。互斥體是為了保護共享資源(全局變量、存儲緩存、被多任務存取的裝置寄存器)。Z-stack的消息隊列遵循FIFO原則。

參考書目:C/C++嵌入式系統程式設計

繼續閱讀