繁体   English   中英

为什么我的hrtimer回调在转发后返回得太早了?

[英]Why does my hrtimer callback return too early after forwarding it?

我想使用hrtimer来控制两个硬件gpio引脚来执行一些总线信号传输。 我在这样的内核模块中设置了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-preempt补丁。

我在范围内看到的是,第一个计时器的工作原理像一个大约65-67微秒的护身符(我可以接受)。 但是转发似乎会出现故障,因为我测量的是引脚A变为低电平和引脚B变为低电平之间的时间在2到50微秒之间。 因此,从本质上讲,第二次触发回调有时发生我定义的24微秒之前 而且这种时机不适用于我的用例。

任何指示我做错了什么?

因此,我自己回答:这是一个错误的期望问题。

我们在这里期望的是在回调过程中将计时器向前设置我们设置的数量(24us)。 但是,如果我们看一下hrtimer_forward_now()的内核实现,我们可以看到时间实际上已添加到计时器的最后一个事件/发生中(请参见delta的计算):

Linux /内核/时间/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 }

这意味着此处不考虑计时器触发与实际执行的回调之间所花费的时间延迟。 hrtimers的时间间隔精确,不受触发和回调之间通常的延迟的影响。 当我们的预期是包括时间计算进去,因为我们想要的计时器的那一刻起重新开始执行我们的计时器回调动作。

我试图将其绘制到下图中: hrtimer期望与现实关系图

跟随红色的气泡,我们得到:

  1. 计时器以X时间启动
  2. X过去了,计时器被触发
  3. 在“延迟X”之后,根据系统的负载和其他因素,将调用hrtimer的回调函数
  4. hrtimer_forward_now根据上一个事件加上新的预期时间(将来可能只有2us,而不是hrtimer_forward_now将新的计时器向前设置。
  5. 这是期望与现实的差异。 hrtimer在最后一个事件之后触发24us,这是因为我们希望它在调用forward_now()之后触发24us。

总而言之,我们完全废弃了上面的代码示例,并在触发两个GPIO引脚之间进行了usleep_range()调用。 该函数的基础实现也可以通过hrtimer完成,但对用户而言是隐藏的,并且在这种情况下可以像我们期望的那样起作用。

我也遇到了这个问题。谢谢TabascoEye的回答。我只想添加一些代码作为示例,以便于理解。

在我的应用程序,我有一个硬件中断(30毫秒+ -3ms间隔)调用reactive_hrtimer()然后timer_do()将10毫秒后调用。 由于输入中断的时间不是正常的,因此我需要自己为hrtimer_add_expires()实现输入。

对于不规则的时间间隔:

enum hrtimer_restart timer_do(struct hrtimer *timer)
{
    /**something**/
    return HRTIMER_NORESTART;
}

void reactive_hrtimer( struct hrtimer *hr_timer, ktime_t ktime_interval)
{
    ktime_t delta;
    ktime_t now;
    now = hrtimer_cb_get_time(hr_timer);
    delta = ktime_sub(now, hrtimer_get_expires(hr_timer));

    hrtimer_add_expires(hr_timer, ktime_add(ktime_interval, delta));
    hrtimer_restart(hr_timer);
 }

这些代码可以放在任何地方,而不是放在回调内部

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM