Forum: Compiler & IDEs Funktion im Main und Interrupt nutzen?


von AVRli (Gast)


Lesenswert?

Hallo! :-)

Ich vermute das ich einen Ablauffehler habe durch die Nutzung eines 
Unterprogrammes im Interrupt und im Main Programm.

Ich arbeite mit AVR Studio/GGC und einem ATmega 644.

Die Funktion macht einen Vergleich von Variablen und gibt mit 1 oder 0 
zurück.

Nun nutze ich diese einmal im Interrupt und in einem bestimmten Zustand 
brauche ich den Vergleich auch im Main Programm.

Kann es passieren das da irgenwas aus dem Ruder läuft? Gleizeitiger 
Zugriff oder der gleichen?

Wenn ja, muß ich dieses Unterprogramm zur Sicherheit einmal extra für 
das Main und einmal für das Interrupt Programm schreiben oder gibt es 
einen anderen Weg?


Vielen Dank für Eure Hilfe.

Gruß AVRli...

von Falk B. (falk)


Lesenswert?

@  AVRli (Gast)

>Nun nutze ich diese einmal im Interrupt und in einem bestimmten Zustand
>brauche ich den Vergleich auch im Main Programm.

>Kann es passieren das da irgenwas aus dem Ruder läuft? Gleizeitiger
>Zugriff oder der gleichen?

Ja, siehe Interrupt, atomarer Zugriff.

>Wenn ja, muß ich dieses Unterprogramm zur Sicherheit einmal extra für
>das Main und einmal für das Interrupt Programm schreiben oder gibt es
>einen anderen Weg?

Man kann die Interrupts kurzzeitig sperren.

MFG
Falk

von Oliver (Gast)


Lesenswert?

>Kann es passieren das da irgenwas aus dem Ruder läuft? Gleizeitiger
>Zugriff oder der gleichen?

Ja, siehe ausserdem Reentranz.

http://de.wikipedia.org/wiki/Eintrittsinvarianz

Wenn du z.B. statische Variablen in der Funktion nutzt, geht es schief, 
ebenso bei vielen library-Funktionen.

>Die Funktion macht einen Vergleich von Variablen und gibt mit 1 oder 0
>zurück.

Das klingt jetzt nicht so kompliziert, als daß sich das, falls 
erforderlich, nicht auch zweimal implementieren ließe.

Oliver

von AVRli (Gast)


Lesenswert?

Hallo und danke für Eure Antworten,

ich werde den Vergleich zweimal schreiben. Einmal für den INT und einmal 
für das MAIN Programm. Den Platz habe ich schon (nehme ich mir eben)...

Was mich aber viel mehr erschrecken ließ ist folgenes:

Ich habe ebend mir den Abschnitt Interrupt im Beitrag GGC 
AVR-GCC-Tutorial durchgelesen. Da stolperte ich über die Aussage das man 
Variablen als volatile deklarieren muß um diese gleichzeitig im INT als 
auch im MAIN Programm zu nutzen. Ok das habe ich auch immer gemacht! Nur 
funktioniert es nach dem was da steht nicht bei Werten die "breiter" als 
8 bit sind. Also ein 16 bit Zähler oder einen Struct Variable???

