Forum: Mikrocontroller und Digitale Elektronik arduino led mit taster schalten


von ard (Gast)


Lesenswert?

Hallo,
ich habe mir die Tage ein Arduino-Starter-Kit bestellt. Damit bin ich 
jetzt am rumspielen und habe wohl irgendwo einen Denkfehler, den ich 
aber nicht finde.

Ich versuche mit einem Taster eine LED beim ersten Tastendruck 
einzuschalten und beim zweiten Tastendruck wieder auszuschalten .
Das klappt soweit auch wenn ich den Code in der LOOP stehen habe.
Habe mich dabei hier ran gehalten:
http://forum.arduino.cc/index.php?topic=55756.0

Also sieht es in meiner LOOP so aus:
1
//LOOP
2
//blinkerRechts
3
      if((blinkerRechtsSchalterZustand == 1) && (blinkerRechtsZustandMerker == 0))
4
      {
5
            digitalWrite(blinkerRechts, HIGH);
6
            blinkerRechtsZustandMerker = 1;
7
            delay(10);   
8
      }
9
      if((blinkerRechtsSchalterZustand == 0) && (blinkerRechtsZustandMerker == 1))
10
      {
11
            blinkerRechtsZustandMerker = 2;
12
            delay(10);
13
      }
14
      if((blinkerRechtsSchalterZustand == 1) && (blinkerRechtsZustandMerker == 2))
15
      {
16
            digitalWrite(blinkerRechts, LOW);
17
            blinkerRechtsZustandMerker = 3;
18
            delay(10);
19
      }
20
      if((blinkerRechtsSchalterZustand == 0) && (blinkerRechtsZustandMerker == 3))
21
      {
22
            blinkerRechtsZustandMerker= 0;
23
            delay (10);
24
      }
Das mit dem delay(10) habe ich zur Entprellung gemacht. Ist wohl nicht 
die beste Lösung, klappt aber soweit.

Nun möchte ich das mit mehreren Tastern und mehreren LEDs machen, bzw. 
mit einem linken Blinker und einem Rechten.
Also habe ich mir eine Funktion gebaut die so aus sieht:
1
void blinker(int blinker, int blinkerSchalterZustand, int blinkerZustandMerker)
2
{
3
  // außerhalb der LOOP
4
  //blinker
5
      if((blinkerSchalterZustand == 1) && (blinkerZustandMerker == 0))
6
      {
7
            digitalWrite(blinker, HIGH);
8
            blinkerZustandMerker = 1;
9
            delay(10);   
10
      }
11
      if((blinkerSchalterZustand == 0) && (blinkerZustandMerker == 1))
12
      {
13
            blinkerZustandMerker = 2;
14
            delay(10);
15
      }
16
      if((blinkerSchalterZustand == 1) && (blinkerZustandMerker == 2))
17
      {
18
            digitalWrite(blinker, LOW);
19
            blinkerZustandMerker = 3;
20
            delay(10);
21
      }
22
      if((blinkerSchalterZustand == 0) && (blinkerZustandMerker == 3))
23
      {
24
            blinkerZustandMerker= 0;
25
            delay (10);
26
      }
27
}

In meiner LOOP sieht es jetzt so aus:
1
blinker(blinkerLinks, blinkerLinksSchalterZustand, blinkerLinksZustandMerker);
2
blinker(blinkerRechts, blinkerRechtsSchalterZustand, blinkerRechtsZustandMerker);

Aber nun kann ich die LED nur noch anschalten. Aus geht sie einfach 
nicht mehr - obwohl es (mMn) genau der gleiche Code ist, nur in einer 
Funktion..

Wo liegt der Fehler?
Besten Dank

: Verschoben durch User
von Markus S. (acepilot)


Lesenswert?

Das Mag deiner Ansicht nach der gleiche Code sein, aber durch das 
verpacken in eine Funktion fängst du dir ein Problem ein. Du übergibst 
die Variable blinkerSchalterZustand und blinkerZustandMerker nicht als 
Reference. Damit kannst du die variable zwar innerhalb der Funktion 
änder, aber sobald du die Funktion verlässt sind diese Änderungen weg. 
Ändere die Übergabe auf Reference dann sollte es gehen.

