Forum: Projekte & Code Entprellen in C, schlanke Routine


von Toni (Gast)


Lesenswert?

Hallo

Hier, wie ich denke, eine weitere sehr schlanke Routine zur Entprellung
eines Tasters.

Toni



***************************************************************

uint16_t status;
uint8_t taste0;

void entprell(void)
{
status = ((status<<1) | (!bit_is_clear(PIND,PIND0)) | 0xe000);
if (status==0xf000)  taste0=0;
else taste0=1;
}

***************************************************************


Funktion kann im Hauptloop stehen, oder per Interrupt regelmässig
aufgerufern werden.

Taste gedrückt entspricht Pegel "0".
In 'status' wird eine '1' von rechts nach links geschoben, die die
letze Flanke '1->0' darstellt.
Falls Taste nicht prellt, folgen nur noch'0'.
Im gesamten werden hier 12 Umgänge geprüft, kann mit Maske/Typ für/von
'status' geändert werden !

Also zu Beginn:status      :1110 0000 0000 0000  (Maske 0xe00)
Wärend prellen, z.B        :1110 0010 0100 1001
Entpellt (12 cycles)       :1111 0000 0000 0000  (status==0xf000)

von Peter D. (peda)


Angehängte Dateien:

Lesenswert?

Du hast Dir bestimmt noch nicht den erzeugten Assembler (56 Byte Code
siehe Anhang) angesehen, sonst würdest Du nicht so leichtfertig von
"schlank" sprechen.

Mit auch 3 Bytes SRAM aber nur 46 Byte Code entprelle ich Dir sogar 8
Tasten gleichzeitig.


Peter


P.S.:
Man sollte schon dem Namen Codesammlung gerecht werden und einen
funktionierenden Code posten.

Dein Fragment erzeugt aber nur tonnenweise Fehlermeldungen.

Also das nächste mal wenigstens etwas compilierbares posten, d.h. mit
den nötigen includes:

#include <sfr_defs.h>
#include <io.h>

von Hagen (Gast)


Lesenswert?

@Toni:

wenn mein AVR mit 16 MHz getaktet ist, und ich deine Entprellroutine in
meiner Mainloop ständig aufrufe dann komme ich auf 16.000.000 / 16 =
1Mhz Entprellung. Jedes Prellen von Tastern muß mit einer Frequenz
größer 1 Mhz reinkommen, alles was drunter liegt wird als Tastendruck
erkannt.

wenn mein AVR nun aber mit 32Khz getaktet ist so muß also 32.000 / 16 =
2Khz sein, alles was schneller als 2Khz ist wird als Prellen erkannt,
alles was langsammer als 2Khz ist wird als Tastendruck erkannt. Das
funktioniert also noch fast, wenn nicht viele Taster mit bis zu 10ms =
100Hz prellen würden.

Ergo: damit deine Entprellroutine sauber funktioniert darf ich sie nut
alle 20 mal in der Mainloop aufrufen, bei 32KHz Takt. Bei 16Mhz Takt
darf ich sie nur alle 8000 male aufrufen. Es ist aber nun das Problem
das in der Mainloop noch viele andere Aufgaben abgearbeitet werden, zb.
alle 5 Sekunden für 2 Sekunden eine Datenüertragung die im 250ms Takt in
die Mainloop zurückkehrt. In diesen Momenten würde der 8000'ernder
Zähler aber nur alle 250ms inkremetiert und somit muß ich diesen Zähler
ständig so anpassen das er auf alle diese zusätzlichen aufgaben
umgestellt wird, um ein gleichmäßiges Pollen zu erreichen.

Naja, mein jetziger Sourcecode, rein virtuell gesehen, verbraucht jetzt
fast 1 Kb an zusäzlichen Code damit ich diesen Zähler so kontinuierlich
an die Prozessorlast anpassen kann das deine Entprellroutine wieder
periodisch alle 10 ms aufgerufen wird.

Gruß Hagen

von Marcel (Gast)


Lesenswert?

wenn du einen timer frei hast, dann packst du das in die isr von dem
timer (siehe peters code)

von einem in hochsprache geschriebenen programm kann man nicht
erwarten, dass da wenige asm befehle rauskommen. Wenn man das will,
dann muss man dann schon eine menege kohle für einen ordentlich
optimierenden compiler hinlegen. oder schreibt es gleich in assembler