Ohh man da muß ich wohl mein ganzen Programm nochmal durchgehen. :-( Ich 
habe schon einige 16bit Zähler die im INT und im MAIN Programm benutzt 
werden.

Ist mir bis jetzt auch nie aufgefallen das es zu Problemen führt.
Wie handelt man das nun... :-(

Gruß AVRli...

von Peter D. (peda)


Lesenswert?

AVRli schrieb:
> Die Funktion macht einen Vergleich von Variablen und gibt mit 1 oder 0
> zurück.

Schreib doch einfach mal die Funktion hin, dann kann man leicht sehen, 
obs Probleme geben könnte.

Ein Quelltext sagt mehr als 1000 Worte !!!

Warum die Leute immer ihr Problem erstmal umständlich umschreiben 
müssen, statt einfach nur copy&paste.


Peter

von Peter D. (peda)


Lesenswert?

AVRli schrieb:
> Ist mir bis jetzt auch nie aufgefallen das es zu Problemen führt.
> Wie handelt man das nun... :-(

Benutze mal den neuesten WINAVR, da gibt es ne hübsche "atomic.h", das 
ist genau die richtige Medizin für alle diese Probleme.

Aber drauf achten, daß man nicht versehentlich langsame 
Funktionsaufrufe, Divisionen, float o.ä. in das atomic mit einpackt, 
also wirklich nur die Parameterübergabe.


Peter

von AVRli (Gast)


Lesenswert?

Dann mache ich es mal ganz ordentlich... ;-)

als erstes meine Funktion mit dem Vergleich...
1
volatile uint16_t clk_year_long;    // Int/Main use
2
3
...
4
5
char not_leap(void)
6
{
7
    if (!(clk_year_long%100))
8
        return (char)(clk_year_long%400);
9
    else
10
        return (char)(clk_year_long%4);
11
}         
12
13
...
14
15
//--- Interrupt Aufruf ---
16
ISR(TIMER2_OVF_vect) {
17
...
18
   if((clk.mon==2) && (not_leap())) {
19
      ...
20
   }
21
...
22
}
23
24
...
25
26
//--- Main Aufruf ---
27
...
28
   if (not_leap()) {
29
      ...
30
   }
31
...


Die Abfrage ist die, die man zweimal schreiben könnte wenn man es dann 
muß.
Und nun noch ein Beiepiel wo ich einen 16bit Zähler verwende...

1
volatile  uint8_t buf_uart_tx[512];    // UART TX-Buffer
2
volatile uint16_t pos_in_uart_tx;    // UART TX-Buffer Position bis wo Zeichen eingefügt wurden
3
volatile uint16_t pos_out_uart_tx;    // UART TX-Buffer Position bis wo Zeichen gesendet wurden
4
5
//--- ISR - UART Dataregister EMPTY
6
ISR(USART0_UDRE_vect)
7
{
8
  if (pos_out_uart_tx == pos_in_uart_tx) {
9
    CLEAR_BIT(UCSR0B,UDRIE0);
10
  }
11
  else {
12
    UDR0 = buf_uart_tx[pos_out_uart_tx];
13
    pos_out_uart_tx ++;
14
    pos_out_uart_tx = pos_out_uart_tx & 0x1FF;
15
  }
16
}
17
18
...
19
20
void tx_char_uart(char chrtx) {
21
  buf_uart_tx[pos_in_uart_tx] = chrtx;
22
  pos_in_uart_tx++;
23
  pos_in_uart_tx = pos_in_uart_tx & 0x1FF;
24
  SET_BIT(UCSR0B,UDRIE0);
25
}


Mit tx_char_uart() wird das Zeichen in den Buffer übertragen.
Nun bleibt zu hoffen das man versteht was ich da mache...

Gruß AVRli...

von Stefan E. (sternst)


Lesenswert?

> Die Abfrage ist die, die man zweimal schreiben könnte wenn man es dann
> muß.

Das bringt aber nicht wirklich viel, denn auch bei zwei Funktionen muss 
der Zugriff auf clk_year_long geschützt werden. Der einzige Vorteil ist, 
dass dieser Schutz dann nur in einer Funktion nötig ist (in der, die von 
main aufgerufen wird), und der Interrupt dadurch geringfügig schneller 
ist. Ich würde nur eine Funktion machen, die dann aber inlinen 
(insbesondere im Interrupt).

> char not_leap(void)

"char" solltest du nur nehmen, wenn es tatsächlich um Zeichen geht. 
Ansonsten "unsigned char" oder "signed char". Oder warum nicht uint8_t? 
uint16_t benutzt du doch auch.

von Stefan E. (sternst)


Lesenswert?

Mein Vorschlag für die Funktion:
1
#include <util/atomic.h>
2
...
3
uint8_t not_leap (void) {
4
5
    uint16_t year;
6
7
    ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
8
        year = clk_year_long;
9
    }
10
11
    if (!(year % 100))
12
        return year % 400;
13
    else
14
        return year % 4;
15
}

von Ahem (Gast)


Lesenswert?

Äh, ich frage mich ob eine Berechnung bezüglich eines Jahres nun 
wirklich in einem Interrupt geschehen muss. Die Jahreszahl ändert sich 
sooooo langsam. Das kann man entweder vorher, so zur Teezeit am 31.12. 
berechnen oder irgendwann am 1.1. wenn der Kater verflogen ist.
Das alles scheint mir eher auf einen Designfehler hinzudeuten, wenn es 
auch als Lehrstück bzg. volatile usw. hiflreich sein kann.

von Oliver (Gast)


Lesenswert?

> Die Abfrage ist die, die man zweimal schreiben könnte wenn man es dann
> muß.

Muß man nicht. Probleme gäbe es, wenn die Funktion interne Zustände 
speichern würde, die dann bei "parallelem" Aufruf überschrieben würden. 
Das ist bei den paar Zeilen aber nicht der Fall.

Oliver

von Simon K. (simon) Benutzerseite


Lesenswert?

Was spricht dagegen einen einfachen 32 Bit Timestamp im Timerinterrupt 
hochzuzählen, statt so ne "Orgie" zu veranstalten? Interruptroutinen 
sollten so klein wie möglich sein.

von AVRli (Gast)


Lesenswert?

Danke für Eure Antworten!

Kann jemand noch ein Statement zu meiner 16bit Zähler Nutzung geben?
Ich habe auch meine Literatur nochmal durchgeblättert. (C Buch für AVR 
controller), da werden 16bit Zähler auch im Main und Interrupt genutzt 
und werden auch nur mit volatile "geschützt".

Hmm ich glaube ja bald das ich noch nicht ganz begriffen habe wann es zu 
Problemen kommen kann und wann nicht.

Wo kann man sich über die atomic funktion informieren?

Gruß AVRli...

von Falk B. (falk)


Lesenswert?

@  AVRli (Gast)

>Kann jemand noch ein Statement zu meiner 16bit Zähler Nutzung geben?

siehe Interrupt.

>Ich habe auch meine Literatur nochmal durchgeblättert. (C Buch für AVR
>controller), da werden 16bit Zähler auch im Main und Interrupt genutzt
>und werden auch nur mit volatile "geschützt".

Ist unter BESTIMMTEN Umständen möglich, z.B. im Zusammenhang mit dem 
Sleep Mode, kann aber auch schief gehen.

>Wo kann man sich über die atomic funktion informieren?

In der Doku der libc, ist irgendwo im WinAVR Verzeichnis is drinnen.

MfG
Falk

von AVRli (Gast)


Lesenswert?

Hallo,

na dann werde ich mal meine ganzen Programme durchforsten wo ich 16 bit 
Zähler im Int und Main verwende.

Ziel ist es dann immer die Int zu sperren wenn man auf die Zähler im 
Main Programm zugreift.

Danke und Gruß AVRli...

von STK500-Besitzer (Gast)


Lesenswert?

>Ziel ist es dann immer die Int zu sperren wenn man auf die Zähler im
>Main Programm zugreift.

Vorher ein CLI und hinterher ein SEI...
So behandelt man doch immer Variablen, die "atomic" behandelt werden 
müssen.

von Simon K. (simon) Benutzerseite


Lesenswert?

STK500-Besitzer schrieb:
>>Ziel ist es dann immer die Int zu sperren wenn man auf die Zähler im
>>Main Programm zugreift.
>
> Vorher ein CLI und hinterher ein SEI...
> So behandelt man doch immer Variablen, die "atomic" behandelt werden
> müssen.
1
#include <util/atomic.h>
2
3
ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
4
{
5
    ....
6
}

gibts auch noch.

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.