Forum: Mikrocontroller und Digitale Elektronik STM32F4: Timerinterrupts nichtdeterministisch


von Niklas G. (erlkoenig) Benutzerseite


Angehängte Dateien:

Lesenswert?

Hallo Forum,

ich versuche derzeit den STM32F407VGT6, der auf dem STM32F4 Discovery 
ist, dazu bringen in regelmäßigen Zeitabständen etwas zu tun; zu 
Testzwecken zunächst den GPIO-Pin D14 mit LED dran togglen, sodass sie 
mit 1Hz blinkt.
Der Code im Anhang tut das auch. Die LED am Pin D12 blinkt synchron 
dazu.

Wenn ich nun das __WFI in Zeile 56 lösche, um zu simulieren dass die 
Mainloop etwas tut (LED an D12 quasi PWM-gedimmt), blinkt die LED an D14 
plötzlich unregelmäßig (so ala 3 interrupts kommen nach Wunsch, einer 
wird ausgelassen) bzw. nach einem seltsamen Muster, das definitiv keinem 
1Hz Rechteck entspricht. Ich habe noch einiges andres rumprobiert, 
selbst kleine änderungen an der Mainloop (mehr blinken) ändert die 
Signalform an D14 - wie ein chaotisches System...
( 
https://my.st.com/public/STe2ecommunities/mcu/Lists/cortex_mx_stm32/Flat.aspx?RootFolder=https%3a%2f%2fmy.st.com%2fpublic%2fSTe2ecommunities%2fmcu%2fLists%2fcortex_mx_stm32%2fTimer%20Interrupt%20frequency%20influenced%20by%20WFE&FolderCTID=0x01200200770978C69A1141439FE559EB459D7580009C4E14902C3CDE46A77F0FFD06506F5B&currentviews=11 
) gefragt, aber nur mit dem Ergebnis dass man das UIF Flag möglichst 
früh löschen soll - das hab ich zwar getan, bringt aber auch nicht 
wirklich was. Das ganze scheint mir irgendeine Form von Hardware-Bug zu 
sein. Gibt es irgendeine Dokumentation dazu, wie man einen Workaround 
baut, der sicher funktioniert? Ich kann ja nicht der einzige sein, der 
Timer-Interrupts verwenden will...!

Ich verwende die gcc-arm-embedded Toolchain ( 
https://launchpad.net/gcc-arm-embedded ), aber daran kann's eigentlich 
auch nicht liegen - der Assemblercode schaut sinnvoll aus.

Über Hilfe wäre ich sehr glücklich, so ein Timer ist doch relativ 
wichtig...

von Jim M. (turboj)


Lesenswert?

Das hier
1
 GPIOD->ODR ^= (1 << 12);

ist bei ARM nicht atomar. Wenn der Timer-Interrupt das ungünstig 
unterbricht, dann geht das im Interrupt getoggelte Bit "verloren".

Abhilfe wäre Bitbanding - hier wird der Read-Modify-Write nicht vom 
Interrupt unterbrochen -  oder BSRR.

von Jo D. (Firma: Jo) (discovery)


Lesenswert?

ersetze diese Zeile:
1
TIM10->SR = (uint16_t)~TIM_IT_Update;

Durch diese Zeile:
1
while( (TIM10->SR & (1<<0)) > 0 ) TIM10->SR &= ~(1<<0); // clear flag

wenn es dann immernoch nicht geht, kontrolliere ob die Code Optimization 
in deinem Compiler aktiv ist und aktiviere sie wenn nicht.

Dürfte das Problem beheben.

PS:

statt das so zu schreiben:
1
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
2
  GPIO_Init(GPIOD, &GPIO_InitStructure);
3
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;
4
  GPIO_Init(GPIOD, &GPIO_InitStructure);
5
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14;
6
  GPIO_Init(GPIOD, &GPIO_InitStructure);
7
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_15;
8
  GPIO_Init(GPIOD, &GPIO_InitStructure);

würde ich das so schreiben (übersichtlicher):
1
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12 | GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;
2
GPIO_Init(GPIOD, &GPIO_InitStructure);

Gruß
Jo

von Jim M. (turboj)


Lesenswert?

Jo discovery schrieb:
> Dürfte das Problem beheben.

Nein. Das Problem des OP ist das der Interrupt und main() auf demselben 
Port arbeiten und das nicht-atomar.

Mit _WFI tritt das Problem nur deswegen nicht auf, weil dann der 
Interrupt an genau dieser Stelle auslöst - und so nicht den kritischen 
Teil unterbrechen kann.

von Jo D. (Firma: Jo) (discovery)


Lesenswert?

Jim Meba schrieb:
> Jo discovery schrieb:
>> Dürfte das Problem beheben.
>
> Nein. Das Problem des OP ist das der Interrupt und main() auf demselben
> Port arbeiten und das nicht-atomar.
>
> Mit _WFI tritt das Problem nur deswegen nicht auf, weil dann der
> Interrupt an genau dieser Stelle auslöst - und so nicht den kritischen
> Teil unterbrechen kann.

Da hast du Recht. Allerdings kommen hier zwei unterschiedliche Probleme 
zusammen:

Zum einen könnte es sein, (das war meine Vermutung) dass das 
Interruptflag nach Verlassen der Interruptroutine noch aktiv ist und 
somit der Interrupt noch ein zweites Mal ausgeführt wird (bekanntes 
Problem, welches ich auch schonmal hatte) und zum zweiten das Problem 
das du genannt hast (Das ist sogar zu 100% ein Problem, da stimme ich 
dir zu).


Gruß
Jo

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Jim Meba schrieb:
> ist bei ARM nicht atomar. Wenn der Timer-Interrupt das ungünstig
> unterbricht, dann geht das im Interrupt getoggelte Bit "verloren".
Das wars tatsächlich... So ein Mist, das Programm hat die ganze Zeit 
funktioniert, nur die Anzeige war falsch :o

Jim Meba schrieb:
> Abhilfe wäre Bitbanding - hier wird der Read-Modify-Write nicht vom
> Interrupt unterbrochen -  oder BSRR.
Nicht ganz, leider - toggeln/xor geht damit nicht. mit __disable_irq und 
__enable_irq drumherum tuts allerdings...

Jo discovery schrieb:
> Durch diese Zeile:
> while( (TIM10->SR & (1<<0)) > 0 ) TIM10->SR &= ~(1<<0); // clear flag
SR = ~1; sollte schon reichen, die Flags ignorieren das Schreiben von 
"1". Die Schleife ist allerdings eine schlaue Idee...
> wenn es dann immernoch nicht geht, kontrolliere ob die Code Optimization
> in deinem Compiler aktiv ist und aktiviere sie wenn nicht.
Die bewirkt eher dass es nicht geht weil dann alles in komischer 
Reihenfolge ist ;-)

Jo discovery schrieb:
> Zum einen könnte es sein, (das war meine Vermutung) dass das
> Interruptflag nach Verlassen der Interruptroutine noch aktiv ist und
> somit der Interrupt noch ein zweites Mal ausgeführt wird (bekanntes
> Problem, welches ich auch schonmal hatte)
Das Problem hatte ich am Anfang, wir ja durch das Löschen des Flags 
gelöst.

Jo discovery schrieb:
> würde ich das so schreiben (übersichtlicher):
> GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12 | GPIO_Pin_13 | GPIO_Pin_14 | 
GPIO_Pin_15;
> GPIO_Init(GPIOD, &GPIO_InitStructure);
Oha, wusste nicht dass das geht...

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.