Frage zu kleinem Countdown (ohne Time.h)

Ich habe (als vollkommener Arduino-Anfänger) folgenden Code geschrieben:

#include <LiquidCrystal.h>

LiquidCrystal lcd(12, 11, 5, 4, 3, 2);

const int minuten_switch = 7;
const int sekunden_switch = 6;
const int start_switch = 8;
int minuten = 0;
int sekunden = 0;

void setup() {
  
  lcd.begin(16, 2);
  pinMode(minuten_switch,INPUT);
  pinMode(sekunden_switch ,INPUT);
  pinMode(start_switch, INPUT);
  
  lcd.print("TIMER:");
  lcd.setCursor(0, 1);
  lcd.print("00:00");

}

void loop() {
  
  if (sekunden == 60)
  {
    minuten = minuten + 1;
    sekunden = 0;
    if (minuten < 10)
    {
      lcd.setCursor(0, 1);
      lcd.print("0");
      lcd.print(minuten);
    }
    else if (minuten >= 10)
    {
      lcd.setCursor(0, 1);
      lcd.print(minuten);
    }
    
    
    if (sekunden < 10)
    {
      lcd.setCursor(3, 1);
      lcd.print("0");
      lcd.print(sekunden);
    }
    else if (sekunden >= 10)
    {
      lcd.setCursor(3, 1);
      lcd.print(sekunden);
    }
  }
  
  if (digitalRead(minuten_switch) == HIGH)
  {
    minuten = minuten + 1;
    delay(250);
    if (minuten < 10)
    {
      lcd.setCursor(0, 1);
      lcd.print("0");
      lcd.print(minuten);
    }
    else if (minuten >= 10)
    {
      lcd.setCursor(0, 1);
      lcd.print(minuten);
    }
    
    if (minuten == 99)
    {
      minuten = 0;
    }
  }
  
  if (digitalRead(sekunden_switch) == HIGH)
  {
    sekunden = sekunden + 1;
    delay(250);
    if (sekunden < 10)
    {
      lcd.setCursor(3, 1);
      lcd.print("0");
      lcd.print(sekunden);
    }
    else if (sekunden >= 10)
    {
      lcd.setCursor(3, 1);
      lcd.print(sekunden);
    }
  }
  
  if (digitalRead(start_switch) == HIGH)
  {
    sekunden = sekunden - 1;
    
    if (sekunden < 10)
    {
      lcd.setCursor(3, 1);
      lcd.print("0");
      lcd.print(sekunden);
    }
    else if (sekunden >= 10)
    {
      lcd.setCursor(3, 1);
      lcd.print(sekunden);
    }
  }
  
}

Es ist -noch- kein richtiger Timer geworden. Das Ziel wäre, dass man per minuten_switch und sekunden_switch die Zeit einstellen kann. Bei einem Druck auf den Taster der auf Pin 8 liegt, sollte er die Zeit hinunter zählen. Aktuell sollte das Programm nur einmal die Sekunden um 1 verringern. (ab if (digitalRead(start_switch) == HIGH))

Das Problem ist, dass bei einem Druck auf den Taster welcher auf Pin 8 liegt, NICHTS passiert. Der Taster funktioniert, das habe ich schon getestet. Daher dachte ich, es liegt am Programm. Was könnte der Fehler sein?

Bitta daher um Hilfestellung. Danke im voraus.

LG

http://forum.arduino.cc/index.php?topic=231591.msg1670880#msg1670880

Dabei werden die internen Pullups verwendet. Also entweder anpassen oder gleich die externen Pulldowns weglassen und den Taster gegen Masse schalten

Warum funktionieren dann die beiden anderen Taster?

Bei den anderen beiden zählt er um jeweils 1 hinauf. Gerade beim dritten Taster passiert nichts.

Tut mir leid. Entweder ich habe etwa falsch verstanden oder ich habe es gar nicht gecheckt. Und mit "Pullups" oder "externen Pulldowns" kann ich erst recht nichts anfangen

Es funktioniert soweit. Jedoch werden if-Abfragen bei deiner Methode (INPUT_PULLUP) komplett ignoriert. Das heißt, wenn ich folgende IF-Abfrage einbaue…

if (sekunden == 60)
         {
           sekunden = 0;
           minuten = minuten + 1;
         }

…passiert wieder nichts. Das Programm zählt einfach über die 60 hinaus.

mmoerth: ...passiert wieder nichts. Das Programm zählt einfach über die 60 hinaus.

Wenn Du ein Programm machen möchtest, das übersichtlich ist, dann solltest Du Dich darum bemühen, keinen Spaghetticode für irgendwelche Daten zu schreiben, sondern dann solltest Du: a) der Aufgabenstellung angemessene Datentypen und Variablen verwenden b) dem Programm eine angemessene Struktur geben

