Forum: Mikrocontroller und Digitale Elektronik ATmega8 Blinkende LEDs mit Taster ein und aus schalten


von Digit-22 N. (digit-22)


Lesenswert?

Moin

ich befasse mich sein ca 2 Wochen mit Microcontroller und habe mir nach 
Anleitung hir im Board den ATmega8 zugelegt. Nach den ersten start 
Schwirigkeiten habe ich es geschafft den kleinen zu Programmieren...
Jetzt habe ich folgendes Problem:
Ich habe 4 Ports (PORTB2 - PORTB5) als Ausgänge und
2 Ports (PORTC0 und PORTC1) als Eingänge festgelegt.
An den Ausgänge habe ich 4 LEDs angeschlossen die nacheinander
(wenn PORC0 Taster gedrückt wird) Blinken.
Jetzt Wolte ich mit einem 2. Taster (PORTC1) die LEDs wieder 
ausschalten.
Das Problem ist wenn ich mit Taster0 die LEDs einschalte fallen die in 
eine While(2) und da krige ich sie auch nicht mehr raus. Ich hab schon 
alles was mir so eingefallen ist ausprobiert.
Hier ist mein Code.

Würde mich über Tipps freuen.


LG
Shabi



#define F_CPU 4000000UL

#include <avr/io.h>
#include <util/delay.h>
#include <stdio.h>
#include <inttypes.h>

int main(void)
{

   DDRB = (1<<PB5) | (1<<PB4) | (1<<PB3) | (1<<PB2);
   DDRC = (0<<PC0) | (0<<PC1);

  while(1)
  {
    if (!(PINC & (1<<PINC0)))
    {
      while(2)
      {
        PORTB^=(1<<PB5);

        _delay_ms(200);

        PORTB^=(1<<PB4);

        _delay_ms(200);

        PORTB^=(1<<PB3);

        _delay_ms(200);

        PORTB^=(1<<PB2);

        _delay_ms(200);
      }
    }
    else if (!(PINC & (1<<PINC1)))
    {
      PORTB = (1<<PB5) | (1<<PB4) | (1<<PB3) | (1<<PB2);
    }
  }
}

von Christian E. (cerker)


Lesenswert?

Ich servier dir keine Lösung, sonst lernst du nichts, aber ich führ dich 
mal auf den Weg:

Wieso "while(2)" .. weil das nach "while(1)" kommt? .. mach dich mal 
schlau was in den Klammern der while-Schleife steht. Nein, es ist keine 
laufende Nummer! Tipp: Schau mal 2 Zeilen weiter oben...

Weiterhin, wie kannst du erreichen, dass das Programm sich merkt das du 
einen Taster gedrückt hast, obwohl du ihn schon wieder losgelassen hast 
(das hast du bereits einmal implizit realisiert).

Wenn du das hast, weisst du auch wie du dein Problem löst. Das die 
untere Abfrage niemals mehr erreicht wird im aktuellen Code sobald die 
Blinkschleife läuft, hast du korrekt erkannt.

Gruß,
Christian

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Wenn Du folgendes schreibst:
1
  while(1)
2
  {
3
    if (!(PINC & (1<<PINC0)))
4
    {
5
        PORTB^=(1<<PB5);
6
        _delay_ms(200);
7
        PORTB^=(1<<PB4);
8
        _delay_ms(200);
9
        PORTB^=(1<<PB3);
10
        _delay_ms(200);
11
        PORTB^=(1<<PB2);
12
        _delay_ms(200);
13
      }
14
    }
15
    else if (!(PINC & (1<<PINC1)))
16
    {
17
      PORTB = (1<<PB5) | (1<<PB4) | (1<<PB3) | (1<<PB2);
18
    }

... dann blinken die LEDs, solange Du Taster 1 drückst. Sobald Du Taster 
2 drückst, leuchten alle LEDs (oder gehen halt alle aus, je nach 
Beschaltung gegen GND/Vcc).

Lass also das while(2) einfach weg.

