Zählen wie oft ein Taster gedrückt wird

Hallo, bin noch relativ neu was arduino angeht. Nutzen in der Schule eine den arduino uno. Ich habe einen Taster der zählt wie oft er gedrückt wird.
Aktuell nehme ich immer den aktuellen Zustand von dem Taster wenn ich das richtig verstehe. Nun soll eine zweite Variante verwendet werden wo ich den letzten Zustand abfragen soll.
Hier auch einmal die Aufgabenstellung:

2. Lösung: Flankenerkennung mit Zustandsvariablen

Statt direkt auf HIGH zu prüfen, speichert man den vorherigen Zustand des Tasters und erkennt die steigende Flanke

Nur habe ich keine Ansatzpunkte dafür und auch nicht wirklich was gefunden bisher.

Danke schonmal im voraus

const int Taster = 4;
int Merker_Taster = 0;
int Merker = 0;
int Taster_Zahl = 0;

void setup() 
{
  pinMode(Taster, INPUT);

  Serial.begin(9600);
}

void loop()
{
  Merker_Taster = digitalRead(Taster);

  if((Merker_Taster == LOW)&&(Merker == 0))
  {
    Merker = 1;
    Taster_Zahl++;
    
    Serial.print("So oft hab ich gedrueckt");
    Serial.println(Taster_Zahl);
  }
 

  if((Merker_Taster == HIGH)&&(Merker == 1))
  {
    Merker = 0;
  }

}

Fangen wir oben im Skech an:

Auf den Wertetype achten:
const uint8_t TasterPin = 4;

int Merker_Taster = 0;
anpassen in
bool Merker_Taster = LOW;

gleiches für die 2 folgenden Zeilen

Also bool ist für mich klar und auch hier verständlich wieso das getauscht wird.

Hier versteh ich nicht ganz wieso das benötigt wird.

Nur mal aus Neugierde, welche Klasse?

Es geht also ums Lernen, daher bin ich etwas pingeliger und liefere Dir keine fertige Lösung, sollst ja selbst drauf kommen :wink:

Wie ist der Taster angeschlossen? Ich bin faul, daher verwende ich den internen PullUp-Widerstand und schalte nach GND. Da die steigende Flanke erkannt werden soll, verwendest Du vermutlich einen externen PullDown-Widerstand und schaltest nach +5 V. Richtig?

Eine Konstante zu verwenden, ist gut, aber warum sollte sie 16 Bits lang sein? So viele Pins hat ein UNO nicht und außerdem ist der UNO ein µC mit acht Bits. Daher besser const byte Taster = 4;. Für den optimierenden Compiler ist auch int kein Problem, der macht das trotzdem richtig.

Der Taster kann nur zwei Zustände, "ein" oder "aus", was true und false entspricht. Daher verschwendest Du Speicher, wenn Du das in 16 Bits speicherst. Daher ist der Typ bool passender.

Kann dieser Wert jemals negativ werden, wenn Du immer 1 addierst? Eher nicht, daher wäre unsigned int Taster_Zahl = 0; oder uint16_t Taster_Zahl = 0; passender. Dann kannst Du 216 = 65536 Mal auf den Taster tippen, bevor die Variable überläuft.

Vielen Dank für deine Antwort.
Bin am Anfang der Techniker Schule und ist für mich ein komplett neues Thema.

Den Taster zu PULLUP zu ändern habe ich bereits gemacht. Hatte den Widerstand vorher extern realisiert.
Das mit der Belegung mit dem Taster verstehe ich auch, das ich da eine zu lange Konstante verwende.

Mit den Merker bool verstehe ich auch. Ich habe ja nur die beiden Zustände HIGH und LOW.

Der Wert für den Zähler der kann nicht negativ werden das ist richtig. Also sagt das unsigned int nur aus das ich da kein Vorzeichen habe.
unit16_t heißt dann das ich unsigned int habe was auf 16bit beschränkt ist oder versteh ich das grade flasch?

Wo wäre dann der Unterschied zwischen const byte und const uint8_t? Oder ist das das selbe weil 1 byte sind ja 8 bit.

Typische Flankenerkennung, allerdings wirst Du bei einem prellendem Taster alles mögliche an Statusänderungen bekommen.

Ich hab gerade sowas gemacht.
Schau mal hier

Wenn Du was nicht verstehst: frag.
Aber ich werde nicht Deine Aufgabe allein lösen.

schau dir mal dieses Beispiel aus den IDE Beispielen an

State Change Detection (Edge Detection) for pushbuttons | Arduino Documentation

Mir scheint das ist 98% dessen was du suchst. Die LED kannst ja weg geben. Und die empfohlenen Verbesserungen bezüglich Variablentypen kannst ja trotzdem noch machen.

const uint8_t Taster = 4;      // unsigned int mit 8 bit
bool Merker_Taster = LOW;
bool Letzter_Zustand_Taster = LOW;
uint16_t Taster_Zahl = 0;      // unsigned int mit 16 bit   ist nicht notwendig das wir bei unsigned int sowieso nur 16bit haben

void setup() 
{
  pinMode(Taster, INPUT_PULLUP);

  Serial.begin(9600);
}

