我想写一个在Linux下控制步进电机的开源核心驱动程序。在这种情况下,尤其是对于3D打印机。
其基本思想是,驱动程序在一个IO端口上保留引脚,然后一次操作这些引脚。它接收一个充满“切换这个,切换那个”值的缓冲区,然后使用硬件计时器将这些值发送到端口。
现在的问题是:有没有办法尽可能快地处理特定的硬件中断?
有问题的芯片是Allwinner H3,我使用的是该芯片的TMR1资源(IRQ51)。我可以很好地使用它,它也可以作为中断工作:
static irqreturn_t stepCore_timer_interrupt(int irq, void *dev_id)
{
writel(TMR1_IRQ_PEND, TMR_IRQ_ST_VREG);
icnt++;
porta_state = readl(PA_VDAT);
porta_state &= porta_mask;
if(icnt & 0x00000001)
{
porta_state |= 0x00000001;
}
writel(porta_state, PA_VDAT);
return IRQ_HANDLED;
}
static struct irqaction stepCore_timer_irq = {
.name = "stepCore_timer",
.flags = IRQF_DISABLED | IRQF_NOBALANCING , IRQF_PERCPU,
.handler = stepCore_timer_interrupt,
.dev_id = NULL,
};
static void stepCore_timer_interrupt_setup(void)
{
int ret;
u32 val;
writel( 24000000, TMR1_INTV_VALUE_VREG );
writel( ( TMR1_MODE_CONTINUOUS | TMR1_CLK_PRES_1 | TMR1_CLK_SRC_OSC24M ), TMR1_CTRL_VREG );
ret = setup_irq(SUNXI_IRQ_TIMER1, &stepCore_timer_irq);
if (ret)
printk("%s: ERROR: failed to install irq %d\n", __func__, SUNXI_IRQ_TIMER1);
else
printk("%s: irq %d installed\n", __func__, SUNXI_IRQ_TIMER1);
ret = irq_set_affinity_hint(SUNXI_IRQ_TIMER1, cpumask_of(3));
if (ret)
printk("%s: ERROR: failed to set irq affinity for irq %d\n", __func__, SUNXI_IRQ_TIMER1);
else
printk("%s: set irq affinity for irq %d\n", __func__, SUNXI_IRQ_TIMER1);
/* Enable timer0 interrupt */
val = readl(TMR_IRQ_EN_VREG);
writel(val | TMR1_IRQ_EN, TMR_IRQ_EN_VREG);
}TMR1在其他方面都没有使用过(实际上,我必须自己添加它),并且到目前为止都可以正常工作。然而,在处理相当简单的IRQ例程时有相当多的延迟。因为我想生成一些可用于3D打印机的代码,所以我非常喜欢一个更“稳定”的定时器中断。
所以,我的问题是:有没有办法在Linux中有一个非常短的IRQ例程,并且具有尽可能高的优先级?或者根本不关心Linux调度器,而只是“它是什么吗”?基本上是一个原始的IRQ处理程序,忽略了Linux认为它应该是什么?
不管怎么说,它运行的内核就是专门用来完成这个任务的。处理程序将尽可能简短:从数组中获取u32,将其写入端口,完成。
最好,我希望有一些东西,只是忽略了Linux的其余部分。是的,我知道这不是做这件事的方法。但这是为一个相当特殊的情况而设计的,所以我毫不犹豫地调整常规的内核源代码以满足这些需求。
哦,这提醒了我,内核是3.4.112,带有合适的preempt-rt补丁。
任何帮助都是非常感谢的。
大家好,
克里斯
发布于 2016-08-13 13:38:19
以下是此问题的一般解决方案。您可以编写一个内核模块,它将覆盖现有的中断处理例程,并将替换为您自己的例程,您可以在其中处理您感兴趣的irq,并将所有irq重定向到现有的内核中断处理例程。对于CPU,您可以通过获取低级x86指令来获取现有的中断描述例程(lidt)地址。我相信这对ARM来说也是可能的。现在,Linux有CPU隔离isolcpus的技术。通过利用这个技术,你可以把CPU从调度器域中取出,也就是说,在你指定一个任务在那个CPU上运行(使用taskset)之前,不会在那个CPU上调度任何任务。将一个CPU移出调度器域后,您可以借助将中断附加到该隔离CPU的技术,您可以通过/proc/irq/IRQ_NUMBER/smp_affinity完成此操作。现在,所有的中断都将由这个隔离的CPU处理,并且100%专用于该中断。使用您自己的IRQ例程,您可以完全控制中断处理。
希望这能有所帮助!
发布于 2016-08-13 16:33:39
你有没有想过用FIQ来解决这个问题。我们有一篇关于它的博客文章:http://free-electrons.com/blog/fiq-handlers-in-the-arm-linux-kernel/
https://stackoverflow.com/questions/38927607
复制相似问题