Du wirst dann merken, dass die Reaktion auf Deinen Taster 1 u.U. 
ziemlich lahm ist. Das liegt daran, dass Du Deinen Taster1 im Worstcase 
nur alle 800ms abfragst. Du solltest Dich also im nächsten Schritt mit 
Timern beschäftigen, damit Du die Delays da wegbekommst.

Viel Spaß noch,

Frank

von huch (Gast)


Lesenswert?

while(2)
{
...
}

bedeutet, dass alles was in "..." steht für immer und ewig ohne Ende 
ausgeführt wird.

Tipp: lerne erstmal c programmieren --> da gibt es auch super Literatur 
zu :-)

von Digit-22 N. (digit-22)


Lesenswert?

Das ging ja schnell.

Christian Erker:
Danke für die Tipps mal schauen ob ich das hinbekomme.

Und Frank M. :
genau das habe ich auch schon ausprobiert. Du hast recht wenn ich nach 
der if abfrage die While schleife raus lasse blinken die LEDs nur so 
lange wie ich den Taster0 gedrückt halte. Und wenn ich Taster1 drücke 
gehen die LEDs aus.


LG
Shabi

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Shabi N. schrieb:
> Und Frank M. :
> genau das habe ich auch schon ausprobiert. Du hast recht wenn ich nach
> der if abfrage die While schleife raus lasse blinken die LEDs nur so
> lange wie ich den Taster0 gedrückt halte. Und wenn ich Taster1 drücke
> gehen die LEDs aus.

Vielleicht möchtest Du das ja anders:

Taster1 kurz drücken und wieder loslassen: LEDs blinken dauerhaft
Taster2 kurz drücken und wieder loslassen: LEDs gehen aus und bleiben 
aus.

Dann wäre die 1. Lösung die folgende:
1
#define AUS  0
2
#define AN   1
3
4
int main(void)
5
{
6
   uint8_t zustand = AUS;
7
8
   DDRB = (1<<PB5) | (1<<PB4) | (1<<PB3) | (1<<PB2);
9
   DDRC = (0<<PC0) | (0<<PC1);
10
  
11
  while(1)
12
  {
13
      if (!(PINC & (1<<PINC0)))
14
      {
15
          zustand = AUS;
16
      }
17
      else if (!(PINC & (1<<PINC1)))
18
      {
19
          zustand = AN;
20
      }
21
22
      if (zustand == AN)
23
      {
24
          PORTB^=(1<<PB5);
25
          _delay_ms(200);
26
          PORTB^=(1<<PB4);
27
          _delay_ms(200);
28
          PORTB^=(1<<PB3);
29
          _delay_ms(200);
30
          PORTB^=(1<<PB2);
31
         _delay_ms(200);
32
      }
33
      else
34
      {
35
         PORTB = (1<<PB5) | (1<<PB4) | (1<<PB3) | (1<<PB2);
36
      }
37
  }
38
}

Trotzdem wirst Du damit das Problem nicht los, dass Du die Taster 
eventuell bis zu knapp 1 Sekunde lang drücken muss, bis Dein Programm 
reagiert. Du könntest die Abfrage natürlich öfter machen... aber richtig 
lösen kannst Du das nur mit einem Timer.

von Digit-22 N. (digit-22)


Lesenswert?

Genau das wollte ich damit erreichen.

würdest du mir nur noch folgende 3 Zeilen erklären was da genau 
passiert?

#define AUS  0  <<<----
#define AN   1  <<<----

int main(void)
{
   uint8_t zustand = AUS;  <<<----


den rest habe ich verstanden und du hast wieder recht.
Wenn ich jetzt die LEDs ein schalte gehat ds recht schnell nur wenn ich 
sie wieder ausschalten will muss ich warten bis die LEDs ein mal 
durchgelaufen sind. Aber das stört mich erst mal nicht bin ja noch ganz 
am Anfang.



LG
Shabi

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Shabi N. schrieb:
> Genau das wollte ich damit erreichen.

Na prima. Ging aus Deinem Eröffnungsposting aber nicht direkt hervor :-)

> würdest du mir nur noch folgende 3 Zeilen erklären was da genau
> passiert?
>
> #define AUS  0  <<<----
> #define AN   1  <<<----