von Hagen (Gast)


Lesenswert?

@Marcel,

jo ich weis das, nur zu einem ordentlichen und funktionstüchtigen Code
hier in der CodeLib sollten die kompletten Sourcen und nach Möglichkeit
ein DEMO rein. Dann würde man sehen das sich die Aussage "schlank"
nochmals relativiert.

Gruß Hagen

von peter dannegger (Gast)


Lesenswert?

"von einem in hochsprache geschriebenen programm kann man nicht
erwarten, dass da wenige asm befehle rauskommen."

Das hat auch nirgends nicht niemand behauptet.

Aber wenn man int aufm 8-Bitter nimmt und viele Anweisungen in eine
Zeile packt, mag das zwar optisch klein aussehen, nicht jedoch in
realem Code.


So schlecht finde ich den GCC garnicht. Zumindest bei meinen Code ist
selbst in Assembler kaum noch was rauszuholen.
Bei Tonis Code sieht man schön den doppel-RET-Bug, aber das sind ja nur
2 Byte toter Code je Funktion.
Man muß das immer auf das gesamte Programm bezogen sehen, da sind dann
10% mehr Code nicht der Rede wert.


Peter

von hanes (Gast)


Lesenswert?

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

hier kannst du deine routine eintragen. Dann gibt es eine übersicht mit
allen entprellroutinen. Mit ihren Vorteilen und nachteilen.

von Markus M. (markus81)


Lesenswert?

Die Entprellung funktioniert wirklich sehr gut. Dank an den Autor!

von Christian K. (Gast)


Lesenswert?

Ich möchte mal eine andere Methode zur Entprellung vorschlagen:

1) Die erste Flanke triggert einen Speicher (z.B. flankengetriggerter
Interrupt)
2) Danach interessiert mich die Taste erstmal nicht, d.h. ich ignoriere
sie für mindestens die Prell-Zeit.
3) Dann werte ich die Taste wieder aus (Interrupt scharf schalten)

Mit dieser Entprellung bekomme ich auch den kürzesten Druck mit, sowie
auch die exaktere Zeit wann die Taste gedrückt wurde.

Natürlich muss man die Prell-Zeit der Taste abschätzen, meistens ist
sie spezifizert. Wenn nicht kann bei Bedienung durch Menschen 20ms-30ms
sicher als kürzestes Intervall zwischen zwei mal drücken angenommen
werden.

Die Methode für eine gewisse Zeit auf einen stabilen Zustand zu warten
macht für mich weniger Sinn, vorausgesetzt natürlich, die Taste prellt
nicht von alleine, d.h. es gibt nur dann eine Flanke wenn auch wirklich
einer versucht darauf rumzudrücken.

Grüße
  Christian

von Bolle (Gast)


Lesenswert?

>Mit dieser Entprellung bekomme ich auch den kürzesten Druck mit, sowie
>auch die exaktere Zeit wann die Taste gedrückt wurde.

Zur "exakten Zeit": Die Bedienung einer Taste durch einen Menschen
(nicht nur solche mit Wurstfingern...) ist mit einer gewissen
zeitlichen Ungenauigkeit behaftet, welche so groß ist, daß es schlicht
keine Rolle spielt, ob der µC das Niederdrücken innerhalb von 4
Maschinenzyklen (Externe-Interrupt-Reaktionszeit) mitbekommt, oder erst
10 ms später.  Und selbst wenn er es "sofort" mitbekommt: Speicherst
Du den Tastenstatus zunächst in einer Variablen ab, läßt den µC dann
erstmal noch 10 ms was anderes rechnen, und wertest danach die
Tastenvariable aus, dann hast Du letztlich doch wieder eine um 10 ms
verzögerte Reaktion.

Zum "kürzesten Druck": Ein Mensch ist anatomisch nicht in der Lage,
eine Taste kürzer als eine gewisse Mindestzeitspanne niederzudrücken.
Wie groß die ist, hängt natürlich von dem Mensch und von dem Schalter
ab, aber sie ist in jedem Fall so groß, daß Dir bei 50 Abtastungen/s
mit Sicherheit auch nicht der kürzeste "man-made" Tastendruck
entgeht.

