#include <asm/param.h> #include <linux/timer.h> void add_timer(struct timer_list * timer); int del_timer(struct timer_list * timer); inline void init_timer(struct timer_list * timer); struct timer_list的定義為: struct timer_list { struct timer_list *next; struct timer_list *prev; unsigned long expires; unsigned long data; void (*function)(unsigned long d); }; 其中expires是要執行function的時間。系統核心有一個全域變數JIFFIES 表示目前時間,一般在調用add_timer時jiffies=JIFFIES+num,表示在num個 系統最小時間間隔後執行function。系統最小時間間隔與所用的硬體平台有關, 在核心裡定義了常數HZ表示一秒內最小時間間隔的數目,則num*HZ表示num 秒。系統計時到預定時間就調用function,並把此子程式從定時隊列裡刪除, 因此如果想要每隔一定時間間隔執行一次的話,就必須在function裡再一次調 用add_timer。function的參數d即為timer裡面的data項。 有時驅動程式需要非常短的延遲來與硬體同步。此時,使用jiffies值無法達到目的。這時就要用核心功能udelay和mdelay。u表示希臘字母“mu”(m),它代表“微”。它們的原型如下: #include <Linux/delay.h> void udelay(unsigned long usecs); //軟體迴圈延遲指定數目的微秒數 void mdelay(unsigned long msecs); //使用 udelay 做迴圈 該函數在絕大多數體繫結構上是作為內嵌函式編譯的。udelay函數裡要用到BogoMips值:它的迴圈基於整數值loops_per_second,這個值是在引導階段計算BogoMips時得到的結果。 udelay函數只能用於擷取較短的時間延遲,因為loops_per_second值的精度只有8位,所以,當計算更長的延遲時會積累出相當大的誤差。儘管最大能允許的延遲將近1s(因為更長的延遲就要溢出),推薦的udelay函數參數最大值是取1000us(1ms)。當延遲大於11ms時可以使用函數mdelay。許多驅動程式需要將任務延遲到以後處理,但又不想藉助中斷。Linux為此提供了三種方法:任務隊列、tasklet和核心定時器。 要特別注意的是udelay是個忙等待函數,在延遲的時間段內無法運行其他的任務。源碼見標頭檔<asm/delay.h>。 目前核心不支援大於1微秒而小於1個時鐘滴答的延遲,但這不是個問題,因為延遲是給硬體或者人去識別的。百分之一秒的時間間隔對人來說延遲精度足夠了,而1毫秒對硬體來說延遲時間也足夠長。如果你真的需要其間的延遲間隔,你只要建立一個連續執行udelay(1000)函數的迴圈。 linux核心延時函數程式碼範例: 1、#include <linux/time.h> void do_gettimeofday(struct timeval *tv) { unsigned long flags; unsigned long usec, sec; read_lock_irqsave(&xtime_lock, flags); sec = xtime.tv_sec; usec = xtime.tv_usec + do_gettimeoffset(); read_unlock_irqrestore(&xtime_lock, flags); while (usec >= 1000000) { usec -= 1000000; sec++; } tv->tv_sec = sec; tv->tv_usec = usec; }void MyDelay(unsigned long delay) { struct timeval tv; do_gettimeofday(&tv) unsigned long start = tv.tv_usec;//unsigned long start = tv.tv_sec; while(tv.tv_usec - start <delay) do_gettimeofday(&tv) } 2、如果驅動程式使用等待隊列等待某個事件,而你又想確保在一段時間後運行該驅動程式時 extern inline long sleep_on_timeout(wait_queue_head_t *q, signed long timeout) { signed long early = 0; current->timeout = jiffies + timeout; sleep_on (q); if (current->timeout > 0) { early = current->timeout - jiffies; current->timeout = 0; } return early; } extern inline long interruptible_sleep_on_timeout(wait_queue_head_t *q, signed long timeout) { signed long early = 0; current->timeout = jiffies + timeout; interruptible_sleep_on (q); if (current->timeout > 0) { early = current->timeout - jiffies; current->timeout = 0; } return early; } 3.無需等待其他事件,則可直接延時等待 extern inline void schedule_timeout(int timeout) { current->timeout = jiffies + timeout; current->state = TASK_INTERRUPTIBLE; schedule(); current->timeout = 0; } set_current_state(TASK_INTERRUPTIBLE); schedule_timeout(jit_delay*HZ); 4 非常短的延時與硬體同步 udelay推薦最大1000us #include<linux/delay.h> void __delay(int loops) { long long dummy; __asm__ __volatile__("gettr " __t0 ", %1/n/t" "_pta 4, " __t0 "/n/t" "addi %0, -1, %0/n/t" "bne %0, r63, " __t0 "/n/t" "ptabs %1, " __t0 "/n/t":"=r"(loops), "=r"(dummy) :"0"(loops)); } void __udelay(unsigned long long usecs, unsigned long lpj) { usecs *= (((unsigned long long) HZ << 32) / 1000000) * lpj; __delay((long long) usecs >> 32); } #ifdef notdef #define mdelay(n) (/ {unsigned long msec=(n); while (msec--) udelay(1000);}) #else #define mdelay(n) (/ (__builtin_constant_p(n) && (n)<=MAX_UDELAY_MS) ? udelay((n)*1000) : / ({unsigned long msec=(n); while (msec--) udelay(1000);})) #endif
|