Mit Preprocessor-Befehlen wie #define kannst Du Texte definieren, die im 
Quelltext - bevor der eigentliche Compiler gestartet wird, ausgetauscht 
werden. Dadurch wird Dein Quelltext lesbarer.

Der Preprocessor ersetzt einfach alles, wo AUS steht, durch 0 und alles, 
wo AN steht, durch 1 - einfach deshalb, weil ich es durch die #defines 
so eingestellt habe.

Ich hätte auch schreiben können:

   zustand = 1;
bzw.
   zustand = 0;
bzw.
   if (zustand == 1)
   {
     ...
   }

Aber weisst Du nach ein paar Monaten noch, was die magischen Werte 1 und 
0 eigentlich bedeuten?

Ich rate Dir zum Lesen ein C-Buch, damit Du solche Grundlagen lernst und 
auch anwendest.

Viel Spaß dabei,

Frank

von Digit-22 N. (digit-22)


Lesenswert?

Super  danke vielmals das hat sich auf jeden fall gelohnt und wieder was 
dazu gelernt.



LG
Shabi

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Shabi N. schrieb:
> Super  danke vielmals das hat sich auf jeden fall gelohnt und wieder was
> dazu gelernt.

Du wirst lachen: Ich habe mit der C-Programmiererei Mitte der 1980er 
unter Unix angefangen, das ist jetzt 28 Jahre her. Und ich lerne heute 
noch dazu...

Stell Dich also schon mal darauf ein... ;-)

von Falk B. (falk)


Lesenswert?

Wenn gleich die Lösung schon ganz OK ist, hat sie noch ein Problem, dass 
nur allzugern ignoriert wird. Deine Taster werden nur einmal pro 
LED-Durchlauf abgefragt. Drückst du nur kurz mittendrin, reagiert dein 
Programm nicht. Und deine LED-Spielchen werden sicherlich sehr schnell 
viel länger werden. Darum sollte man frühzeitig das richtige Konzept 
erlernen, das da lautet Multitasking. Klingt kompliziert, ist es 
aber nicht.
1
#define AUS  0
2
#define AN   1
3
4
int main(void)
5
{
6
   uint8_t zustand = AUS;
7
   uint8_t zeit =0;
8
9
   DDRB = (1<<PB5) | (1<<PB4) | (1<<PB3) | (1<<PB2);
10
   DDRC = (0<<PC0) | (0<<PC1);
11
  
12
  while(1)
13
  {
14
      if (!(PINC & (1<<PINC0)))
15
      {
16
          zustand = AUS;
17
      }
18
      else if (!(PINC & (1<<PINC1)))
19
      {
20
          zustand = AN;
21
      }
22
23
      if (zustand == AN)
24
      {          
25
          switch (zeit) {
26
              case 0: PORTB^=(1<<PB5); break;
27
              case 1: PORTB^=(1<<PB4); break;
28
              case 2: PORTB^=(1<<PB3); break;
29
              case 3: PORTB^=(1<<PB2); break;
30
          }         
31
          zeit++;
32
          if (zeit >= 4) zeit=0;
33
      }
34
      else
35
      {
36
         PORTB = (1<<PB5) | (1<<PB4) | (1<<PB3) | (1<<PB2);
37
         zeit =0;
38
      }
39
      
40
      _delay_ms(200);
41
  }
42
}

Jetzt werden deine Tasten alle 200ms geprüft, EGAL wie lange deine 
LED-Kette blinkert!

von Karl H. (kbuchegg)


Lesenswert?

>   while(1)
>  {
>      if (!(PINC & (1<<PINC0)))
>      {
>          zustand = AUS;
>      }
>      else if (!(PINC & (1<<PINC1)))
>      {
>          zustand = AN;
>      }
>
>      if (zustand == AN)
>      {
>          PORTB^=(1<<PB5);
>          _delay_ms(200);
>          PORTB^=(1<<PB4);
>          _delay_ms(200);
>          PORTB^=(1<<PB3);
>          _delay_ms(200);
>          PORTB^=(1<<PB2);
>         _delay_ms(200);
>      }
>      else
>      {
>         PORTB = (1<<PB5) | (1<<PB4) | (1<<PB3) | (1<<PB2);
>      }
>  }

