Hallo allerseits!
Da immer wieder danach gefragt wird, wie man einen Sketch auf Tastendruck startet, habe ich mein Arduino-Geschreibsel um ein Abschnittchen ergänzt. Und weil es dazu passt, habe ich noch ein Abschnittchen zum Thema Entprellen dazugeschrieben.
Könnte das mal jemand lesen und Bescheid geben ob da etwas fehlt oder falshc ist?
Siehe hier (unten).
Vielen Dank vorweg!
Gregor
Nachdem mir doch noch eine Änderung eingefallen ist, habe ich noch einmal damit begonnen, das zu überarbeiten. Die oben gestellte Frage bitte ignorieren (ein Posting löschen geht gerade nicht).
Das Entprellen entprellt nicht, sondern wartet genauso wie der erste Kode.
Da bool state; mit LOW definiert ist, wird das while beim 'Entprellen' nicht mal betreten.
Zum Entprellen muss man die Taste nicht 2x abfragen. Das delay verzögert die loopausführung ja auch wieder. Man muss eigentlich nur dafür sorgen, dass die Taste nicht zu oft abgefragt wird. Einfach mit einer millis() abfrage dafür sorgen, dass gegebenenfalls die Abfrage übersprungen wird. 'State' bleibt dann einfach wie es war. Braucht der loop() sowieso länger als die Prellzeit der Taste, ist gar keine besondere Maßnahme nötig.
if ( millis() - lastcheck ) > prellzeit ) {
state=digitalRead(switchPin);
lastcheck = millis();
}
Hat man mehrere Tasten abzufragen, kann man die alle gemeinsam in dem if abfragen. Das macht man einmal am Anfang vom loop(). Im loop werden dann für das Prüfen des Tastenstatus die entsprechenden Variablen verwendet.
Hallo,
das da noch niemand drauf kam, auf diese umgekehrte vereinfachte Herangehensweise. Hut ab. 
Das erinnert mich an meinen Lieblingsspruch. "Alle sagen es geht nicht. Dann kam jemand der wusste das nicht und hat es gemacht."
Habe es soeben getestet, entprellt wirklich. Hatte nämlich erst die Tage zwei neue Entprellfunktion erstellt mit Tastfunktion und Taster als Schalter Funktion. Mit 2x einlesen und Zustandsvergleich.
Wenn es um Tastenbetätigung geht, ist in der Regel sogar eindelay(5);bei einem Wechsel ausreichend. Gegen mircoBahners Ansatz, nur alle x millisekunden mal die Tasten abzufragen, spricht aber auch nichts.
Zum Entprellen muss man die Taste nicht 2x abfragen
Sehr richtig. Wenn ein Zustandswechsel erkannt wurde, war es auch einer. Das störende Prellen passiert danach.
michael_x:
Gegen mircoBahners Ansatz, nur alle x millisekunden mal die Tasten abzufragen, spricht aber auch nichts.
Dann kriegt man aber legitime Tastendrücke nicht, wenn man gerade im falschen Moment der Prellzeit abfragt.
Ich dachte, nur alle paar ms mechanische Taster abfragen, um das Prellen zu vermeiden, wäre state of the art.
Ich hab das schon von Anfang an so gemacht.
ElEspanol:
Ich dachte, nur alle paar ms mechanische Taster abfragen, um das Prellen zu vermeiden, wäre state of the art.
Ich hab das schon von Anfang an so gemacht.
Ich hab's auch noch nie anders gemacht 
ElCaron:
Dann kriegt man aber legitime Tastendrücke nicht, wenn man gerade im falschen Moment der Prellzeit abfragt.
Nein, das ist kein Problem, auch wenn ein Abfragezeitpunkt mitten in der Prellzeit liegt. Diese Abfrage erkennt dann entweder den alten, oder schon den neuen Zustand. Die nächste Abfrage erkennt aber auf jeden Fall den neuen Zustand. D.h. es kann lediglich passieren, dass man den neuen Zustand einen Abfragezyklus später erkennt. Das ist aber bei der Abfrage von mechanischen Schaltern kein Problem. Wenn der Taster ein paar ms später gedrückt worden wäre, hätte man es auch erst beim nächsten Abfragezyklus erkannt.
Das Verfahren lässt sich auch sehr gut verwenden, wenn die Taster in einer Matrix angeordnet sind um IO's zu sparen. Die gesamte Matrixabfrage kommt dann in das millis()-if und man kennt alle Schalterzustände ohne jeden einzelnen entprellen zu müssen.
Hallo,
ich habe heute deine Einmal-Tasterabfrage Intervall Idee bei mir umgesetzt. Also meine bisherige 2x Abfrage darauf umgebaut. Dabei musste ich nun leider feststellen, dass das Einmalintervall doch nicht so optimal ist. Man bekommt unkontrollierte Tastendruck-Zustände. Erhöht man nämlich die Entprellzeit und hämmert auf die Taste, dann schaltet zum Bsp. die Test-LED unkontrolliert um. Obwohl es eigentlich gilt die Entprellzeit einzuhalten nach Erkennung der Tasterzustandsänderung. Der Effekt ist der als wenn man in loop delay verwendet und den Taster permanent abfragt. Es wird zum reinen Zufallsspiel.
Doc_Arduino:
... dass das Einmalintervall doch nicht so optimal ist. Man bekommt unkontrollierte Tastendruck-Zustände. ...
Diese Beobachtung habe ich bei einem schnellen Test auch gemacht und wollte sie noch genauer untersuchen.
Gruß
Gregor
Hallo,
den Unterschied beider Ansätze kann man schlecht beschreiben. Muss man selbst mal getestet haben. Bei einer reinen Taster-Funktion-Erkennung mag das einmalige erkennen im Intervall noch funktionieren. Jedoch auch hier tritt das Problem schon auf, dass man zufällig ein Signal einfängt, weil das Intervall gerade um ist. Mag durchaus Sinn der Sache sein mit der Vereinfachung. Im Kern möchte man jedoch den eigentlichen Taster entprellt wissen und nicht aller x ms irgendeinen Pegel einlesen. Der kann nämlich auch in dem Moment als nicht gedrückt erkannt werden, wenn er ungünstig prellt.
Bei meiner Schalterfunktion mit Taster kommt dann das Problem so richtig zum tragen. Die LED schaltet unkontrolliert um.
So gut ich die Vereinfachung am Anfang auch fand, momentan überzeugt mich das nicht mehr so. Leider.
Das verstehe ich jetzt nicht. Wie gesagt mache ich das schon immer so, und solche Effekte wie Du beschreibst hatte ich noch nie.
Wichtig ist natürlich, dass man den Taster wirklich nur 1x in dem Intervall abfragt, auch wenn man den Status im Loop z.B. mehrfach benötigt. Das ist wie in der SPS-Technik: Am Anfang des Loops alle Eingänge abfragen und speichern. Im Loop werden dann nur die gespeicherten Werte verwendet, und erst beim nächsten Loop-Start werden wieder die Eingänge abgefragt.
Kannst Du den Sketch mal zeigen, wo das auftritt?
Zur Verdeutlichung mal eine kleine Grafik:
Oben das direkte Signal vom Taster, in der Mitte die Abtastintervalle. Unten der Zustand der Variablen mit dem Tasterstatus im Loop. Das ist ein einwandfrei entprelltes Signal. Auch wenn der Abtastzeitpunkt mitten in die Prellzeit fällt. Ob in der Prellzeit HIGH oder LOW erkannt wird spielt keine Rolle. Dann verschiebt sich die Flanke im entprellten Tasterstatus nur um eine Intervallzeit nach links oder rechts.
Wie EIEspanol oben schrieb, ist das eigentlich 'State of the art' beim Einlesen von mechanischen Schaltern. Ich kenn es nicht anders.
Es sollte natürlich klar sein, dass man nur einmal abfragt und dann mit der Zustandsvariable arbeitet, und idealerweise nach dem hier im Forum schon mehrfach erwähnten EVA-Prinzip.
Bei einer entsprechenden Intervallzeit kann man da auch schön einfach Variablen inkrementieren bzw. Dekrementieren, wenn man länger drückt.
Und die ganzen anderen Funktionen wie langes Drücken etc. kann man auch machen, nur anstatt digitalRead nimmt man halt die Zustandsvariable.
P.S. Ich möchte noch anmerken, ich habe das ganze nie professionell gelert, sondern bin Autodidakt und lasse mich gerne von anderen inspirieren.
Hallo,
mir fiel das Problem bzw. der Unterschied der Methoden wieder auf, als ich das im direkten Vergleich getestet habe. Man befasst sich damit ja nicht jeden Tag neu. Es bleibt ein Unterschied ob ich im Intervall irgendein Signal abfrage oder gezielt einen Taster entprelle.
Desweiteren teste ich Code immer mit Extremwerten. Nur dann fallen Ungereimtheiten auf. Wenn ich den Einmal-Intervall Code mit 500ms teste und wild auf den Taster hämmer, dann erhalte ich Zufallswerte. Weil eben nicht die Entprellzeit von 500ms eingehalten sondern nur aller 500ms irgendwas abgefragt wird. Das ist der kleine aber feine Unterschied.
Mit der üblichen 2x Abfragemethode, vor und nach der Entprellzeit, muss ich den Taster genau so lange drücken wie die eingestellte Entprellzeit ist. Damit erhalte ich klare Zustände.
meine bisherige Version:
const byte LED_30 = 30;
const byte LED_31 = 31;
const byte push_button_Pin = 2;
const byte switch_Pin = 3;
void setup()
{
pinMode(push_button_Pin, INPUT_PULLUP); // mit INPUT_PULLUP invertierte Logik
pinMode(switch_Pin, INPUT_PULLUP); // mit INPUT_PULLUP invertierte Logik
pinMode(LED_30, OUTPUT);
pinMode(LED_31, OUTPUT);
}
void loop()
{
bool state1 = Taster_Handling(push_button_Pin);
digitalWrite(LED_30, state1); // Taster abfragen
bool state2 = Schalter_Handling(switch_Pin);
digitalWrite(LED_31, state2); // Taster abfragen
}
bool Taster_Handling (byte Pin_Button)
{
static unsigned long pushButtonDetectTime = 0;
const unsigned int Entprellzeit = 40; // Taster Entprellzeit [ms]
static bool last_state_Button = HIGH; // INPUT_PULLUP invertierte Logik
static bool state_change = false;
static bool state_push = LOW; // bestimmt Ausgangssignal-Logik, negiert oder nicht
bool read_Button = digitalRead(Pin_Button); // Tasterstatus einlesen
if (read_Button != last_state_Button && state_change == false) { // wenn Tasterstatus verschieden zu vorher
pushButtonDetectTime = millis(); // Zeit vom letzten Tastendruck merken
state_change = true; // Änderung temporär sperren
}
if (state_change == true && (millis() - pushButtonDetectTime) > Entprellzeit) {
if (read_Button != last_state_Button) {
last_state_Button = read_Button; // neuen Status merken
state_push = !last_state_Button; // beeinflusst Ausgangssignal, negiert oder nicht
}
state_change = false;
}
return state_push;
}
bool Schalter_Handling (byte Pin_Button)
{
static unsigned long pushButtonDetectTime = 0;
const unsigned int Entprellzeit = 40; // Taster Entprellzeit [ms]
static bool last_state_Button = HIGH; // INPUT_PULLUP invertierte Logik
static bool state_change = false;
static bool state_switch = LOW; // bestimmt Ausgangssignal-Logik, negiert oder nicht
bool read_Button = digitalRead(Pin_Button); // Tasterstatus einlesen
if (read_Button != last_state_Button && state_change == false) { // wenn Tasterstatus verschieden zu vorher
pushButtonDetectTime = millis(); // Zeit vom letzten Tastendruck merken
state_change = true; // Änderung temporär sperren
}
if (state_change == true && (millis() - pushButtonDetectTime) > Entprellzeit) {
if (read_Button != last_state_Button) {
if (read_Button == LOW) { // Taster gedrückt? read_Button LOW?
last_state_Button = read_Button; // neuen Status merken
state_switch = !state_switch; // Schalter-Status umschalten
}
else { // read_Button HIGH ?
last_state_Button = read_Button; // neuen Status merken
}
}
state_change = false;
}
return state_switch;
}
Hallo,
jetzt testet mal beide Codes mit verschiedenen "Entprellzeiten".
Wer 4 Taster und 4 LEDs dran hat, kann auch beide Sketche zusammenwerfen.
die umgesetzte Intervall-Methode:
const byte LED_30 = 30;
const byte LED_31 = 31;
const byte push_button_Pin = 2;
const byte switch_Pin = 3;
void setup()
{
pinMode(push_button_Pin, INPUT_PULLUP); // mit INPUT_PULLUP invertierte Logik
pinMode(switch_Pin, INPUT_PULLUP); // mit INPUT_PULLUP invertierte Logik
pinMode(LED_30, OUTPUT);
pinMode(LED_31, OUTPUT);
}
void loop()
{
bool state1 = Taster_Handling(push_button_Pin);
digitalWrite(LED_30, state1); // Taster abfragen
bool state2 = Schalter_Handling(switch_Pin);
digitalWrite(LED_31, state2); // Taster abfragen
}
bool Taster_Handling (byte Pin_Button)
{
const unsigned int Entprellzeit = 500; // Taster Entprellzeit [ms]
static unsigned long last_ButtonCheck = 0;
static bool last_state_Button = HIGH; // bestimmt Ausgangssignal-Logik, negiert oder nicht
if (millis() - last_ButtonCheck > Entprellzeit) { // wenn Tasterstatus verschieden zu vorher
last_ButtonCheck = millis(); // Zeit vom letzten Check merken
last_state_Button = digitalRead(Pin_Button);
}
return !last_state_Button; // by press HIGH
}
bool Schalter_Handling (byte Pin_Button)
{
const unsigned int Entprellzeit = 500; // Taster Entprellzeit [ms]
static unsigned long last_ButtonCheck = 0;
static bool read_Button = HIGH; // INPUT_PULLUP invertierte Logik
static bool last_state_Button = HIGH; // INPUT_PULLUP invertierte Logik
static bool state_switch = LOW; // bestimmt Ausgangssignal-Logik, negiert oder nicht
if (millis() - last_ButtonCheck > Entprellzeit) { // wenn Tasterstatus verschieden zu vorher
last_ButtonCheck = millis(); // Zeit vom letzten Check merken
read_Button = digitalRead(Pin_Button);
if (read_Button != last_state_Button) {
if (read_Button == LOW) { // Taster gedrückt? read_Button LOW?
last_state_Button = read_Button; // neuen Status merken
state_switch = !state_switch; // Schalter-Status umschalten
}
else { // read_Button HIGH? Taster nicht gedrückt
last_state_Button = read_Button; // neuen Status merken
}
}
}
return state_switch;
}
Die Zeiten für Prellen, Taster einlesen, und die vorgesehene Taster-Betätigungszeit sollten sich deutlich unterscheiden.
Wenn ein ausgeleierter Taster ca 2..5 ms lang prellt und man keine Probleme mit "schnellem auf den Taster Hämmern" ( 50 ms ?) haben will, ist Taster einlesen alle 10..20 ms eine gute Wahl.
Microbahners Bild ist sehr deutlich, wobei da relativ oft, im Vergleich zum angenommenen Prellverhalten, gelesen wird.
Vorausgesetzt, dass keine wilden Störungen auf der Taster-Leitung sind, reicht einmal lesen immer aus.
Wenn man keine "superkurzen Doppelklicks" als 2 Betätigungen erkennen muss, ist auch eine Abfragezeit von 100 ms ok. ( 500 ms wie in Doc's Beispiel wäre mir allerdings etwas zu träge. )
Doc's Beispiel bool Taster_Handling(byte pin) {...} hat übrigens den gravierenden Nachteil, dass im vorgesehenen Intervall nur einmal ein Taster abgefragt wird, auch wenn Taster_Handling mehrfach aufgerufen wird !
void loop() {
bool State_x =Taster_Handling(pin_x);
bool State_y =Taster_Handling(pin_y); // kommt wohl selten zum Zuge :(
...
}
Hallo,
warum werde ich falsch zitiert bzw. interpretiert? Ich habe nirgends geschrieben das ich einen Taster mit 500ms Entprellzeit behandeln möchte. Ich hatte geschrieben das ich den Code mit Extremwerten teste. Meinetwegen auch im Sekundenbereich. Nur dann sieht man wie sich der Code wirklich verhält.
kommt wohl selten zum Zuge :(
Wie kommst du darauf? Jeder Taster hat seine eigene Funktion mit eigener Zeitvariable.
Wegen dem angesprochenen Nachteil. Das ist kein Nachteil. Das ist das Funktionsprinzip der üblichen Tasterentprellung wie man sie überall liest. Man schaut, hat sich am Tastereingang etwas verändert. Wenn ja, merkt man sich das und prüft nach der Entprellzeit nochmal ob der Zustand gleich geblieben ist oder nicht. Ist er gleich, hat man das Tastersignal ordentlich entprellt erkannt. Wenn nicht, war es kein Tastendruck.
Eure Intervall-Tasterabfrage funktioniert ja im Prinzip und ist ausreichend genau für einfache Tasterabfragen. Ist aber nicht das Allheilmittel schlechthin. Wenn man sich nur auf die Schematik versteift, sieht das man Probleme nicht. Viele Codeentwürfe funktionieren auf dem Papier einwandfrei. Bis man sie auf Herz und Nieren testet. Ich habe beide gegeneinander getestet und Unterschiede festgestellt. Das habe ich öffentlich mitgeteilt.
Hallo,
// kommt wohl selten zum Zuge :(
jetzt weiß ich wie du darauf kommst.
Du dachtest ich nutze die Funktion für alle Taster? Nein, da haben wir aneinander vorbei geredet. Das ist die Vorbereitung für meine Lib. Damit bekommt jeder Taster seine eigene Instanz.