Forum: Compiler & IDEs Wieso funktioniert dieser Code? (LED-Blinker)


von Markus H. (markus-94209-)


Lesenswert?

Hi Leutz,

bin grad dabei mich ein wenig mit C zu beschäftigen. Als erstes
"Projekt" hab ich einen Wechselblinker angegriffen. Also zwei Led's
die abwechselnd blinken. Der Code:
1
int main(void)
2
{int i, n = 40;
3
DDRB = 0b00001100;
4
PORTB = 0x00;
5
while (1)
6
{
7
PORTB ^= 0b00000100;       // EX-OR
8
for (i=0; i<n; i++){
9
  _delay_ms(25);
10
  }
11
PORTB ^= 0b00001000;       // EX-OR
12
}
13
}

Das funktioniert auch, nur warum? Beim zweiten EX-OR springt der doch
sofort nach der Anweisung zurück zur Zeile mit dem ersten EX-OR, oder?
Also müßte nach dem zweiten EX-OR gar keine Verzögerung mehr zu sehen
sein...?!?

Markus

von Joachim B. (joachimb)


Lesenswert?

Hallo Markus,

die beiden LEDs werden getrennt umgeschaltet. Wie Du richtig
feststellst, gibt es keine Pause nach dem 2. EX-OR.
Man könnte das zweite EX-OR streichen und stattdessen mit dem 1. EX-OR
beide LEDs gemeinsam umschalten:
PORTB ^= 0b00001100;

Gruß
Joachim

von Unbekannter (Gast)


Lesenswert?

Hihi, das ist ja mal ein lustiger "logischer" Fehler...

Wir nehmen mal an, wir sind in der Delay-Schleife. Dann passiert
folgendes:

  1.) Delay, lange Pause

  2.) PORTB ^= 0b00001000;   /* Die zweiteLED wird umgeschaltet, es
                                leuchten nun beide LEDs oder keine */

  3.) Ende der while(1)-Schleife und Rücksprung an den Anfang
      der while(1)-Schleife. Es ist nur sehr wenig Zeit vergangen.

  4.) PORTB ^= 0b00000100;  /* Die erste LED wird umgeschaltet, es
                               leuchtet wieder nur eine LED */

  5.) Delay-Schleife, lange Pause.

  6.) Wieder bei Schritt 2 weiter....


Der Fehler ist, dass die beiden Schreibzugriffe auf den Port nicht
untereinander stehen. Die Port-Zugriffe schalten die LED ja nicht an
oder aus, sondern schalten sie um.

Prinzipiell ist die Stelle, an der der Rücksprung der While-Schleife
erfolgt egal. Wichtig ist nur die Anordnung:

   - LED 1 umschalten
   - LED 2 umschlaten
   - Pause machen

Wo nun die Wiederholung der While-Schleife passiert ist egal.

Also, ein sehr "lustiger Fehler"...

von Μαtthias W. (matthias) Benutzerseite


Lesenswert?

Hi

wieso denn Fehler? Das Programm funktioniert einwandfrei als
Wechselblinker mit zwei LEDs. Eigentlich eine sehr elegante Lösung.

Matthias

von Markus H. (markus-94209-)


Lesenswert?

Also so richtig kann ich euch nicht folgen...

@Joachim: Dann wäre es ja kein Wechselblinker mehr. Die LED's würden
dann gleichzeitig an oder aus sein.

[ZITAT]
Prinzipiell ist die Stelle, an der der Rücksprung der While-Schleife
erfolgt egal. Wichtig ist nur die Anordnung:

   - LED 1 umschalten
   - LED 2 umschlaten
   - Pause machen
[/ZITAT]
Rein logisch würde das ja bedeuten LED1 würde sofort bei der zweiten
EX-OR Funktion wieder umgeschalten ohne Verzögerung, oder?!?
Oder wird einem PORTB ^= 0b00001000 nur daß gesetzte vierte Bit
"getoogled"???

von Μαtthias W. (matthias) Benutzerseite


Lesenswert?

Hi

genau so ist das. PORTB ^= 0b00001000 togglet das Bit3, PORTB ^=
0b00000100 das Bit 2. Damit sollte dann die Funktion des
Wechselblinkers klar sein. Wichtig ist natürlich der Ausgangszustand
von PortB. Sind die zwei Bits nicht gleich vor eintritt in die Schleife
ist es kein Wechselblinker mehr.

Matthias

von Markus H. (markus-94209-)


Lesenswert?

@Matthias:
Ja, so, aah!

Jetzt hab ich's gefressen, danke!

Gruß, Markus

von Olaf Stieleke (Gast)


Lesenswert?

Ganz am Anfang steht die Zeile DDRB=0x00001100. Im ersten Moment sind
also beide LED eingeschaltet. Einige Mikrosekunden später folgt in der
Schleife das erste XOR, welches eine der LED wieder ausknipst - es
leuchtet nur eine LED.

Es folgt die Verzögerung, dann das zweite XOR. Dieses knipst die zweite
LED aus - beide sind also dunkel. Nur wenige Mikrosekunden später
erreichen wir durch die Schleife wieder das erste XOR, das die erste
LED wieder anknipst, wieder leuchtet nur eine LED.

Erneut die Verzögerung, dann das zweite XOR, beide LED leuchten - aber
nur ein paar µs, denn wir erreichen wieder das erste XOR, das LED
Nummer 1 wieder ausschaltet.

Ein wirklicher Wechselblinker ist das nicht, schließlich sind die LED
manchmal beide an oder aus. Die Zeiträume, in denen das der Fall ist,
sind aber so kurz, das man das optisch nicht mehr wahrnimmt.

Eine elegante und pfiffige Lösung, finde ich - aber nicht wirklich
perfekt. Ich schlage vor, das ganze ein wenig anders zu formulieren:
1
DDRB=0x00001000;
2
3
while(1)
4
{
5
   PORTB ^= 0b00001100;
6
   for (i=0; i<n; i++) _delay_ms(25);
7
}

von Olaf Stieleke (Gast)


Lesenswert?

...und irgendwann lerne ich das mit dem Code-Tag auch noch :-)

von Olaf Stieleke (Gast)


Lesenswert?

Kleiner Fehler: DDRB bleibt natürlich 0x00001100. Man fügt eine Zeile
dahinter ein: PORTB=0x00001000;.

Sorry dafür...

von Markus H. (markus-94209-)


Lesenswert?

@Olaf:
Danke nochmal für deine Ausführungen, jetzt hab ich's auf alle Fälle
gefressen. Deine Lösung ist, denke ich, programmiertechnisch die beste.
Das äußert sich für mich daran, das sie sofort durchschaubar ist wie sie
funktioniert.

Gruß, Markus

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.