Linux 高精度定時器hrtimers簡單介紹和應用場景
hrtimer:high-resolution kernel timers:
hrtimers的誕生是由於核心開發者在使用過程中發現,原始的定時器kernel/timers.c
,已經可以滿足所有場景的,但是在實際的大量測試中發現還是無法滿足所有場景,所以hrtimers就誕生了。這裡簡單介紹下關鍵結構體和一個應用場景,具體遠離下篇部落格再來分析。
1.結構體簡單介紹
1.struct hrtimer
這個是高精度物件的成員變數,核心註釋已經很詳細了,具體細節下篇來說,這裡重點關注的是function,這個是我們需要實現的方法。
/**
* struct hrtimer - the basic hrtimer structure
* @node: timerqueue node, which also manages node.expires,
* the absolute expiry time in the hrtimers internal
* representation. The time is related to the clock on
* which the timer is based. Is setup by adding
* slack to the _softexpires value. For non range timers
* identical to _softexpires.
* @_softexpires: the absolute earliest expiry time of the hrtimer.
* The time which was given as expiry time when the timer
* was armed.
* @function: timer expiry callback function
* @base: pointer to the timer base (per cpu and per clock)
* @state: state information (See bit values above )
* @start_pid: timer statistics field to store the pid of the task which
* started the timer
* @start_site: timer statistics field to store the site where the timer
* was started
* @start_comm: timer statistics field to store the name of the process which
* started the timer
*
* The hrtimer structure must be initialized by hrtimer_init()
*/
struct hrtimer {
struct timerqueue_node node;
ktime_t _softexpires;
enum hrtimer_restart (*function)(struct hrtimer *);
struct hrtimer_clock_base *base;
unsigned long state;
#ifdef CONFIG_TIMER_STATS
int start_pid;
void *start_site;
char start_comm[16];
#endif
};
2.ktime_t
這個用來表示時間,單位為納米。在啟動定時器之前,需要設定一個時間來決定何時呼叫回撥函式。
typedef union ktime ktime_t;
/*
* ktime_t:
*
* A single 64-bit variable is used to store the hrtimers
* internal representation of time values in scalar nanoseconds. The
* design plays out best on 64-bit CPUs, where most conversions are
* NOPs and most arithmetic ktime_t operations are plain arithmetic
* operations.
*
*/
union ktime {
s64 tv64;
};
2.hrtimers應用場景
1).建立定時器物件&設定首次觸發時間
最近在做一個Camera的flash驅動,該camera需要pwm來控制亮度,而硬體上該GPIO口,沒有pwm功能,所以只能來模擬pwm了。
本地flash控制物件中巢狀的有hrtimer物件:
struct flash_gpio_ctrl {
struct hrtimer *flash_timer;
ktime_t kt;
int flash_level;
int IsFlash_stop;
int loop_count;
uint32_t gpio_enf;
uint32_t gpio_enm; //pwm
};
建立定時器以及啟動定時器:
static void flash_pwm_start(void)
{
hrtimer_init(flash_gpio_ctrl.flash_timer,CLOCK_MONOTONIC,HRTIMER_MODE_REL);
flash_gpio_ctrl.flash_timer->function = hrtimer_handler;
flash_gpio_ctrl.kt = ktime_set(0, 10000);
hrtimer_start(flash_gpio_ctrl.flash_timer,flash_gpio_ctrl.kt,HRTIMER_MODE_REL);
pr_err("timer_start");
}
1.hrtimer_init:這個是用來建立timer定時器物件,由核心封裝好了,具體細節先不用關係,只需要把定時器物件巢狀進我們定義的結構體中就行了。
2.function:這個就是回撥函式指標,這裡註冊回撥函式。
3.ktime_set(0, 10000):設定首次觸發時間,這裡我設定了10ms。
- 4.hrtimer_start();這裡傳入定時器物件,和觸發時間,就開啟定時器了。
2).回撥函式預覽
static enum hrtimer_restart hrtimer_handler(struct hrtimer *timer)
{
flash_flag = !flash_flag;
gpio_direction_output(flash_gpio_ctrl.gpio_enm, 0);
if(flash_gpio_ctrl.loop_count != 1){
if (flash_flag) {
gpio_direction_output(flash_gpio_ctrl.gpio_enm, 1);
flash_gpio_ctrl.kt = ktime_set(0, BASE_TIME * (flash_gpio_ctrl.flash_level) * 1000);
hrtimer_forward_now(flash_gpio_ctrl.flash_timer, flash_gpio_ctrl.kt);
//pr_err("pulse low level:%d",flash_gpio_ctrl.flash_level);
} else {
gpio_direction_output(flash_gpio_ctrl.gpio_enm, 0);
flash_gpio_ctrl.kt = ktime_set(0, 50000 - (BASE_TIME * (flash_gpio_ctrl.flash_level) * 1000));
hrtimer_forward_now(flash_gpio_ctrl.flash_timer, flash_gpio_ctrl.kt);
//pr_err("pulse high levle");
}
flash_gpio_ctrl.loop_count--;
pr_err("flash_timer out\n");
return HRTIMER_RESTART;
}else{
flash_gpio_ctrl.loop_count--;
return HRTIMER_NORESTART;
}
}
1.loop_count:這裡我設定的是一個迴圈觸發的定時器,所以為了不讓定時器無休止的進行下去,設定一個觸發次數。次數減為0,則推出定時器。
2.HRTIMER_RESTART:這個返回值告訴定時器核心,這裡是定時器會重新觸發,而且上面又重新設定了觸發的時間。
3.hrtimer_forward_now:重新設定定時器觸發的時間。
至此定時器就已經執行起來了,每割一定時間都會觸發回撥函式。而且產生的pwm能使flash亮起來,但是由於佔空比不是很準確,所以flash亮度有些不穩定。(此外還有一種方法是啟動一個執行緒,來同步產生控制訊號)