Jetzt habe ich aber gesehen das das Auslesen der globalen Variable
publicUsTimerOverflows nicht mit einem Assemberbefehl funktioniert.
Damit würde ich wahrscheinlich Probleme mit der ISR bekommen die die
globale Variable beschreibt. Die ISR deaktivieren möchte ich nicht.
Hier der vom Compiler erzeugte Code der Funktion:
Meine Frage ist nun ist es möglich den Wert der globalen Variable
publicUsTimerOverflows in die lokale Variable tempOverflows mit einem
Assablerbefehl zu kopieren und wie würde das aussehen.
PS: Mit Assemblerprogrammierung kenne ich mich leider nur sehr schlecht
aus und hoffe daher auf eure Hilfe.
obamoa schrieb:> Die ISR deaktivieren möchte ich nicht.
Dann möchtest Du auch nicht wirklich programmieren lernen.
Gleich mit unsauberen Tricks anfangen zu wollen, zerstört Deinen
Programmierstil nachhaltig.
Atomare Abschnitte gehören zum Programmieren einfach dazu.
Und je größer die Programme werden, umso mehr solcher Abschnitte sind
notwendig.
Peter
>möglich den Wert der globalen Variable>publicUsTimerOverflows in die lokale Variable tempOverflows mit einem>Assablerbefehl zu kopieren und wie würde das aussehen.
Das steht hier:
132 tempOverflows = publicUsTimerOverflows;
0x0800bbf8: ldr r3, [pc, #32] ; (0x800bc1c <getUsTime+52>)
0x0800bbfa: ldr r3, [r3, #0]
0x0800bbfc: str r3, [r7, #0]
Nach jedem Assemblerbefehl wird überprüft ob ein Interrupt aufgetretten
ist. Wird nun ein Interrupt zwischen den 3 Assemblerbefehlen der zum
auslesen der globalen Variablen erkannt wird dieser ausgefüht bevor die
Daten komplett ausgelesen konnten.
obamoa schrieb:> Ach ja das hab ich ganz vergessen, es handelt sich dabei um einen STM32> also ARM-Cortex M3.
Macht nix, das Prinzip bleibt das Gleiche.
Das Prinzip schon, die Praxis nicht. Der STM32 läd 16- und 32-Bit
Variablen in einem einzigen Befehl, der Zugriff ist hier also
automatisch atomar. Anders beim AVR.
Wenn Du einen Timer in SW erweiterst, hast Du ein ganz anderes Problem.
Der HW-Timer und SW-Timer werden zu unterschiedlichen Zeiten gezählt,
sie sind also nicht jederzeit gültig.
Dafür gibt es eine einfache Lösung:
Beitrag "AVR Timer mit 32 Bit"
Peter
>Nach jedem Assemblerbefehl wird überprüft ob ein Interrupt aufgetretten>ist. Wird nun ein Interrupt zwischen den 3 Assemblerbefehlen der zum>auslesen der globalen Variablen erkannt wird dieser ausgefüht bevor die>Daten komplett ausgelesen konnten.
...
>Meine Frage ist ja ob es möglich ist aus den drei Befehlen einen zu>machen und so die Probleme mit einer ISR zu vermeiden.
Nicht notwendig, da ist gar kein Prob.
Wie von PeDa bemerkt, steckt das Prob hier:
> tempCounter = TIM_GetCounter(TIM_US_TIMER);> tempOverflows = publicUsTimerOverflows;> return ( (tempOverflows * 0x0000FFFF) + (uint32_t)tempCounter);
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
die temp* könnten inkonsistent sein, wenn ein Interrupt dazwischenfunkt.
obamoa schrieb:> Mir ist nur nicht ganz klar ob ich jetzt den Timeroverflow-Interrupt> während der Auslesevorgänge deaktivieren muss.
Mir ist nicht klar, wavor du dich eigentlich fürchtest, wenn du die
Interrupts mal ganz kurz deaktivierst. Die Interrupts gehen ja deswegen
nicht verloren, sie werden einfach nur um ein bischen was später
ausgeführt. Viel wichtiger ist aber, dass du beim Auslesen der Variablen
einen konsistenten Zustand ausliest.
obamoa schrieb:> Mir ist nur nicht ganz klar ob ich jetzt den Timeroverflow-Interrupt> während der Auslesevorgänge deaktivieren muss.
Ja klar!
Das Lesen von HW-Timer, SW-Timer, Interruptflag muß zusammen ein
atomarer Block sein.
Peter
Ich hab die Funtktion auch gerade getestet. Funktioniert alles so weit
fehlerfrei.
Das mit den konsistenten Daten war mir vorher nicht bewusst.
@kbuchegg
Du hast natürlich recht. Ich hab mich da in was verrant.
Danke für eure Hilfe!!
obamoa schrieb:> return ( (tempOverflows * 0x0000FFFF) +>> Ich hab die Funtktion auch gerade getestet. Funktioniert alles so weit> fehlerfrei.
Nö.
Entweder << 16 oder * 0x10000.
Peter
Es ist keine gute Idee, nur den einen Interrupt zu sperren.
Es kann dadurch zu einer Prioritätsumkehr kommen, da ja ein niederer,
lange dauernder Interrupt nun unterbrechen darf.
D.h. die Sperre kann erheblich länger dauern, als erwartet.
Eine globale Interruptsperre sperrt dagegen definiert und nur die
kürzest mögliche Zeit.
Peter
Als ich noch Assembler gemacht habe, war üblich, sich den
Interruptstatus zu merken, dann zu disablen und am Ende den alten Status
wiederherzustellen,
Ich hab noch sowas im Kopf:
PUSH PSW
DI
.
.
POP PSW
MfG Klaus
Peter Dannegger schrieb:> Eine globale Interruptsperre sperrt dagegen definiert und nur die> kürzest mögliche Zeit.
Bei den Cortex-M kann man in solchen Fällen an Stelle einer totalen
Interrupt-Sperre die Interrupt-Priorität so setzen, dass nur höher
priorisierte Interrupts durchkommen. Sinngemäss:
saved_prio = BASEPRI;
BASEPRI = Prio_vom_Timer_Interrupt;
...
BASEPRI = saved_prio;
Perfekt wird es, wenn man sicherstellt, dass BASEPRI nicht versehentlich
reduziert wird, wenn verschachtelt aufgerufen.
A. K. schrieb:> Bei den Cortex-M sollte man in solchen Fällen an Stelle einer totalen> Interrupt-Sperre die Interrupt-Priorität so setzen, dass nur höher> priorisierte Interrupts durchkommen (=> BASEPRI).
Warum?
Popelige 3 Variablen atomar zu lesen geht bestimmt schneller, als das
ganze Interrupt-Prolog-Epilog-Brimborium. D.h. niemand merkt diese
kleine Verzögerung von ein paar Zyklen.
Es gibt also keinen Grund für eine unnötig komlizierte andere Lösung.
Vermutlich wird das Umschalten der Priorität sogar selber unter
Interruptsperre erfolgen müssen. Man spart also garnichts aber hat
zusätzlichen Code.
Peter
Peter Dannegger schrieb:> Popelige 3 Variablen atomar zu lesen geht bestimmt schneller,
Wenns nur das ist - in seinem letzten Code sind aber 2 Aufrufe aus der
STM32 Lib drin. Die sind mitunter deutlich aufwendiger implementiert.
Ausserdem ging es mir eher darum, das generelle Prinzip zu zeigen. Die
Sache mit BASEPRI scheint nicht allgemein bekannt zu sein.
> Vermutlich wird das Umschalten der Priorität sogar selber unter> Interruptsperre erfolgen müssen.
Ich wüsste nicht weshalb.
A. K. schrieb:> Perfekt wird es, wenn man sicherstellt, dass BASEPRI nicht versehentlich> reduziert wird, wenn verschachtelt aufgerufen.
=> BASEPRI_MAX. Wurde im CMSIS aber wohl vergessen.