Zu dem, was ich in Deiner Beschreibung vermisse, nämlich die Nennung
des Nachteils Deiner Methode: Du belegst einen wertvollen externen
Interrupt mit einer läppischen Eingabetaste.  Was ist, wenn Du den
Interrupt dringend für eine andere Aufgabe benötigst, bei der es
wirklich auf Speed ankommt?  Und vielleicht hast Du auch nicht nur eine
Taste, sondern eine 4x4-Tastenmatrix.  Wie willst Du die an einen oder
zwei Interrupteingänge anschließen?

von Peter Dannegger (Gast)


Lesenswert?

"daß es schlicht keine Rolle spielt, ob der µC das Niederdrücken
innerhalb von 4 Maschinenzyklen (Externe-Interrupt-Reaktionszeit)
mitbekommt, oder erst 10 ms später."


Und nicht nur das, es ist in der Regel auch völlig unerwünscht !!!

Z.B. mit Gummisohlen über Teppich laufen und sich dem Gerät nähern: Ein
Funke springt über, der externe Interrupt löst aus, obwohl man gar
nichts drücken wollte !


Der Fahrstuhl in unserem Haus hat z.B. eine solche saublöde
Entprellroutine. Man muß die Taste nur berühren (nicht mal drücken) und
schon leuchten beide Fahrtrichtungen d.h. die Tür geht immer zweimal auf
und zu.
Abhilfe: Man muß sich vor dem Drücken immer erst an der Fahrstuhltür
erden.

Mit einer richtigen Entprellroutine würde sowas aber nicht passieren.


Peter

von Christian K. (Gast)


Lesenswert?

Ich benutze eigentlich immer gerne einen interrupt-fähigen Eingang, da
bei den meisten µC's dieser den µC aus den Sleep Mode holen kann. (ich
habe fast nur mit mobilen Geräten zu tun)

Auch Keypad's werden interrupt enabled (meistens ist die Veroderung
schon im Chip implementiert) betrieben.

ESD sichere Tasten natürlich vorrausgesetzt.

Grüße
  Christian

von Peter Dannegger (Gast)


Lesenswert?

"Ich benutze eigentlich immer gerne einen interrupt-fähigen Eingang, da
bei den meisten µC's dieser den µC aus den Sleep Mode holen kann."


Genau dazu ist ja der Pin-Change-Interrupt da. Man läßt damit die CPU
aufwachen, wenn an einem Eingang irgendwas passiert.

Die eigentliche Auswertung, was genau passiert ist, machen dann aber
die entsprechenden Routinen.

Und wenn sich herausstellt, daß nichts passiert ist, sondern es nur ein
Störimpuls war, geht die CPU eben wieder in den Sleep-Mode.


Ein paar richtige Zeilen Software sind bestimmt wesentlich kostenärmer
als teuere ESD-feste Tasten.

Die Tasten am Fahrstuhl sehen auch nicht gerade billig aus, aber sie
tuns eben nicht wegen der fehlerhaften Software.

Nicht alle Softwarefehler lassen sich durch Hardware korrigieren (und
umgekehrt), beides muß richtig sein.


Peter

von Werner D. (dorni)


Lesenswert?

