Forum: Mikrocontroller und Digitale Elektronik Danneggers Entprellroutine im AVR benutzen


von Mark T. (bitts)


Lesenswert?

Hallo,

kann man Peter Danneggers Entprellroutine auch in der Arduino IDE und 
einem Mega32U4 benutzen?

Wenn ja, gibt es die Komfortversion schon als fertigen Sketch?

von Konrad S. (maybee)


Lesenswert?

Mark Thalle schrieb:
> kann man Peter Danneggers Entprellroutine auch in der Arduino IDE und
> einem Mega32U4 benutzen?

Ja.

> Wenn ja, gibt es die Komfortversion schon als fertigen Sketch?

Vermutlich nicht.

von Karl H. (kbuchegg)


Lesenswert?

Mark Thalle schrieb:
> Hallo,
>
> kann man Peter Danneggers Entprellroutine auch in der Arduino IDE und
> einem Mega32U4 benutzen?

im Prinzip ja.
Ist ja nichts anderes als ein Timer, der in regelmässigen Abständen 
einen Interrupt auslöst und ein wenig Code in der ISR. Der ISR Code ist 
da wie dort derselbe, bis auf mglw. den Zugriff auf den Input-Port.
Wie man in der Arduino Umgebung fachgerecht einen Timer samt zugehörigem 
Interrupt installiert, solltest du allerdings wissen. Du bist ja 
schliesslich der Arduino Programmierer. Aber der ganze Bitpfriemelteil 
in der ISR bleibt 100% identisch. Der ist nicht hardwareabhängig.

> Wenn ja, gibt es die Komfortversion schon als fertigen Sketch?

Möglich, dass sich das jemand schon mal gemacht hat. Ich glaubs aber 
ehrlich gesagt nicht wirklich.

: Bearbeitet durch User
von Konrad S. (maybee)


Lesenswert?

Karl Heinz schrieb:
> Wie man in der Arduino Umgebung fachgerecht einen Timer samt zugehörigem
> Interrupt installiert, solltest du allerdings wissen.

Wenn ich nicht irre, dann ist nur der Timer 0 vom Arduino-System belegt.

von Karl H. (kbuchegg)


Lesenswert?

Konrad S. schrieb:
> Karl Heinz schrieb:
>> Wie man in der Arduino Umgebung fachgerecht einen Timer samt zugehörigem
>> Interrupt installiert, solltest du allerdings wissen.
>
> Wenn ich nicht irre, dann ist nur der Timer 0 vom Arduino-System belegt.


Keine Ahnung. Irgendeiner muss es sein, denn irgendeiner muss ja die 
Systemuhr für millis() implementieren.

Aber wie für fast alles, gibt es auch für die Timer auf den 
Arduino-Webseiten zugehörige Doku und Beispiele, die man halt auch als 
Ardunio Programmierer mal lesen sollte. Da würde man dann auch was 
lernen, was über das Fragen nach fertigen Schketschen hinausgeht.
Darauf wollte ich eigentlich hinaus :-)

http://playground.arduino.cc/Deutsch/HalloWeltMitInterruptUndTimerlibrary

: Bearbeitet durch User
von Konrad S. (maybee)


Lesenswert?

Ah, hier: http://www.gammon.com.au/forum/?id=11488

"One of the internal timers (timer 0) is set up to interrupt roughly 
1000 times a second, and increment an internal counter which effectively 
becomes the millis() counter."

Karl Heinz schrieb:
> was über das Fragen nach fertigen Schketschen hinausgeht.
> Darauf wollte ich eigentlich hinaus :-)

Ich höre, was du nicht sagst! ;-)

von Jürgen S. (jurs)


Lesenswert?

Mark Thalle schrieb:
> kann man Peter Danneggers Entprellroutine auch in der Arduino IDE und
> einem Mega32U4 benutzen?

Im Prinzip ja.

Aber wozu soll es gut sein?

Die Arduino-Software stellt Dir in jedem Arduino-Sketch die Funktionen 
millis() und micros() als Zeitbasis zur Verfügung.

Alleine bereits mit Hilfe von millis() wird doch jedes softwaremäßige 
Entprellen zu einem völlig trivialen Selbstgänger.

So what?

von Peter D. (peda)


Lesenswert?

Jürgen S. schrieb:
> Alleine bereits mit Hilfe von millis() wird doch jedes softwaremäßige
> Entprellen zu einem völlig trivialen Selbstgänger.

Prust, Lach.

von Mark T. (bitts)


Lesenswert?

Es war gestern wohl schon zu spät und ich war nach der Sucherei bzgl. 
der Entprellroutine schon etwas malle. Im Betreff wollte ich nach dem 
Arduino gefragt haben.


Karl Heinz schrieb:
> Mark Thalle schrieb:

>> kann man Peter Danneggers Entprellroutine auch in der Arduino IDE und
>> einem Mega32U4 benutzen?
>
> im Prinzip ja.
> Ist ja nichts anderes als ein Timer, der in regelmässigen Abständen
> einen Interrupt auslöst und ein wenig Code in der ISR. Der ISR Code ist

So weit hatte ich die Threads und den Artikel hier im Forum auch 
verstanden und das leuchtet mir auch ein.
Kleine Teile des C-Codes kann ich auch nachvollziehen, aber es ist zu 
viel dabei, was ich nicht verstehe.

> da wie dort derselbe, bis auf mglw. den Zugriff auf den Input-Port.
> Wie man in der Arduino Umgebung fachgerecht einen Timer samt zugehörigem
> Interrupt installiert, solltest du allerdings wissen. Du bist ja
> schliesslich der Arduino Programmierer. Aber der ganze Bitpfriemelteil
> in der ISR bleibt 100% identisch. Der ist nicht hardwareabhängig.

Ich fange gerade mit Arduino an und habe "Hallo Welt" erfolgreich 
getestet.
Ansonsten habe ich vor vielen Jahren mal ganz oberflächlich was mit PICs 
gemacht. Timer und Interrupts habe ich noch nie angefasst.
Mit C hatte ich noch nie etwas zu tun. Ich habe lediglich mal ein klein 
wenig mit Borland Pascal und Python gemacht, aber auch höchstens auf dem 
Niveau dessen, was man unbedingt während der Schule und des Studiums 
braucht.
Ich bin also kein Programmierer.

In meinem ersten Arduino-Projekt wollte ich nur ein paar digitale 
Eingänge einlesen und abhängig von deren Kombination ein paar Ausgänge 
setzen.


Nachdem, was ich im Arduino-Bereich gelesen habe, traue ich mir auch zu, 
selbst irgendwie Tasten zu entprellen. So effizient, wie die 
Dannegger-Methode wird das aber garantiert nicht, weshalb ich gerne 
darauf zurück greifen würde.
Die Übersetzung von C oder ASM in die Arduino IDE traue ich mir aber 
nicht zu.

>> Wenn ja, gibt es die Komfortversion schon als fertigen Sketch?
>
> Möglich, dass sich das jemand schon mal gemacht hat. Ich glaubs aber
> ehrlich gesagt nicht wirklich.

Ich habe auch nach längerer Suche nichts dazu gefunden.
Das finde ich merkwürdig, denn die Dannegger-Methode scheint ziemlich 
anerkannt und effizient zu sein. Für einen halbwegs fähigen 
Programmierer dürfte es doch nur ein paar Minuten dauern, bis er das für 
einen Arduino umgefriemelt hat.

: Bearbeitet durch User
von Cyblord -. (cyblord)


Lesenswert?

Mark Thalle schrieb:
> Für einen halbwegs fähigen
> Programmierer dürfte es doch nur ein paar Minuten dauern, bis er das für
> einen Arduino umgefriemelt hat.

Aber warum würde ein "halbwegs fähiger Programmierer" sowas tun? Evt. 
mit Waffe am Kopf oder nach schwerer Hirnverletzung, aber sonst? Warum 
sollte man sich in die Niederungen begeben? Hilft der Adler dem 
Regenwurm ein Loch zu buddeln?

von Peter D. (peda)


Lesenswert?

Das Hauptproblem dürfte sein, daß die Arduino-Lib über Nummern auf die 
Pins zugreift, die sich zufällig aus dem Platinenlayout ergeben haben.
Die Entprellroutine möchte aber über die nativen Portbezeichner 
(PINA..L) zugreifen.
Das kann beim Anfänger zur Verwirrung führen, da er nicht weiß, welche 
Arduino-Nummer mit welchem Portpin des AVR korrespondiert.

von Jürgen S. (jurs)


Lesenswert?

Mark Thalle schrieb:
> Ich habe auch nach längerer Suche nichts dazu gefunden.

Beschreibe doch mal konkret, um was es bei Dir genau geht und wo das 
Problem liegt?

Also es geht um mehrere Taster/Schalter, das habe ich richtig 
verstanden?

Was brauchst Du denn von den Schaltern an Information:

Nur den HIGH/LOW Status?

Oder eine Info, "Taster wechselt den Status" (Flankenwechsel HIGH/LOW), 
um genau auf das Drücken oder das Loslassen des Tasters bzw. das Umlegen 
des Schalters mit einer Aktion zu reagieren?

Oder möchtest Du bei einem Taster auf unterschiedlich lange Tastendrücke 
reagieren, z.B. "kurzer Tastendruck<1s" und "langer Tastendruck>=1s"?

Oder brauchst Du sowas wie zählende Multifunktionsschalter, um mehr als 
zwei Funktionen auf einen Taster zu legen, bei dem zum Beispiel "Taster 
dreimal schnell nacheinander gedrückt" von "Taster viermal schnell 
nacheinander gedrückt" anhand von Timeout-Bedingungen unterschieden 
wird, um je nach Anzahl verschiedene Funktionen aufzurufen, gesteuert 
über nur einen Taster?

Hast Du Dir schon überlegt, wie die Taster hardwareseitig angeschlossen 
werden sollen: Generell mit PullDown-Widerständen? Oder sollen die 
internen PullUp-Widerstände des Atmega-Controllers zum Pullen des Inputs 
verwendet werden?

Je nachdem ist es dann mit 3 Zeilen Code getan. Oder etwas mehr, je 
nachdem was die Schalter an Logik ausspucken sollen.

Der häufigste Anwendungsfall dürfte wohl sein:
a) Du möchtest auf das Drücken (oder Loslassen) eines Tasters reagieren
b) Die Stellung anderer Schalter HIGH/LOW soll dabei berücksichtigt 
werden

Kann man das grob so beschreiben, was Du machen möchtest?

Dann könnte ich Dir dafür mal ein paar Zeilen kommentierten Demo-Sketch 
schreiben wie es mit der Erfassung und Entprellung in einem 
Arduino-Sketch funktioniert, wenn Du möchtest.

von Konrad S. (maybee)


Lesenswert?

Mark Thalle schrieb:
> Die Übersetzung von C oder ASM in die Arduino IDE traue ich mir aber
> nicht zu.

Die "Arduino IDE" versteht C problemlos, weil dahinter ein gcc die 
Arbeit macht.

von Karl H. (kbuchegg)


Lesenswert?

Mark Thalle schrieb:

> So weit hatte ich die Threads und den Artikel hier im Forum auch
> verstanden und das leuchtet mir auch ein.
> Kleine Teile des C-Codes kann ich auch nachvollziehen, aber es ist zu
> viel dabei, was ich nicht verstehe.

Das macht nichts. Selbst gestandene Programmierer brauchen 20 Minuten 
oder mehr, bis sie den ISR Code in allen Einzelheiten analysiert haben.

Aber: Den muss man als Anwender auch nicht verstehen. Dazu funktioniert 
er einfach zu gut. Du kannst ja wahrscheinlich auch keinen Code 
schreiben, der die Wurzel einer Zahl berechnet, hast aber keine 
Hemmungen die Funktion sqrt() zu verwenden. Oder: Kannst du eine 
dynamische Speicherverwaltung implementieren? Ich denke mal, die Antwort 
lautet: nein. Trotzdem benutzt du malloc() oder new ohne mit der Wimper 
zu zucken.

Sieh den Code als Baustein an, der dein Problem löst. Mehr ist es nicht. 
Ob du die Feinheiten der paar Zeilen Code in der ISR in allen Details 
verstehst oder nicht, spielt da keine Rolle.

Wie PeDa schon sagte: Das Hauptpropblem wird wohl sein, dass der Code 
die Portpins direkt über das PIN Register abfrägt, während in der 
Arduino Welt wohl mehr der Umweg über digitalRead gebräuchlich sein 
dürfte.


> Ansonsten habe ich vor vielen Jahren mal ganz oberflächlich was mit PICs
> gemacht. Timer und Interrupts habe ich noch nie angefasst.

Dann wird es Zeit das zu lernen.
Ein ordentliches reales Programm (also nicht irgendeine SPielerei) kommt 
in den wenigsten Fällen ohne einen Timer bzw. einen Timerinterrupt aus.


> In meinem ersten Arduino-Projekt wollte ich nur ein paar digitale
> Eingänge einlesen und abhängig von deren Kombination ein paar Ausgänge
> setzen.

Guter Tip.
Dann überleg dir zunächst Aufgabenstellungen, in denen du ohne 
Entprellung bzw. Tastenrepeat auskommst. Anstatt eine LED mit nur einem 
Taster ein und aus zu schalten, kann man das ja auch mit 2 Tastern 
machen. Der eine schaltet ein, der andere schaltet aus. Dazu muss man 
nichts entprellen.

> Nachdem, was ich im Arduino-Bereich gelesen habe, traue ich mir auch zu,
> selbst irgendwie Tasten zu entprellen.

Lass es.
Wenn schon, dann such nach einem fertigen Enptrellcode, der auch aus der 
Arduino Welt stammt. Da gibt es mit Sicherheit etwas.
Das mag zwar nicht so toll und effizient wie der PeDa Code sein und auch 
nicht die Möglichkeiten bieten, aber als Anfänger ist deine 
Entprelllösung mit Sicherheit schlimmer als alles was du irgendwo als 
vorgefertigter Sketch kriegen kannst.

: Bearbeitet durch User
von Quack (Gast)


Lesenswert?

Mark Thalle schrieb:
> Das finde ich merkwürdig, denn die Dannegger-Methode scheint ziemlich
> anerkannt und effizient zu sein.

Sei versichert, die "Dannegger-Methode" ist ein rein lokales Phaenomaen 
dieses Forums. Ausserhalb hat davon noch nie Jemand gehoert und das 
Verstaendnis fuer die Huldigung dieses schlecht dokumentierten 
Schnipsels Code geht ausserhalb dieses Forums gegen Null.

