[STM32] Inaktiver Interrupt feuert?

Gast #5832796
Lesenswert?

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
void EXTI1_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
void EXTI0_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?
Angehängte Dateien:
Gast #5832882
Lesenswert?

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.
Gast #5832912
Lesenswert?

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.
Gast #5832921
Lesenswert?

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 ...
Gast #5832930
Lesenswert?

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.
5
 */
6
__STATIC_INLINE void NVIC_ClearPendingIRQ(IRQn_Type IRQn)
7
{
8
  NVIC->ICPR[(((uint32_t)(int32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL));
9
}
Gast #5832959
Lesenswert?

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’.

Werde den Effekt überprüfen.
Gast #5833130
Lesenswert?

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.
Gast #5833134
Lesenswert?

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.
Gast #5833138
Lesenswert?

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.
#5833200
Lesenswert?

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.
Gast #5834267
Lesenswert?

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.
#5835132
Lesenswert?

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:
1
EXTI->PR = 1;  // nicht |= und nicht 0
2
(void) EXTI->PR; // dummy Lesezugriff
3
NVIC_ClearPendingIRQ(EXTI0_IRQn);
4
NVIC_EnableIRQ(EXTI0_IRQn);
#5835139
Lesenswert?

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.
Gast #5835189
Lesenswert?

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?
Gast #5835374
Lesenswert?

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!
Gast #5835931
Lesenswert?

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!

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren