mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik UP - DOWN Taster Entprellung mit Timer


Autor: t3sla (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

ich weiß, dass Tasterentprellung softwareseitig mittels Timer hier schon 
1000-mal durchgekaut wurde, aber ich weiß nicht wie ich in meinem 
Anwendungsfall die Entprellroutinen integrieren kann.
Ich habe 2 Taster, jeweils ein UP und ein DOWN Taster, die eine globale 
Variable inkrementieren bzw. dekrementieren sollen. Es soll nichts 
passieren wenn beide Tasten gleichzeitig gedrückt werden (UP und DOWN 
gleichzeitig macht keinen Sinn). Außerdem soll, auf einen Tastendruck 
egal welcher Länge, nur einmal reagiert werden (als einmal inkrement 
oder dekrement).
Ich habe bereits einen Timer in Verwendung der alle 1,43ms ausgelöst 
wird und möchte dort die Entprellroutine integrieren.
Ich dachte mir, dass so etwas schon sicher mal von jemandem hier 
programmiert wurde und würde mich freuen wenn hier jemand seinen C Code 
posten könnte.

mfg
t3sla

PS.: Beide Taster hängen an einem Port (Port D)!

Autor: Stephan Henning (stephan-)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
1,4ms ist etwas kurz. Besser 4-6ms.
Manche Tater prellen mehrere ms lang.
Wenn Int kommt, Tasten einlesen. Status festhalten und wieder warten.
Dann wieder lesen, vergleichen mit alt wenn gut dann gedrückt, wenn 
nicht dann wieder neu warten.

Baue Dir ein Stausregitser, ähnlich wie bei Dannis Entprellung.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
t3sla schrieb:

> programmiert wurde und würde mich freuen wenn hier jemand seinen C Code
> posten könnte.

Was gefällt dir an der Forums-Standard-PeDa-Lösung nicht?

http://www.mikrocontroller.net/articles/Entprellun...

Zuverlässig, schnell, simpel in der Anwendung, 1000fach im Einsatz

PS: Wenn du bereits einen 1.4ms Timer in der Verwendung hast, kannst du 
den Code einfach in dessen ISR einfügen. Die im Code erwähnten 10ms 
musst du nicht so eng sehen. Ev. noch einen kleinen Zähler davor, sodass 
der Entprellcode in der ISR nur bei jedem 3ten oder 4ten Aufruf 
ausgeführt wird, dann reicht das schon.

Autor: t3sla (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Die Standard Lösung gefällt mir schon, nur verstehe ich sie nicht recht 
und ist außerdem viel zu komplex und umständlich für meinen Fall. Es 
soll zudem alles in der ISR erfolgen, auch die Manipulation der globalen 
Variable. Außerdem muss die ISR selbst, so kurz wie möglich sein. In der 
main wird dann nur der Wert der globalen Variable abgefragt.

Ich will wirklich nur die spezielle UP-, DOWN Taster Lösung. Ich 
verlange nicht, dass jemand den Code für mich schreibt, aber ich bin mir 
fast sicher das soetwas schon von irgendjemand hier, für irgendein 
Projekt, geschrieben wurde.

mfg
t3sla

Autor: Stephan Henning (stephan-)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Karl heinz Buchegger schrieb:
>  Ev. noch einen kleinen Zähler davor, sodass
> der Entprellcode in der ISR nur bei jedem 3ten oder 4ten Aufruf
> ausgeführt wird, dann reicht das schon.

na ob das bei seinem Wissensstand eine gute Idee ist.....
Ich denke den Timer zu ändern dürfte für ihn einfacher sein.

Auf der anderen Seite ist es natürlich der beste Weg unterschiedliche 
Zeitkonstanten mit einem Timer zu erzeugen, ohne ihn ständig 
umzukonfigurieren. Lernen muß er das ja doch mal.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
t3sla schrieb:
> Die Standard Lösung gefällt mir schon, nur verstehe ich sie nicht recht
> und ist außerdem viel zu komplex und umständlich für meinen Fall.

?
Die Funktionalität ist clever aufgebaut, zugegeben.
Nur kriegst du das mit anderem Code auch nicht einfacher hin, selbst 
wenn du nur 2 Taster hast.

> Es
> soll zudem alles in der ISR erfolgen, auch die Manipulation der globalen
> Variable.

Macht die Lösung doch.
Die komplette Auswertung der Tasten findet in der ISR statt.

> Außerdem muss die ISR selbst, so kurz wie möglich sein. In der
> main wird dann nur der Wert der globalen Variable abgefragt.

Auch das ist doch genau das, was die PeDa Lösung macht.
Reden wir wirklich vom selben Code?

Und wenn wir von 'so kurz wie möglich' reden.
Noch simpler (im Sinne von: schneller) als die PeDa Lösung geht schon 
fast nicht mehr. Das Teil ist extrem ausgefuchst.

Und wenn du den Autorepeat Teil nicht brauchst, dann lass ihn einfach 
weg
uint8_t ISR_count;

ISR( TIMER0_OVF_vect )
{
  static uint8_t ct0, ct1;
  uint8_t i;
 
  ISR_count++;
  if( ISR_count == 4 )
  {
    ISR_count = 0;

    i = key_state ^ ~KEY_PIN;                       // key changed ?
    ct0 = ~( ct0 & i );                             // reset or count ct0
    ct1 = ct0 ^ (ct1 & i);                          // reset or count ct1
    i &= ct0 & ct1;                                 // count until roll over ?
    key_state ^= i;                                 // then toggle debounced state
    key_press |= key_state & i;                     // 0->1: key press detect
 
  }

  ... dein bisheriger ISR Code
}

das möchte ich sehen, wie du eine Tastenauswertung/Entprellung für 2 
Tasten noch kompakter und auch mit weniger Rechenzeitverbrauch 
hinkriegst.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Stephan Henning schrieb:
> Karl heinz Buchegger schrieb:
>>  Ev. noch einen kleinen Zähler davor, sodass
>> der Entprellcode in der ISR nur bei jedem 3ten oder 4ten Aufruf
>> ausgeführt wird, dann reicht das schon.
>
> na ob das bei seinem Wissensstand eine gute Idee ist.....

lol

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
t3sla schrieb:
> Die Standard Lösung gefällt mir schon, nur verstehe ich sie nicht recht
> und ist außerdem viel zu komplex und umständlich für meinen Fall.

Das liegt im Auge des Betrachters.
99% der anderen Lösungen sind deutlich umständlicher oder weniger 
wirksam.

Die Repeat-Funktionen lösche einfach, wenn Du sie nicht benötigst.
Allerdings stört das bischen toter Code nicht, wenn Du nicht grade nen 
ATtiny13 nimmst.


> Es
> soll zudem alles in der ISR erfolgen, auch die Manipulation der globalen
> Variable.

Es hindert Dich niemand daran, get_key_press() gleich mit im Interrupt 
aufzurufen. Allerdings sind dann das cli() und sei() zu entfernen, da im 
Interrupt immer Interruptsperre herrscht.


> Ich will wirklich nur die spezielle UP-, DOWN Taster Lösung.

Das ist ja gerade der Witz an der universellen Lösung, man braucht keine 
speziellen Lösungen mehr.
One size fits all!


> Ich
> verlange nicht, dass jemand den Code für mich schreibt

Wenn Du die universelle Lösung nicht willst, schon.


> aber ich bin mir
> fast sicher das soetwas schon von irgendjemand hier, für irgendein
> Projekt, geschrieben wurde.

Denke ich auch, daß von den entprellten Tasten oft 2 für up/down 
verwendet werden.
Sieht dann etwa so aus:

if( get_key_press( 1<<KEY_UP ))
  my_var++;
if( get_key_press( 1<<KEY_DOWN ))
  my_var--;



Peter

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Karl heinz Buchegger schrieb:

>> Es
>> soll zudem alles in der ISR erfolgen, auch die Manipulation der globalen
>> Variable.

Ach jetzt versteh ich erst.
Du hast eine globale Variable und willst die mit den Tasten rauf/runter 
zählen.

Warum muss das in der ISR passieren?
Das kann doch in der Hauptschleife (wo es auch hingehört) genausogut 
erledigt werden.

Aber seis drumm
uint8_t ISR_count;

uint8_t gloableVariable;
 
ISR( TIMER0_OVF_vect )
{
  static uint8_t ct0, ct1;
  uint8_t i;

  ISR_count++;
  if( ISR_count == 4 )
  {
    ISR_count = 0;

    i = key_state ^ ~KEY_PIN;                       // key changed ?
    ct0 = ~( ct0 & i );                             // reset or count ct0
    ct1 = ct0 ^ (ct1 & i);                          // reset or count ct1
    i &= ct0 & ct1;                                 // count until roll over ?
    key_state ^= i;                                 // then toggle debounced state
    key_press |= key_state & i;                     // 0->1: key press detect

    if( key_press & ( 1 << UP_KEY ) ) {
      key_press ^= ( 1 << UP_KEY );
      gloableVariable++;
    }

    if( key_press & ( 1 << DOWN_KEY ) ) {
      key_press ^= ( 1 << DOWN_KEY );
      gloableVariable--;
    }
  }

  ... dein bisheriger ISR Code
}

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Peter Dannegger schrieb:

> Die Repeat-Funktionen lösche einfach, wenn Du sie nicht benötigst.

Wobei ich auch mal sagen muss, das gerade diese Funktionalität für mich 
das Geilste überhaupt in deinem Code ist.

Die Einstellung irgendwelcher Konfigurationsparamter mit einem 
Wertebereich von 0 bis 1000 wird so deutlich vereinfacht. Anstatt sich 
die Finger wundzutippen einfach auf dem Taster bleiben und schon zählt 
der Parameter hoch. Die letzten Feinheiten mit einzelnen Tastendrücken 
erledigen und fertig.
Schon alleine für dieses Feature hast du dir ein Bier verdient.

Kurzen und Langen Tastendruck unterscheiden zu können hab ich bisher 
noch nie benötigt :-)

Autor: Andy Andy (andy-online)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hier mal ein Lösungsansatz oder Hinweis oder Denkanstoß.

Erfahrungsgemäß ist es von Vorteil, so wenig wie möglich in der ISR zu 
machen. Ich mach das immer so, dass ich in der ISR in einem Statusbyte 
Merkerbits setzte bzw. nur das minimal notwendigste mach und dann später 
im Programm drauf reagiere.

ISR( TIMER0_OVF_vect )
{
  ++ucTimerInt;
}

void main(void)
{
  time();
  
  if(ucTimerInt == 7) // sind dann ca 10ms
  {
    SetBit(ucTimeControl,b10msOvr);
    ucTimerInt = 0;
  }

  if(CheckBit(ucTimeControl,b10msTimer))
  {
    if(CheckBit(PORTD,0)
    {
      ++ ucCounter;
    }
  }

  if (ucCounter > 6)
  {
     //hier ist dann der Tastendruck gültig
  }
}


void time(void)
{
  ClearBit(ucTimeControl,b10msTimer);

  if(CheckBit(ucTimeControl,b10msOvr))
  {
    SetBit(ucTimeContrl,b10msTimer));
  }
}


Einziger vernachlässigbarer Nachteil ist dabei, dass ich einen 
kompletten Programm Durchlauf unter 10ms (in meinem Beispiel) 
garantieren muss.

Wesentlicher Vorteil ist, dass ich das b10msTimer Bit einen 
Programmdurchlauf an jeder Stelle abfragen kann.

Ich Entprell meine Taster immer bis 60ms. Unter 10ms find ich es fast zu 
knapp.

Autor: Taz (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo Andy Andy
hätte ich ungefähr auch so gemacht.
was bei dir noch fehlt ist, das ucCounter zurückgesetzt wird wenn Taste 
nicht gedrückt.
if(CheckBit(PORTD,0)
    {
      ++ ucCounter;
    } else ucCounter=0;
@ t3sla
wenns in der ISR sein soll und zwei Tasten, dann (etwas abstrakter) so
ISR()
{
if (Key1down()) Key1Counter++);  // Taste gedrückt Zähler zählt hoch
else Key1Counter=0;              // wenn nicht dann 0
if (Key2down()) Key2Counter++);  // dasselbe in grün
else Key2Counter=0;

// Hat der Zähler lange genug hochgezählt -> Taste gedrückt
if (Key1Counter>=Zaehlerstand_entsprechent_fuer_100ms) Key1pressed=true;
//(in der Main dann Key1pressed=false; zum zuruecksetzen) 
if (Key2Counter>=Zaehlerstand_entsprechent_fuer_100ms) Key2pressed=true;
//(in der Main dann Key1pressed=false; zum zuruecksetzen) 
}
Key2Counter gibt auch auskunft ob lang oder kurz gedückt(oh Gott noch 
mehr ifs)

will man verhindern das ein langes Festhalten der Taste noch ein Event 
auslöst muss man am einfachsten das if erweitern:
[c]
if (Key2Counter>=Zählerstand_entsprechent_für_100ms)
{
  Key2pressed=true;
  Key2Counter=Zählerstand_entsprechent_für_100ms; //Zähler halt!
} else Key2pressed=false;


Aber es gibt viele Lösungen such dir was aus.

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Taz schrieb:
> Key2Counter gibt auch auskunft ob lang oder kurz gedückt(oh Gott noch
> mehr ifs)
>
> will man verhindern das ein langes Festhalten der Taste noch ein Event
> auslöst muss man am einfachsten das if erweitern:

Ja, so ist das eben, wenn man unbedingt bei jedem Projekt seine 
Speziallösungen von neuem entwickeln will.
Man pappt hier und da noch was ran, bis man völlig den Überblick 
verloren hat. Und mit jeder weiteren Taste bläht sich das Ganze umso 
mehr auf.

Man könnte seine Zeit aber auch nutzbringender oder angenehmer 
verbringen.


Peter

Autor: Stephan Henning (stephan-)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
tja Peter,
mit "Copy and Paste" kommt man auf Dauer aber auch nicht weiter.
Für das Verständnis ist es schon gut. Wenn er weis wie es geht, weis er 
auch Deine zu schätzen....hoffe ich. Dann kann er sie immer noch nehmen.

Lieber so, als wenn jemand alles von hier wild zusammenkopiert und sich 
dann wundert das sein Temp. messender DCF77 Empfänger die per USB 
erhaltenen Daten vom PC nicht an die Funkwetterstation per WLAN senden 
kann und der Schrittmotor nicht den richtigen Wochentag anzeigt weil die 
LED´s mit 10 Ohm an 24V geschaltet wurden.   :-))