Sinnvoll ist auch immer wenn du kompletten Code hier postest und nicht 
nur Ausschnitte. Zu oft liegen die Fehler in den Codeteilen verborgen 
die nicht mit gepostet wurden.

von ard (Gast)


Lesenswert?

Dank dir. :)
Wenn das mein Informatik-Prof sieht - erst letztes Semester, das 
tauschen mit Referenzen gehabt... Aber hier nicht drauf gekommen.

von Stefan F. (Gast)


Lesenswert?

"blinkerSchalterZustand" klingt erstmal nach einem gute gewählten Namen. 
Aber was die Werte 0 1 und 2 bedeuten, kann ich nicht schnell erkennen. 
Benutze dafür Enumerations oder Konstanten.

Vorschlag:
#define GEDRUECKT 1
#define LOSGELASSEN 0

#define GEDRUECKT_ZUM_EINSCHALTEN 0
#define LOSGELASSEN_NACH_EINSCHALTEN 1
#define GEDRUECKTE_ZUM_AUSSCHALTEN 2
#define LOSGELASSEN_ZUM_AUSSSCHALTEN 3

"blinkerZustandMerker" gefällt mir gar nicht. Der Name sagt mir zwar, 
dass da irgendwas vermerkt wird, aber nicht genau genug. Hier schlage 
ich vor: "vorherigerBlinkerZustand".

Dein Lösungsansatz mit dem Zustandsautomat gefällt mir im Prinzip, 
allerdings könntest du den Code deutlich übersichtlicher und besser 
erweiterbar gestalten, wenn du folgende Punkte berücksichtigst:

- Jeder Zustandsautomat hat genau eine Zustandsvariable, die Auskunft 
darüber gibt, auf welches Ereignis der Automat wartet (nicht, welche 
Signale gerade anliegen!).
- Bei jedem Zustand wartet der Automat auf ein oder mehrere Ereignisse 
und löst ggf. die gewünschte Aktion aus.

1
void Task_LedSteuerung()
2
{
3
     static uint8_t zustand;
4
     uint8_t taster=digitalRead(...);
5
     switch (zustand)
6
     {
7
        case 0: // Warte auf Einschalten
8
           if (taster==1)
9
           {
10
              digitalWrite(...,HIGH);
11
              zustand=1;
12
           }
13
           break;
14
15
        case 1: // Warte auf Loslassen
16
           if (taster==0)
17
           {
18
              zustand=2;
19
           }
20
           break;
21
22
        case 2: // Warte auf Ausschalten
23
           if (taster==1)
24
           {
25
              digitalWrite(...,LOW);
26
              zustand=3;
27
           }
28
           break;
29
30
        case 3: // Warte auf Loslassen
31
           if (taster==0)
32
           {
33
              zustand=0;
34
           }
35
           break;
36
     }
37
}
38
39
void loop()
40
{
41
   Task_LedSteuerung();
42
   delay(10);
43
}

Auch hier wäre wieder eine Enumeration oder Konstanten für die Stati 
sinnvoll. Ich zeige das im nächsten Beispiel.

Du verwendest delay um den Taster zu entprellen. Während der µC wartet, 
kann er nichts anderes tun. Häufig istb es allerdings nötig, ständig 
etwas zu tun. Zum Beispiel andere Taster abfragen. Oder etwas auf einem 
LED-Matrix Display darstellen.

Daher benutze besser einen Systemtimer. Um den Code jetzt nicht 
unübersichtlich zu machen, schlage ich vor, die Entprellung des Tasters 
in einen separaten Task zu packen. Etwa so:

1
uint8_t ereignis_taster_gedrueckt;
2
3
// Setzt die Variable taster_gedrueckt=1 wenn der Taster gedrückt wurde.
4
// Beim Loslassen des Tasters bleibt die Variable unverändert!
5
void Task_Entprellen()
6
{
7
    static enum {WARTE_DRUCK, ENTPRELLEN1, WARTE_LOSLASSEN, ENTPRELLEN2} zustand=WARTE_DRUCK;
8
    static long warteSeit;
9
10
    uint8_t taster=digitalRead(...);
11
    switch (zustand)
12
    {
13
        case WARTE_DRUCK:
14
          if (taster==1)
15
          {
16
              taster_gedrueckt=1;
17
              status=ENTPRELLEN1;
18
              warteSeit=millis();
19
          }
20
          break;
21
22
        case ENTPRELLEN1:
23
          if (millis()-warteSeit>50)
24
          {
25
              status=WARTE_LOSLASSEN;
26
          }
27
          break;
28
29
        case WARTE_LOSLASSEN:
30
          if (taster==0)
31
          {
32
              // Absichtlich auskommentiert: taster_gedrueckt=0;
33
              status=ENTPRELLEN2;
34
              warteSeit=millis();
35
          }
36
          break;
37
38
        case ENTPRELLEN2:
39
          if (millis()-warteSeit>50)
40
          {
41
              status=WARTE_DRUCK;
42
          }
43
          break;
44
    }
45
}
46
47
void Task_LedSteuerung()
48
{
49
     static enum {WARTE_EINSCHALTEN, WARTE_AUSSCHALTEN} zustand=WARTE_EINSCHALTEN;
50
     switch (zustand)
51
     {
52
        case WARTE_EINSCHALTEN: 
53
           if (ereignis_taster_gedrueckt)
54
           {
55
              digitalWrite(...,HIGH);
56
              // Ereignis als "erledigt" kennzeichnen
57
              ereignis_taster_gedrueckt=0;
58
              zustand=WARTE_AUSSCHALTEN;
59
           }
60
           break;
61
62
        case WARTE_AUSSCHALTEN:
63
           if (ereignis_taster_gedrueckt)
64
           {
65
              digitalWrite(...,LOW);
66
              // Ereignis als "erledigt" kennzeichnen
67
              ereignis_taster_gedrueckt=0;
68
              zustand=WARTE_EINSCHALTEN;
69
           }
70
           break;
71
     }
72
}
73
74
void loop()
75
{
76
   Task_Entprellen();
77
   Task_LedSteuerung();
78
}

Der Aufruf von delay() entfällt, weil der Task_Entprellen() bereits nach 
erkanntem Tastendruck (bzw. Loslassen) eine Weile wartet. Allerdings 
wartet er nicht mit delay, sondern indem er immer wieder bei jedem 
loop() prüft, ob schon mehr als 50 Millisekunden verstrichen sind.

von Stefan F. (Gast)


Lesenswert?

Sorry, ich habe da oben mehrmals "taster_gedrueckt" geschrieben, wo es 
eigentlich "ereignis_taster_gedrueckt" heissen sollte.

von Arduino Anfänger (Gast)


Lesenswert?

Du kannst aber auch bei jedem Tastendruck einfach den Zustand der LED 
invertieren - dann musst Du Dir den vorherigen Zustand nicht merken.

digitalWrite(LED1_, !digitalRead(LED_1));

Gruß
Thomas

von Arduino Anfänger (Gast)


Lesenswert?

sorry, da war ein Tippfehler "LED_1"  nicht "LED1_"

digitalWrite(LED_1, !digitalRead(LED_1));

von Harald W. (wilhelms)


Lesenswert?

ard schrieb:

> Ich versuche mit einem Taster eine LED beim ersten Tastendruck
> einzuschalten und beim zweiten Tastendruck wieder auszuschalten .

Hmm, dafür nehm ich eigentlich immer ein Flipflop-IC...

von Stefan F. (Gast)


Lesenswert?

> Du kannst aber auch bei jedem Tastendruck einfach den Zustand der
> LED invertieren

Ja, wenn der Taster eine Entprell-Schaltung bekommt. Ansonsten: Nein. 
Die LED würde nach jedem Tastendruck einen zufälligen Status haben.

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.