Das ist eine Möglichkeit.
Es gibt aber noch andere EINFACHE Möglichkeiten, mit der du den Nachteil 
der seltenen Tastenabfrage noch weiter drücken kannst:
Bau dir zum Beispiel einen 'Schrittzähler' ein. Der Schrittzähler sagt 
dir, welche LED zu leuchten hat.
1
  ...
2
3
  Schritt = 0;
4
5
  while(1)
6
  {
7
      if (!(PINC & (1<<PINC0)))
8
      {
9
          zustand = AUS;
10
      }
11
      else if (!(PINC & (1<<PINC1)))
12
      {
13
          zustand = AN;
14
      }
15
16
      if (zustand == AN)
17
      {
18
        Schritt++;
19
        if (Schritt == 4)
20
          Schritt = 0;
21
22
        PORTB = (1<<PB5) | (1<<PB4) | (1<<PB3) | (1<<PB2);
23
24
        if (Schritt == 0)
25
          PORTB &= ~(1<<PB5);
26
        else if (Schritt == 1)
27
          PORTB &= ~(1<<PB4);
28
        else if (Schritt == 2)
29
          PORTB &= ~(1<<PB3);
30
        else if (Schritt == 3)
31
          PORTB &= ~(1<<PB2);
32
33
        _delay_ms(200);
34
      }
35
      else
36
      {
37
        PORTB = (1<<PB5) | (1<<PB4) | (1<<PB3) | (1<<PB2);
38
      }
39
  }

Vorteil:

jetzt wird die Tastenabfrage nach JEDEM Lampenwechsel gemacht. Die 
Verzögerungszeit nach der spätestens auf einen Tastendruck reagiert 
wird, ist von 4*200ms (800ms) auf 200ms gesunken. 2 Zehntelsekunden ist 
zwar noch nicht "sofort", aber immerhin schon nahe drann, so dass die 
meisten Benutzer damit kein Problem haben.

von Karl H. (kbuchegg)


Lesenswert?

@Falk

:-)

@Shabi
Wenn die 200ms immer noch zu lang sind:
Nun, kein Mensch sagt, dass man 200ms am Stück warten muss. Wartet man 
10 mal 20ms, dann hat man in Summe auch 200ms gewartet. Dann darf man 
halt nicht bei jedem Schritt in die nächste Schaltstufe gehen, sondern 
nur bei jedem 10.ten. Im Endeffekt kommt das aufs gleiche raus. Mit 
einem kleinen Unterschied: Die Tastenabfrage findet dann alls 20ms 
statt. Und 2 Tausendstel Sekunden sind für einen Menschen viel zu kurz 
um das als Verzögerung wahr zu nehmen.

Also:
Was sollst du mitnehmen?
Es gibt Variablen. In Variablen kann man sich Werte speichern. Diese 
Werte können zb auch Beschreibungen (in Form von Zahlen) sein, die einen 
Zustand beschreiben. Jede Zahl steht für einen Zustand, zb Lampe 1 ein, 
Motor 1 dreht, Rollo oben, etc. Bei dir dann zum Beispiel eben auch: 
Lauflicht läuft oder Lauflicht läuft nicht. Eine andere Variable 
beschreibt an welcher Stelle die Lauflichtsequenz gerade ist.

Es ist meistens eine ganz gute Strategie, wenn man 'Auslöser' 
(Tastendruck) von der tatsächlichen Aktion (bei dir Lauflicht) mit 
solchen Zustandsvariablen trennt. Der Auslöser vermerkt in der Variablen 
lediglich, ob die Aktion auszuführen ist oder nicht (oder auch wie sie 
auszuführen ist). Davon getrennt ist der Teil im Programm, der anhand 
der Zustandsvariablen die entsprechende Aktion dann auch tatsächlich 
ausführt. Und wenn die Aktion insgesammt zu lange dauert, dann 
unterteilt man sie in einzelne Phasen, wieder gekoppelt mit einer 
weiteren Variablen, die beschreibt, in welcher Phase sich die 'Aktion' 
gerade befindet.


