Forum: Mikrocontroller und Digitale Elektronik Globale Variable und ISR


von Philipp S. (phischl)


Angehängte Dateien:

Lesenswert?

Hallo!

Ich habe ein Problem mit einer globalen Variable und der Timer ISR.
Es werden per Funk 3 Bytes Empfangen.
"BA1" "000" ... "BA2" "000" ... "BA1" "000" ...
Wenn nun ein "BAx" kommt, soll eine Gewisse Zeit lang Led1 bzw Led2 
leuchten.
Das ganze funktioniert auch so wies soll, wenn die if-Abfragen in der 
ISR sind. Verschiebe ich jedoch die Abfragen in die while(1)-Schleife, 
leuchten beide Leds dauerhaft. Sieht so aus, als ob es was mit der 
Zähl-Variable zu tun hat. Ich hab hier im Forum schon gesucht, hab 
volatile hinzugefügt, funktioniert aber leider immer noch nicht.
Programmiert wird ein ATmega8 in C. Getestet wird mit einer realen 
Hardware.
Code ist im Anhang.

Danke für eure Hilfe.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Alle globalen Variablen, auf die Du in der ISR und woanders zugreifst, 
müssen volatile deklariert werden.

Bei "counter_light" hast Du dieses gemacht, warum nicht auch bei "name"?

von Bronco (Gast)


Lesenswert?

Frank M. schrieb:
> Verschiebe ich jedoch die Abfragen in die while(1)-Schleife,
> leuchten beide Leds dauerhaft.

Und wie erzeugst Du in diesem Fall das Zeitverhalten?
Zeig doch mal den Code, der nicht tut!

von Philipp S. (phischl)


Lesenswert?

Ziel ist eigentlich, dass die if(counter_light <= DAUER_LICHT)-Abfrage 
in die while(1)-Schleife kommt. In die ISR sollte doch möglichst wenig 
Code oder?
Verschiebe ich die Abfrage ins Hauptprogramm, greife ich in der ISR nur 
mehr auf counter_light, und auf name nur mehr im Hauptprogramm zu.
Jedoch leuchten dann beide Leds dauerhaft.
Ich werd aber auch name volatile deklarieren und schauen obs was bringt.

von Philipp S. (phischl)


Angehängte Dateien:

Lesenswert?

Im Anhang ist jetzt das Programm, wo beide Leds nach einem empfangenen 
"BA1" und "BA2" dauerhaft leuchten.

von Karl H. (kbuchegg)


Lesenswert?

Philipp S. schrieb:
> Ziel ist eigentlich, dass die if(counter_light <= DAUER_LICHT)-Abfrage
> in die while(1)-Schleife kommt. In die ISR sollte doch möglichst wenig
> Code oder?

Jain.
Es ist aber auch nicht so, dass man in einer ISR gar nichts tun darf und 
ausnahmslos alles und jedes in die Hauptschleife verschieben muss.

In einer ISR sollte man sich hüten vor
* Ausgaben aller Art auf LCD und UART
* längere Berechnungen

ein paar Variablen zu erhöhen, gegen eine Grenze zu testen und daraufhin 
ein paar Portpins zu schalten (die nur in der ISR benutzt werden), ist 
schon ok. Das ist beileibe noch nicht 'zuviel'.

Ganz im Gegenteil hat man dadurch die Gewissheit, dass genau diese 
Funktionalität auch dann weiterläuft, wenn in der Hauptschleife viel zu 
tun ist, weil zb der Benutzer in einem Menü rumfuhrwerkt. Es ist 
schliesslich nicht wirklich einzusehen, warum ein Lauflicht plötzlich 
aufhören soll zu laufen, nur weil der Benutzer gerade an einem Parameter 
für die UART rumstellt und die Hauptschleife keine Zeit hat sich um das 
Lauflicht zu kümmern. Legt man aber das Weiterschalten des Lauflichts 
(das ja nicht viel Zeit kostet) in die ISR, dann läuft das dann auch in 
diesen Fällen noch sauber weiter. Dei Abschaltung einer Lampe nach 10 
Sekunden muss auch dann zuverlässig nach 10 Sekunden erfolgen, wenn der 
Benutzer gerade am Einstellmenü für die Helligkeit der LCD Beleuchtung 
rumspielt.