Da die Tasterentprellung doch ein grundlegendes Problem darstellt, 
möchte ein Anfänger Tonis Programm als Lernhilfe anwenden
   (ähnelt dem von http://www.ganssle.com).
Nachdem der Code unvollständig ist, macht es erhebliche Probleme dies 
nachzuvollziehen!

Wäre nun schön, wenn jemand den kompletten funktionsfähigen Code 
reinstellen bzw. posten könnte.

von Sven P. (Gast)


Lesenswert?

Zum Entprellen folgender Ansatz:
"buttons" enthält den Wert des Portregisters (PINx). Bei jedem Durchlauf 
wird die Warteschlange "debouncer" eins weitergeschubst und gleichzeitig 
mit den aktuellen Tastenzuständen verunded. Schließlich werden die 
aktuellen Tastenzustände an den Anfang der Schlange gestellt.
Ausgewertet wird dann der letzte Eintrag der Schlange.

Resultat: Erst, wenn ein Impuls störungsfrei durch die ganze Schlange 
gewandert ist, wird er ausgewertet.

1
volatile uint8_t debouncer[5];
2
3
/* Im Timer-Interrupt wird dann alle paar ms folgendes gemacht: */
4
for (i = 0; i < 4; i++) {
5
  debouncer[i + 1] = debouncer[i] & buttons;
6
}
7
debouncer[0] = buttons;

Auszug aus dem Listfile (der GCC hat die Schleife aufgewickelt!):
1
      /* Debounce buttons */
2
      for (i = 0; i < 4; i++) {
3
        debouncer[i + 1] = debouncer[i] & buttons;
4
 18e:  90 91 76 00   lds  r25, 0x0076
5
 192:  80 91 79 00   lds  r24, 0x0079
6
 196:  89 23         and  r24, r25
7
 198:  80 93 7a 00   sts  0x007A, r24
8
 19c:  80 91 7a 00   lds  r24, 0x007A
9
 1a0:  89 23         and  r24, r25
10
 1a2:  80 93 7b 00   sts  0x007B, r24
11
 1a6:  80 91 7b 00   lds  r24, 0x007B
12
 1aa:  89 23         and  r24, r25
13
 1ac:  80 93 7c 00   sts  0x007C, r24
14
 1b0:  80 91 7c 00   lds  r24, 0x007C
15
 1b4:  89 23         and  r24, r25
16
 1b6:  80 93 7d 00   sts  0x007D, r24
17
      }
18
      debouncer[0] = buttons;
19
 1ba:  90 93 79 00   sts  0x0079, r25

von Franz (Gast)


Lesenswert?

Hallo

zuerst eine Vorbemerkung: schlank muss nicht mit gut gleichgesetzt 
werden.

Ich schreibe die Entprellung immer in einem Timerinterrupt. Dann kann 
ich ohne Probleme im Hauptprogramm den Schalter (als Globale, die im 
Interupt gesetzt worden ist) abfragen.

Im Timerinterrupt zähle ich ein Variable hoch, falls die Taste gedrückt 
ist und ich zähle die Variable herunter, falls die Taste nicht gedrückt 
ist.
Dann setze ich noch eine obere Grenze für die Variable, sodass nicht 
über 1000 gezählt wird und eine Grenze nach unten, sodass die Variable 
den Wert 0 nicht unterschreitet.

Beim Drücken der Taste läuft die Variable, langsam nach oben.

Mit einer oberen und unteren Schwelle, setzt ich (wie bei einem 
Schmitt-Trigger) den Schalterwert.

Das Schöne ist, dass alles im Hintergrund läuft.


(Der anagebene C-Code ist "frei" geschrieben und noch nicht über einen 
Compiler gelaufen. Kleinere  Fehler können noch vorhanden sein)

Beispiel:

1
//globale
2
int z, Schalter;
3
4
Timer_intterupt()
5
{
6
7
 if(PORTD.0 == 1)
8
    {
9
    z++;
10
    if(z>= 1000)
11
          z= 1000;
12
     }
13
 else
14
    {
15
     z--;
16
     if (z<=0)
17
     z=0;
18
     }   
19
     
20
if (z> 950)
21
   Schalter = 1;
22
 if (z< 50)
23
   Schalter =0;
24
}



Gruß
Franz

: Bearbeitet durch User
von Peter D. (peda)


Lesenswert?

Werner Dornstädter wrote:
> Da die Tasterentprellung doch ein grundlegendes Problem darstellt,

Nur für die absolut Lernresistenten, die starrsinnig die im Tutorial 
genannte effektive Methode für 1..8 Tasten ignorieren müssen, stellt es 
ein Problem dar.


> Wäre nun schön, wenn jemand den kompletten funktionsfähigen Code
> reinstellen bzw. posten könnte.

Warum sollte man etwas komplettieren, was schon im Ansatz deutlich 
aufwendiger ist?
Reicht Dir eine effektive und funktionierende Methode denn nicht?

Peter

von Werner D. (dorni)


Lesenswert?

Danke für Eure Antworten und Ratschläge, doch die Aufgabenstellung ist 
etwas anders.
Ich will erstmalig in C Programmieren und einen Mikrocontroller
einsetzen.
Und nun habe ich die Aufgabenstellung Schalter entprellen gewählt, um an
dieser Aufgabe die Codes verstehen lernen.
Wie ja jedem bekannt ist, hat man als Anfänger schon Probleme beim
Kompilieren und nun müssen auch noch die entsprechenden Fehlermeldungen
verstanden und behoben werden.
Lange Rede kurzer Sinn: anbei meine ersten Gehversuche. So und nun hängt 
es! Aufgrund meiner Recherchen fand ich diesen Beitrag, der mir 
weiterhelfen könnte.
1
#include <stdint.h>      // Definition der Datentypen
2
#include <avr/io.h>      // Wir brauchen Zugriff auf die I/O's des Controllers
3
#include <avr/interrupt.h>
4
#include <stdbool.h>                  //
5
 
6
#ifndef F_CPU
7
#define F_CPU           1000000                   // processor clock frequency
8
#endif
9
10
#define bool_t int
11
12
#define ON true
13
#define OFF false
14
15
16
//----------------------------------------------------------------------------
17
// Diese Funktion liest den Schalterzustand der Hardware aus. 
18
// Service routine called by a timer interrupt
19
// extern bool_t RawKeyPressed(); 
20
   bool_t RawKeyPressed()
21
//----------------------------------------------------------------------------
22
  
23
   { 
24
25
   }
26
27
28
//----------------------------------------------------------------------------
29
// Service routine called by a timer interrupt
30
// Routine liefert TRUE, wenn einmal ein entprellter Übergang 
31
// zum Schalter geschlossen aufgetreten ist.
32
   bool_t DebounceSwitch2()
33
//----------------------------------------------------------------------------
34
35
   { 
36
    static uint16_t State = 0;                     // Current debounce status 
37
    State=(State<<1) | !RawKeyPressed() | 0xe000; 
38
    if(State==0xf000) return true; 
39
    return false;
40
   } 
41
42
43
//----------------------------------------------------------------------------
44
// Service routine called by a timer interrupt
45
   int main (void)
46
//----------------------------------------------------------------------------
47
   {
48
49
50
   }
51
52
//----------------------------------------------------------------------------

Danke für Euer Verständnis.
Sollte das Thread wohl verlegen, da hier ja nichts lauffähiges zu finden 
ist?

von Strahlemann (Gast)


Lesenswert?

Ich verstehe auch nicht, warum solch ein 0815-Thema immer und immer 
wieder durchgekaut wird. Wo ist das Problem?

10ms Timer-Interrupt {

    Wenn Taste gedrückt {
        Zähler um 1 erhöhen
    } else {
        Zähler auf 0 setzen
    }

    Wenn Zähler = 5 {
        Aktion ausführen
    }

    Wenn Zähler > 100 {
        Aktion_bei_lange_gedrückter_Taste ausführen (falls gewünscht)
    }

}

Delays nach Belieben anpassen...

von Gast (Gast)


Lesenswert?

> Ich verstehe auch nicht, warum solch ein 0815-Thema immer und immer
> wieder durchgekaut wird. Wo ist das Problem?
Das Problem ist, dass viele meinen, dass ein C-Turorial durchzuarbeiten
verschwendetet Zeit ist.
Man googelt lieber stundenlang bis man irgendwo einen Code-Schnipsel
findet, der sich kompilieren lässt und hält sich danach für den
genialen Programmierer.
Beim nächsten Mal gehr es dann wieder von vorne los.

von Peter D. (peda)


Lesenswert?

Werner Dornstädter wrote:
> Wie ja jedem bekannt ist, hat man als Anfänger schon Probleme beim
> Kompilieren und nun müssen auch noch die entsprechenden Fehlermeldungen
> verstanden und behoben werden.

Man kann versuchen, vor den Fehlermeldungen davonzurennen.
Man kann versuchen, vor seinem eigenen Schatten davonzurennen.
Die Erfolgsaussichten sind beidesmal sehr gering.

Wenn Du die erste Fehlermeldung im exakten Wortlaut postest und das 
C-File als Anhang, damit man die Zeilennummer zuordnen kann, sollte sich 
schnell jemand finden, der Dir weiterhilft.
Bei C-Fragen vorzugsweise im GCC-Forum posten.


Peter

von Hannes L. (hannes)


Lesenswert?

Strahlemann wrote:
> Ich verstehe auch nicht, warum solch ein 0815-Thema immer und immer
> wieder durchgekaut wird. Wo ist das Problem?
>
> 10ms Timer-Interrupt {
>
>     Wenn Taste gedrückt {

Besser:
Wenn Tastenzustand anders ist als der intern gemerkte entprellte Zustand


>         Zähler um 1 erhöhen
>     } else {
>         Zähler auf 0 setzen
>     }
>
>     Wenn Zähler = 5 {

Wenn Zähler = 3, denn ein Zweibit-Zähler kann nunmal nur von 0 bis 3,
und bei kleinen Controllern mit knappen Ressourcen kann man schon Wert 
darauf legen, effizient zu programmieren.

>         Aktion ausführen

Intern gemerkten entprellten Zustand toggeln,
prüfen, ob es Tastendruck oder Tastenloslassen war,
bei Tastendruck Key_press-Flag setzen, das in der Mainloop ausgewertet 
und gelöscht wird...

>     }
>
>     Wenn Zähler > 100 {
>         Aktion_bei_lange_gedrückter_Taste ausführen (falls gewünscht)

Dabei aber auch noch zwischen erster Verzögerung und Repeat-Verzögerung 
unterscheiden...

>     }
>
> }
>
> Delays nach Belieben anpassen...

...

von Marek (Gast)


Lesenswert?

Ich habe die besagte Routine aus der Artikelsammlung getestet. 
Funktioniert bei mir. Nun versuche ich sie ganz zu verstehen dabei tue 
ich mich aber schwer. Ist es egal ob die Taste 0-1 oder 1-0 wechselt? 
Die internen pullups sind ja an, also geht nur ein 1-0 wechsel?
Erläuterung zu der routine wäre nett.

von Marek (Gast)


Lesenswert?

Will mein Problem noch etwas genauer sagen. in ct0 und ct1 ist für 
entprellte Tasten immer das entsprechende bit gesetzt? Wenn sie prellt 
und einmal eine 0 erscheint wird wieder resettet das Bit?

von Marek (Gast)


Lesenswert?

Also hab noch mal genau versucht alles nachzuvollziehen, aber ich denke 
ich bin zu dumm für euer forum.
Jede Taste hat ein bin in ct1 und 0, sowie i. die werden einzeln 
addiert, aber wie wird dabei das carry berücksichtigt?

von Kachel - Heinz (Gast)


Lesenswert?

> Will mein Problem noch etwas genauer sagen. in ct0 und ct1 ist für
> entprellte Tasten immer das entsprechende bit gesetzt?

Ich kenne (und verstehe) zwar nur die ASM-Routine, der Algorithmus wird 
aber identisch sein.

Die beiden Variablen ct0 und ct1 sind keine zwei 8-Bit-Variablen, 
sondern werden als acht 2-Bit-Variablen (8 Zähler) interpretiert. Dabei 
ist Bit 0 jedes Zählers in ct0 und Bit 1 jedes Zähler in ct1. Die 
Bitposition ist dann sozusagen die Zählernummer und entspricht der 
Bitposition des Port-Eingangs, an dem der entsprechende Taster 
angeschlossen ist.

Somit hat jeder Eingang seinen eigenen 2-Bit-Zähler, der Zählerstände 
von 0 bis 3 ermöglicht.

Wenn eine eingelesene Taste denselben Zustand hat, der intern als gültig 
entprellt gespeichert ist (key_state in der ASM-Version), dann wird der 
Prellzähler dieser Taste gelöscht. Besteht aber ein Unterschied zwischen 
neu und gemerkt, dann wird der Prellzähler erhöht. Beides passiert 
parallel für alle 8 Bits eines Ports gleichzeitig.
Schafft es ein Prellzähler nun bis zum Zählerstand 3 (binär 11, Bit in 
ct0 und ct1 gesetzt), dann hat der Taster 4 mal (Entprell-Runden) 
hintereinander entgegengesetzten Pegel gegenüber vorher gehabt, worauf 
der intern gespeicherte Tastenzustand (key_state in ASM) dieser Taste 
(also nur dieses Bit) invertiert wird und der Tastendruck als gültig 
übernommen wird. Nun wird anhand key_state geprüft, ob es sich um das 
Drücken oder Loslassen der Taste handelt. War es Drücken, dann wird auch 
noch das entsprechende Bit in key_press (in ASM, die Namen in C kenne 
ich nicht, dürften aber ähnlich lauten) gesetzt, also das Tastenflag, 
das dem Hauptprogramm einen neuen Tastendruck meldet.

Jede Taste hat also folgende Bitvariablen:
- key_state  intern gemerkter (entprellter) gültiger Zustand der Taste
- key_press  Merker, dass die Taste neu betätigt wurde
- ct0        Entprell-Rundenzähler Bit 0
- ct1        Entprell-Rundenzähler Bit 1
wobei jede Variable die Bits aller 8 Tasten entsprechend ihrer 
Bitposition am Port enthält.

> Wenn sie prellt
> und einmal eine 0 erscheint wird wieder resettet das Bit?

Nööö, siehe oben...

Ich hoffe, ich konnte helfen...

KH

von Kachel - Heinz (Gast)


Lesenswert?

> Jede Taste hat ein bin in ct1 und 0, sowie i. die werden einzeln
> addiert, aber wie wird dabei das carry berücksichtigt?

Es gibt kein Carry. Der Zähler zählt nur von 0 bis 3. Bei 3 wird die 
Taste (besser ausgedrückt der Pegelwechsel am Eingang) akzeptiert, bei 
Störungen durch Prellen wird auf 0 zurück gesetzt.

KH

von flieger-michl (Gast)


Lesenswert?

Hallo Kachel Heinz,

ich lese schon eine ganze Weile im Forum und versuche Peters 
Entprellroutine zu verstehen. Deine Erklärung brachte die Erleuchtung!

Danke dafür
Michl

von TheMason (Gast)


Lesenswert?

also die variante von peter ist wohl kaum zu schlagen (von der anzahl 
der code-bytes pro taster) und der effizienz. aber ich denke es ist auch 
(gerade für anfänger) die am schwierigsten zu verstehende variante.
die variante mit dem schieberegister die eingangs gepostet wurde ist 
sicherlich auch ne lösung, selbst wenn es in meinen augen unnötig 
kompliziert ist (verweundung von integer, wo char reicht; maskieren mit 
0xe000 [wofür überhaupt]). hier mal eine (ungetestete) ersatzvariante 
zum obigen code :

#define TASTEN_PORT   PINA
#define TASTEN_BIT    6

static char shiftreg  = 0;
static char state = 0;

char Entprellen (void)
{
  shiftreg = (shiftreg << 1) | ((TASTEN_PORT >> TASTEN_BIT) & 1);
  if (shiftreg == 0x7f) { state = 1; } // taste nicht gedrückt
  if (shiftreg == 0x80) { state = 0; } // taste gedrückt
  return state;
}

selbst wenn diese variante nicht getestet ist, sollte (ja ich weiß :-)) 
sie dennoch funktionieren. verwende z.b. in fpga's gerne schieberegister 
mit einer solchen abfrage (auf 0b0111 bzw 0xb1000) gerne zum 
einsynchronisieren von signalen die in einer anderen takt-domäne laufen. 
beim entprellen sollte dies denselbigen zweck erfüllen.
kurz zum verständnis : das schieberegister "taktet" den taster ein. es 
wird
eine 0->1 flanke erkannt, sobald das letzte bit (in dem falle bit 7) 0 
und alle anderen bits 1 sind. eine 1->0 flanke sobald das letzte bit 1 
und alle anderen 0 sind. irgendwelche zwischenzustände ändern nichts am 
taster-zustand (was in dem falle ja auch gewollt ist). selbst wenn der 
taster schon grottig schlecht ist und noch 100ms lang prellen würde, 
sobald 7 einsen und eine null, bzw 7 nullen und eine 1 im 
schieberegister sind, wird ein tastendruck bzw loslassen erkannt.
die polarität des tasters ist der einfachheit halber positiv (also 
gedrückte taste = ein high pegel am eingang). wenn es umgekehrt sein 
sollte wird einfach state = 1 und state = 0 vertauscht.

die entprell-routine einfach in einen 1ms timer reinpacken und dann 
sollte nach spätestens 8ms ein tastendruck sauber erkannt werden.
für die hautpschleife ist diese routine denke ich nichts (es sei denn es 
wird in der hauptschleife ein flag abgefragt welches von einem timer aus 
jede ms gesetzt wird).

aber nochmals : an effizienz ist peters code nie und nimmer zu schlagen. 
hab meinen code nur mal als ergänzung zum obigen code gepostet

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.