mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik arduino led mit taster schalten


Autor: ard (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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:
//LOOP
//blinkerRechts
      if((blinkerRechtsSchalterZustand == 1) && (blinkerRechtsZustandMerker == 0))
      {
            digitalWrite(blinkerRechts, HIGH);
            blinkerRechtsZustandMerker = 1;
            delay(10);   
      }
      if((blinkerRechtsSchalterZustand == 0) && (blinkerRechtsZustandMerker == 1))
      {
            blinkerRechtsZustandMerker = 2;
            delay(10);
      }
      if((blinkerRechtsSchalterZustand == 1) && (blinkerRechtsZustandMerker == 2))
      {
            digitalWrite(blinkerRechts, LOW);
            blinkerRechtsZustandMerker = 3;
            delay(10);
      }
      if((blinkerRechtsSchalterZustand == 0) && (blinkerRechtsZustandMerker == 3))
      {
            blinkerRechtsZustandMerker= 0;
            delay (10);
      }
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:
void blinker(int blinker, int blinkerSchalterZustand, int blinkerZustandMerker)
{
  // außerhalb der LOOP
  //blinker
      if((blinkerSchalterZustand == 1) && (blinkerZustandMerker == 0))
      {
            digitalWrite(blinker, HIGH);
            blinkerZustandMerker = 1;
            delay(10);   
      }
      if((blinkerSchalterZustand == 0) && (blinkerZustandMerker == 1))
      {
            blinkerZustandMerker = 2;
            delay(10);
      }
      if((blinkerSchalterZustand == 1) && (blinkerZustandMerker == 2))
      {
            digitalWrite(blinker, LOW);
            blinkerZustandMerker = 3;
            delay(10);
      }
      if((blinkerSchalterZustand == 0) && (blinkerZustandMerker == 3))
      {
            blinkerZustandMerker= 0;
            delay (10);
      }
}

In meiner LOOP sieht es jetzt so aus:
blinker(blinkerLinks, blinkerLinksSchalterZustand, blinkerLinksZustandMerker);
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 Moderator
Autor: Markus Sierpinski (acepilot)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: ard (Gast)
Datum:

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

Autor: Stefanus F. (stefanus)
Datum:

Bewertung
1 lesenswert
nicht 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.

void Task_LedSteuerung()
{
     static uint8_t zustand;
     uint8_t taster=digitalRead(...);
     switch (zustand)
     {
        case 0: // Warte auf Einschalten
           if (taster==1)
           {
              digitalWrite(...,HIGH);
              zustand=1;
           }
           break;

        case 1: // Warte auf Loslassen
           if (taster==0)
           {
              zustand=2;
           }
           break;

        case 2: // Warte auf Ausschalten
           if (taster==1)
           {
              digitalWrite(...,LOW);
              zustand=3;
           }
           break;

        case 3: // Warte auf Loslassen
           if (taster==0)
           {
              zustand=0;
           }
           break;
     }
}

void loop()
{
   Task_LedSteuerung();
   delay(10);
}

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:

uint8_t ereignis_taster_gedrueckt;

// Setzt die Variable taster_gedrueckt=1 wenn der Taster gedrückt wurde.
// Beim Loslassen des Tasters bleibt die Variable unverändert!
void Task_Entprellen()
{
    static enum {WARTE_DRUCK, ENTPRELLEN1, WARTE_LOSLASSEN, ENTPRELLEN2} zustand=WARTE_DRUCK;
    static long warteSeit;

    uint8_t taster=digitalRead(...);
    switch (zustand)
    {
        case WARTE_DRUCK:
          if (taster==1)
          {
              taster_gedrueckt=1;
              status=ENTPRELLEN1;
              warteSeit=millis();
          }
          break;

        case ENTPRELLEN1:
          if (millis()-warteSeit>50)
          {
              status=WARTE_LOSLASSEN;
          }
          break;

        case WARTE_LOSLASSEN:
          if (taster==0)
          {
              // Absichtlich auskommentiert: taster_gedrueckt=0;
              status=ENTPRELLEN2;
              warteSeit=millis();
          }
          break;

        case ENTPRELLEN2:
          if (millis()-warteSeit>50)
          {
              status=WARTE_DRUCK;
          }
          break;
    }
}

void Task_LedSteuerung()
{
     static enum {WARTE_EINSCHALTEN, WARTE_AUSSCHALTEN} zustand=WARTE_EINSCHALTEN;
     switch (zustand)
     {
        case WARTE_EINSCHALTEN: 
           if (ereignis_taster_gedrueckt)
           {
              digitalWrite(...,HIGH);
              // Ereignis als "erledigt" kennzeichnen
              ereignis_taster_gedrueckt=0;
              zustand=WARTE_AUSSCHALTEN;
           }
           break;

        case WARTE_AUSSCHALTEN:
           if (ereignis_taster_gedrueckt)
           {
              digitalWrite(...,LOW);
              // Ereignis als "erledigt" kennzeichnen
              ereignis_taster_gedrueckt=0;
              zustand=WARTE_EINSCHALTEN;
           }
           break;
     }
}

void loop()
{
   Task_Entprellen();
   Task_LedSteuerung();
}

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.

: Bearbeitet durch User
Autor: Stefanus F. (stefanus)
Datum:

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

Autor: Arduino Anfänger (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Arduino Anfänger (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
sorry, da war ein Tippfehler "LED_1"  nicht "LED1_"

digitalWrite(LED_1, !digitalRead(LED_1));

Autor: Harald Wilhelms (wilhelms)
Datum:

Bewertung
0 lesenswert
nicht 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...

Autor: Stefanus F. (stefanus)
Datum:

Bewertung
0 lesenswert
nicht 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.

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.