In Sachen Programmstruktur hat sich die Aufteilung nach dem EVA-Prinzip sehr bewährt: E - Eingabe (mit Einzeltasten, Tastenfeld, Potentiometer, Drehgeber, IR-Fernbedienung etc.) V - Verarbeitung (Eingabedaten so verarbeiten, dass sie ausgegeben werden können) A - Ausgabe (Daten ausgeben über Status-LEDs, Relais, Funk, SD-Karte, Serial, Text-LCD, Grafik-LCD)

Durch die modulare Trennung Eingabe/Verarbeitung/Ausgabe wird das Programm auch leicht wartbar: Wenn Du beispielsweise die Zeiteinstellung ("Eingabe") umstellen möchtest von einer Tastenbedienung und Du möchtest lieber ein drehbares Potentiometer oder eine IR-Fernbedienung zur Einstellung der Zeit verwenden, dann braucht später nur die Funktion für die "Eingabe" geändert werden.

Und wenn Du statt einer Ausgabe auf LCD-Display später ein Relais schalten möchtest oder ein Funksignal nach Timerablauf gesendet werden soll, brauchst Du nur die Funktion "Ausgabe" ändern.

So werden Programme nicht nur übersichtlicher, sondern auch leichter wartbar.

Bist Du Dir denn jetzt schaltungstechnisch darüber im klaren, wie Du Deine drei Taster angeschlossen hast? So wie im Button-Tutorial http://arduino.cc/en/tutorial/button mit PullDown-Widerstand? Oder ohne Widerstand nur den Taster beschaltet?

An einem Microcontroller sind "offene" Eingänge stets "floatend" und liefern keinen definierten Pegel zurück. Deshalb MÜSSEN Taster nicht nur im gedrückten Zustand an einem bestimmten Pegel anliegen, sondern auch im offenen Zustand. Falls Du keinen externen Pull-Widerstand mit jedem Taster verwenden möchtest, kannst Du den internen Pull-Widerstand über den pinMode "INPUT_PULLUP" aktivieren, womit dann ein offener Schalter "HIGH" liefert und ein geschlossener "LOW".

Ist das die Beschaltung (nur Taster, kein externer PullWiderstand am Taster), die Du verwenden möchtest?

hallo jurs,

ich bedanke mich sehr herzlich für deine wirklich ausführliche Hilfestellung. Das mit der Beschallung des Tasters wusste ich bis jetzt noch nicht. Daher auch ein Danke für diesen Hinweis. Die Sache mit dem E-V-A finde ich ebenfalls sehr interessant. Aktuell habe ich noch nicht die Gelegenheit gehab das zu testen, werde es aber heute noch probieren.

:)

LG

mmoerth:
Das mit der Beschallung des Tasters wusste ich bis jetzt noch nicht.

Na ja, eigentlich Beschaltung und nicht Beschallung.
Jedenfalls müßtest Du mit Deinen Programmen eine Nummer kleiner anfangen!

Man kann sinnvollerweise nicht den dritten Schritt (drei Schalter auswerten) machen, wenn man den ersten Schritt (einen Schalter auswerten) nicht fehlerfrei hinbekommt. Das kann nur in völliger Konfusion und garantierter Fehlfunktion enden.

Anbei mal ein Beispielcode zur Verdeutlichung der Trennung in Eingabe-Verarbeitung-Ausgabe.
Beachte bitte, was in der loop-Funktion drinsteht!
Vielleicht kannst Du Dir für Deine Programme was abgucken.

Zum Verständnis des Codes schaust Du Dir am besten auch mal an, was “static” im Zusammenhang mit einer deklarierten Variablen in einer Funktion bedeutet.

Die Auswertung der Schalter erfolgt so: Reagiert wird immer auf das Drücken eines Schalters, d.h. wenn der vorherige Schalterzustand “nicht gedrückt” war und der Schalter in den Zustand “gedrückt” übergeht. Damit das “Prellen” mechanischer Taster http://de.wikipedia.org/wiki/Prellen nicht zu Fehlfunktionen (Mehrfachzählungen) führt, ist in der loop-Funktion ein kurzes Delay von 5 Millisekunden enthalten, so dass ein Tasterzustand nicht öfter als alle 5 Millisekunden ausgewertet wird.

#include <LiquidCrystal.h>

LiquidCrystal lcd(12, 11, 5, 4, 3, 2);

/* in der nachfolgenden Zeile wird festgelegt, ob die Schalter
   mit PullUp oder PullDown Widerständen beschaltet sind.
   INPUT_PULLUP aktiviert die internen PullUp-Widerstände des Atmega
   INPUT setzt voraus, dass die Schalter mit externen PullDown-Widerständen beschaltet sind
*/
#define INPUTMODE INPUT_PULLUP  // INPUT_PULLUP oder INPUT setzen
const int minuten_switch = 7;
const int sekunden_switch = 6;
const int start_switch = 8;