Ich bezweifle, dass Entprellen das erste wichtige Problem ist, das du 
als Arduino-Einsteiger loesen musst. Aber hier gibt es ein Beispiel fuer 
Arduino, das hinreichend funktioniert:

http://arduino.cc/en/Tutorial/Debounce

Das ist zwar nicht genial und verehrungswuerdig, aber du kannst dir ja 
zum Ziel setzen, eine geniale Alternative fuer Arduino zu schaffen, die 
dann allen Nutzern der Arduino-Welt zur Verfuegung steht? Das waere 
allemal mehr, als zum Beispiel Lord Maulheld auf die Reihe bekommt...

von Mark T. (bitts)


Lesenswert?

Peter Dannegger schrieb:
> Das Hauptproblem dürfte sein, daß die Arduino-Lib über Nummern auf die
> Pins zugreift, die sich zufällig aus dem Platinenlayout ergeben haben.
> Die Entprellroutine möchte aber über die nativen Portbezeichner
> (PINA..L) zugreifen.
> Das kann beim Anfänger zur Verwirrung führen, da er nicht weiß, welche
> Arduino-Nummer mit welchem Portpin des AVR korrespondiert.

Das ist zumindest ein Problem.
Ich habe die Komfortroutine aus dem Artikel in Mikrokontroller.net 1:1 
in die Arduino-IDE kopiert und kompilieren lassen.
Die erste Fehlermeldung ist:
error: 'PORTA' was not declared in this scope

Die Bezeichnung "PORTA" kommt mir noch aus PIC-Zeiten bekannt vor, 
sodass ich mir sicher war zu wissen, was gemeint ist. Die Suche nach 
diesem Begriff im Arduino-Bereich war leider nicht erfolgreich.

von spess53 (Gast)


Lesenswert?

Hi

>Die Bezeichnung "PORTA" kommt mir noch aus PIC-Zeiten bekannt vor,
>sodass ich mir sicher war zu wissen, was gemeint ist. Die Suche nach
>diesem Begriff im Arduino-Bereich war leider nicht erfolgreich.

Dann nimm mal das Datenblatt von dem AVR, der auf deinem Arduino verbaut 
ist, zur Hand.

MfG Spess

von Mark T. (bitts)


Lesenswert?

cyblord ---- schrieb:
> Mark Thalle schrieb:
>> Für einen halbwegs fähigen
>> Programmierer dürfte es doch nur ein paar Minuten dauern, bis er das für
>> einen Arduino umgefriemelt hat.
>
> Aber warum würde ein "halbwegs fähiger Programmierer" sowas tun? Evt.

Vielleicht weil er Spaß daran hat und die fertigen und vermutlich 
relativ billigen Arduinos auch für sich benutzen kann.

> mit Waffe am Kopf oder nach schwerer Hirnverletzung, aber sonst? Warum
> sollte man sich in die Niederungen begeben? Hilft der Adler dem
> Regenwurm ein Loch zu buddeln?

Warum veröffentlicht Peter seine Entprellroutine? Ich vermute, dass er 
nichts daran verdient und es ihm Freude bereitet.

von Karl H. (kbuchegg)


Lesenswert?

Quack schrieb:
> Mark Thalle schrieb:
>> Das finde ich merkwürdig, denn die Dannegger-Methode scheint ziemlich
>> anerkannt und effizient zu sein.
>
> Sei versichert, die "Dannegger-Methode" ist ein rein lokales Phaenomaen
> dieses Forums. Ausserhalb hat davon noch nie Jemand gehoert

sagen wir mal: zu wenige
und sagen wir mal: leider

Denn das Ding ist ein echter 'fire and forget' Code. Die paar Zeilen in 
die ISR einbauen (die man sowieso als Heartbeat hat) und im Handumdrehen 
hat man eine zuverlässige Entprellung für 8 Tasten, die alle Stückeln 
spielt, die man braucht.

von Cyblord -. (cyblord)


Lesenswert?

Mark Thalle schrieb:
> Warum veröffentlicht Peter seine Entprellroutine? Ich vermute, dass er
> nichts daran verdient und es ihm Freude bereitet.

Es geht auch nicht ums veröffentlichen, sondern um das Anfertigken eines 
Arduino-Sketch. Ich habe selbst schon einiges an Code veröffentlicht. 
Daran ist ja nichts verwerfliches.

von HoppelPoppel (Gast)


Lesenswert?

Quack schrieb:
>aber du kannst dir ja
>zum Ziel setzen, eine geniale Alternative fuer Arduino zu schaffen, die
>dann allen Nutzern der Arduino-Welt zur Verfuegung steht? Das waere
>allemal mehr, als zum Beispiel Lord Maulheld auf die Reihe bekommt...

Dem ist nichts hinzuzufügen.

von Mark T. (bitts)


Lesenswert?

Karl Heinz schrieb:
> Mark Thalle schrieb:

> Sieh den Code als Baustein an, der dein Problem löst. Mehr ist es nicht.
> Ob du die Feinheiten der paar Zeilen Code in der ISR in allen Details
> verstehst oder nicht, spielt da keine Rolle.

So wollte ich es auch machen.
Wenn ich den Code aber im Arduino benutzen möchte, muss ich den 
umgefriemelt bekommen. Und dazu muss ich wenigstens grob verstehen, was 
die einzelnen Befehle machen.

>> Nachdem, was ich im Arduino-Bereich gelesen habe, traue ich mir auch zu,
>> selbst irgendwie Tasten zu entprellen.
>
> Lass es.
> Wenn schon, dann such nach einem fertigen Enptrellcode, der auch aus der
> Arduino Welt stammt. Da gibt es mit Sicherheit etwas.
> Das mag zwar nicht so toll und effizient wie der PeDa Code sein und auch
> nicht die Möglichkeiten bieten, aber als Anfänger ist deine
> Entprelllösung mit Sicherheit schlimmer als alles was du irgendwo als
> vorgefertigter Sketch kriegen kannst.

Das ist wahrscheinlich so.
Dann werde ich das wohl auf die weniger effizienten Sketche 
zurückgreifen. Ich bin gespannt, ob das Entprellen damit zuverlässig 
funktioniert.

von Bernd N (Gast)


Lesenswert?

>> Wenn ich den Code aber im Arduino benutzen möchte, muss ich den
>> umgefriemelt bekommen. Und dazu muss ich wenigstens grob verstehen, was
>> die einzelnen Befehle machen.

Peters Code ist ja ausführlich erklärt. Der Thread zum Thema ist ja mehr 
als ein Roman.

Ansonsten schaust du hier...

http://www.compuphase.com/electronics/debouncing.htm

von Jürgen S. (jurs)


Lesenswert?

Mark Thalle schrieb:
> So wollte ich es auch machen.
> Wenn ich den Code aber im Arduino benutzen möchte, muss ich den
> umgefriemelt bekommen. Und dazu muss ich wenigstens grob verstehen, was
> die einzelnen Befehle machen.

Arduino-Beispielcodes sind oftmals extrem schlechte Beispielcodes.

Bei dem Debounce-Beispiel aus dem Tutorial finde ich es zum Beispiel 
gruselig, dass absolut nichts in die Richtung vorgeschlagen wird, mehr 
als einen Button auszuwerten.

Als totalen Anfängercode zum Auswerten und Entprellen mit einem 
Arduino-Sketch kann ich Dir das anbieten: Entprellen mit delay für 
mehrere gleichzeitig ausgewertete Taster. Einmal pro loop-Umlauf wird am 
Ende der loop ein kleines delay von einigen Millisekunden eingefügt, 
schon tritt kein Tastenprellen mehr auf.

Ich nenne das mal "Entprellen für Arme":
1
#define INPUTMODE INPUT_PULLUP  // INPUT oder INPUT_PULLUP
2
#define PRELLZEIT 5             // Prellzeit in Millisekunden
3
byte buttonPins[]={2,3,4,5,6,7,8}; // Arduino Pins
4
#define NUMBUTTONS sizeof(buttonPins)
5
byte buttonState[NUMBUTTONS];
6
7
void setup() {
8
  Serial.begin(9600);
9
  for (int i=0;i<NUMBUTTONS;i++) pinMode(buttonPins[i],INPUTMODE);
10
}
11
12
void loop() {
13
  for (int i=0;i<NUMBUTTONS;i++)
14
  {
15
    byte curState=digitalRead(buttonPins[i]); 
16
    if (INPUTMODE==INPUT_PULLUP) curState=!curState; // Vertauschte Logik bei INPPUT_PULLUP
17
    if (curState!=buttonState[i]) // Flankenwechsel am Button festgestellt
18
    {
19
      Serial.print(millis()/1000.0,3); // Zeitstempel mit Millisekundenauflösung
20
      Serial.print("\tChange on Pin-");Serial.print(buttonPins[i]);
21
      if (curState==true) Serial.println(" PRESSED");
22
      else Serial.println(" RELEASED");
23
    }
24
    buttonState[i]=curState;
25
  }
26
  delay(PRELLZEIT); // ==> Zeitliches Entprellen für Arme (mit delay)
27
}

Diese Art des Entprellens ist extrem leicht durchzuführen, sie ist 
allerdings nur für recht "langsame" Programme geeignet. Mit fünf 
Millisekunden delay in der loop sorgst Du nämlich dafür, dass die 
loop-Funktion nur maximal 200 mal pro Sekunde laufen kann. Das reicht 
natürlich dicke aus, um eine Modelleisenbahn zu steuern, oder 
irgendwelche Lampen und Motoren manuell ein und aus zu schalten. Es 
reicht auch aus, um manuell irgendwelche Einstellwert zu erfassen, um 
zum Beispiel eine Uhrzeit oder das Datum auf einem LCD-Display zu 
stellen oder Konfigurationsdaten zu bearbeiten und speichern.

In vielen Fällen reicht so eine Lösung deshalb locker aus.

Aber wenn Du extrem schnelle Vorgänge messen oder steuern möchtest, wo 
die loop-Funktion viele tausende Male pro Sekunde läuft, ist das 
Entprellen durch ein (wenn auch kleines) delay völlig ungeeignet.

Was möchtest Du denn mit den Buttons machen?

von nhvjzrrrft5r (Gast)


Lesenswert?

Quack schrieb:
> Sei versichert, die "Dannegger-Methode" ist ein rein lokales Phaenomaen
> dieses Forums. Ausserhalb hat davon noch nie Jemand gehoert und das
> Verstaendnis fuer die Huldigung dieses schlecht dokumentierten
> Schnipsels Code geht ausserhalb dieses Forums gegen Null.


So ist es.

Das Problem ist leider, dass man hier im Forum einen massiven Glaubens-
krieg beginnt, wenn man diesen Code kritisiert.

Dann bilden sich hier immer zwei Lager und durch die Unterstützung
von einem oder mehreren Moderatoren wird fanatisch jede kritische 
Meinung
nieder gemacht.

So hatten wir das schon mehrmals hier im Forum bei diesem Thema.


Zum Thema Code:

Das ist das Resultat der Anforderungen die Herr Dannegger sich bei der 
Entwicklung des Codes gestellt hat und die (neutral ausgedrückt)
nicht immer mit den eigenen Anforderungen harmonieren.

Ich bin mir auch ganz sicher, dass der Code anders ausgesehen
hätte, wenn Herr Dannagger andere Anforderungen gehabt hätte.

So bleibt am Schluss: Man muss ihn ja nicht einsetzen, wenn es nicht 
passt.

von Mark T. (bitts)


Lesenswert?

spess53 schrieb:
> Hi
>
>>Die Bezeichnung "PORTA" kommt mir noch aus PIC-Zeiten bekannt vor,
>>sodass ich mir sicher war zu wissen, was gemeint ist. Die Suche nach
>>diesem Begriff im Arduino-Bereich war leider nicht erfolgreich.
>
> Dann nimm mal das Datenblatt von dem AVR, der auf deinem Arduino verbaut
> ist, zur Hand.


Habe ich gemacht. PORTA gibt es beim 32U4 merkwürdigerweise nicht.
Ich habe jetzt einfach PORTB für die Tasten und PORTD für die LEDs 
genommen, nur um zu sehen, ob das Programm dann ohne Fehler kompiliert 
werden kann.
Das geht.

Dann kommen weitere Fehler:

error: 'TCCR0' was not declared in this scope
error: 'TIMSK' was not declared in this scope

Im Datenblatt finde ich  TCCR0A und TCCR0B

Beim Timer Control Register sollte man wohl schon wissen, welches man 
nimmt :-)

Ich vermute, dass hier der Timer konfiguriert wird, damit er mit einer 
geeigneten Frequenz ausgelöst wird.

  TCCR0 = (1<<CS02)|(1<<CS00);         // divide by 1024

Die Bitbezeichnung CS00 und CS02 finde ich im Datenblatt bei TCCR0B.

Wenn ich das im Code ändere, ist die entsprechende Fehlermeldung weg.
Merkwürdigerweise gibt es aber auch keine Fehlermeldung, wenn ich TCCR0A 
verwende:

  TCCR0A = (1<<CS02)|(1<<CS00);         // divide by 1024

Das finde ich merkwürdig, weil in TCCR0A die Bits CS00 und CS02 nicht 
vorhanden sind - laut Datenblatt.



Bleibt noch:
error: 'TIMSK' was not declared in this scope
  TIMSK |= 1<<TOIE0;                   // enable timer interrupt

Im Datenblatt finde ich TIMSK0..3.
Da das Bit TOIE0 gesetzt wird, welches zum TIMSK0-Register gehört, würde 
ich dieses nehmen.
Andererseits könnte gerade das erste Register für interne Zwecke oder 
durch irgendwelche anderen Dinge belegt sein.

Wenn ich TIMSK0 verwende kann ich den Code aber schon mal komplett 
fehlerfrei kompilieren.

Jetzt habe ich aber Muffen den Code in den Arduino zu laden, denn wenn 
der Mist macht, dann knabbert er mir vielleicht am Bootloader und ich 
kann den Arduino nicht mehr ansprechen.


Außerdem weiß ich nicht, an welchem Portpin die integrierte LED hängt.

Das ist bei Arduinos wohl nicht so offensichtlich. So einen hier habe 
ich:
https://learn.sparkfun.com/tutorials/pro-micro--fio-v3-hookup-guide/hardware-overview-pro-micro

: Bearbeitet durch User
von Konrad S. (maybee)


Lesenswert?

Mark Thalle schrieb:
> Beim Timer Control Register sollte man wohl schon wissen, welches man
> nimmt :-)

Konrad S. schrieb:
> Ah, hier: http://www.gammon.com.au/forum/?id=11488
>
> "One of the internal timers (timer 0) is set up to interrupt roughly
> 1000 times a second, and increment an internal counter which effectively
> becomes the millis() counter."

Wenn du das Arduino-System nicht stören willst, dann lass die Finger vom 
Timer 0.

> Außerdem weiß ich nicht, an welchem Portpin die integrierte LED hängt.

Hier 
https://learn.sparkfun.com/tutorials/pro-micro--fio-v3-hookup-guide/resources--going-further 
gibt es einen Link "Fio v3 Schematic" mit dem Schaltplan. Da steht alles 
drin.

von Jürgen S. (jurs)


Lesenswert?

Mark Thalle schrieb:
> Dann kommen weitere Fehler:
>
> error: 'TCCR0' was not declared in this scope
> error: 'TIMSK' was not declared in this scope
>
> Im Datenblatt finde ich  TCCR0A und TCCR0B
>
> Beim Timer Control Register sollte man wohl schon wissen, welches man
> nimmt :-)

Möchtest Du für das Board jetzt eigentlich mit "Arduino-like 
Programming" eine Firmware schreiben, mit den Arduino-Komfortfunktionen 
aus der Arduino-Core Library und dem Programmgrundgerüst aus "setup()" 
und "loop()" Funktion?

Oder möchtest Du das Board komplett auf Registerebene programmieren 
einschließlich Timer0, ohne die Arduino Konfortfunktionen und mit einer 
Progrmmhauptschleife "main()"?

Ich frage nur mal nach, denn: Sobald Du "setup()" und "loop()" als 
Programmgrundgerüst verwendest, wird Timer0 von der Arduino Core-Library 
bereits dafür aufgebraucht, um die Komfortfunktionen millis(), micros(), 
delay() und delayMicroconds() zur Verfügung zu stellen.

Sobald Du selbst an Timer0 rummachst, hast Du danach diese vier 
Funktionen aus dem Arduino Core nicht mehr zur Verfügung, bzw. diese 
vier (und etliche andere) funktionieren dann nicht mehr.

Wenn Du mit Timer0 in der Arduino-Software selbst rummachen möchtest, 
schreibst Du am besten ein Programm mit einer main() Funktion, ohne 
setup() und loop(). Dann ist das Arduino Core-Framework komplett außen 
vor.

Oder wenn Du die Arduino Core-Funktionen behalten möchtest, nimmst Du 
besser einen anderen Timer zum Rummachen, aber nicht Timer0!

von Mark T. (bitts)


Lesenswert?

Jürgen S. schrieb:
> Mark Thalle schrieb:

> Arduino-Beispielcodes sind oftmals extrem schlechte Beispielcodes.
>
> Bei dem Debounce-Beispiel aus dem Tutorial finde ich es zum Beispiel
> gruselig, dass absolut nichts in die Richtung vorgeschlagen wird, mehr
> als einen Button auszuwerten.

Ich habe bisher auch nur Lösungen für jeweils eine Taste gefunden. Eine 
Auswertung für langen Tastendruck oder Doppel-Klick, habe ich auch noch 
nicht gesehen.
So etwas habe ich schon mal mit Siemens Logo gebastelt. Das würde ich 
wahrscheinlich auch mit dem Arduino hinbekommen.

> Als totalen Anfängercode zum Auswerten und Entprellen mit einem
> Arduino-Sketch kann ich Dir das anbieten: Entprellen mit delay für
> mehrere gleichzeitig ausgewertete Taster. Einmal pro loop-Umlauf wird am
> Ende der loop ein kleines delay von einigen Millisekunden eingefügt,
> schon tritt kein Tastenprellen mehr auf.
>
> Ich nenne das mal "Entprellen für Arme":

:-) Wenn's funktioniert, hätte ich Spaß dran.

Erstmal Danke für den Code.


> Diese Art des Entprellens ist extrem leicht durchzuführen, sie ist
> allerdings nur für recht "langsame" Programme geeignet. Mit fünf
> Millisekunden delay in der loop sorgst Du nämlich dafür, dass die
> loop-Funktion nur maximal 200 mal pro Sekunde laufen kann.

Ich wüsste jetzt nicht, wie ich es umsetzen könnte, aber rein 
theoretisch könnte man doch in der Delay-Zeit etwas anderes machen.
Und man könnte auch einen Zähler hochlaufen lassen, der dafür sorgt, 
dass nur bei jedem 10. Durchlauf der Tastenzustand überprüft wird. Dann 
hätte man nur noch 0,5ms Delay und das restliche Programm würde 10 mal 
schneller laufen.
Je nach Anforderungen könnte man das dann ausreizen.

> Was möchtest Du denn mit den Buttons machen?

Ich möchte mit meinem ersten Projekt tatsächlich nur ein paar Relais und 
damit Raumbeleuchtung schalten. Das muss also nicht schnell sein. 
Prellen sollte es aber auf keinen Fall.

Ich würde aber gerne Doppelklicks und lange Betätigung einer Taste 
auswerten.

Das stelle ich mir im Moment so vor:

Pro Taste läuft ein Zähler hoch, solange die Taste ununterbrochen 
gedrückt ist.
Wird ein bestimmter Wert erreicht, wird das als "langer Tastendruck" 
ausgewertet und ein entsprechendes Bit gesetzt.

Beim Loslassen einer Taste läuft ebenfalls ein Zähler hoch. Wenn 
innerhalb einer gewissen Zeit die Taste wieder gedrückt wird, wird das 
als Doppel-Klick gewertet.

Ich habe allerdings noch kein Gefühl dafür, wie viele Ressourcen so 
etwas verbraucht und ob ich mit der Vorgehensweise auch viel mehr Tasten 
auswerten kann. Das wäre nämlich mein Ziel für spätere Projekte.
Bei der Siemens Logo ist man auf relativ wenige Funktionsbausteine 
begrenzt. Da bin ich schon bei sehr wenigen Tastenauswertungen mit 
Doppelklicks an die Grenzen gestoßen.


Außerdem möchte ich die Ausgänge vor zu häufigem Schalten schützen.
Kurz an und direkt wieder aus, soll beliebig schnell funktionieren. Nach 
dem Ausschalten sollen die Ausgänge aber erst nach einer geeigneten 
Verzögerung wieder eingeschaltet werden können.

Das ist es, was ich mir bisher so vorgenommen habe.

: Bearbeitet durch User
von Konrad S. (maybee)


Lesenswert?

Mark Thalle schrieb:
> :-) Wenn's funktioniert, hätte ich Spaß dran.

Spiel ruhig erst mal mit Jürgens Code, dann merkst du die Schwachstellen 
und kannst versuchen den Code zu verbessern. Wenn du davon die Schnauze 
voll hast, wendest du dich einfach wieder PeDas Code zu.

Kleiner Merkspruch: delay() löst keine Probleme, es verzögert sie nur!

von Mark T. (bitts)


Lesenswert?

Konrad S. schrieb:
> Mark Thalle schrieb:
>> Beim Timer Control Register sollte man wohl schon wissen, welches man
>> nimmt :-)
>
> Konrad S. schrieb:
>> Ah, hier: http://www.gammon.com.au/forum/?id=11488
>>
>> "One of the internal timers (timer 0) is set up to interrupt roughly
>> 1000 times a second, and increment an internal counter which effectively
>> becomes the millis() counter."
>
> Wenn du das Arduino-System nicht stören willst, dann lass die Finger vom
> Timer 0.

Ich meine, dass ich so etwas in der Art schon mit einem halben Auge 
gelesen hatte.
Kann ich an der Stelle dann einfach Timer 1..3 nehmen?

>
>> Außerdem weiß ich nicht, an welchem Portpin die integrierte LED hängt.
>
> Hier
> https://learn.sparkfun.com/tutorials/pro-micro--fi...
> gibt es einen Link "Fio v3 Schematic" mit dem Schaltplan. Da steht alles
> drin.


Danke. Das werde ich mir nachher rauskramen.

Ihr haut mir hier die Antworten schneller um die Ohren, als ich 
reagieren kann :-)
Auch dafür vielen Dank.

Ich würde jetzt gerne direkt weiter lesen und Sachen umsetzen, aber 
jetzt muss ich mich erst um meine Racker kümmern.
Meine Abwesenheit liegt nicht an Desinteresse.

von Mike (Gast)


Lesenswert?

Karl Heinz schrieb:
> Quack schrieb:
>> Mark Thalle schrieb:
>>> Das finde ich merkwürdig, denn die Dannegger-Methode scheint ziemlich
>>> anerkannt und effizient zu sein.
>>
>> Sei versichert, die "Dannegger-Methode" ist ein rein lokales Phaenomaen
>> dieses Forums. Ausserhalb hat davon noch nie Jemand gehoert
>
> sagen wir mal: zu wenige
> und sagen wir mal: leider
>
> Denn das Ding ist ein echter 'fire and forget' Code. Die paar Zeilen in
> die ISR einbauen (die man sowieso als Heartbeat hat) und im Handumdrehen
> hat man eine zuverlässige Entprellung für 8 Tasten, die alle Stückeln
> spielt, die man braucht.

richtig, die Routine ist jetzt nicht das was ein fähiger Programmierer 
auch selbst hinbekommen würde und eine ganz besonders hohen Intellekt 
bedarf es auch nicht soetwas zu schreiben. Ich denke Peter ist zwar 
stolz auf die Beliebtheit seiner Routine, aber ich denke nicht das er 
der Ansicht ist das wäre jetzt DIE geniale Erfindung. Er hats einfach 
gebraucht und für alle Anderen zur Verfügung gestellt. So läuft das 
richtig. Immerhin hat sie es ja sogar auch als Tasten-Manager-Bibliothek 
in Luna geschafft (in Assembler implementiert) und erfreut sich auch da 
hoher Beliebtheit.

von Peter D. (peda)


Lesenswert?

Jürgen S. schrieb:
> um die Komfortfunktionen millis(), micros(),
> delay() und delayMicroconds() zur Verfügung zu stellen.

Wenn diese sich in den Timerinterrupt einklinken, dann muß man natürlich 
nicht noch einen Timerinterrupt programmieren.
Der Code muß nur etwa alle 10ms ausgeführt werden. Wer das macht, ist 
völlig egal.

von Jürgen S. (jurs)


Lesenswert?

Mark Thalle schrieb:
> Ich habe bisher auch nur Lösungen für jeweils eine Taste gefunden.

Wie ich schon schrieb: Viele Arduino-Beispielcodes sind schlecht gemacht 
und orientieren sich nicht an der Praxis.

> Ich wüsste jetzt nicht, wie ich es umsetzen könnte, aber rein
> theoretisch könnte man doch in der Delay-Zeit etwas anderes machen.
> Und man könnte auch einen Zähler hochlaufen lassen, der dafür sorgt,
> dass nur bei jedem 10. Durchlauf der Tastenzustand überprüft wird. Dann
> hätte man nur noch 0,5ms Delay und das restliche Programm würde 10 mal
> schneller laufen.
> Je nach Anforderungen könnte man das dann ausreizen.

Das hast Du vom Prinzip er schon richtig erkannt: In der Zeit, in der 
mit delay() die Programmausführung blockiert wird, könnte das Programm 
auch viele, viele andere Dinge erledigen. Ein schnell agierendes 
Programm muß immer und grundsätzlich auf die Blockierung der 
Programmausführung mit delay() verzichten.

In der Praxis kann dazu der Stand des millis()-Zählers verwendet werden: 
Du merkst Dir einfach über den Stand des millis()-Zählers, wann die 
Taster zuletzt abgefragt wurden. Und dann läßt Du alle Funktionen im 
Sketch ablaufen, aber die Taster-Abfrage ist in der loop erst wieder 
dran, wenn der millis-Zähler um die Prellzeit weitergezählt hat. So 
laufen dann beispielsweise alle Funktionen in der loop() zigtausendmal 
pro Sekunde, aber die Taster-Abfrage läuft nur 200 mal pro Sekunde.

Um meinen oben geposteten Code entsprechend anzupassen, wären nur 
relativ geringe Änderungen notwendig.

>> Was möchtest Du denn mit den Buttons machen?
>
> Ich möchte mit meinem ersten Projekt tatsächlich nur ein paar Relais und
> damit Raumbeleuchtung schalten. Das muss also nicht schnell sein.
> Prellen sollte es aber auf keinen Fall.
>
> Ich würde aber gerne Doppelklicks und lange Betätigung einer Taste
> auswerten.
>
> Das stelle ich mir im Moment so vor:
>
> Pro Taste läuft ein Zähler hoch, solange die Taste ununterbrochen
> gedrückt ist.
> Wird ein bestimmter Wert erreicht, wird das als "langer Tastendruck"
> ausgewertet und ein entsprechendes Bit gesetzt.
>
> Beim Loslassen einer Taste läuft ebenfalls ein Zähler hoch. Wenn
> innerhalb einer gewissen Zeit die Taste wieder gedrückt wird, wird das
> als Doppel-Klick gewertet.

Hm, das finde ich ungünstig. Den Doppelklick würdest Du erkennen, wenn 
der Taster zweimal gedrückt wird. Einen langen Klick würdest Du 
erkennen, wenn ein gedrückter Taster nach langer Zeit losgelassen wird. 
Das ist ein bisschen überkreuz, denn so kann aus einem Doppelklick ja 
auch noch ein anschließender langer Klick werden:
- drücken - loslassen - drücken (Doppelklick komplett) - lange halten - 
loslassen
Dann wird am Schluß nach dem Doppelklick noch ein langer Tastendruck 
erkannt.

Im Endeffekt kannst Du natürlich jede Programmlogik programmieren, aber 
wenn die Logik darauf basiert Low-High Flankenwechsel auszuwerten 
(Taster drücken) und auch High-Low Flankenwechsel (Taster loslassen), 
dann schleppst Du schon ein paar Statusvariablen durch die Programmlogik 
mit durch.

Wesentlich einfacher wäre es zum Beispiel, folgende Logiken zu 
implementieren:
1. kurz drücken (<1s), lang drücken (1-2s), superlange drücken (>3s)
oder
2. zählen beim Drücken: x-mal drücken mit nachfolgendem Timeout

Mit 2. könntest Du über einen einzigen Taster locker 10 Funktionen 
realisieren, indem Du immer schnell x-mal hintereinander drückst und 
dann den Timeout (z.B. 1s) wartest, und 1s nach dem letzten Zählimpuls 
wird die Aktion ausgeführt, die x-mal drücken entspricht.

> Ich habe allerdings noch kein Gefühl dafür, wie viele Ressourcen so
> etwas verbraucht und ob ich mit der Vorgehensweise auch viel mehr Tasten
> auswerten kann. Das wäre nämlich mein Ziel für spätere Projekte.

"Ganz viele Tasten" wirst Du vermutlich nicht mehr an einzelnen 
digitalen Inputs auswerten wollen, weil Dir die digitalen Pins sonst 
ausgehen. Zum Beispiel kann man mit einer Hilfsbeschaltung aus einigen 
Widerständen auch sechs Buttons an einem einzigen Analogeingang 
auswerten. Oder eine Tastaturmatrix aus 3*4 = 12 Tasten mit 7 digitalen 
Eingängen. Oder, oder, oder.

> Außerdem möchte ich die Ausgänge vor zu häufigem Schalten schützen.
> Kurz an und direkt wieder aus, soll beliebig schnell funktionieren. Nach
> dem Ausschalten sollen die Ausgänge aber erst nach einer geeigneten
> Verzögerung wieder eingeschaltet werden können.

Das wäre dann Sache Deiner Programmlogik. Viele Arduino-Anfänger 
scheitern an einer sauberen Programmlogik, weil sie keinerlei 
Vorstellung von einer sauber durchführbaren Programmlogik haben.

Zum Beispiel sehe ich immer wieder solche Anfängerprogramme mit einer 
Logik, die in Pseudocode irgendwie so aussieht:
1
void loop()
2
{
3
  Wenn Taste-1 gedrueckt ==> dann tue dies
4
  Wenn Taste-2 gedrueckt ==> dann tue das
5
  Wenn Taste-3 gedrueckt ==> dann tue jenes
6
}

Das funktioniert genau so lange wie die Tasten und die damit gestuerten 
Funktionen absolut nichts miteinander zu tun haben. In der Praxis also 
eigentlich nie.

Deshalb müßtest Du darauf achten, eine saubere Programmlogik zu bauen, 
und eine sehr einfache Methode dazu ist das "EVA-Prinzip". Du mußt nur 
das Programm in drei logische Funktionsbereiche "Eingabe", 
"Verarbeitung" und "Ausgabe" aufteilen. Grundschema der Arduino-loop:
1
void loop()
2
{
3
  eingabe();
4
  verarbeitung();
5
  ausgabe();
6
}

Das bedeutet: In der Funktion "eingabe()" fragst Du nicht einen 
einzelnen Taster ab, um ihn zu verarbeiten, sondern Du fragst ALLE 
Buttons ab und speicherst den Schaltzustand in Variablen.

Im nachfolgenden Schritt "verarbeitung()" führst Du dann Deine logischen 
Operationen zunächst mal virtuell an Zustandsvariablen durch, aber ohne 
tatsächlich irgendwelche Ausgänge zu schalten. Dabei kannst Du die 
virtuellen Zustandsvariablen auch aufgrund unterschiedlicher Bedingungen 
mehrfach ändern, wenn nötig.

Und erst im letzten Schritt, werden die ermittelten Zustände dann an den 
Ausgängen oder auf einem Display, auf Serial oder wie auch immer gesetzt 
oder angezeigt.

Diese mangelnde Trennung in der loop:
- alle Eingangswerte lesen
- alle Eingangswerte verarbeiten und in Zustände umwandeln
- alle ermittelten Zustände ausgeben
erzeugt bei Anfängern meistens den Knoten im Gehirn für die passende 
Programmlogik.

Denn Anfänger denken oft nach dem Schema
- wenn der eine Button so steht, dann mache an den Augängen dies
- wenn der zweite Button so steht, dann mache an den Augängen das
- wenn der dritte Button so steht, dann mache an den Augängen jenes
Und so ist dann keine einwandfreie Programmlogik mehr möglich, wenn 
Abhängigkeiten untereinander bestehen.

von Jürgen S. (jurs)


Lesenswert?

Konrad S. schrieb:
> Mark Thalle schrieb:
>> :-) Wenn's funktioniert, hätte ich Spaß dran.
>
> Spiel ruhig erst mal mit Jürgens Code, dann merkst du die Schwachstellen
> und kannst versuchen den Code zu verbessern. Wenn du davon die Schnauze
> voll hast, wendest du dich einfach wieder PeDas Code zu.
>
> Kleiner Merkspruch: delay() löst keine Probleme, es verzögert sie nur!

Warum sollte er von einem kleinen Demo-Code "die Schnauze voll" haben?

Der Code ist zum Ausprobieren für einen Anfänger mit geringen Ansprüchen 
an die Ablaufgeschwindigkeit.

Und der Code ist bei weitem nicht ausgereizt und läßt sich für höhere 
Ansprüche auch leicht modifizieren.

Okay, hier dann noch einmal eine leicht modifizierte Version des oben 
geposteten Codes, ohne delay() zu verwenden und mit einer kleinen 
Benchmark-Zeitmessung, die nebenbei läuft:
1
#define INPUTMODE INPUT_PULLUP  // INPUT oder INPUT_PULLUP
2
#define PRELLZEIT 5             // Prellzeit in Millisekunden
3
byte buttonPins[]={2,3,4,5,6,7,8}; // Arduino Pins
4
#define NUMBUTTONS sizeof(buttonPins)
5
byte buttonState[NUMBUTTONS];
6
7
void setup() {
8
  Serial.begin(9600);
9
  for (int i=0;i<NUMBUTTONS;i++) pinMode(buttonPins[i],INPUTMODE);
10
}
11
12
void loop() {
13
  static unsigned long lastRunTime;
14
  static unsigned long counterStartTime;
15
  static unsigned long counter;
16
  if (millis()-lastRunTime>=PRELLZEIT) // Nur wenn die Prellzeit abgelaufen ist
17
  {
18
    lastRunTime=millis();
19
    for (int i=0;i<NUMBUTTONS;i++)
20
    {
21
      byte curState=digitalRead(buttonPins[i]); 
22
      if (INPUTMODE==INPUT_PULLUP) curState=!curState; // Vertauschte Logik bei INPPUT_PULLUP
23
      if (curState!=buttonState[i]) // Flankenwechsel am Button festgestellt
24
      {
25
        Serial.print(millis()/1000.0,3); // Zeitstempel mit Millisekundenauflösung
26
        Serial.print("\tChange on Pin-");Serial.print(buttonPins[i]);
27
        if (curState==true) Serial.println(" PRESSED");
28
        else Serial.println(" RELEASED");
29
      }
30
      buttonState[i]=curState;
31
    } // for
32
  } // if(millis()...
33
  counter++;
34
  if (counter==100000L)
35
  {
36
    Serial.print("Average Time per loop(): ");
37
    Serial.print((micros()-counterStartTime)/100000.0);
38
    Serial.println(" microseconds");
39
    counter=0;
40
    counterStartTime=micros();
41
  }
42
} // loop

Der durchschnittliche Zeitbedarf für ein Durchlaufen der loop-Funktion 
ändert sich auf einem mit 16 MHz getakteten Controller durch die 
Vermeidung des delay() von über 5ms = 5000µs auf durchschnittlich 
weniger als 10µs mit dem modifizierten Code.

von Quack (Gast)


Lesenswert?

Jürgen S. schrieb:
> Mit fünf
> Millisekunden delay in der loop sorgst Du nämlich dafür, dass die
> loop-Funktion nur maximal 200 mal pro Sekunde laufen kann.

Einfach keinen delay() nehmen, sondern die Entprellroutine zeitgesteuert 
aufrufen. Eine Zeitbasis ist mit millis() ja vorhanden. So kann man 
recht trivial verschiedene Tasks zeitgesteuert in loop() abhandeln - 
quasi kooperatives Multitasking. :-)

if( millis() - lastCallMillis > DELAY) ...

von stefanus (Gast)


Lesenswert?

> Deshalb müßtest Du darauf achten, eine saubere Programmlogik zu
> bauen, und eine sehr einfache Methode dazu ist das "EVA-Prinzip".

Was auch eine gute Basis für nächsten Schritt ist, dem 
Zustandsautomaten.

von Mark T. (bitts)


Lesenswert?

Jürgen S. schrieb:
> Mark Thalle schrieb:

> In der Praxis kann dazu der Stand des millis()-Zählers verwendet werden:
> Du merkst Dir einfach über den Stand des millis()-Zählers, wann die
> Taster zuletzt abgefragt wurden. Und dann läßt Du alle Funktionen im
> Sketch ablaufen, aber die Taster-Abfrage ist in der loop erst wieder
> dran, wenn der millis-Zähler um die Prellzeit weitergezählt hat. So
> laufen dann beispielsweise alle Funktionen in der loop() zigtausendmal
> pro Sekunde, aber die Taster-Abfrage läuft nur 200 mal pro Sekunde.

Einen Sketch, der darauf beruht, meine ich gestern gesehen und 
grundsätzlich verstanden zu haben.

>> Ich würde aber gerne Doppelklicks und lange Betätigung einer Taste
>> auswerten.
>>
>> Das stelle ich mir im Moment so vor:
>>
>> Pro Taste läuft ein Zähler hoch, solange die Taste ununterbrochen
>> gedrückt ist.
>> Wird ein bestimmter Wert erreicht, wird das als "langer Tastendruck"
>> ausgewertet und ein entsprechendes Bit gesetzt.
>>
>> Beim Loslassen einer Taste läuft ebenfalls ein Zähler hoch. Wenn
>> innerhalb einer gewissen Zeit die Taste wieder gedrückt wird, wird das
>> als Doppel-Klick gewertet.
>
> Hm, das finde ich ungünstig. Den Doppelklick würdest Du erkennen, wenn
> der Taster zweimal gedrückt wird. Einen langen Klick würdest Du
> erkennen, wenn ein gedrückter Taster nach langer Zeit losgelassen wird.
> Das ist ein bisschen überkreuz, denn so kann aus einem Doppelklick ja
> auch noch ein anschließender langer Klick werden:
> - drücken - loslassen - drücken (Doppelklick komplett) - lange halten -
> loslassen
> Dann wird am Schluß nach dem Doppelklick noch ein langer Tastendruck
> erkannt.
>
> Im Endeffekt kannst Du natürlich jede Programmlogik programmieren, aber
> wenn die Logik darauf basiert Low-High Flankenwechsel auszuwerten
> (Taster drücken) und auch High-Low Flankenwechsel (Taster loslassen),
> dann schleppst Du schon ein paar Statusvariablen durch die Programmlogik
> mit durch.

Bis ins letzte Detail habe ich mir das noch nicht überlegt, aber wenn 
ich so drüber nachdenke, dann müssen doch immer die fallenden Flanken 
ausgewertet werden, weil sonst weder ein Doppelklick, noch ein langer 
Tastendruck erkannt werden kann, ohne dass vorher die Aktion für 
einfaches kurzes Klicken ausgewertet wird.

> Wesentlich einfacher wäre es zum Beispiel, folgende Logiken zu
> implementieren:
> 1. kurz drücken (<1s), lang drücken (1-2s), superlange drücken (>3s)
> oder
> 2. zählen beim Drücken: x-mal drücken mit nachfolgendem Timeout
>
> Mit 2. könntest Du über einen einzigen Taster locker 10 Funktionen
> realisieren, indem Du immer schnell x-mal hintereinander drückst und
> dann den Timeout (z.B. 1s) wartest, und 1s nach dem letzten Zählimpuls
> wird die Aktion ausgeführt, die x-mal drücken entspricht.

Ja genau. So etwas hatte ich für die Rollladenbedienung schon geplant, 
wobei ich mich auf maximal 3 oder 4 Funktionen beschränken wollte, weil 
es sonst nicht besonders bedienerfreundlich ist.

>> Ich habe allerdings noch kein Gefühl dafür, wie viele Ressourcen so
>> etwas verbraucht und ob ich mit der Vorgehensweise auch viel mehr Tasten
>> auswerten kann. Das wäre nämlich mein Ziel für spätere Projekte.
>
> "Ganz viele Tasten" wirst Du vermutlich nicht mehr an einzelnen
> digitalen Inputs auswerten wollen, weil Dir die digitalen Pins sonst
> ausgehen. Zum Beispiel kann man mit einer Hilfsbeschaltung aus einigen
> Widerständen auch sechs Buttons an einem einzigen Analogeingang
> auswerten. Oder eine Tastaturmatrix aus 3*4 = 12 Tasten mit 7 digitalen
> Eingängen. Oder, oder, oder.

Solche "Spielereien" habe ich in den 90ern theoretisch nachvollzogen. 
Damals habe ich auch schon mal ein paar zaghafte Anläufe genommen und 
wollte was mit PICs für den Modellbau machen. Das war aber immer sehr 
aufwendig und zeitraubend. Es musste immer ein EPROM gebrannt und 
gelöscht werden, passende Quarze angebracht werden, der uC musste immer 
umgesteckt werden, der Programmer an der seriellen Schnittstelle war 
bräsig in der Handhabung und lief nur unter DOS, und in ASM habe ich 
immer sehr schnell den Wald vor lauter Bäumen nicht mehr gesehen.

Die Arduinos scheinen ideal für mich zu sein. Da bekomme ich ein 
fertiges Modul, welches ich nur noch an USB anschließen muss und schon 
loslegen kann.

Mehrere Taster an einem Eingang möchte ich aber möglichst vermeiden.
Das dürfte bei langen Leitungen störanfällig werden. Es ist 
wahrscheinlich besser, einen Arduino mit entsprechend vielen Ports zu 
nehmen. Ein Arduino Mega hat 54 digitale IO-Pins. Das reicht für mich 
locker.

>
>> Außerdem möchte ich die Ausgänge vor zu häufigem Schalten schützen.
>> Kurz an und direkt wieder aus, soll beliebig schnell funktionieren. Nach
>> dem Ausschalten sollen die Ausgänge aber erst nach einer geeigneten
>> Verzögerung wieder eingeschaltet werden können.
>
> Das wäre dann Sache Deiner Programmlogik. Viele Arduino-Anfänger
> scheitern an einer sauberen Programmlogik, weil sie keinerlei
> Vorstellung von einer sauber durchführbaren Programmlogik haben.

Da verhaspel ich mich auch immer wieder.

> Deshalb müßtest Du darauf achten, eine saubere Programmlogik zu bauen,
> und eine sehr einfache Methode dazu ist das "EVA-Prinzip". Du mußt nur
> das Programm in drei logische Funktionsbereiche "Eingabe",
> "Verarbeitung" und "Ausgabe" aufteilen. Grundschema der Arduino-loop:
>
1
> void loop()
2
> {
3
>   eingabe();
4
>   verarbeitung();
5
>   ausgabe();
6
> }
7
>