void loop()
{
  Merker_Taster = digitalRead(Taster);

  if(Merker_Taster != Letzter_Zustand_Taster)
  {
    delay(50);
    if(Merker_Taster == HIGH)
  {
    Taster_Zahl++;
  }
    
    
    Serial.print("So oft hab ich gedrueckt");
    Serial.println(Taster_Zahl);
  }
  Letzter_Zustand_Taster = Merker_Taster;        //Aktualisieren des Merkers
}

Hab mir die Sachen mal angeschaut und ein wenig geändert und selber probiert und bin aktuell bei diesem Ergebnis.

Formatiere mal mit Strg+T in der IDE, dann stehen die Klammern besser.
Das erleichtert auch Dir selbst den Überblick.

Gruß Tommy

Die Ausgabe passt nicht.
Du zählst nicht den Tastendruck, sondern die Flankenwechsel.

Und das mit der falschen Klammer weist Du ja schon

Versteh ich nicht ganz wenn ich ehrlich bin.

Ich muss ja den Flankenwechsel erfassen und speichern und anhand davon zähle ich doch beim betätigen des Tasters.

Auch wenn dir das nicht hilft... enthält es doch alle nötigen Komponenten.
Ist also nur "Anschauungsmaterial"

Aus meiner Wühlkiste:

#include <CombiePin.h>
#include <CombieTimer.h>
#include <CombieTools.h>
#include <CombieTypeMangling.h>

using namespace Combie::Millis;


using Taster         = Combie::Pin::TasterGND<2>; // Taster zwischen Pin und GND(invertierend)
using Counter        = Combie::Tools::Counter<unsigned>;

Counter            counter;
Combie::Timer::EntprellTimer     tasterEntprell {20_ms};
Combie::Tools::FlankenErkennung  tasterFlankenerkennung;


void setup() 
{
  Serial.begin(9600);
  Taster{}.initPullup();
  counter.onCount( [](Counter &counter)   // Lambda Funktion
                   {
                     Serial.print("Anzahl: ");
                     Serial.println(counter);
                   }
                 );
}

void loop() 
{
  counter = tasterFlankenerkennung = tasterEntprell = Taster{};
}

Die genutzten Libs findest du wenn du nach combielib.zip hier im Forum suchst.

Du zählst die Flanke beim drücken des Tasters UND beim loslassen.

 if (Merker_Taster != Letzter_Zustand_Taster) {        
    delay(50);
    if (Merker_Taster == HIGH) {
      Taster_Zahl++;
    }
  }
 

Verstehe leider deinen Punkt nicht oder mein Programm nicht richtig.
Im ersten if Schaue ich ob die beiden Werte die ich miteinander vergleiche nicht gleich sind.
Wenn das der fall ist komme ich zum zweiten if wo geschaut wird ob mein Merker HIGH ist.
Nur dann zähle ich doch. Also wenn der Taster gedrückt wird.
Also so verstehe ich mein Programm aktuell

Ah. Ok, dann mein Fehler. Ich hab die Klammer übersehen da die Einrückung so ulkig war. :man_facepalming:
Wenn Du jetzt noch das delay() auflöst, dann sieht das nicht so schlecht aus.

Kann ich das delay einfach weglassen?
Oder brauche ich das zum entprellen oder so

Du kannst es weglassen, wenn Du dafür die Routine zum entprellen änderst.

hab ich mal gemacht.
Und den Text berichtigt :slight_smile:

const uint8_t Taster = 4;      // unsigned int mit 8 bit
bool Merker_Taster = LOW;
bool Letzter_Zustand_Taster = LOW;
uint16_t Taster_Zahl = 0;      // unsigned int mit 16 bit   ist nicht notwendig das wir bei unsigned int sowieso nur 16bit haben
constexpr uint32_t debounceTime {50}; // Zeit in ms
uint32_t lastSwitchTime; // Merker


void setup()
{
  pinMode(Taster, INPUT_PULLUP);
  Serial.begin(9600);
}
void loop()
{
  if (millis() - lastSwitchTime >= debounceTime)
  {
    Merker_Taster = digitalRead(Taster);
    if (Merker_Taster != Letzter_Zustand_Taster)
    {
      lastSwitchTime = millis();
      if (Merker_Taster == HIGH) // Taste losgelassen
      {
        Taster_Zahl++; //
      }
      Serial.print(F("So oft hab ich losgelassen"));
      Serial.println(Taster_Zahl);
    }
    Letzter_Zustand_Taster = Merker_Taster;        //Aktualisieren des Merkers
  }
}

Danke schonmal.
Werde ich mir mal anschauen habe bisher noch nicht mit millis gearbeitet hatte aber schon gesehen das es die Möglichkeit gibt.

Man kann natürlich zum Entprellen etwas "ordentliches" nehmen. ( wie @my_xy_projekt gezeigt hat)
Wobei ich denke, bei der aktuellen Aufgabenstellung ist es auch OK, wenn man einfach etwas dagegen unternimmt, dass ein µController generell viel zu schnell ist:
Bei einem richtigen Taster ( statt von Hand zwei Drähte sich berühren lassen ) reicht auch ein delay(5); zum Entprellen.
Ob das nur bei einem Flankenwechsel oder bei jedem Durchlauf dran kommt, ist hier sogar egal.

Aber irgendwas zum Entprellen eines mechanischen Tasters muss sein. Probier's aus.

Eine Frage hab ich noch, weil es mir erst gerade aufgefallen ist.

Wieso Habe ich das F bei Serial.print?
Ist das überhaupt notwendig bei dem Programm?