int timerLaufzeit = 0;
boolean timerGestartet=false;

void eingabe()
{
  static boolean oldMinSwitch=false; // Statische Variable für letzten Schalterzustand
  static boolean oldSecSwitch=false; // Statische Variable für letzten Schalterzustand
  static boolean oldStartSwitch=false; // Statische Variable für letzten Schalterzustand
  boolean MinSwitch= digitalRead(minuten_switch); // aktueller Schalterzustand
  boolean SecSwitch= digitalRead(sekunden_switch); // aktueller Schalterzustand
  boolean StartSwitch= digitalRead(start_switch); // aktueller Schalterzustand
  if (INPUTMODE==INPUT_PULLUP) // Vertauschte Schaltlogik bei PullUp
  {
    MinSwitch=!MinSwitch; // ausgelesenen Zustand vertauschen wg. PullUp
    SecSwitch=!SecSwitch; // ausgelesenen Zustand vertauschen wg. PullUp
    StartSwitch=!StartSwitch; // ausgelesenen Zustand vertauschen wg. PullUp
  }
  if (MinSwitch==HIGH && oldMinSwitch==LOW) timerLaufzeit+=60; // Zustandsänderung gedrückt?
  oldMinSwitch=MinSwitch; // aktueller Schalterzustand wird zum alten Schalterzustand
  if (SecSwitch==HIGH && oldSecSwitch==LOW) timerLaufzeit++; // Zustandsänderung gedrückt?
  oldSecSwitch=SecSwitch; // aktueller Schalterzustand wird zum alten Schalterzustand
  if (StartSwitch==HIGH && oldStartSwitch==LOW) timerGestartet=true; // Zustandsänderung gedrückt?
  oldStartSwitch=StartSwitch; // aktueller Schalterzustand wird zum alten Schalterzustand
}

void verarbeitung()
{
  static unsigned long lastMillis=0; // statischer Zähler für letzten Stand des millis() Timers
  static unsigned long timedMillis=0;// statischer Zähler für Millisekunden beim Herunterzählen
  unsigned long nowMillis=millis();  // aktueller Stand des millis() Timers
  if (timerGestartet && timerLaufzeit>0) // Zähler läuft und Restzeit vorhanden
  {
    timedMillis+= nowMillis-lastMillis; // Millisekunden hochzählen
    if (timedMillis>=1000)  // Nach jeweils 1000 Millisekunden
    {
      timedMillis=0;  // Millisekundenzähler auf 0 setzen
      timerLaufzeit--; // Eine Sekunde Laufzeit abziehen
      if (timerLaufzeit==0) timerGestartet=false; // Timer anhalten
    }
  }
  lastMillis=nowMillis; // Zählerstand merken, bei dem diese Funktion zuletzt lief
}

void ausgabe()
{
  int minuten=timerLaufzeit/60; // Ganzzahldivision durch 60
  int sekunden=timerLaufzeit%60;// Modulo-Divisionsrecht bei geteilt durch 60
  lcd.setCursor(0, 1);
  if (minuten < 10) lcd.print("0");
  lcd.print(minuten);
  lcd.print(":");
  if (sekunden < 10) lcd.print("0");
  lcd.print(sekunden);
}


void setup() {
  lcd.begin(16, 2);
  pinMode(minuten_switch,INPUTMODE);
  pinMode(sekunden_switch ,INPUTMODE);
  pinMode(start_switch, INPUTMODE);
  lcd.print("TIMER:");
}

void loop() 
{
  eingabe();
  verarbeitung();
  ausgabe();
  delay(5); // Kleines Delay zum softwaremäßigen Entprellen der mechanischen Schalter
}

Der Code ist so wie er dasteht für eine Schaltung OHNE PULLDOWN-Widerstände, stattdessen werden die internen PullUps aktiviert. Im Quellcode kann man leicht zwischen INPUT_PULLUP (interne PullUps) oder INPUT (externe PullDowns) wechseln, je nach vorhandener Schaltung.

Bei den internen Variablen verwende ich nicht Minuten und Sekunden getrennt, sondern eine Variable “timerLaufzeit”, in der die Timerlaufzeit in Sekunden drinsteht. Wenn man die verschiedenen Buttons drückt, wird diese Variable entweder um 60 Sekunden hochgesetzt (plus 1 Minute) oder um 1 Sekunde, dadurch vermeidet man Umstände beim Minutenwechsel.

Der aktuelle Zählerstand in Minuten und Sekunden wird erst bei der Ausgabe mit Modulo-Arithmetik ausgerechnet, und dann auf dem LCD ausgegeben.

Nur mal so als Demo, wie ein modulares und leicht wartbares Programm aussehen könnte.