Eine ordentliche Struktur ist die halbe Miete. Ich verrenne mich aber 
auch immer wieder.

> Und erst im letzten Schritt, werden die ermittelten Zustände dann an den
> Ausgängen oder auf einem Display, auf Serial oder wie auch immer gesetzt
> oder angezeigt.

So kenne ich es auch von Siemens S7-200. Bei der SPS kann man zwar auch 
im Programm jederzeit einen Ausgang setzen, aber es wird letztlich nur 
der Zustand am Hardwareausgang eingestellt, der am Ende der 
Programmschleife im Prozessabbild gespeichert wurde.


> Diese mangelnde Trennung in der loop:
> - alle Eingangswerte lesen
> - alle Eingangswerte verarbeiten und in Zustände umwandeln
> - alle ermittelten Zustände ausgeben
> erzeugt bei Anfängern meistens den Knoten im Gehirn für die passende
> Programmlogik.

Jupp, ich weiß, wovon du redest :)

von Mark T. (bitts)


Lesenswert?

Jürgen S. schrieb:
> Mark Thalle schrieb:
>> Dann kommen weitere Fehler:
>>
>> error: 'TCCR0' was not declared in this scope
>> error: 'TIMSK' was not declared in this scope
>>
>> Im Datenblatt finde ich  TCCR0A und TCCR0B
>>
>> Beim Timer Control Register sollte man wohl schon wissen, welches man
>> nimmt :-)
>
> Möchtest Du für das Board jetzt eigentlich mit "Arduino-like
> Programming" eine Firmware schreiben, mit den Arduino-Komfortfunktionen
> aus der Arduino-Core Library und dem Programmgrundgerüst aus "setup()"
> und "loop()" Funktion?
>
> Oder möchtest Du das Board komplett auf Registerebene programmieren
> einschließlich Timer0, ohne die Arduino Konfortfunktionen und mit einer
> Progrmmhauptschleife "main()"?

Ich möchte zumindest für den Anfang erst mal in der Arduino-Umgebung 
bleiben.
Ob und wenn ja, wie man einen Arduino mit main() programmiert, weiß ich 
nicht. Damit möchte ich mich jetzt auch nicht unbedingt aufhalten.
Ich möchte besonders nicht Arduino Bootloader schrotten, denn dann komme 
ich nicht mehr an das Teil heran.


Könnte man denn den C-Code problemlos mit Arduino-Code kombinieren, wenn 
man die Finger vom Timer0 lässt?

von foo (Gast)


Lesenswert?

Mark Thalle schrieb:
> Könnte man denn den C-Code problemlos mit Arduino-Code kombinieren, wenn
> man die Finger vom Timer0 lässt?

Das ist C, bzw C++ und nicht Arduino Magic neue Sprache...

von Jürgen S. (jurs)


Lesenswert?

Mark Thalle schrieb:
> Ich möchte zumindest für den Anfang erst mal in der Arduino-Umgebung
> bleiben.
> Ob und wenn ja, wie man einen Arduino mit main() programmiert, weiß ich
> nicht. Damit möchte ich mich jetzt auch nicht unbedingt aufhalten.
> Ich möchte besonders nicht Arduino Bootloader schrotten, denn dann komme
> ich nicht mehr an das Teil heran.
>

Was hast Du für ein Board? Mit 32U4 müßte es ein Leonardo oder Micro 
sein?

Wenn Du nur ein USB-Kabel hast, um ein 32U4 Board per Bootloader mit 
einem Sketch zu versehen, bist Du ständig in höchster Gefahr, Dein Board 
zu bricken.

Wenn Du nur mit USB-Kabel und Bootloader arbeiten möchtest, holst Du Dir 
besser ein UNO oder MEGA oder NANO Board oder so etwas.

Beim 32U4 läuft nämlich die gesamte serielle Kommunikation per USB über 
den 32U4 und keinen gesonderten Chip, und wenn Du einen Arduino-Sketch 
hochlädst, der die USB-Schnittstelle außer Gefecht setzt, bekommst Du 
über USB-Kabel keinen Sketch mehr hochgeladen.

Und das geht auch mit Arduino Funktionen ganz leicht. Und danach 
bekommst Du nur noch mit einem ISP-Programmer einen neuen Sketch 
hochgeladen.

Andere Boards haben einen zweiten Chip für die serielle Kommunikation 
on-board, so dass die serielle Kommunikation immer erhalten bleibt, egal 
was Du auf den Hauptcontroller als Sketch draufgeladen hast. Die 32U4 
Boards haben das NICHT.

Also zum Herumspielen mit den 32U4 Boards gehört meines Erachtens auch 
immer ein ISP-Programmer zur Grundausstattung. Auch bei Verwendung der 
Arduino-Software. Sonst kann der Spaß mit dem Board beim kleinsten 
Gedankenfehler vorbei sein und Du bekommst keinen neuen Sketch mehr 
drauf.

Wenn Du keinen ISP-Programmer hast: Greife lieber zum UNO, MEGA oder 
NANO für erste Anfänger-Experimente!

Ein Board mit 32U4 Controller brauchst Du eigentlich erst dann, wenn Du 
das Board als HID-Device programmieren und an Deinen PC als entweder 
"Tastatur" oder "Maus" anschließen möchtest, um von Deinem Arduino aus 
den PC zu steuern, wie mit einer angeschlossenen Tastatur oder Maus. 
Ansonsten hat der 32U4 keinen Vorteil und Du riskierst, das Board zu 
bricken und dann bekommst Du nur mit einem ISP Programmer wieder einen 
anderen Sketch drauf geladen.

> Könnte man denn den C-Code problemlos mit Arduino-Code kombinieren, wenn
> man die Finger vom Timer0 lässt?

Ja, Du kannst jederzeit auf Registerebene programmieren, wenn es Dir 
Spaß macht. Du kannst auch jederzeit Arduino-Komfortbefehle und 
Programmierung auf Registerebene abwechselnd verwenden. Du kannst sogar 
der Arduino-Software im laufenden Betrieb den Timer0 entziehen und in 
zweckentfremden, aber dann laufen Befehle wie millis(), delay(), 
micros() und delayMicroseconds() nicht mehr.

Aber wie gesagt: Wenn Du ein Board mit 32U4 Controller programmierst, 
hast Du besser einen ISP-Programmer in der Hinterhand, um auf ein durch 
einen fehlerhaften Sketch gebricktes Board wieder einen neuen Sketch 
laden zu können.

von Mark T. (bitts)


Lesenswert?

Jürgen S. schrieb:
> Mark Thalle schrieb:
>> Ich möchte zumindest für den Anfang erst mal in der Arduino-Umgebung
>> bleiben.
>> Ob und wenn ja, wie man einen Arduino mit main() programmiert, weiß ich
>> nicht. Damit möchte ich mich jetzt auch nicht unbedingt aufhalten.
>> Ich möchte besonders nicht Arduino Bootloader schrotten, denn dann komme
>> ich nicht mehr an das Teil heran.
>>
>
> Was hast Du für ein Board? Mit 32U4 müßte es ein Leonardo oder Micro
> sein?

Dieser hier ist es:
https://learn.sparkfun.com/tutorials/pro-micro--fio-v3-hookup-guide/hardware-overview-pro-micro

Es schimpft sich "Pro Micro". Wenn ich in der IDE den Leonardo 
einstelle, dann kann ich damit auch Programme hochladen. Ich habe mir 
aber von Sparkfun das passende Addon besorgt, sodass ich ihn als Pro 
Micro ansprechen kann.

> Wenn Du keinen ISP-Programmer hast: Greife lieber zum UNO, MEGA oder
> NANO für erste Anfänger-Experimente!

Hm, ich hatte mir die Liste der Arduinos bei Wikipedia angesehen und mir 
aus der Tabelle den "Micro" ausgeguckt. In der Bucht habe ich dann den 
Pro Micro für kleines Geld gesehen und zugegriffen und dachte, dass es 
sogar ein Vorteil wäre, wenn ich keinen Reset-Button zum Programmieren 
drücken muss.

Gut, dass du mich vor dem Bricken gewarnt hast.

Ein ISP müsste doch auch über einen Arduino realisiert werden können. 
Ein ISP dürfte dann doch nicht teurer als ein kleiner Arduino sein.

>> Könnte man denn den C-Code problemlos mit Arduino-Code kombinieren, wenn
>> man die Finger vom Timer0 lässt?
>
> Ja, Du kannst jederzeit auf Registerebene programmieren, wenn es Dir
> Spaß macht. Du kannst auch jederzeit Arduino-Komfortbefehle und
> Programmierung auf Registerebene abwechselnd verwenden. Du kannst sogar
> der Arduino-Software im laufenden Betrieb den Timer0 entziehen und in
> zweckentfremden, aber dann laufen Befehle wie millis(), delay(),
> micros() und delayMicroseconds() nicht mehr.

Das hört sich komfortabel an.

> Aber wie gesagt: Wenn Du ein Board mit 32U4 Controller programmierst,
> hast Du besser einen ISP-Programmer in der Hinterhand, um auf ein durch
> einen fehlerhaften Sketch gebricktes Board wieder einen neuen Sketch
> laden zu können.

Ich stehe nun vor der Wahl, mir einen anderen Arduino oder einen ISP zu 
besorgen. Ein ISP für Arduinos ist bestimmt kein Hexenwerk und deswegen 
denke ich, dass es so etwas auch für kleines Geld geben kann.
Kannst du mir einen ISP empfehlen?

von Mark T. (bitts)


Lesenswert?

Jürgen S. schrieb:
> Konrad S. schrieb:

> Okay, hier dann noch einmal eine leicht modifizierte Version des oben
> geposteten Codes, ohne delay() zu verwenden und mit einer kleinen
> Benchmark-Zeitmessung, die nebenbei läuft:

Ich habe das mal in meinen Arduino geladen und setze und lösche damit 
die LED an PIN17.

Dann wollte ich das Programm auch verstehen, aber es hakt gleich am 
Anfang.

In loop() deklarierst du 3 Variablen mit static.
Ich habe zu static nur wenig ergoogeln können, was mich weiter bringt. 
Wenn ich das richtig verstehe, werden die Variablen nur ein einziges Mal 
initialisiert und behalten dann immer ihre Speicheradresse und den 
zuletzt zugewiesenen Wert.

Zuerst habe ich mich gefragt, was dieser Aufruf dann in der Schleife zu 
suchen hat, wenn man ihn nur einmal machen muss. Das liegt vielleicht 
daran, dass man ansonsten die Zugehörigkeit der Variable zur Funktion 
nicht hinbekommt.

Dann habe ich mich gefragt, mit welchen Werten die Variablen 
initialisiert werden. Ist das definiert, oder kann das ein zufälliger 
Wert sein?
Wenn letzteres, dann könnte z.B. lastRunTime einen sehr großen Wert bei 
der Initialisierung erhalten.
Damit würde bei der nächsten if-Abfrage erst nach sehr langer Zeit die 
Tastenabfrage gestartet und lastRunTime neu gesetzt werden - wenn ich 
das richtig verstehe.


>
1
> #define INPUTMODE INPUT_PULLUP  // INPUT oder INPUT_PULLUP
2
> #define PRELLZEIT 5             // Prellzeit in Millisekunden
3
> byte buttonPins[]={2,3,4,5,6,7,8}; // Arduino Pins
4
> #define NUMBUTTONS sizeof(buttonPins)
5
> byte buttonState[NUMBUTTONS];
6
> 
7
> void setup() {
8
>   Serial.begin(9600);
9
>   for (int i=0;i<NUMBUTTONS;i++) pinMode(buttonPins[i],INPUTMODE);
10
> }
11
> 
12
> void loop() {
13
>   static unsigned long lastRunTime;
14
>   static unsigned long counterStartTime;
15
>   static unsigned long counter;
16
>   if (millis()-lastRunTime>=PRELLZEIT) // Nur wenn die Prellzeit 
17
> abgelaufen ist
18
>   {
19
>     lastRunTime=millis();
20
>     for (int i=0;i<NUMBUTTONS;i++)
21
>     {
22
>       byte curState=digitalRead(buttonPins[i]);
23
>       if (INPUTMODE==INPUT_PULLUP) curState=!curState; // Vertauschte 
24
> Logik bei INPPUT_PULLUP
25
>       if (curState!=buttonState[i]) // Flankenwechsel am Button 
26
> festgestellt
27
>       {
28
>         Serial.print(millis()/1000.0,3); // Zeitstempel mit 
29
> Millisekundenauflösung
30
>         Serial.print("\tChange on Pin-");Serial.print(buttonPins[i]);
31
>         if (curState==true) Serial.println(" PRESSED");
32
>         else Serial.println(" RELEASED");
33
>       }
34
>       buttonState[i]=curState;
35
>     } // for
36
>   } // if(millis()...
37
>   counter++;
38
>   if (counter==100000L)
39
>   {
40
>     Serial.print("Average Time per loop(): ");
41
>     Serial.print((micros()-counterStartTime)/100000.0);
42
>     Serial.println(" microseconds");
43
>     counter=0;
44
>     counterStartTime=micros();
45
>   }
46
> } // loop
47
>
>

von Michael L. (michaelx)


Lesenswert?

nhvjzrrrft5r schrieb:
> Quack schrieb:
>> Sei versichert, die "Dannegger-Methode" ist ein rein lokales Phaenomaen
>> dieses Forums. Ausserhalb hat davon noch nie Jemand gehoert und das
>> Verstaendnis fuer die Huldigung dieses schlecht dokumentierten
>> Schnipsels Code geht ausserhalb dieses Forums gegen Null.
>
>
> So ist es.

... sagt der Priester zum Pfaffen.

> Das Problem ist leider, dass man hier im Forum einen massiven Glaubens-
> krieg beginnt, wenn man diesen Code kritisiert.

Kritisieren!? - Wenn es denn mal so wäre.

Üblicherweise läuft es so ab, dass der Code nicht verstanden wurde, der 
Jenige ihn deswegen doof und sonst noch was findet, um sodann mit 
irgendwelchem Gebastel daher zu kommen, was er als das Non-Plus-Ultra 
ansieht.

Dann kommen die dazu, die einfach nur GLAUBEN, dass der Dannegger-Code 
Scheiße ist, und diese(!) beginnen - von daher hat du (jedoch anders als 
beabsichtigt) Recht - ihren GLAUBENskrieg gegen jedwede vernünftigen 
Argumente.