Und dann sollen wir den Fehler finden...

Autor: Andy Andy (andy-online)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich hab mein "Entprell-Rad" einmal erfunden und nutze dies immer wieder. 
Kann damit beliebig viele PINS auf verschieden PORTS entprellen. Sehr 
übersichtlich und auführlich gecodet. Reagiert auf fallende oder 
steigende Flanke des PINS.

Mein Beispiel oben soll keine komplett Lösung darstellen sondern nur 
einen Lösungsansatz für Anfänger sein.

Ich wollte das Augenmerk auf die ISR legen. Das ist eine Einsteigerfalle 
die gefährlicher wird, je größer das Projekt wird. Auch wenn dann 
mehrere ISR ins Spiel kommen die nicht priorisiert werden können (auf 
den ATMEGAS).

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Peter Dannegger schrieb:
> Taz schrieb:
>> Key2Counter gibt auch auskunft ob lang oder kurz gedückt(oh Gott noch
>> mehr ifs)
>>
>> will man verhindern das ein langes Festhalten der Taste noch ein Event
>> auslöst muss man am einfachsten das if erweitern:
>
> Ja, so ist das eben, wenn man unbedingt bei jedem Projekt seine
> Speziallösungen von neuem entwickeln will.

Ich mag mich irren. Bei Prozessen, die über mehrere Funktionsaufrufe 
gehen ist das nur durch Codebetrachtung immer etwas schwer zu sagen, 
aber ich bin der Meinung, dass diese Lösung so wie sie gepostet wurde, 
nicht funktioniert. Bleibt man länger als 0.1 Sekunden auf der Taste, 
dann werden viele, viele Tastendrücke registriert.

