Zstack OSAL詳解

xiaolei05發表於2011-10-03
 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都會掃描看有無按鍵按下。

相關文章