Ich möchte einen Open-Source-Core-Treiber für die Steuerung von Schrittmotoren in Linux schreiben. In diesem Fall speziell für 3D-Drucker.ARM A7 Linux-Roh-Interrupt-Handling möglich?
Die Grundidee ist, dass der Treiber Pins an einem IO-Port reserviert und dann diese Pins auf einmal manipuliert. Er empfängt einen Puffer voll mit "toggle this, toggle that" -Werten und gibt diese dann mit einem Hardware-Timer an den Port aus.
Jetzt ist die Frage: Gibt es eine Möglichkeit, einen bestimmten Hardware-Interrupt so schnell wie möglich zu behandeln?
Der betreffende Chip ist ein Allwinner H3, und ich verwende die TMR1-Ressource des besagten Chips (IRQ 51). Ich benutze kann es ganz gut, und es funktioniert als Interrupt auch:
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 sonst ungenutzt ist (in der Tat hatte ich es selbst hinzufügen) und so arbeitet weit. Es gibt jedoch eine gewisse Latenz bei der Handhabung der ziemlich einfachen IRQ-Routine. Da ich einen Code erzeugen möchte, der für einen 3D-Drucker verwendbar ist, mag ich einen "stabileren" Timer-Interrupt.
Meine Frage ist also: Gibt es eine Möglichkeit, eine sehr kurze IRQ-Routine in Linux zu haben, die die höchstmögliche Priorität hat? Oder kümmert es überhaupt nicht um den Linux-Scheduler und "macht es einfach"? Im Grunde ein roher IRQ-Handler, ignorierend, was Linux denkt, dass es sein sollte?
Der Kern, auf dem es läuft, ist sowieso genau dieser Aufgabe gewidmet. Der Handler wird so kurz wie möglich sein: hole ein u32 von einem Array, schreibe das an den Port, fertig.
Vorzugsweise möchte ich etwas haben, das einfach den Rest von Linux alle zusammen ignoriert. Ja, ich weiß, dass das nicht der richtige Weg ist. Aber das ist für einen speziellen Fall gedacht, daher habe ich keine Bedenken, die regulären Kernel-Quellen an diese Bedürfnisse anzupassen.
Oh, das erinnert mich, der Kernel ist 3.4.112 mit den passenden preempt-rt Patches.
Jede Hilfe wird sehr geschätzt.
Grüße,
Chris
Haben Sie sich den RTAI-Kernel angesehen? Thet Laufwerke wie Linux-Cnc und sollte leicht mit Ihren Timing-Anforderungen fertig werden. – tofro
Ich habe Referenzen auf den RTAI-Kernel gefunden, ja. Aber das ziemlich traurige Problem ist, dass das Zeug für Allwinner-Chips nur teilweise im Mainstream-Kernel ist. Th H3 ist dort nicht existent. Als solcher muss ich mich durch den alten Kernel 3.4.39, den ich bis 3.4.112 bekam, und dann bis zur Vorgängerversion davon packen. – ChrisK
Auch brauche ich keine anspruchsvolle RT-Funktionalität. Alles was ich will (wenn das überhaupt möglich ist) ist, dass ein IRQ im GIC auf dem Bare Metal behandelt wird. In diesem Fall IRQ 51 (für TMR1), mit wenig bis gar keiner Interaktion. – ChrisK