Ich habe die externen Interrupts EXTI1 und EXTI0 für zwei Signale M und
S so definiert, dass beide IR auf jeder Flanke feuern. EXTI1 hat eine
höhere Prio als EXTI0.
M ist so etwas wie der Master von S, indem der EXTI1-Handler zu
bestimmten Zeitpunkten EXTI0 aktiviert oder deaktiviert. Der
EXTI0-Handler hat keine Kontrollfunktion.
1
voidEXTI1_IRQHandler(void)// M, high prio
2
{
3
__HAL_GPIO_EXTI_CLEAR_IT(GPIO_PIN_1);
4
5
TRIG_M_ON;
6
if(M){
7
NVIC_EnableIRQ(EXTI0_IRQn);
8
do_work_3();
9
}else{
10
NVIC_DisableIRQ(EXTI0_IRQn);
11
}
12
TRIG_M_OFF;
13
}
14
15
voidEXTI0_IRQHandler(void)// S, low prio
16
{
17
__HAL_GPIO_EXTI_CLEAR_IT(GPIO_PIN_0);
18
19
TRIG_S_ON;
20
if(S)
21
do_work_1();
22
else
23
do_work_2();
24
TRIG_S_OFF;
25
}
Die Makros TRIG_* schalten zwei weitere Signale high or low, um die
Ausführung der Handler zu verbildlichen (hS, hM).
Wie man nun im angehängten Signalverlauf sieht, wird an (1) EXTI0 vom
EXTI1-Handler aktiviert. EXTI1 soll dann feuern, wenn Signal S wechselt,
tatsächlich sieht man aber zwei Durchläufe (2) und (3) des
EXTI0-Handlers. (2) befindet sich sogar noch vor dem Wechsel von S, als
ob dieser Lauf an (4) getriggert und bis (2) pending gewesen wäre - aber
er war ja deaktiviert. (5) deaktiviert EXTI0 wieder.
Woher stammt dieser zweite Aufruf (2) des EXTI0-Handlers? Ist mein
Aufruf zum Deaktivieren des EXTI0-IRs falsch?
Kontrolliere mal ob deine Initialisierung einen Schreibzugriff auf das
EXTI->PR Register macht.
Ich hatte beim STM32F3 festgestellt, dass mal dieses Regsiter während
der Initialisierung beschrieben muss, um den ersten falschen Interrupt
zu verhindern.
EXTI ist ein Sammel-Interrupt, und das Erste, was man im IRQ-Handler tun
sollte, ist festzustellen, woher der IRQ kommt, bzw. was der eigentliche
Auslöser war.
Davon ist in deinem Code aber nichts zu sehen.
Harry L. schrieb:> EXTI ist ein Sammel-Interrupt, und das Erste, was man im IRQ-Handler tun> sollte, ist festzustellen, woher der IRQ kommt, bzw. was der eigentliche> Auslöser war.> Davon ist in deinem Code aber nichts zu sehen.
Es gibt nur eine Quelle, also ist auch keine Prüfung nötig.
Stefanus F. schrieb:> Kontrolliere mal ob deine Initialisierung einen Schreibzugriff auf das> EXTI->PR Register macht.
Also immer
1
NVIC_EnableIRQ(EXTI0_IRQn);
2
NVIC_ClearPendingIRQ(EXTI0_IRQn);
schreiben?
Ich war mir da auch nicht sicher, ob das Aktivieren des IR diesen schon
feuert.
Ich habe es eingebaut, aber zum Überprüfen muss ich erst wieder die
richtige Stelle im Signal-Mitschnitt finden ...
Das EXTI-PR Register befindet sich außerhalb des NVIC, ich wäre das her
überrascht, wenn die Funktion NVIC_ClearPendingIRQ() hier die richtige
wäre.
Beim STM32F303 tut sie es jedenfalls nicht:
1
/**
2
\brief Clear Pending Interrupt
3
\details Clears the pending bit of an external interrupt.
4
\param [in] IRQn External interrupt number. Value cannot be negative.
Stefanus F. schrieb:> Das EXTI-PR Register befindet sich außerhalb des NVIC, ich wäre das her> überrascht, wenn die Funktion NVIC_ClearPendingIRQ() hier die richtige> wäre.
Achso, ich hatte als Beschreibung "pending" gelesen und dachte an das
Pending Bit.
Dann aber:
1
NVIC_EnableIRQ(EXTI0_IRQn);
2
EXTI->PR=1;// This bit is cleared by programming it to ‘1’.
lars schrieb:> Leider feuert EXTI0 auch damit noch doppelt.
Jedesmal oder nur bei jeder steigenden Flanke oder nur bei jeder
fallenden Flanke oder nur bei der ersten Flanke?
Stefanus F. schrieb:> lars schrieb:>> Leider feuert EXTI0 auch damit noch doppelt.>> Jedesmal oder nur bei jeder steigenden Flanke oder nur bei jeder> fallenden Flanke oder nur bei der ersten Flanke?
Jedesmal, aber wenn ich wüsste, bei welcher Flanke, wäre ich schon einen
großen Schritt weiter.
Eigentlich hätte ich auf steigende Flanke getippt (siehe Capture), aber
wenn ich EXTI0 auf nur fallende Flanke umstelle, tritt der Fehler
trotzdem auf. (Wenn ich auf nur steigende Flanke umstelle, verschwindet
der richtige Aufruf, und der falsche bleibt.)
Eigentlich war Deine Idee mit EXTI->PR nicht schlecht. Vielleicht sind
diese fehlerhaften Handler einfach nur lange pending (also noch vor 5),
weil der EXTI0-IR zu spät deaktiviert wird.
Ich muß mir das nochmal anschauen.
lars schrieb:> Werde den Effekt überprüfen.
Anderer Gedanke:
Bist du sicher dass
do_work_1();
do_work_2();
oder auch
do_work_3();
nicht zuviel Zeit brauchen?
Wenn Extis "prellen" kommen die Interrupts vielleicht zu
schnell daher?
Solche "verdächtige" Funktionen/Funktionalitäten sollten
möglichst ausserhalb einer ISR ausgeführt werden.
mitlesa schrieb:> Bist du sicher dass>> do_work_1();> do_work_2();> do_work_3();>> nicht zuviel Zeit brauchen?
Tatsächlich habe ich sehr viel Zeit damit verbracht, damit genau das
nicht passiert. Ich würde sagen, daran liegt es nicht, man würde es ja
auch auf dem Capture sehen.
Der Interrupt-Pfad besteht aus der Hardware, dem NVIC und dem Prozessor.
Ein Interrupt kann sowohl in der Hardware (also dem GPIO-Block), als
auch im NVIC als "pending" markiert sein. Du musst also, bevor du den
Interrupt aktivierst, erst in der Hardware und danach im NVIC die
Pending-Bits löschen. Hältst du die Reihenfolge nicht ein, kommt der
Interrupt trotzdem durch.
S. R. schrieb:> Der Interrupt-Pfad besteht aus der Hardware, dem NVIC und dem Prozessor.
Ich glaube, zwischen Hardware und dem NVIC fehlt in dieser Aufzählung
noch der Extended/External Interrupt Controller (EXTI). Vielleicht
meinst du das Ding mit "Hardware".
Ich hab mich im Detail nur mit den SAM3X befasst, dort gilt:
PIO -> NVIC -> Cortex-M3
Also ja, ich meinte wahrscheinlich den.
Die STM32 hab ich 2015 zum letzten Mal angeschaut, aber nicht so tief.
Stefanus F. schrieb:> Ich glaube, zwischen Hardware und dem NVIC fehlt in dieser Aufzählung> noch der Extended/External Interrupt Controller (EXTI).
OK, aber
1
EXTI->PR=1;// nicht |= und nicht 0
2
NVIC_ClearPendingIRQ(EXTI0_IRQn);
3
NVIC_EnableIRQ(EXTI0_IRQn);
wäre dann schon komplett? Frage nur, weil es erstmal noch nicht
funktioniert hat.
lars schrieb:> OK, aber> EXTI->PR = 1; // nicht |= und nicht 0> NVIC_ClearPendingIRQ(EXTI0_IRQn);> NVIC_EnableIRQ(EXTI0_IRQn);>> wäre dann schon komplett? Frage nur, weil es erstmal noch nicht> funktioniert hat.
Bei obigem Code kann IMHO ClearPending ausgeführt werden BEVOR die
Hardware das Pending Bit auch wirklich außen am NVIC gelöscht hat - das
macht ClearPending unwirksam.
Abhilfe: Auf die Hardware warten - das geht am Einfachsten mit einem
Lesezugriff:
Ach ja: Aus einem ganz ähnlichen Grund kann man zweimal in einen IQR
Handler laufen, wenn das Löschen des Flags in der Hardware unmittelbar
vor dem Rücksprung erfolgt.
Auch dann ist der Interrupt nämlich noch Pending, und der µC macht ein
Tail-Chaining in dieselbe Funktion.
Jim,
auf den von Dir beschriebenen Effekt bin ich mal in Zusammenhang mit der
RTC gestoßen. Die hat jedoch einen Pegel-Interrupt. Bist du sicher, dass
das Problem auch bei Flanken-Interrupts auftreten kann?
Jim M. schrieb:> Bei obigem Code kann IMHO ClearPending ausgeführt werden BEVOR die> Hardware das Pending Bit auch wirklich außen am NVIC gelöscht hat - das> macht ClearPending unwirksam.>> Abhilfe: Auf die Hardware warten - das geht am Einfachsten mit einem> Lesezugriff:
Alle Achtung, diese Änderung hat jetzt auch noch die allerletze falsche
Handlerausführung beseitigt. Darauf wäre ich jetzt nicht gekommen.
Herzlichen Dank an alle, Fall gelöst!
lars schrieb:> hat jetzt auch noch die allerletze falsche> Handlerausführung beseitigt
Wow! Das sollte man sich auf einen roten Zettel schreiben und an die
Wand hängen.
Stefanus F. schrieb:> Bist du sicher, dass> das Problem auch bei Flanken-Interrupts auftreten kann?
Oftmals wird der Flanken Trigger im Peripherial ausgewertet und am NVIC
intern liegt ein Level Signal an.
Jim M. schrieb:> lars schrieb:>> OK, aber>> EXTI->PR = 1; // nicht |= und nicht 0>> NVIC_ClearPendingIRQ(EXTI0_IRQn);>> NVIC_EnableIRQ(EXTI0_IRQn);>>>> wäre dann schon komplett? Frage nur, weil es erstmal noch nicht>> funktioniert hat.>> Bei obigem Code kann IMHO ClearPending ausgeführt werden BEVOR die> Hardware das Pending Bit auch wirklich außen am NVIC gelöscht hat - das> macht ClearPending unwirksam.>> Abhilfe: Auf die Hardware warten - das geht am Einfachsten mit einem> Lesezugriff:> EXTI->PR = 1; // nicht |= und nicht 0> (void) EXTI->PR; // dummy Lesezugriff> NVIC_ClearPendingIRQ(EXTI0_IRQn);> NVIC_EnableIRQ(EXTI0_IRQn);
vermutlich könnte die "sauberere" Lösung eine Barriere sein:
__asm volatile( "isb" );
http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dai0321a/BIHJGFEE.html
aber das grenzt jetzt eher an Haarspalterei. Kudos an Jim!