Also: Es stimmt schon, dass man ISR nicht zu lang machen soll. Aber man 
muss das auch nicht als Dogma sehen, dass man gar nichts tun darf.

von Gebhard R. (Firma: Raich Gerätebau & Entwicklung) (geb)


Lesenswert?

name == 1 != name == '1'

Grüsse

von Karl H. (kbuchegg)


Lesenswert?

Tja.

Wenn
    rf01_rxdata(data, 3);
darauf wartet, dass 3 Bytes empfangen werden, dann wird der restliche 
Code danach solange nicht ausgeführt, BIS dann irgendwann 3 Bytes 
empfangen werden.


Dein Code ist ein klassisches Beispiel dafür, was man besser mit einer 
ISR abhandelt. In der Hauptschleife wird etwas eingeschaltet, ein 
entsprechender Timer-Mechanmismus wird installiert, der nach einer 
gewissen Zeit selbsttätig und ohne Zutun der Hauptschleife die 
Abschaltung vornimmt.

von Philipp S. (phischl)


Lesenswert?

oh. Soweit hab ich nicht gedacht, dass der Fehler beim Daten empfangen 
liegt. Vielen Dank Karl Heinz.
Werd die Abfragen nun in der ISR lassen.

von Karl H. (kbuchegg)


Lesenswert?

Und PS.
AM einfachsten ist es, wenn du für jede LED so etwas wie eine 
Countdown-Uhr vorsiehst (eine Eieruhr)
D.h. in der Hauptschleife wird die Uhr auf 10 Sekunden gestellt und in 
der ISR wird jede Sekunde der der LED zugeordnete Countdown um 1 
heruntergezählt. Hat der jeweilige Countdown den Wert 0 erreicht, dann 
schaltet man in der ISR den entsprechenden Ausgang aus.

So rum geht es einfacher. Vor allen Dingen auch deshalb, weil du in der 
Hauptschleife dann diesen Countdown auch ganz einfach wieder 
zurücksetzen kannst und weil du viele derartige Stufen einbauen kannst, 
die dann auch unabhängig voneinander eine Zeit abzählen. Warum soll LED 
1 nicht nach 20 Sekunden abschalten und irgendwann mitten drinnen 
(während LED 1 noch 13 Sekunden zu brennen hat) kommt der Befehl LED 2 
einzuschalten und nach 4 Sekunden wieder auszuschalten? Mit einer 
'Eieruhr' für jede LED ist das alles kein Thema. Sind nur ein paar 
Variablen. Mehr nicht
1
volatile uint8_t Timer1;
2
volatile uint8_t Timer2;
3
4
ISR( .... )
5
{
6
  if( Timer1 > 0 )
7
  {
8
    Timer1--;
9
    if( Timer1 == 0 )
10
      PORT.....  // LED 1 ausschalten
11
  }
12
13
  if( Timer2 > 0 )
14
  {
15
    Timer2--;
16
    if( Timer2 == 0 )
17
      PORT....  // LED 2 ausschalten
18
  }
19
}
20
21
int main()
22
{
23
   ...
24
25
  while( 1 )
26
  {
27
28
   ....
29
    if( irgendwas )
30
    {
31
      PORT....        // LED 1 einschalten
32
      Timer1 = ...    // und Timer1 auf einen Wert setzen, so dass die
33
                      // ISR die LED nach der gewünschten Zeit wieder
34
                      // ausschaltet
35
    }
36
37
    if( irgendwas_anderes )
38
    {
39
      PORT....        // LED 2 einschalten
40
      Timer2 = ...    // und Timer2 auf einen Wert setzen, so dass die
41
                      // ISR die LED nach der gewünschten Zeit wieder
42
                      // ausschaltet
43
    }
44
  }
45
}

Und PS: den Timer lässt du ganz einfach ständig durchlaufen und die 
Konfigurationsflags lässt du in in der Hauptschleife in Ruhe. Es stört 
nicht, wenn man einen bereits abgeschalteten Pin nochmals abschaltet.

von Philipp S. (phischl)


Lesenswert?

Danke für deinen Denkanstoß! Klingt einfacher so. Werd ich gleich 
ausprobieren. Danke!

von Falk B. (falk)


Lesenswert?

Siehe Interrupt

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.