globale Variable mit einem assembler befehl auslesen

Gast #2837198
Lesenswert?

Hallo,

ich habe eine kurze Frage. Ich möchte gern eine globale Variabe in einer 
Funktion auslesen.
1
uint32_t getUsTime(void)
2
{   
3
    uint32_t tempOverflows;
4
    uint16_t tempCounter;
5
    
6
    tempCounter = TIM_GetCounter(TIM_US_TIMER);
7
    tempOverflows = publicUsTimerOverflows;
8

9
    return ( (tempOverflows * 0x0000FFFF) + (uint32_t)tempCounter);
10
}

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:
1
            getUsTime:
2
0x0800bbe8:   push {r7, lr}
3
0x0800bbea:   sub sp, #8
4
0x0800bbec:   add r7, sp, #0
5
131             tempCounter = TIM_GetCounter(TIM_US_TIMER);
6
0x0800bbee:   ldr r0, [pc, #40]       ; (0x800bc18 <getUsTime+48>)
7
0x0800bbf0:   bl 0x8000bcc <TIM_GetCounter>
8
0x0800bbf4:   mov r3, r0
9
0x0800bbf6:   strh r3, [r7, #6]
10
132             tempOverflows = publicUsTimerOverflows;
11
0x0800bbf8:   ldr r3, [pc, #32]       ; (0x800bc1c <getUsTime+52>)
12
0x0800bbfa:   ldr r3, [r3, #0]
13
0x0800bbfc:   str r3, [r7, #0]
14
134             return ( (tempOverflows * 0x0000FFFF) + (uint32_t)tempCounter);
15
0x0800bbfe:   ldr r2, [r7, #0]
16
0x0800bc00:   mov r3, r2
17
0x0800bc02:   mov.w r3, r3, lsl #16
18
0x0800bc06:   subs r2, r3, r2
19
0x0800bc08:   ldrh r3, [r7, #6]
20
0x0800bc0a:   adds r3, r2, r3
21
135         }

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

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

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

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

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

Ich habe die Funktion nach PeDa Vorbild angepasst:
1
uint32_t getUsTime(void)
2
{   
3
    uint32_t tempOverflows;
4
    uint16_t tempCounter;
5
    ITStatus waitingForIsr;
6
    
7
    tempCounter = TIM_GetCounter(TIM_US_TIMER);
8
    tempOverflows = publicUsTimerOverflows;
9
    waitingForIsr = TIM_GetITStatus(TIM_US_TIMER, TIM_IT_Update);
10

11
    if((waitingForIsr == SET) && !(0x8000 & tempCounter))
12
    {
13
        tempOverflows++;
14
    }
15

16
    return ( (tempOverflows * 0x0000FFFF) + (uint32_t)tempCounter);
17
}

Mir ist nur nicht ganz klar ob ich jetzt den Timeroverflow-Interrupt 
während der Auslesevorgänge deaktivieren muss.
#2837326
Lesenswert?

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

Ich hab den Interrupt wärend des auslesens der Daten deaktivert.
1
uint32_t getUsTime(void)
2
{   
3
    uint32_t tempOverflows;
4
    uint16_t tempCounter;
5
    ITStatus waitingForIsr;
6
    
7
    // Disable the Selected IRQ Channels -------------------------------------
8
    NVIC->ICER[TIM_US_TIMER_IRQn >> 0x05] = (uint32_t)0x01 << (TIM_US_TIMER_IRQn & (uint8_t)0x1F);
9

10
    tempCounter = TIM_GetCounter(TIM_US_TIMER);
11
    tempOverflows = publicUsTimerOverflows;
12
    waitingForIsr = TIM_GetITStatus(TIM_US_TIMER, TIM_IT_Update);
13

14
    // Enable the Selected IRQ Channels --------------------------------------
15
    NVIC->ISER[TIM_US_TIMER_IRQn >> 0x05] = (uint32_t)0x01 << (TIM_US_TIMER_IRQn & (uint8_t)0x1F);
16

17
    //Überprüfen der Daten auf konsistenz
18
    // Wenn Overflow dedektiert wurde aber ISR noch nicht ausgeführt wurde (Interrupt Status auf SET) und der
19
    // Überlauf vor dem Auslesen des Timerwerts war (höchstwertiges Bit des Timers ist 0),
20
    // muss dieser Überlauf auch noch mit berücksichtigt werden!
21
    if((waitingForIsr == SET) && !(0x8000 & tempCounter))
22
    {
23
        tempOverflows++;
24
    }
25

26
    return ( (tempOverflows * 0x0000FFFF) + (uint32_t)tempCounter);
27
}

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

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

@Peter:
Du hast recht! Danke für den Tipp und die Erklärung!

Hier meine neu Funktion:
1
uint32_t getUsTime(void)
2
{   
3
    uint32_t tempOverflows;
4
    uint16_t tempCounter;
5
    ITStatus waitingForIsr;
6
    
7
    //Interrupts global sperren
8
    __disable_irq();
9

10
    //Atomic Code section START
11
    tempCounter = TIM_GetCounter(TIM_US_TIMER);
12
    tempOverflows = publicUsTimerOverflows;
13
    waitingForIsr = TIM_GetITStatus(TIM_US_TIMER, TIM_IT_Update);
14
    //Atomic Code section STOP
15

16
    //Interrupts global freigeben
17
    __enable_irq();
18

19
    //Überprüfen der Daten auf konsistenz
20
    // Wenn Overflow dedektiert wurde aber ISR noch nicht ausgeführt wurde (Interrupt Status auf SET) und der
21
    // Überlauf vor dem Auslesen des Timerwerts war (höchstwertiges Bit des Timers ist 0),
22
    // muss dieser Überlauf auch noch mit berücksichtigt werden!
23
    if((waitingForIsr == SET) && !(0x8000 & tempCounter))
24
    {
25
        tempOverflows++;
26
    }
27

28
    return ( (tempOverflows * 0x00010000) + (uint32_t)tempCounter);
29
}
Gast #2838572
Lesenswert?

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
#2838786
Lesenswert?

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

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
#2838803
Lesenswert?

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.

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