Das ganze ist eingebettet in die eine 'große' Hauptschleife

   while( 1 ) {

     ...

   }

die dein Programm immer hat. Die ist aber auch die einzige 
Endlosschleife! Andere Schleifen in dieser Form sind nicht zulässig, 
denn jede Logik lässt sich immer irgendwie in eine ähnliche Struktur 
bringen.
Du musst weg vom Denken: Ich mach jetzt in einer Schleife dieses und 
jenes. Deine Denkweise (in der µC Programmierung) muss in die Richtung 
gehen:
Ich habe da einen Zeitpunkt nach dem anderen - mit jedem Durchlauf durch 
die große while Schleife ist etwas Zeit vergangen - was gibt es genau 
jetzt, nachdem ein bischen Zeit vergangen ist, zu tun.

Also weg vom Denken in Schleifen, hin zum Denken in Ereignissen, die in 
bestimmten Zeitabständen auszuwerten sind.

von Falk B. (falk)


Lesenswert?

Zwei Dumme, ein Gedanke! ;-)

von Digit-22 N. (digit-22)


Lesenswert?

Danke Leute für die vielen Tipps.
Ich merke das ich noch nicht Microcontrollermäßig denke und mir 
warscheinlich alles viel komplizierter vorstelle als es wirklich ist.

Wenn ich mir die Quellcodes jetzt anschaue, leuchtet auch vieles ein.

Werde mir das ganze noch mehrmals genauer anschauen und analysieren bis 
es komplett sitzt.



SUUUUPPER

Danke nochmals an alle. So macht das Thema richtig spaß.

LG
Shabi

von Karl H. (kbuchegg)


Lesenswert?

Shabi N. schrieb:

> warscheinlich alles viel komplizierter vorstelle als es wirklich ist.

Das tun die meisten.
Sie sind vom Fernsehen/Film 'verdorben' worden.
"Computer, die Schilde rekonfigurieren" funktioniert nun mal nur auf der 
Enterprise. Und auf die Eingabe "Suche alle Personen, die in Beziehung 
zur Toten stehen und ein Motiv haben" kriegst du von einem heutigen 
Computer höchtens ein lakonisches "Syntax Error".

Computer sind einerseits (im elektronischen Aufbau) viel komplexer als 
viele Menschen glauben, können dafür von sich aus (auf Logikebene) viel 
weniger als die meisten Menschen glauben. Will man einen Computer 
programmieren, muss man einfach (im Gegensatz zu: kompliziert) denken! 
Ganz banale Vorgehensmuster, wie wir sie jeden Tag problemlos benutzen. 
Oder bleibst du beim Herd stehen, bis die Suppe fertig gekocht ist? Ich 
wette, du tust das nicht. Du schaltest ein und siehst nur ab und zu beim 
Herd vorbei, ob die Suppe schon kocht. Und in der Zwischenzeit machst du 
was anderes.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Falk Brunner schrieb:
>           if (zeit >= 4) zeit=0;

Karl Heinz Buchegger schrieb:
>         if (Schritt == 4)
>           Schritt = 0;

Hier sieht man den Unterschied zwischen dem Angsthasen, der seinem 
Programm nicht traut und dem, der sich sicher ist, dass die 
Schrittvariable niemals größer als 4 werden kann :-)

Nichts für ungut, ich selber programmiere eher genauso "defensiv" wie 
Falk - im Zweifel lieber mit ">=" als mit "==". Kostet den µC dasselbe 
an Arbeit.

Gruß,

Frank

von Peter (Gast)


Lesenswert?

Frank M. schrieb:
> Hier sieht man den Unterschied zwischen dem Angsthasen, der seinem
> Programm nicht traut und dem, der sich sicher ist, dass die
> Schrittvariable niemals größer als 4 werden kann :-)

Dazu mal ein Zitat von Bertrand Russell:

"Das ist der ganze Jammer: Die Dummen sind so sicher und die Gescheiten 
so voller Zweifel."

;)

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.