Autor: Taz (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hi Karl heinz Buchegger
ich weiss nicht was mit 'Bei Prozessen, die über mehrere 
Funktionsaufrufe gehen..' gemeint sein soll, aber ich wette das der Code 
funktioniert ob er das tut was t3sla will sei dahingestellt.

ich hatte geschrieben :
" will man verhindern das ein langes Festhalten der Taste noch ein Event
auslöst muss man am einfachsten das if erweitern:

if (Key2Counter>=Zählerstand_entsprechent_für_100ms)
{
  Key2pressed=true;
  Key2Counter=Zählerstand_entsprechent_für_100ms; //Zähler halt!
} else Key2pressed=false;
"
Das Event das ausgelöst wird heisst Key_pressed (OK ich hätte besser 
Key_Down geschrieben) bedeutet Taste ist gedrückt und nicht es wurde 
eine Taste gedrückt (Key_click oder so) es ist Aufgabe der Main Routine 
entsprechend auf das Event zureagieren, das hängt aber vom Anwendungfall 
ab.
Und über so etwas einfaches brauche ich nicht lang drüber nachzudenken, 
den Code schreib ich einfach so runter. Schwieriger ist immer 
herauszufinden was das Programm machen soll - die Anforderungen.
Aber um nochmal auf die Ursprüngliche Frage zurückzukommen, es sollte 
eine Variable hoch bzw runtergezählt werden und das nur einmal.
einfach in der ISR:
wenn Key1pressed = true ist Variable++
wenn Key2pressed = true ist Variable--
am Anfang der ISR Key1pressed & Key2pressed abfragen und die ISR 
vorzeitig verlassen, so wird der Tastendruck nur einmal gesehen.
Zweites Problem beide Tasten gleichzeitig, wenn ich das mal umsetze 
bedeutet das, wenn innerhalb von den ersten 100ms beide Taster gedrückt 
werden, soll nicht passieren -> ISR vorzeitig verlassen wenn beide 
Tastencounter ungleich 0 sind. (if (Key1Counter&Key2Counter) return;)
Ich möche keinen Copy und Paste Code hier posten
1. schreibe ich nicht für AVRs
2. müste ich den Code durchtesten (keine Zeit)
3. wenn ihr keine Anfänger seid sollt ihr euch selber Gedanken machen
4. lernt man nichts, wenn man Code einfach so übernimmt

ich hoffe das hilft jedem eine Lösung zu finden, sonst muss ich doch mal 
die ganze Routine als Copy und Paste Version posten.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Taz schrieb:
> Hi Karl heinz Buchegger
> ich weiss nicht was mit 'Bei Prozessen, die über mehrere
> Funktionsaufrufe gehen..' gemeint sein soll, aber ich wette das der Code
> funktioniert ob er das tut was t3sla will sei dahingestellt.
>
> ich hatte geschrieben :
> " will man verhindern das ein langes Festhalten der Taste noch ein Event
> auslöst muss man am einfachsten das if erweitern:
>
> if (Key2Counter>=Zählerstand_entsprechent_für_100ms)
> {
>   Key2pressed=true;
>   Key2Counter=Zählerstand_entsprechent_für_100ms; //Zähler halt!
> } else Key2pressed=false;
> "
> Das Event das ausgelöst wird heisst Key_pressed (OK ich hätte besser
> Key_Down geschrieben) bedeutet Taste ist gedrückt und nicht es wurde
> eine Taste gedrückt (Key_click oder so) es ist Aufgabe der Main Routine
> entsprechend auf das Event zureagieren, das hängt aber vom Anwendungfall
> ab.

Gut.
Die Hauptschleife wird also so aussehen

  while( 0 ) {

    if( Key2pressed ) {
      Key2pressed = false;
      // mach was, wenn Taste 2 gedrückt wurde
    }

schön.
In der ISR wurde Key2Counter auf Zählerstand_entsprechent_für_100ms 
gesetzt. Das hindert aber

  if (Key2down()) Key2Counter++);  // dasselbe in grün
  else Key2Counter=0;

in der ISR, beim nächsten Aufruf, nicht daran Key2Counter wieder zu 
erhöhen, wenn die Taste immer noch gedrückt ist.

Als Folge davon ist dann

  Key2Counter>=Zählerstand_entsprechent_für_100ms

wieder true (*), und

  Key2pressed=true;

zeigt der Hauptschleife den nächsten Tastendruck an (Hinweis: Die Taste 
wurde bisher nicht losgelassen, sondern ist immer noch gedrückt. 
Trotzdem hat die Hauptschleife bisher 2 Tastendrücke registriert. Und 
wenn die Taste noch länger gedrückt gehalten wird, registriert die 
Hauptschleife weitere keypress Events)

> Ich möche keinen Copy und Paste Code hier posten
> 1. schreibe ich nicht für AVRs
> 2. müste ich den Code durchtesten (keine Zeit)

Geht mir auch so.
Schau, mir ist es im Grunde egal ob deine Tastenauswertung funktioniert 
oder nicht. Aber einen Anfänger kann sowas zur Verzweiflung bringen. 
Denn auch wenn du das anders siehst: Auch Copy&Paste Code hat seinen 
Wert, WENN sich der OP die Mühe macht ihn zu analysieren, wie er 
funktioniert, vorausgesetzt dass er funktioniert.


(*) Im Grunde ist das Hochzählen des Key2Counter völlig irrelevant.
Schon alleine die Sequenz

if (Key2Counter>=Zählerstand_entsprechent_für_100ms)
{
  Key2pressed=true;
  Key2Counter=Zählerstand_entsprechent_für_100ms; //Zähler halt!
} else Key2pressed=false;

sorgt dafür, dass Key2pressed bei jedem ISR Aufruf Key2pressed mit true 
hinterlässt, wenn Key2Counter erst einmal 
Zählerstand_entsprechent_für_100ms erreicht hat.

Wie gesagt: Ich mag falsch liegen. Ich habs nicht tatsächlich 
ausprobiert. Es steht auch ausser Frage, dass man das alles auf deiner 
Basis funktionierend hinkriegen kann. Nur eine Lösung, die man zu einer 
funktionierenden Lösung umbauen kann, ist kein guter Kandidat um einem 
Newbie eine Anregung zu geben.

Autor: Taz (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
wenn t3sla ein Newbie wäre und ein einfache Lösung gefragt wäre hätte 
ich folgendes vorgeschlagen:
#define TasteUp 1
#define TasteDown 2
#define Maximum 100  // Max 100Prozent
#define Minimum 0    // Min 0 Prozent
in der main()

do{
.
.
// Taste Up auswerten
if (PortA&TasteUp) // TasteUp gedrückt
{
  delay_ms(100); // wegen Schalterprellen 100ms warten
  UpDownCounter++;
  if(UpDownCounter > Maximum)
     UPDownCounter=Maximum;
  while(PortA&TasteUp); // nichtstun solange Taste noch gedrückt
  delay_ms(100); // wegen Schalterprellen 100ms warten
}

// Taste Down auswerten
if (PortA&TasteDown) // TasteDown gedrückt
{
  delay_ms(100); // wegen Schalterprellen 100ms warten
  UpDownCounter--;
  if(UpDownCounter < Minimum)
     UPDownCounter=Minimum;
  while(PortA&TasteDown); // nichtstun solange Taste noch gedrückt
  delay_ms(100); // wegen Schalterprellen 100ms warten
}

.
.
}while(1);
ich würde keine ISR verschwenden.

Autor: Andy Andy (andy-online)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Taz schrieb:
...
>   delay_ms(100); // wegen Schalterprellen 100ms warten
...

Genau das ist das was ich an Code hasse den ich nicht selbst geschrieben 
hab.
Das mag ja funktionieren, hält aber mein Programm für 100ms auf. In 
dieser Zeit kann ich NICHTS anders machen.

OK seh ich ein wenn ich 2 Tasten hab die eine LED ein oder aus schalten.
Wird das Programm aber etwas umfangreicher, was meiner Erfahrung nach zu 
Beginn nur bedingt abschätzbar ist, wird man an den delay_ms zu Grunde 
gehen.
Das ist meiner Meinung nach absoluter Schwachsinn.

Korrigier mich einer wenn ich falsch liege.

>ich würde keine ISR verschwenden.

Der Timer und dessen Interrupt ist doch dazu da um ihn zu verwenden.
Wenn man einen Timer mit 10ms Interrupt hat kann daraus ja sämtliche 
Zeiten generieren.

Autor: Taz (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
"Korrigier mich einer wenn ich falsch liege."
du liegst falsch, wenn ich als User eine Taste drücke spielen 100ms 
überhaupt keine Rolle der Mensch ist viel zu langsam. Wie oft kann ich 
den die Taste in 100ms drücken ? mal von Schalterprellen abgesehen wo 
ich sowieso warten muss.
Natürlich muss man dafür sorgen das wichtig Prozesse nicht unterbrochen 
werden. Deshalb sollten die wichtigen Funktion in einer ISR abgearbeitet 
werden (Datenaufnahe Verarbeitung speichern) und unwichtige Funktion wie 
Taster einlesen Bildschirmausgaben u.s.w. eben nicht oder in ISR mit 
einer niedrigen Priorität (der darf die andern ISR natürlich nicht 
sperren).
Und ich als Programmierer lege die Prioritäten fest.
(nebenbei ein NotAus Schalter hat die höchste Priorität).

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Taz schrieb:

> Natürlich muss man dafür sorgen das wichtig Prozesse nicht unterbrochen
> werden.

genau das ist der springende Punkt.

> Deshalb sollten die wichtigen Funktion in einer ISR abgearbeitet
> werden (Datenaufnahe Verarbeitung speichern) und unwichtige Funktion wie
> Taster einlesen Bildschirmausgaben u.s.w. eben nicht oder in ISR mit
> einer niedrigen Priorität (der darf die andern ISR natürlich nicht
> sperren).

Tastenabfrage in einer ISR verbraucht weniger als 1% Rechenzeit. Ist 
also völlig vernachlässigbar.

Ganz abgesehen davon, dass viele Programm sowieso sowas wie eine langsam 
mitlaufende 'Uhr' in einer ISR haben um einen Zeit-Basistakt zu haben.

Autor: Andy Andy (andy-online)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@Taz
Ich hab nicht behauptet, dass das Entprellen nicht notwendig ist. Das 
muss sein natürlich. Aber der weg mit delay_ms ist denkbar ungünstig. 
Die 100ms spielen für den User keine Rolle halten aber das Programm 
sinnlos auf.
Grundsatz Nummer eins ist: Ein Programm muss so schnell wie möglich 
durchlaufen und darf nicht angehalten werden um z.B. auf ein Ergebnis zu 
warten. Wenn ich "warten" muss dann reagiere ich meinetwegen 1000 
Programmdurchläufe später darauf. Schau dir meinen Ansatz weiter oben 
an.

>Deshalb sollten die wichtigen Funktion in einer ISR abgearbeitet
>werden (Datenaufnahe Verarbeitung speichern)

NEIN:
Wenn z.B. eine Timer mit 2ms Interrupt am Laufen hast und in diesem Int 
einen Wert ins EEPROM schreiben will dann wird dieses Vorhaben 
scheitern. Ein Schreibzyklus aufs EEPROM dauert ca. 8ms.

Glaub mir in einer ISR darf nur so wenig wie möglich Code stehen.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Taz schrieb:

> Natürlich muss man dafür sorgen das wichtig Prozesse nicht unterbrochen
> werden. Deshalb sollten die wichtigen Funktion in einer ISR abgearbeitet
> werden (Datenaufnahe Verarbeitung speichern) und unwichtige Funktion wie
> Taster einlesen Bildschirmausgaben u.s.w. eben nicht oder in ISR mit
> einer niedrigen Priorität (der darf die andern ISR natürlich nicht
> sperren).

  delay_ms(100); // wegen Schalterprellen 100ms warten
  UpDownCounter++;
  if(UpDownCounter > Maximum)
     UPDownCounter=Maximum;
  while(PortA&TasteUp); // nichtstun solange Taste noch gedrückt

Du denkst also dein Benutzer ist begeistert, wenn er seine UART-Buffer 
dadurch zum Überlaufen bringen kann, indem er eine Taste drückt :-) und 
somit die Verarbeitung von Kommandos, die über die UART hereinkommen und 
deren Bearbeitung schon mal etwas länger dauert (und daher nicht in 
einer ISR passiert), blockiert.
Und das alles nur, weil er das LCD heller stellen wollte :-)

Autor: Taz (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@ Andy Andy
Dein Grundsatz Nummer Eins gefällt mir nicht weil es doch stark 
Anwendungsabhängig ist. Und als Anfänger direkt so zu programmieren auch 
wenn unnötig ist macht einfach keinen Spass. Du gehst von Profis aus und 
ich habe extra betont 'Lösung für Anfänger'.
Und dein Beispiel: in 2ms Takt auf ein EEPROM zuschreiben das 8ms 
braucht würde doch nie funktionieren. Die Aussage das in der ISR 
möglichst wenig Code stehen darf ist natürlich nur ein Ratschlag. Die 
Zeit die eine ISR verbraucht darf natürlich nicht länger sein als die 
Zeit zwischen den ISRs plus etwas Zeit für den Rest. Meine ISR braucht 
etwa 90% der Zeit (ADC lesen verarbeiten speichen..bei f=10kHz) der Rest 
ist für RS232 u.s.w.

@  Karl heinz Buchegger
wie ich schon geschrieben habe das war eine einfache Lösung für einen 
Anfänger.
noch schlimmer als das Delay ist das while solang der Taster gedrückt 
ist läuft nichts mehr.
Wenn Du jetzt RS232 Funktionen ins Spiel bringst kann ich natürlich 
wieder duzende von Lösungen präsentieren aber immer wieder kann man 
einen Einwand machen. z.B. könnte die RS232 kann auch einen ISR auslösen 
(wäre eine Möglichkeit), aber dann ......u.s.w.
wenn dir das delay nicht gefällt dann vielleicht so:
.
unsigned long Taste1Counter=0;

if (PortA&TasteUp) // TasteUp gedrückt
  Taste1Counter++;
else Taste1Counter=0;

if ( Taste1Counter==80000 ) // Wert 80000 ist experimentel ermittelt 
{
  UpDownCounter++;
}
if(UpDownCounter > Maximum)
   UPDownCounter=Maximum;
.
.
1.Problem dabei TastenCounter zählt weiter und wird irgendwann 
überlaufen, kann man mit einem weiterm if und einem Flag lösen bei 32Bit 
wird das aber sehr lange dauern.
2.Problem die 80000 sind laufzeitabhängig und somit nicht ganz konstant 
und wenn die Schleife grösser wird muss man den Wert neu bestimmen.
Diese Lösung, denke ich kann man einem Anfänger auch noch anbieten.
Sonst habe ich keine einfache, leichtverständliche, Anfängertaugliche 
Lösung mehr parat.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Taz schrieb:

> wieder duzende von Lösungen präsentieren aber immer wieder kann man
> einen Einwand machen.

Siehst du.
Und genau jetzt kommen wir zum springenden Punkt.

Diese Lösung hier

http://www.mikrocontroller.net/articles/Entprellun...

funktioniert immer. Alles was ich dazu brauche ist ein Timer. Noch 
nicht mal die Taktung des Timers ist besonders kritisch.
Sie verbraucht minimal Rechenzeit, entprellt zuverlässig 1 bis 8 Tasten 
und behindert das restliche Programm in keinster Weise (wenn wir mal 
davon ausgehen, dass ein Zeitverbrauch < 1% akzeptabel ist).

Ganz im Gegenteil: Das Kochrezept über Jobflags (den um etwas anderes 
handelt es sich hier konzeptionell nicht), verallgemeinert hervorragend, 
sodass ein Newbie daraus nur lernen kann, wie er seinen Programmen eine 
Grundstruktur geben kann, mit der er nicht allzu falsch liegen wird.

Es handelt sich dabei also tatsächlich um eine "One size fits all" 
Lösung. Und sowas ist wahrlich selten.

Und wenn ich mir die Ansätze ansehe, die zum Fixen der kleinen 
Wehwehchen der vorgestellten Lösungen immer wieder nachgeschoben wurden 
- dagegen ist die PeDa Lösung eigentlich schon als simpel anzusehen. 
Gut, im genannten Artikel könnte man noch detaillierter ausführen, wie 
dieser vertikale Zähler funktioniert. Gestehe ich gerne zu. Am Anfang 
sieht das alles verwirrend aus, aber wenn man das erst mal durchschaut 
hat, ist es eigentlich ganz einfach.

Autor: Taz (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@Karl heinz Buchegger
glaubst du wirklich das ein Newbi also ein absoluter Anfänger das hier 
versteht?
ct0 = ~( ct0 & i );                             // reset or count ct0
  ct1 = ct0 ^ (ct1 & i);                          // reset or count ct1
  i &= ct0 & ct1;                                 // count until roll over ?
  key_state ^= i;                                 // then toggle debounced state
  key_press |= key_state & i;
...
ich habe gar nicht gegen diese Lösung im Gegenteil finde sehr gut aber 
eben nicht Anfängertauglich.

PS: wieso steht da count ct0 ? wird doch garnicht gezählt oder.

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Taz schrieb:
> Und über so etwas einfaches brauche ich nicht lang drüber nachzudenken,
> den Code schreib ich einfach so runter.

Wenn man nicht darüber nachdenkt, dann ist Tasten einlesen schwierig.

Das sieht man leider an vielen kommerziellen Geräten, wo die Entwickler 
der irrigen Meinung sind, man müsse dem keine Bedeutung beimessen.
Ich ärgere mich regelmäßig darüber, wenn z.B Tastendrücke verloren 
gehen.

Wenn man aber einmal richtig drüber nachgedacht hat, dann ist Tasten 
einlesen popel einfach und vor allem nicht Ressourcen fressend.
Und dann muß man einfach nur noch die Tastenpins anpassen und die 
gewünschten Funktionen aus der Tasten-Lib aufrufen.


> Schwieriger ist immer
> herauszufinden was das Programm machen soll - die Anforderungen.

Deshalb ist es sinnvoll, universelle Tastenfunktionen zur Verfügung zu 
haben, auch wenn man nicht alle braucht.

Und es ist sehr nützlich, wenn diese Funktionen dann nicht viele 
Ressourcen (CPU-Zeit) belegen, damit es zu möglichst wenig 
Seiteneffekten mit den anderen Programmroutinen kommt.

Man sieht leider oft bei vermeintlich "einfachen" Tastenroutinen, daß 
z.B. Displays hängen oder LEDs flackern, wenn man ne Taste drückt.

Nebenbei ist die universelle Tastenabfrage auch noch sehr portabel, also 
sehr leicht auf verschiedene MCs anzupassen.


Peter

Autor: Taz (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Peter Dannegger schrieb:
" Wenn man nicht darüber nachdenkt, dann ist Tasten einlesen schwierig.
....
Wenn man aber einmal richtig drüber nachgedacht hat, dann ist Tasten
einlesen popel einfach .... "
Also wenn man es kann ist es einfach, dann nehme ich das mal als 
Kompliment.

Ich bin auch für universelle Lösungen wenn:
1. ich sie verstehe
2. wenn ich mich nicht an der Leistungs- bzw. Grössengrenze bewege

Ich habe viele vorgefertigte Routinen die ich einfach einbinde.
Aber die universellen ADC Routinen muss ich regelmässig anpassen wenn 
ich richtig schnell werden will. Du hast aber Recht man sollte schon 
versuchen Funktionen so zuschreiben das man sie universell benutzen 
kann.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Taz schrieb:
> @Karl heinz Buchegger
> glaubst du wirklich das ein Newbi also ein absoluter Anfänger das hier
> versteht?

Glaub ich ehrlich gesagt nicht.
Aber ein Newbie versteht auch nicht wie itoa arbeitet.

Aber ich versteh schon, worauf du hinaus willst. Und deine Intention ist 
absolut zu begrüssen. Der Punkt ist nur: Eine saubere, gut 
funktionierende Tastenroutine, die im Nähkästchen liegt und abgerufen 
werden kann, ist gar nicht so simpel zu finden.

> PS: wieso steht da count ct0 ? wird doch garnicht gezählt oder.

Doch, wird es.
Aber anders. ct0 und ct1 bilden gemeinsam acht Stück 2-Bit Zähler

Bit 0 von ct0 und Bit 0 von ct1 gehören zusammen und bilden einen 
Zähler, Bit 1 von ... etc

     Bit 7  6   5   4   3   2   1   0

                                #########
      +---+---+---+---+---+---+-#-+---+ #
 ct0  |   |   |   |   |   |   | # |   | #
      +---+---+---+---+---+---+-#-+---+ #
                                #       #
      +---+---+---+---+---+---+-#-+---+ #
 ct1  |   |   |   |   |   |   | # |   | #
      +---+---+---+---+---+---+-#-+---+ #
                                #########
                                   ^
                                   |
                                   +--- diese beiden Bits sind 1 Zähler

Der restliche ^ und & sind nichts anderes als ein 2 Bit Zähler, der auf 
allen 8 Bit dieser 8 'Counter' parallel arbeitet.

Auf sowas muss man erst mal kommen!

Autor: Taz (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Danke für die Erklärung aber da ich kein AVR programmiere fällt es mir 
etwas schwer das Programm im Kopf 'ablaufen' zulassen - zu viele 
Unbekannte zu viele Variablen (und die auch noch vom Type static).
Trotzdem erkenne ich nicht das ct0 hochzählt also 0,1,2,3..
Aber von der Programmierung her sieht es schon verdammt gut aus, hat 
bestimmt viel Mühe gekostet. Ich hätte es bei weitem nicht so gut 
hinbekommen - Respekt.

Autor: Taz (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ahhh eine halbe Tasse Kaffe später hab ich verstanden.
Ich hab verstanden das '// reset or count ct0' sich auf ct0 bezieht, 
deshalb hab ich ein ct0++ erwartet, was Blödsinn wäre.
ct0 gibt seinen Status an ct1 weiter und bildet so mit ct1 einen Zähler.
Danke

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Karl heinz Buchegger schrieb:
> Der Punkt ist nur: Eine saubere, gut
> funktionierende Tastenroutine, die im Nähkästchen liegt und abgerufen
> werden kann, ist gar nicht so simpel zu finden.

Man kann bei einer Aufgabe nie alle Parameter optimieren, man muß daher 
Prioritäten setzen, z.B.:

1. gut funktionierend
2. universell
3. Ressourcen schonend
4. leicht verständlich

Ich denke, das Häckchen für meine Routine bei 1. - 3. steht außer Frage.

Ich habe bisher auch keine andere Routine gesehen, die bei 4. besser 
abschneidet ohne bei den anderen Punkten erhebliche Abstriche in Kauf zu 
nehmen.


Peter

Antwort schreiben

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

Wichtige Regeln - erst lesen, dann posten!

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

Formatierung (mehr Informationen...)

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




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

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