![](/img/trans.png)
[英]Why does this pclose() implementation return early with ECHILD unless invocation is delayed after popen()?
[英]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;
}
}
所以这个想法是
我将BeagleBoneBlack与内核4.1.2一起使用rt-preempt补丁。
我在范围内看到的是,第一个计时器的工作原理像一个大约65-67微秒的护身符(我可以接受)。 但是转发似乎会出现故障,因为我测量的是引脚A变为低电平和引脚B变为低电平之间的时间在2到50微秒之间。 因此,从本质上讲,第二次触发回调有时发生在我定义的24微秒之前 。 而且这种时机不适用于我的用例。
任何指示我做错了什么?
因此,我自己回答:这是一个错误的期望问题。
我们在这里期望的是在回调过程中将计时器向前设置我们设置的数量(24us)。 但是,如果我们看一下hrtimer_forward_now()
的内核实现,我们可以看到时间实际上已添加到计时器的最后一个事件/发生中(请参见delta
的计算):
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_forward_now
根据上一个事件加上新的预期时间(将来可能只有2us,而不是hrtimer_forward_now
将新的计时器向前设置。 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.