> Das ist das Resultat der Anforderungen die Herr Dannegger sich bei der
> Entwicklung des Codes gestellt hat und die (neutral ausgedrückt)
> nicht immer mit den eigenen Anforderungen harmonieren.

Mag sein. Aber Peter Dannegger hat sich die Anforderungen nicht einfach 
vom Baum geschüttelt, sondern diese basieren augenscheinlich auf 
praxisorientierten Überlegungen und logischer Herangehensweise, und 
werden auch entsprechend begründet.

> Ich bin mir auch ganz sicher, dass der Code anders ausgesehen
> hätte, wenn Herr Dannagger andere Anforderungen gehabt hätte.

Hätte hätte Fahrradkette. - Als ob man "irgendwie zusammengeschustert" 
eine Anforderung nennen könnte.

> So bleibt am Schluss: Man muss ihn ja nicht einsetzen, wenn es nicht
> passt.

Stimmt! Wem der Code auf Grund von was auch immer nicht "gefällt", der 
soll halt die Finger davon lassen.

von F. F. (foldi)


Lesenswert?

Ich kann es nicht mehr mit ansehen.
In deiner Arduino IDE sind Beispiele und eines davon heißt Debounce:
1
/* 
2
 Debounce
3
 
4
 Each time the input pin goes from LOW to HIGH (e.g. because of a push-button
5
 press), the output pin is toggled from LOW to HIGH or HIGH to LOW.  There's
6
 a minimum delay between toggles to debounce the circuit (i.e. to ignore
7
 noise).  
8
 
9
 The circuit:
10
 * LED attached from pin 13 to ground
11
 * pushbutton attached from pin 2 to +5V
12
 * 10K resistor attached from pin 2 to ground
13
 
14
 * Note: On most Arduino boards, there is already an LED on the board
15
 connected to pin 13, so you don't need any extra components for this example.
16
 
17
 
18
 created 21 November 2006
19
 by David A. Mellis
20
 modified 30 Aug 2011
21
 by Limor Fried
22
 modified 28 Dec 2012
23
 by Mike Walters
24
 
25
 This example code is in the public domain.
26
 
27
 http://www.arduino.cc/en/Tutorial/Debounce
28
 */
29
30
// constants won't change. They're used here to 
31
// set pin numbers:
32
const int buttonPin = 2;    // the number of the pushbutton pin
33
const int ledPin = 13;      // the number of the LED pin
34
35
// Variables will change:
36
int ledState = HIGH;         // the current state of the output pin
37
int buttonState;             // the current reading from the input pin
38
int lastButtonState = LOW;   // the previous reading from the input pin
39
40
// the following variables are long's because the time, measured in miliseconds,
41
// will quickly become a bigger number than can be stored in an int.
42
long lastDebounceTime = 0;  // the last time the output pin was toggled
43
long debounceDelay = 50;    // the debounce time; increase if the output flickers
44
45
void setup() {
46
  pinMode(buttonPin, INPUT);
47
  pinMode(ledPin, OUTPUT);
48
49
  // set initial LED state
50
  digitalWrite(ledPin, ledState);
51
}
52
53
void loop() {
54
  // read the state of the switch into a local variable:
55
  int reading = digitalRead(buttonPin);
56
57
  // check to see if you just pressed the button 
58
  // (i.e. the input went from LOW to HIGH),  and you've waited 
59
  // long enough since the last press to ignore any noise:  
60
61
  // If the switch changed, due to noise or pressing:
62
  if (reading != lastButtonState) {
63
    // reset the debouncing timer
64
    lastDebounceTime = millis();
65
  } 
66
  
67
  if ((millis() - lastDebounceTime) > debounceDelay) {
68
    // whatever the reading is at, it's been there for longer
69
    // than the debounce delay, so take it as the actual current state:
70
71
    // if the button state has changed:
72
    if (reading != buttonState) {
73
      buttonState = reading;
74
75
      // only toggle the LED if the new button state is HIGH
76
      if (buttonState == HIGH) {
77
        ledState = !ledState;
78
      }
79
    }
80
  }
81
  
82
  // set the LED:
83
  digitalWrite(ledPin, ledState);
84
85
  // save the reading.  Next time through the loop,
86
  // it'll be the lastButtonState:
87
  lastButtonState = reading;
88
}

von Jürgen S. (jurs)


Lesenswert?

Mark Thalle schrieb:
> In loop() deklarierst du 3 Variablen mit static.
> Ich habe zu static nur wenig ergoogeln können, was mich weiter bringt.
> Wenn ich das richtig verstehe, werden die Variablen nur ein einziges Mal
> initialisiert und behalten dann immer ihre Speicheradresse und den
> zuletzt zugewiesenen Wert.

Ja, die "static" Variablen behalten zwischen den Funktionsaufrufen ihren 
Wert. Man könnte stattdessen auch globale Variablen außerhalb der 
Funktion deklarieren.

Aber innerhalb der Funktion deklariert, haben diese Variablen ihren 
Gültigkeitsbereich auch nur innerhalb der Funktion.

> Zuerst habe ich mich gefragt, was dieser Aufruf dann in der Schleife zu
> suchen hat, wenn man ihn nur einmal machen muss. Das liegt vielleicht
> daran, dass man ansonsten die Zugehörigkeit der Variable zur Funktion
> nicht hinbekommt.

Die loop-Funktion heißt nur loop (Schleife), aber diese Funktion läuft 
immer von Anfang bis Ende durch. Es ist tatsächlich die (unsichtbare) 
main()-Funktion der Arduino-Software, die loop() immer wieder aufruft. 
Und damit bestimmte Variablen zwischen den Funktionsaufrufen ihre Werte 
beibehalten, sind sie innerhalb der Funktion "static" deklariert.

> Dann habe ich mich gefragt, mit welchen Werten die Variablen
> initialisiert werden. Ist das definiert, oder kann das ein zufälliger
> Wert sein?

Die werden mit null initialisiert. Im Endeffekt wie auch globale 
Variablen.

von F. F. (foldi)


Lesenswert?

Wenn ich das richtig verstanden habe, dann will der TO doch mit Arduino 
arbeiten.
Die einfachste Methode, wenn nichts zeitkritisches ansteht:
1
buttonState = digitalRead(button);
2
     delay(10);
3
if (button == HIGH) {     
4
    digitalWrite(led, HIGH);
5
   ...
6
  }

Das schlägt Massimo Banzi übrigens in seinem Buch vor und funktioniert.

von Jürgen S. (jurs)


Lesenswert?

F. Fo schrieb:
> Wenn ich das richtig verstanden habe, dann will der TO doch mit Arduino
> arbeiten.
> Die einfachste Methode, wenn nichts zeitkritisches ansteht:
>
>
1
> 
2
> buttonState = digitalRead(button);
3
>      delay(10);
4
> if (button == HIGH) {
5
>     digitalWrite(led, HIGH);
6
>    ...
7
>   }
8
> 
9
>
>
> Das schlägt Massimo Banzi übrigens in seinem Buch vor und funktioniert.

Bei dem von Dir vorher geposteten Debounce-Beispiel hatte der TO das 
Problem, dass er damit nicht MEHRERE BUTTONS gleichzeitig auswerten 
konnte. Genau das möchte er aber.

Und bei dem von Dir zuletzt geposteten Minimalbeispiel einer 
Buttonabfrage wird nicht einmal mehr ein Flankenwechsel als "Click" 
erkannt, sondern nur noch der aktuelle HIGH oder LOW Status.

Falls Du den Thread nicht weiter mitverfolgt hast, der TO möchte:
- MEHRERE BUTTONS ABFRAGEN
- und zwar möglichst auf VERSCHIEDENE AKTIONEN wie "Short Click", 
"Double Click" und "Long Click", die unterscheidbar sein sollen und auf 
die das Programm mit unterschiedlichen Funktionsaufrufen reagieren kann.

Da war ja bereits mein Posting von heute Vormittag 11:49 Uhr ergibiger, 
weil es mehrere Buttons ausgewertet hat.

Hier nun nochmal ein überarbeiteter Code, der mehrere Buttons auf kurze 
und lange Tastendrücke abfragen kann. Der Button feuert seine Aktion 
dabei beim Loslassen des Schalters, um lange und kurze Tastendrücke zu 
unterscheiden.

Den Benchmark lasse ich mal als nebenläufige Funktion nebenbei laufen.
1
#define INPUTMODE INPUT_PULLUP  // INPUT oder INPUT_PULLUP
2
#define PRELLZEIT 5             // Prellzeit in Millisekunden
3
#define SHORTCLICKTIME 250       // Längste Zeit für einen SHORTCLICK
4
byte buttonPins[]={2,3,4,5,6,7,8}; // Arduino Pins
5
#define NUMBUTTONS sizeof(buttonPins)
6
byte buttonState[NUMBUTTONS];  // Aktueller Status des Buttons HIGH/LOW
7
enum {NONE, SHORTCLICK, LONGCLICK}; 
8
byte buttonResult[NUMBUTTONS]; // Aktueller Klickstatus der Buttons NONE/SHORTCLICK/LONGCLICK
9
10
void setup() {
11
  Serial.begin(9600);
12
  for (int i=0;i<NUMBUTTONS;i++) pinMode(buttonPins[i],INPUTMODE);
13
}
14
15
static inline boolean eingabe()
16
// Rückgabewert false ==> Prellzeit läuft, Taster wurden nicht abgefragt
17
// Rückgabewert true ==> Taster wurden abgefragt und Status gesetzt
18
{
19
  static unsigned long lastRunTime;
20
  static unsigned long buttonDownTime[NUMBUTTONS];
21
  unsigned long now=millis();
22
  if (now-lastRunTime<PRELLZEIT) return false; // Prellzeit läuft noch
23
  lastRunTime=now;
24
  for (int i=0;i<NUMBUTTONS;i++)
25
  {
26
    byte curState=digitalRead(buttonPins[i]); 
27
    if (INPUTMODE==INPUT_PULLUP) curState=!curState; // Vertauschte Logik bei INPPUT_PULLUP
28
    buttonResult[i]=NONE; // Letztes buttonResult löschen
29
    if (curState!=buttonState[i]) // Flankenwechsel am Button festgestellt
30
    {
31
      if (curState)   // Taster wird gedrückt, Zeit merken
32
      {
33
        buttonDownTime[i]=now; 
34
      }
35
      else  // Taster wird losgelassen
36
      {
37
        if (now-buttonDownTime[i]<=SHORTCLICKTIME)
38
          buttonResult[i]=SHORTCLICK;
39
        else  
40
          buttonResult[i]=LONGCLICK;
41
      }
42
    }
43
    buttonState[i]=curState;
44
  } // for
45
  return true;
46
}
47
48
49
static inline void verarbeitung()
50
{
51
  // dummy function 
52
}
53
54
55
static inline void ausgabe()
56
// Klickstatus von geklickten Buttons mit Zeitstempel ausgeben
57
{
58
  for (int i=0;i<NUMBUTTONS;i++)
59
  {
60
    if (buttonResult[i]!=NONE) // Ein Button wurde geklickt
61
    {
62
      Serial.print(millis()/1000.0,3); // Zeitstempel mit Millisekundenauflösung
63
      Serial.print("\tPin-");Serial.print(buttonPins[i]);
64
      if (buttonResult[i]==SHORTCLICK) Serial.println(" CLICK");
65
      else if(buttonResult[i]==LONGCLICK) Serial.println(" LONG CLICK");
66
    }
67
  }
68
}
69
70
static inline void benchmark() {
71
  static unsigned long counterStartTime;
72
  static unsigned long counter;
73
  counter++;
74
  if (counter>=1000000L)
75
  {
76
    Serial.print("Average Time per loop(): ");
77
    Serial.print((micros()-counterStartTime)/1000000.0);
78
    Serial.println(" microseconds");
79
    counter=0;
80
    counterStartTime=micros();
81
  }
82
}
83
84
85
void loop() {
86
  if (eingabe())
87
  {
88
    verarbeitung();
89
    ausgabe();
90
  }
91
  benchmark();
92
}

von Konrad S. (maybee)


Lesenswert?

@Jürgen S.
Und während du noch an deinem Code rumschraubst - was an und für sich 
recht lobenswert ist - läuft PeDas Code seit Jahren problemlos. Dieses 
Rad wurde schon erfunden und es läuft schön rund. ;-)

von Jürgen S. (jurs)


Lesenswert?

Konrad S. schrieb:
> @Jürgen S.
> Und während du noch an deinem Code rumschraubst - was an und für sich
> recht lobenswert ist - läuft PeDas Code seit Jahren problemlos. *Dieses*
> Rad wurde schon erfunden und es läuft schön rund. ;-)

Wo ist denn bei PeDas Code die Auswertung und Unterscheidung von:
- Click
- Double Click
- Long Keypress
???

Der TO wollte nicht nur einen ganzen Schwung Buttons abfragen und darauf 
reagieren, sondern er wollte auch möglichst drei verschiedenen Aktionen 
pro Taster abfragen können, und zwar kurzen Tastendruck, Doppelklick und 
langen Tastendruck mit lange gedrückter Taste.

Wo das in PeDas Code implementiert ist, mußt Du mir zeigen.
Als Arduino-Programmierer sehe ich das leider gerade nicht.

Ich sehe als Arduino-Programmierer momentan nicht mal, dass in PeDas 
Code eine flexible Handhabung der Button-Beschaltung mit entweder 
PullDown-Widerstand oder mit den internen PullUp-Widerständen im Code 
eingebaut wäre.

von Konrad S. (maybee)


Lesenswert?

Du bist nicht allzusehr vorbelastet durch den Artikel Entprellung, 
kann das sein?

von Konrad S. (maybee)


Lesenswert?

Jürgen S. schrieb:
> Wenn Du nur ein USB-Kabel hast, um ein 32U4 Board per Bootloader mit
> einem Sketch zu versehen, bist Du ständig in höchster Gefahr, Dein Board
> zu bricken.
>
> Wenn Du nur mit USB-Kabel und Bootloader arbeiten möchtest, holst Du Dir
> besser ein UNO oder MEGA oder NANO Board oder so etwas.
>
> Beim 32U4 läuft nämlich die gesamte serielle Kommunikation per USB über
> den 32U4 und keinen gesonderten Chip, und wenn Du einen Arduino-Sketch
> hochlädst, der die USB-Schnittstelle außer Gefecht setzt, bekommst Du
> über USB-Kabel keinen Sketch mehr hochgeladen.
>
> Und das geht auch mit Arduino Funktionen ganz leicht. Und danach
> bekommst Du nur noch mit einem ISP-Programmer einen neuen Sketch
> hochgeladen.

