Tick工做原理其實就是硬件定時器的工做原理,1個系統tick就表明一個定時器硬件中斷。定時器的工做原理很簡單,就是內部有一個遞減的計數器,當減到0時產生一箇中斷,如圖 1-1所示:c++
圖 1-1定時器工做原理ui
假設定時器模塊的輸入頻率是1MHz,系統定義的1S內tick數是100,也就是100Hz,能夠計算出遞減計數器要設置的值爲1MHz/100Hz=10000。能夠看出遞減計數器至關於一個分頻器,輸入端每來一個脈衝,其值就減去1,當減到0時產生一箇中斷,同時其值自動重載成10000,如此循環下去。spa
系統獲取時間相關接口是基於tick來工做的,可是這是有偏差的,如圖 2-1所示:接口
圖 2-1系統獲取時間io
虛線表示下一個tick中斷還未產生,若是此時來獲取時間,獲取到的時間只是以前tick累計的時間。假設tick中斷產生時刻和獲取時間那一時刻之間的跨度是4ms,那麼獲取的時間就有4ms的偏差,高精度時鐘就是爲了消除這種偏差而誕生的。原理
上述偏差產生的根本緣由是沒有將tick中斷產生時刻和獲取時間那一時刻之間的跨度更新到時間裏去,若是計算出這段時間並加到獲取的時間裏去就能夠校訂獲取的時間了。結合圖 1-1和圖 2-1,基本的校訂原理以下所述:循環
1個tick時間等價於遞減計數器的初始值,假設是10000,也就是說遞減10000次至關於過了一個tick時間硬件
用1000 * 1000 * 1000 / 10000獲得遞減一次的時間,單位是ns定時器
用計數器初始值減去獲取時間那一時刻計數器中的值,就獲得了獲取時間時刻計數器已經遞減的次數請求
用遞減一次的時間 *遞減的次數,就獲得tick中斷產生時刻和獲取時間那一時刻之間的時間跨度
當系統是多核時,系統產生一個由CPU0來處理的tick中斷,當CPU0尚未更新整個系統的tick數時,這時CPU1來獲取時間,按照基本原理的計算以後還要加上一個tick的時間纔是正確的。
程序清單 4-1
VOID bspTickHighResolution (struct timespec *ptv)
{
REGISTER UINT32 uiCntCur, uiDone;
uiCntCur = (UINT32)timerGetCnt(4);
uiDone = GuiFullCnt - uiCntCur;
/*
*檢查是否有 TICK中斷請求
*/
if (rSRCPND & BIT_TIMER4) {
/*
*這裏因爲 TICK沒有及時更新,因此須要從新獲取而且加上一個 TICK的時間
*/
uiCntCur = (UINT32)timerGetCnt(4);
uiDone = GuiFullCnt - uiCntCur;
if (uiCntCur != 0) {
uiDone += GuiFullCnt;
}
}
ptv->tv_nsec += (LONG)((Gui64NSecPerCnt7 * uiDone) >> 7);
if (ptv->tv_nsec >= 1000000000) {
ptv->tv_nsec -= 1000000000;
ptv->tv_sec++;
}
}
GuiFullCnt表示遞減計數器的初始值,也就是產生1個tick時間的計數值;Gui64NSecPerCnt7表示遞減一次的時間,可是這個時間被擴大了128倍,目的是爲了提升計算精度
uiDone表示獲取時間時刻計數器已經遞減的次數,uiCntCur表示獲取時間時刻計數器當前值
if (rSRCPND & BIT_TIMER4)用於特殊狀況的判斷,條件成立表示系統的tick數還沒來得及更新,不成立表示系統的tick數已經被更新
當uiCntCur=0時,表示tick中斷剛剛產生,這時uiDone就已經表明一個tick的計數值,因此就無需再加上一個tick的計數值了
最後將修正後的時間賦值給tv_nsec成員