2016-03-04 20 views
0

我想使用hrtimer來控制兩個硬件gpio引腳,以實現一些總線信號。我成立了一個hrtimer內核模塊在這樣爲什麼我的hrtimer回調在轉發之後返回太早?

#include <linux/slab.h> 
#include <linux/delay.h> 
#include <linux/ktime.h> 
#include <linux/hrtimer.h> 

#define PIN_A_HIGH_TO_A_LOW_US 48 /* microseconds */ 
#define PIN_A_LOW_TO_B_LOW_US 24 /* microseconds */ 

static struct kt_data { 
    struct hrtimer timer; 
    ktime_t period; 
} *data; 

typedef enum { 
    eIdle = 0, 
    eSetPinALow, 
    eSetPinBLow, 
} teControlState; 

static enum hrtimer_restart TimerCallback(struct hrtimer *var); 
static void StopTimer(void); 

static teControlState cycle_state = eIdle; 

static enum hrtimer_restart TimerCallback(struct hrtimer *var) 
{ 
    local_irq_disable(); 

    switch (cycle_state) { 
    case eSetPinALow: 
     SetPinA_Low(); 
     data->period = ktime_set(0, PIN_A_LOW_TO_B_LOW_US * 1000); 
     cycle_state = eSetPinBLow; 
     break; 
    case eSetPinBLow: 
     SetPinB_Low(); 
     /* Do Stuff */ 
     /* no break */ 
    default: 
     cycle_state = eIdle; 
     break; 
    } 

    if (cycle_state != eIdle) { 
     hrtimer_forward_now(var, data->period); 
     local_irq_enable(); 
     return HRTIMER_RESTART; 
    } 

    local_irq_enable(); 
    return HRTIMER_NORESTART; 
} 

void StartBusCycleControl(void) 
{ 
    SetPinA_High(); 
    SetPinB_High(); 

    data->period = ktime_set(0, PIN_A_HIGH_TO_A_LOW_US * 1000); 
    cycle_state = eSetPinALow; 
    hrtimer_start(&data->timer, data->period, HRTIMER_MODE_REL); 
} 

int InitTimer(void) 
{ 
    data = kmalloc(sizeof(*data), GFP_KERNEL); 

    if (data) { 
     hrtimer_init(&data->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); 
     data->timer.function = TimerCallback; 
     printk(KERN_INFO DRV_NAME 
       ": %s hr timer successfully initialized\n", __func__); 
     return 0; 
    } else { 
     printk(KERN_CRIT DRV_NAME 
       ": %s failed to initialize the hr timer\n", __func__); 
     return -ENOMEM; 
    } 
} 

這樣的想法是,

  • 兩個引腳高點開始
  • hrtimer設置後48微秒
  • 到期
  • 在回調函數中,引腳A被拉低
  • 計時器被向前推進24微秒
  • 回調引發了第二次的時候,銷B被拉低

我使用BeagleBoneBlack與內核4.1.2與RT-搶佔補丁。

我在範圍內看到的第一個計時器的工作原理就像一個約65-67微秒的魅力(我可以忍受這一點)。 但是轉發似乎失靈,因爲我測量的引腳A變低和引腳B變低的時間在2到50微秒之間。 所以本質上,第二次觸發回調有時會發生在我定義的24微秒之前之前。 而且這個時間對我的用例不起作用。

任何指向我在做什麼錯?

+0

'我測量的引腳A低電平和引腳B低電平之間的時間在2到50微秒之間。「 - **這個時間你如何測量?請清理你提供的代碼:'StartBusCycleControl'永遠不會被使用,'StopTimer'也不會被使用。另一方面,使用'SetPinA_Low'和'SetPinB_Low',但從未定義。 – Tsyvarev

+0

它們用示波器測量。代碼當然是不完整的。 setpin函數只寫入一些將gpio引腳拉低或拉高的寄存器。 – TabascoEye

+0

我認爲這是由於基礎時鐘硬件的更新週期。雖然當啓用高分辨率定時器時,內核中的hrtimer支持的分辨率爲1 ns,但記錄時間的硬件寄存器不一定每1 ns更新一次。如果它們每N納秒更新一次,那麼從hrtimer讀取的「now」值可能會過時達N納秒。因此,當你調用'hrtimer_forward_now(var,data-> period)'時,更新的失效時間可能會相差最多N納秒。 –

回答

1

所以要自己回答:這是一個錯誤的期望問題。

我們在這裏期望的是在回調期間設置定時器正向我們設置的量(24us)。但是,如果我們在內核實現的hrtimer_forward_now()看看我們可以看到,時間實際上是添加到計時器的最後事件/發生(見delta計算):

Linux/kernel/time/hrtimer.c

833 u64 hrtimer_forward(struct hrtimer *timer, ktime_t now, ktime_t interval) 
834 { 
835   u64 orun = 1; 
836   ktime_t delta; 
837 
838   delta = ktime_sub(now, hrtimer_get_expires(timer)); 
839 
840   if (delta.tv64 < 0) 
841     return 0; 
842 
843   if (WARN_ON(timer->state & HRTIMER_STATE_ENQUEUED)) 
844     return 0; 
845 
846   if (interval.tv64 < hrtimer_resolution) 
847     interval.tv64 = hrtimer_resolution; 
848 
849   if (unlikely(delta.tv64 >= interval.tv64)) { 
850     s64 incr = ktime_to_ns(interval); 
851 
852     orun = ktime_divns(delta, incr); 
853     hrtimer_add_expires_ns(timer, incr * orun); 
854     if (hrtimer_get_expires_tv64(timer) > now.tv64) 
855       return orun; 
856     /* 
857     * This (and the ktime_add() below) is the 
858     * correction for exact: 
859     */ 
860     orun++; 
861   } 
862   hrtimer_add_expires(timer, interval); 
863 
864   return orun; 
865 } 

這意味着它在定時器觸發和實際執行的回調之間花費的時間延遲在此不予考慮。時間表旨在精確的間隔時間內,不受通常的發射和回調之間延遲的影響。 我們的預期時間爲,其中包括,因爲我們希望計時器從我們在計時器回調中執行操作的那一刻開始重新計算。

我試圖吸引到下面的示意圖: hrtimer expectation vs reality diagram

繼紅色編號氣泡我們得到:

  1. 啓動計時器後X時間火
  2. 時間X已經過去了,定時器被觸發
  3. 「延遲X」取決於系統負載和其他因素後,hrtimer的回調函數被稱爲
  4. hrtimer_forward_now根據上次事件加上新的預期時間(可能僅在未來2天而不是24天)設置新計時器
  5. 這是預期與實際的差異。最後事件後hrtimer火災24us的時候,我們希望它的調用後火24us到forward_now()

要總括起來,我們就徹底搗毀了上面的代碼示例,並用usleep_range()調用去觸發之間兩個GPIO引腳。這個函數的底層實現也是用hrtimer完成的,但是它對用戶是隱藏的,它在這種情況下就像我們期望的那樣。

相關問題