Aha, der Arduino-Profi spricht.
Seit mehr als zwei Jahren ist auch eine Problemlösung dafür bekannt - 
ohne ISP-Programmer.
https://forum.sparkfun.com/viewtopic.php?f=32&t=31518
Und eigentlich müsste klar sein, warum diese Lösung funktionieren muss.

von Peter D. (peda)


Lesenswert?

Jürgen S. schrieb:
> Beim 32U4 läuft nämlich die gesamte serielle Kommunikation per USB über
> den 32U4 und keinen gesonderten Chip, und wenn Du einen Arduino-Sketch
> hochlädst, der die USB-Schnittstelle außer Gefecht setzt, bekommst Du
> über USB-Kabel keinen Sketch mehr hochgeladen.

Sicher?
Hast Du das selbst getestet?

Ich würde die Arduino-Entwickler mindestens für so intelligent halten, 
daß sie den USB-Bootloader in der Bootsektion plazieren.
Damit ist er nach jedem Power-On-Reset erreichbar, völlig schnurz, was 
die Applikation macht.

Mir ist zumindest kein Fall bekannt, daß jemand einen Bootloader mit 
Selbstzerstörung programmiert hat.

Außer in der Tuning-Szene. Da gibt es Geräte, die genau einmal laufen 
sollen, damit nur ein Fahrzeug getunt werden kann.

von Konrad S. (maybee)


Lesenswert?

Eben, dem Bootloader geht es nach wie vor bestens und er kann USB 
vollständig von sich aus bedienen. Wie sollte das auch sonst 
funktionieren? Durch einen Sketch, der das Arduino-Laufzeitsystem 
plattmacht (¨brickt¨), geht die Fähigkeit verloren, aus dem 
Normalbetrieb heraus in den Bootloader zu springen und zu einem 
beliebigen Zeitpunkt einen Upload einzuleiten. Die im Link genannte 
Lösung startet einfach den Upload und löst über einen Reset, z.B. durch 
Einstecken des USB-Kabels, den Start des Bootloaders aus.

P.S.: Dieser Beitrag richtet sich nicht an dich, Peter. ;-)

von Jürgen S. (jurs)


Lesenswert?

Konrad S. schrieb:
> Seit mehr als zwei Jahren ist auch eine Problemlösung dafür bekannt -
> ohne ISP-Programmer.
> https://forum.sparkfun.com/viewtopic.php?f=32&t=31518
> Und eigentlich müsste klar sein, warum diese Lösung funktionieren muss.

Danke für den Hinweis! Das muß ich bei Gelegenheit noch mal intensiver 
ausprobieren. Obwohl ich es bereits mal so getestet hatte und dabei mit 
einer handvoll Versuchen nicht hinbekommen habe. Vielleicht habe ich zu 
früh aufgegeben.

Peter Dannegger schrieb:
> Sicher?
> Hast Du das selbst getestet?

Nicht besonders intensiv, denn ich habe einen ISP-Programmer.

Das Problem unter Windows mit dem 32U4-Bootloader ist nämlich auch: Der 
gebrickte 32U4-Arduino hat keine COM-Schnittstelle mehr, und ohne 
existierende COM-Schnittstelle kann die Arduino-Software keinen Upload 
auf eine bestimmte COM-Schnittstelle starten. Der Bootloader hat 
allerdings nur ein extrem kurzes Timeout, so dass nachdem Windows nach 
dem Reset wieder eine COM-Schnittstelle sieht, der Bootloader-Upload 
extrem schnell beginnen muss, damit er funktioniert.

Andererseits beginnt der Upload nicht sofort, wenn man in der 
Arduino-Software auf "Upload" klickt, sondern dann wird immer erst 
nochmal der aktuelle Sketch kompiliert und gelinkt bevor der eigentliche 
Bootloader-Upload startet

Vielleicht hatte ich bei meinem Test auch nur das falsche Timing und zu 
wenig Geduld, um den richtigen Augenblick für den Bootloader zu treffen. 
Wenn es funktioniert, ist es für die 32U4 Arduinos definitiv hakeliger 
als bei den Arduino-Boards, die für die USB-Serial Kommunikation einen 
eigenen Chip (FTDI, 8U2, 16U2) auf dem Board haben und die deshalb auch 
bei einem Controller-Reset ständig den COM-Port im 
Windows-Betriebssystem behalten.

von Konrad S. (maybee)


Lesenswert?

In den Arduino-Einstellungen kannst du für den Upload-Vorgang ein 
Häkchen für Verbose setzen. Du bekommst dann die beim Upload ablaufenden 
Befehle angezeigt. Daraus suchst du dir das eine Kommando raus, das den 
eigentlichen Upload macht und rufst es aus einer Konsole heraus auf. So 
bekommst du das mit dem Timing besser hin.

von Jürgen S. (jurs)


Lesenswert?

Konrad S. schrieb:
> In den Arduino-Einstellungen kannst du für den Upload-Vorgang ein
> Häkchen für Verbose setzen.

Das ist der TOP-TIP, um wieder einen Sketch ohne ISP-Programmer auf ein 
gebricktes 32U4 Arduino-Board zu bekommen!

> Du bekommst dann die beim Upload ablaufenden
> Befehle angezeigt. Daraus suchst du dir das eine Kommando raus, das den
> eigentlichen Upload macht und rufst es aus einer Konsole heraus auf.

Gerade mal ausprobiert: Leider startet der Upload ja nicht mehr, so dass 
das Kommando, das den eigentlichen Upload macht, nicht mehr angezeigt 
wird, wenn der Arduino bereits gebrickt ist.

Man müßte sich das Kommando also vorher rausgesucht und gemerkt haben, 
bevor man das Board brickt.

Allerdings ist die erweiterte Ausgabe beim Upload trotzdem hilfreich, 
weil man beim Timing für das Reset genau im richtigen Moment zuschlagen 
kann.

Sobald im Ausgabefenster der COM-Portscan startet:
1
Forcing reset using 1200bps open/close on port COM15
2
PORTS {COM10, COM15, } / {COM10, COM15, } => {}
3
PORTS {COM10, COM15, } / {COM10, COM15, } => {}
4
PORTS {COM10, COM15, } / {COM10, COM15, } => {}
Macht man dann den Reset, klappt es auch mit dem Upload.

Danke für den Tip!
Ich nehme also alles über gebrickte LEONARDO/MICRO Boards und die 
Notwendigkeit von ISP-Programming gesagte zurück und behaupte das 
Gegenteil: Man kann gebrickte LEONARDO/MICRO-Boards auch einfach über 
den normalen Arduino-Bootloader wieder flottmachen. Einfach die 
erweiterte Ausgabe beim Upload aktivieren und dann im genau richtigen 
Moment mit dem Reset am Board zuschlagen.

von Mark T. (bitts)


Lesenswert?

Hallo Jürgen,

ich habe leider erst gerade eben die Zeit dazu gehabt, mit deinen Code 
anzusehen und auszuprobieren. Im Vergleich zu dem, wie ich es gebastelt 
hätte, sieht das sehr schön aus.
Mit enum und der Zusammenfassung der Eingänge zu einem Array habe ich 
nun auch was gelernt, was ich umsetzen kann.

Ich denke, dass ich dein Programm grundsätzlich verstanden habe.

Ich wollte es nun um die Doppelklick-Erkennung erweitern.
Das geht aber leider nicht so einfach :-), denn du setzt den Ausgang 
sofort, wenn eine Taste losgelassen wird.

Sehe ich es richtig, dass man die Abfragen komplett anders aufbauen 
muss, wenn man auch einen Doubleclick erkennen möchte, ohne vor einem 
Doubleclick immer kurz einen Shortclick auszugeben?

von Jürgen S. (jurs)


Lesenswert?

Mark Thalle schrieb:
> Ich wollte es nun um die Doppelklick-Erkennung erweitern.
> Das geht aber leider nicht so einfach :-), denn du setzt den Ausgang
> sofort, wenn eine Taste losgelassen wird.

Ja, so ganz einfach ist es nicht.
Aber auch nicht soooo schwierig.

> Sehe ich es richtig, dass man die Abfragen komplett anders aufbauen
> muss, wenn man auch einen Doubleclick erkennen möchte, ohne vor einem
> Doubleclick immer kurz einen Shortclick auszugeben?

Nein, das kann man erweitern. Aber ich brauche wohl noch einen 
Hilfsstatus, um den ersten Klick eine Weile zu speichern, während noch 
ein Doppelklick draus werden kann. Und wenn kein Doppelklick innerhalb 
einer bestimmten Zeit draus wird, wird es ein Shortklick, beim erneuten 
Drücken ein Doppelklick.

Ich lasse mir mal was einfallen und poste es dann.

von Karl H. (kbuchegg)


Lesenswert?

Jürgen S. schrieb:

> Wo ist denn bei PeDas Code die Auswertung und Unterscheidung von:
> - Click
> - Double Click
> - Long Keypress
> ???
>
> Der TO wollte nicht nur einen ganzen Schwung Buttons abfragen und darauf
> reagieren, sondern er wollte auch möglichst drei verschiedenen Aktionen
> pro Taster abfragen können, und zwar kurzen Tastendruck, Doppelklick und
> langen Tastendruck mit lange gedrückter Taste.
>
> Wo das in PeDas Code implementiert ist, mußt Du mir zeigen.

Doppelklick ist in deinem Code auch nicht drinnen. Also ist das ein 
eiher schlechtes Argument. PeDas Code kann Normal-Zeit, Lange Drückzeit 
und (was für mich immer besonders für Zahleneingaben wichtig war) 
Autorepeat.

> Ich sehe als Arduino-Programmierer momentan nicht mal, dass in PeDas
> Code eine flexible Handhabung der Button-Beschaltung mit entweder
> PullDown-Widerstand oder mit den internen PullUp-Widerständen im Code
> eingebaut wäre.

Ist ganz einfach: Auf einem AVR braucht das tatsächlich keiner wirklich, 
weil Tasten sowieso immer gleich angehängt werden: Taster auf Low mit 
eingeschaltetem internen Pullup

Das faszinierende an PeDas Code besteht darin, dass diese paar 
Anweisungen
1
  i = key_state ^ ~KEY_PIN;                       // key changed ?
2
  ct0 = ~( ct0 & i );                             // reset or count ct0
3
  ct1 = ct0 ^ (ct1 & i);                          // reset or count ct1
4
  i &= ct0 & ct1;                                 // count until roll over ?
5
  key_state ^= i;                                 // then toggle debounced state
6
  key_press |= key_state & i;                     // 0->1: key press detect
7
 
8
  if( (key_state & REPEAT_MASK) == 0 )            // check repeat function
9
     rpt = REPEAT_START;                          // start delay
10
  if( --rpt == 0 ){
11
    rpt = REPEAT_NEXT;                            // repeat delay
12
    key_rpt |= key_state & REPEAT_MASK;
13
  }
das Entprellen bzw. die Unterscheidung kurz/lang bzw. den Autorepeat 
regeln. Und zwar für 8 Tasten. Brauch ich kurz/lang nicht bzw. brauch 
ich keinen Autorepeat, dann sind wir bei
1
  i = key_state ^ ~KEY_PIN;                       // key changed ?
2
  ct0 = ~( ct0 & i );                             // reset or count ct0
3
  ct1 = ct0 ^ (ct1 & i);                          // reset or count ct1
4
  i &= ct0 & ct1;                                 // count until roll over ?
5
  key_state ^= i;                                 // then toggle debounced state
6
  key_press |= key_state & i;                     // 0->1: key press detect
ebenfalls wieder für maximal 8 Tasten. Ich hab nicht nachgesehen, aber 
es würde mich sehr wundern, wenn der Compiler da mehr als 20 Taktzklen 
dafür verbrutzeln würde. Und nein, ich bin nicht der Meinung von Mike 
vom 27.6, wenn er schreibt
> richtig, die Routine ist jetzt nicht das was ein fähiger
> Programmierer auch selbst hinbekommen würde und eine ganz besonders
> hohen Intellekt bedarf es auch nicht soetwas zu schreiben.
Ganz im Gegenteil finde ich das Codestück als ziemlich trickreich. Das 
ist Parallelprozessing durch Vektorbearbeitung in Reinkultur.

In der Arduino Programmierung besteht das 'Problem' mit diesem Code 
darin, dass hier mittels
1
    ........ KEY_PIN ....
direkt auf einen Port zugegriffen wird um an den aktuellen Zustand der 8 
Input Pins zu kommen. Das kann man natürlich auch auf einem Ardunion 
machen. Macht man das allerdings Arduino typisch zb mittels
1
    ....... ( digitalRead( 2 ) << 2 ) +
2
            ( digitalRead( 5 ) << 1 ) +
3
            ( digitalRead( 9 )

dann ist das insofern ein bischen witzlos, weil die ganze Abfrage 
mittels digitalRead ein Vielfaches an Rechenzeit gegenüber der 
Entprellung kosten würde.
Machbar wäre es allerdings.

Auf den Interrupt könnte man sogar verzichten, wenn man den ISR Code in 
der Form des Zeitvergleichs mittels millis() in loop() einbaut. Da die 
Entprellung insofern nicht besonders zeitkritisch ist, als als Zeitbasis 
alles von ca. 5ms bis hin zu ca. 40 oder 50ms tauglich ist.
(Ich weiß auch nicht, ob man sich mittels Attach auf einem Arduino noch 
zusätzlich an den Timer 0 hängen kann oder nicht. Wenn das möglich ist, 
dann wäre natürlich auch das ein gangbarer Weg)

von lkmiller (Gast)


Lesenswert?

Karl Heinz schrieb:
> Und nein, ich bin nicht der Meinung von Mike vom 27.6, wenn er schreibt
>> richtig, die Routine ist jetzt nicht das was ein fähiger
>> Programmierer auch selbst hinbekommen würde und eine ganz besonders
>> hohen Intellekt bedarf es auch nicht soetwas zu schreiben.
> Ganz im Gegenteil finde ich das Codestück als ziemlich trickreich.
Ich schließe mich an. Dieser Code ist genial. Und er wird m.E. nur von 
denen schlechtgeredet, die ihn nicht verstanden haben. Das ist nämlich 
wirklich nicht einfach, obwohl es nur ein paar Zeilen sind...

Als Tipp: alle Variablen als einzelne Bits ansehen. Dann sind ct1 und 
ct0 die Bits eines Zwei-Bit-Zählers.

von Mark T. (bitts)


Lesenswert?

Jürgen S. schrieb:
>> Sehe ich es richtig, dass man die Abfragen komplett anders aufbauen
>> muss, wenn man auch einen Doubleclick erkennen möchte, ohne vor einem
>> Doubleclick immer kurz einen Shortclick auszugeben?
>
> Nein, das kann man erweitern. Aber ich brauche wohl noch einen
> Hilfsstatus, um den ersten Klick eine Weile zu speichern, während noch
> ein Doppelklick draus werden kann. Und wenn kein Doppelklick innerhalb
> einer bestimmten Zeit draus wird, wird es ein Shortklick, beim erneuten
> Drücken ein Doppelklick.
>
> Ich lasse mir mal was einfallen und poste es dann.

Das ist nett, aber so war das nicht gemeint, besonders dann nicht, wenn 
es zeitaufwendig ist.
Ich wollte nur nachgefragt haben, damit ich nicht vielleicht eine sehr 
simple Lösung übersehe und mit hier stundenlang den Kopf zerbreche.

An eine Hilfsvariable hatte ich auch schon gedacht und das hätte ich 
vermutlich auch selbst in absehbarer Zeit in den innersten Teil deiner 
verschachtelten Abfragen implementieren können.

Hier:
1
      else  // Taster wird losgelassen
2
      {
3
        if (now-buttonDownTime[i]<=SHORTCLICKTIME)
4
          buttonResult[i]=SHORTCLICK;
5
        else  
6
          buttonResult[i]=LONGCLICK;
7
      }

Allerdings wird dieser Teil nur durchlaufen, wenn sich der Tastenzustand 
geändert hat, und das macht er ja nach dem Timeout nicht mehr, wenn kein 
Doppelklick kommt.

von Jürgen S. (jurs)


Lesenswert?

Karl Heinz schrieb:
> Doppelklick ist in deinem Code auch nicht drinnen.

OK, Tastenauswertung mit Doppelklickerkennung dann in diesem Posting 
weiter unten. Ist ja leicht erweiterbar.

Am PeDa Code ist für Arduino-User nur nachteilig, dass standardmäßig der 
Timer0 verbraten wird, und Timer0 ist nun ausgerechnet das Grundgerüst, 
über das die Arduino-Software dem Programmierer ein Timing-Grundgerüst 
für seine Anwendung bereitstellt, mit Funktionen für Millisekundenzähler 
"millis()", Mikrosekundenzähler mit 4µs Auflösung "micros()" sowie 
Delayroutinen im Millisekundentakt "delay()" und im Mikrosekundentakt 
"delayMicroseconds()".

Diese Arduino-Timingroutinen sind für extrem viele Zwecke nutzbar und 
längst nicht nur zur Tastaturentprellung und -abfrage nützlich. Wenn ich 
mal Zeit und Muße finde, schaue ich mir den PeDa Code mal darauf an, wie 
man ihn für Arduino-Programierer nutzbringend umschreiben kann, ohne 
dabei Timer0 und die daran hängenden Funktionen komplett stillzulegen 
und ohne einen weiteren der wenigen Timer zu verbraten.

Aber die Entprellung von Tastern ist unter Arduino nun wirklich keine 
Geheimwissenschaft, egal ob man nun nur auf Tastendruck, kurze und lange 
Tastendrücke oder kurze, lange und doppelte Tastendrücke reagieren 
möchte.

Bei Bedarf würde ich an meinen unten stehenden Code sogar noch eine 
vierte Funktion drauflegen:
- kurzer Tastndruck
- Doppelklick
- langer Tastendruck
- superlanger Tastendruck

Und vor allem sind die reinen Arduino-Funktionen portabel innerhalb der 
Arduino-Welt: Derselbe Code mit Hilfe der Arduino Core-Funktionen 
programmiert läuft unverändert auf einem 8-Bit Atmega Controller genau 
so wie auf einem Atmel SAM3X8E ARM Cortex-M3.

Anyway, hier die Auswertung von Shortklick, Doubleclick und Longclick 
mit Arduino, zusammen mit einem kleinen Counter-Benchmark in der loop:
1
// Tastenerkennung SHORTCLICK, DOUBLECLICK, LONGCLICK
2
#define INPUTMODE INPUT_PULLUP  // INPUT oder INPUT_PULLUP
3
#define PRELLZEIT 5             // Prellzeit in Millisekunden
4
// #define SHORTCLICKTIME 250      // Längste Zeit für einen SHORTCLICK
5
#define DOUBLECLICKTIME 400     // Längste Zeit für den zweiten Klick beim DOUBLECLICK
6
#define LONGCLICKTIME 600       // Mindestzeit für einen LONGGLICK
7
byte buttonPins[]={2,3,4,5,6,7,8}; // Arduino Pins
8
#define NUMBUTTONS sizeof(buttonPins)
9
byte buttonState[NUMBUTTONS];  // Aktueller Status des Buttons HIGH/LOW
10
enum {NONE, FIRSTDOWN, FIRSTUP, SHORTCLICK, DOUBLECLICK, LONGCLICK}; 
11
byte buttonResult[NUMBUTTONS]; // Aktueller Klickstatus der Buttons NONE/SHORTCLICK/LONGCLICK
12
13
void setup() {
14
  Serial.begin(9600);
15
  for (int i=0;i<NUMBUTTONS;i++) pinMode(buttonPins[i],INPUTMODE);
16
}
17
18
static inline boolean eingabe()
19
// Rückgabewert false ==> Prellzeit läuft, Taster wurden nicht abgefragt
20
// Rückgabewert true ==> Taster wurden abgefragt und Status gesetzt
21
{
22
  static unsigned long lastRunTime;
23
  static unsigned long buttonDownTime[NUMBUTTONS];
24
  unsigned long now=millis();
25
  if (now-lastRunTime<PRELLZEIT) return false; // Prellzeit läuft noch
26
  lastRunTime=now;
27
  for (int i=0;i<NUMBUTTONS;i++)
28
  {
29
    byte curState=digitalRead(buttonPins[i]); 
30
    if (INPUTMODE==INPUT_PULLUP) curState=!curState; // Vertauschte Logik bei INPPUT_PULLUP
31
    if (buttonResult[i]>=SHORTCLICK) buttonResult[i]=NONE; // Letztes buttonResult löschen
32
    if (curState!=buttonState[i]) // Flankenwechsel am Button festgestellt
33
    {
34
      if (curState)   // Taster wird gedrückt, Zeit merken
35
      {
36
        if (buttonResult[i]==FIRSTUP && now-buttonDownTime[i]<DOUBLECLICKTIME)
37
          buttonResult[i]=DOUBLECLICK;
38
        else
39
        {  
40
          buttonDownTime[i]=now; 
41
          buttonResult[i]=FIRSTDOWN;
42
        } 
43
      }
44
      else  // Taster wird losgelassen
45
      {
46
        if (buttonResult[i]==FIRSTDOWN) buttonResult[i]=FIRSTUP;
47
        if (now-buttonDownTime[i]>=LONGCLICKTIME) buttonResult[i]=LONGCLICK;
48
      }
49
    }
50
    else // kein Flankenwechsel, Up/Down Status ist unverändert
51
    {
52
      if (buttonResult[i]==FIRSTUP && now-buttonDownTime[i]>DOUBLECLICKTIME) 
53
        buttonResult[i]=SHORTCLICK;
54
    }
55
    buttonState[i]=curState;
56
  } // for
57
  return true;
58
}
59
60
61
static inline void verarbeitung()
62
{
63
  // dummy function 
64
}
65
66
67
static inline void ausgabe()
68
// Klickstatus von geklickten Buttons mit Zeitstempel ausgeben
69
{
70
  for (int i=0;i<NUMBUTTONS;i++)
71
  {
72
    if (buttonResult[i]>=SHORTCLICK) // Ein Button wurde geklickt
73
    {
74
      Serial.print(millis()/1000.0,3); // Zeitstempel mit Millisekundenauflösung
75
      Serial.print("\tPin-");Serial.print(buttonPins[i]);
76
      if (buttonResult[i]==SHORTCLICK) Serial.println(" CLICK");
77
      else if(buttonResult[i]==DOUBLECLICK) Serial.println(" DOUBLE CLICK");
78
      else if(buttonResult[i]==LONGCLICK) Serial.println(" LONG CLICK");
79
    }
80
  }
81
}
82
83
static inline void benchmark() {
84
  static unsigned long counterStartTime;
85
  static unsigned long counter;
86
  counter++;
87
  if (counter>=1000000L)
88
  {
89
    Serial.print("Average Time per loop(): ");
90
    Serial.print((micros()-counterStartTime)/1000000.0);
91
    Serial.println(" microseconds");
92
    counter=0;
93
    counterStartTime=micros();
94
  }
95
}
96
97
98
void loop() {
99
  if (eingabe())
100
  {
101
    verarbeitung();
102
    ausgabe();
103
  }
104
  benchmark();
105
}

Beim Auswerten sind die Statuswerte NONE, FIRSTDOWN, FIRSTUP zu 
ignorieren, und nur auf SHORTCLICK, DOUBLECLICK, LONGCLICK zu prüfen, um 
darauf im Programm zu reagieren. Das Beispielprogramm gibt im Klickfall 
einfach einen entsprechenden Text auf Serial aus.

Viel Spaß damit, falls es ein Arduino-Programmierer gebrauchen kann!
Und wer nicht, der nutzt eben anderen Code.
Es gibt beim Programmieren ja immer mehrere Möglichkeiten.

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Jürgen S. schrieb:
> Wenn ich mal Zeit und Muße finde, schaue ich mir den PeDa Code mal
> darauf an, wie man ihn für Arduino-Programierer nutzbringend umschreiben
> kann, ohne dabei Timer0 und die daran hängenden Funktionen komplett
> stillzulegen
Es wäre in 10 Minuten erledigt, wenn man sich in den millis() 
Timerinterrupt einhängen könnte.

Und "nutzbringend" ist der Code schon, da musst du nichts mehr 
umschreiben...

von Konrad S. (maybee)


Lesenswert?

Lothar Miller schrieb:
> Es wäre in 10 Minuten erledigt, wenn man sich in den millis()
> Timerinterrupt einhängen könnte.

Kann man. Die relevante Datei ist
 hardware/arduino/cores/arduino/wiring.c
im Arduino Verzeichnis.

von Peter D. (peda)


Lesenswert?

Jürgen S. schrieb:
> Am PeDa Code ist für Arduino-User nur nachteilig, dass standardmäßig der
> Timer0 verbraten wird,

Der Timer wird nicht verbraten, er wird benutzt. Ein Timer kann viele 
zeitabhängighe Dinge nacheinander erledigen.

Falls schon ein Timer anderweitig benutzt wird, kann man den 
Entprelltask einfach mit hineinhängen. Er ist ja nicht in chinesich 
geschrieben, man kann ihn ruhig lesen und den Code woanders aufrufen.
Den T0 neu zu laden, ist auch nur bei den allerersten AVRs notwendig (T0 
ohne CTC Modus).

von Mark T. (bitts)


Lesenswert?

Jürgen S. schrieb:

> Beim Auswerten sind die Statuswerte NONE, FIRSTDOWN, FIRSTUP zu
> ignorieren, und nur auf SHORTCLICK, DOUBLECLICK, LONGCLICK zu prüfen, um
> darauf im Programm zu reagieren. Das Beispielprogramm gibt im Klickfall
> einfach einen entsprechenden Text auf Serial aus.
>
> Viel Spaß damit, falls es ein Arduino-Programmierer gebrauchen kann!
> Und wer nicht, der nutzt eben anderen Code.
> Es gibt beim Programmieren ja immer mehrere Möglichkeiten.

Unter anderem dann auch die, die ich mir zusammen prutscheln würde :-)
Ich hätte nicht gedacht, dass man mit so wenigen Änderungen auch einen 
Double-Click integriert bekommt. Ausgehend von deinem Programm hätte ich 
die komplette Abfrageverschachtelung umgebaut. Das wäre lange nicht so 
kompakt und sehr viel unübersichtlicher geworden.

Alternativ hatte ich mir auch überlegt, deine Abfragen zu lassen, wie 
sie sind, und übergeordnet die Shortklicks zu zählen. Damit könnte ich 
dann auch einfacher 3x, 4x,..-Klicks realisieren.
Für mich wäre es übersichtlicher und leichter zu ändern, würde aber 
wahrscheinlich mehr Programmspeicher in Anspruch nehmen und es wäre 
langsamer.
Aber gut, darüber mache ich mir dann Gedanken, wenn ich irgendwas davon 
wirklich benötige.

von Mark T. (bitts)


Lesenswert?

Jürgen S. schrieb:
> Deshalb müßtest Du darauf achten, eine saubere Programmlogik zu bauen,
> und eine sehr einfache Methode dazu ist das "EVA-Prinzip". Du mußt nur
> das Programm in drei logische Funktionsbereiche "Eingabe",
> "Verarbeitung" und "Ausgabe" aufteilen. Grundschema der Arduino-loop:


Spricht etwas dagegen, die Ausgänge bei jedem Durchlauf von loop() mit 
digitalWrite() neu zu setzen, auch wenn sich das Prozessabbild nichts 
geändert hat?

Vom Pic kenne ich es so, dass das kein Problem ist. Bei digitalWrite() 
bin ich mir nicht sicher.

von stefanus (Gast)


Lesenswert?

Bei allen mir bekannten Controllern passiert nichts, wenn man einen 
Ausgang auf den gleichen Pegel setzt, den er schon vorher hatte. Also 
keine Spikes beim Umschalten, oder so.

von Konrad S. (maybee)


Lesenswert?

Bedienung mit Multi-Klick ist nicht unbedingt der Hit. Der harmlosere 
Punkt ist, dass ein n-Klick erst erkannt wird, wenn der Startzeitpunkt 
des n+1-Klicks ereignislos verstrichen ist. Problematischer finde ich, 
dass bei zu schnell nacheinander ausgeführten Einzelklicks ein 
Doppelklick nicht nur erkannt, sondern auch ausgeführt wird. Beschränkt 
man sich auf die Unterscheidung kurzer Klick und langer Klick, so 
erreicht man - meiner Meinung nach - eine sicherere Bedienung.

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Konrad S. schrieb:
> Bedienung mit Multi-Klick ist nicht unbedingt der Hit.
Der Hit wäre dann eher wieder die "OneKey" Eingabe: man morst seinen 
Befehl in das Gerät hinein. Das hätte wenigstens den Vorteil, dass ein 
weltweit bekanntes Eingabeverfahren verwendet wird...

: Bearbeitet durch Moderator
von Konrad S. (maybee)


Lesenswert?

Ich morse immer nur E und T. ;-)

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.