Forum: Mikrocontroller und Digitale Elektronik Taster entprellen


von Michael G. (magoo)


Lesenswert?

Hallo zusammen,

da bei meinem aktuellen Projekt der Mikrocontroller (Infineon C515C) 
Eingangssignale über einen Taster erhalten soll, stehe ich vor dem 
Problem, dass ich das Prellen des Tasters verhindern, bzw. "filtern" 
muss.

Ich habe hier im Forum und über google schon ein paar Lösungen gefunden, 
auf die ich aber leider niemals selbst gekommen wäre.
Deshalb habe ich mir eine sehr einfache, wenn auch nicht optimale Lösung 
ausgedacht.

Um das Ganze zu vervollständigen - der Pin des Mikrocontroller wird per 
Relais auf Masse geschalten, dabei sollen im Programm per Tastendruck 4 
Stufen durchgeschalten werden können.

Zur Lösung:
Ich dachte mir, externe Interrupts (auf negative Flanke getriggert) zu 
verwenden. In der Interrupt Routine wird dann eine Variable an die 
gewählt Stufe angepasst. Um nun das Tasterprellen nicht zu 
berücksichtigen, möchte ich einfach in der Interrupt Routine, den 
Interrupt deaktivieren und gleichzeitig einen Timer starten. Nach Ablauf 
des Timers (ca. 10 - 50 ms) wird der Interrupt wieder freigeschalten.
Dies sollte doch verhindern, dass die unabsichtlich auftretenden 
negativen Flanken aufgrund des Tasterprellens registriert werden.

So wie ich im Netz gelesen habe, liegt ein manueller Tastendruck bei ein 
paar hundert ms, dementsprechend sollte man nicht durch den gesperrten 
Interrupt behindert werden, oder?


Mit freundlichen Grüßen

von Jens (Gast)


Lesenswert?

Ich mache es genau so und bei mir klappt es sehr zuverlässig.

Gruß
Jens

von Michael G. (magoo)


Lesenswert?

Danke für die schnelle Rückmeldung :)

Wie lange sperrst du denn dann die Interrupts, um das Prellen nicht zu 
registrieren?

Gruß

von MaWin (Gast)


Lesenswert?

> auf die ich aber leider niemals selbst gekommen wäre.
> Ich dachte mir, externe Interrupts (auf negative Flanke
> getriggert) zu verwenden.

Warum bestehst du darauf, unbedingt deine eigene schlechtere
Lösung anwenden zu müssen ?

Gibt es dafür einen Genialitätspreis ?

Du willst nicht den Guttenberg spielen ?

Wenn man einen Taster in halbwegs regelmässigen Zeitabständen, die ÜBER 
der Prellzeit des Tasters liegen und UNTER der Reaktionszeit die der 
Benutzer wünscht (also meist ein sehr weiter Bereich) abfragt, bekommt 
man automatisch und ohne jeglichen Programmcode entprellte 
Tastensignale, bei denen man dann in Ruhe das Drücken, das Loslassen, 
die Zeitdauer erkennen kann.

Bei vielen Programmen ergibt sich von selbst eine Programmstelle, die so 
regelmässig durchlaufen wird, daß man den Code problemlos einfügen kann.

Wer gar noch Impulse, die nur 1 Abfrageintervall lang dauerten, also zu 
kurz waren als daß ein Mensch sie hätte eingeben können, unterdrückt, 
dann hat man sogar eine Entstörung, die deine flankengesteuerte Auswahl 
nicht bringt.

Und spätestens wenn du statt Tastern Inkremenetaldrehgeber abfragen 
sollst, merkst du, daß man mit der Reaktion auf Flanken per Interrupt 
auf dem Holzweg ist. Bei einzelnen Tastern geht noch beides. Aber sei 
dir sicher: Deine PC-Tastatur tut es nicht so wie du!

von Ina (Gast)


Lesenswert?

>Dies sollte doch verhindern, dass die unabsichtlich auftretenden
>negativen Flanken aufgrund des Tasterprellens registriert werden.

Ja, und die Leute die das machen, posten hier regelmäßig, daß es so 
gerade nicht funktioniert, weil sie irgend etwas falsch gemacht haben. 
Wie schon Mawin gesagt hat, solltest du die Interruptvariante sein 
lassen. Poll alle 10msec die Tastereingänge und verwende das Verfahren 
von Peter Dannegger mit den vertikalen Zählern. Damit kannst du 8 Taster 
auf einen Schlag entprellen. Schau in den Tutorials nach.

von Jens (Gast)


Lesenswert?

Michael G. schrieb:
> Wie lange sperrst du denn dann die Interrupts, um das Prellen nicht zu
> registrieren?

Auch wenn das anscheinend ein böse böse Technik ist ;-), ich mache es 
so:

Per Interrupt reagiert der uC auf einen Tastereingabe und setzt eine 
Variable (nennen wir sie buffer) für diesen Eingang auf einen Wert 
w. Zudem ist noch ein Timer so konfiguriert, der die ISR mit einer 
festen Frequenz aufruft, bei mir ca. 4kHz. Nun wird in der ISR (wenn sie 
denn durch den Timer-Overflow aufgerufen wurde) überprüft, ob buffer > 
0. Wenn ja, dann ziehe einen ab: *w*--;.

Erst wenn buffer wieder auf 0 ist, wird auf einen neuen Tastendruck 
reagiert, und wieder buffer auf *w gesetzt.

Wie du w wählst, hängt von deiner Anwendung ab, so im Zehnerbereich 
tut es meistens.

Ich hweiß nicht, ob es eine gängige Praxis ist, das so zu handhaben, 
aber ich mache es so, und es klappt echt gut.

Gruß
Jens

von Michael G. (magoo)


Lesenswert?

MaWin schrieb:
> Warum bestehst du darauf, unbedingt deine eigene schlechtere
> Lösung anwenden zu müssen ?
>
> Gibt es dafür einen Genialitätspreis ?
>
> Du willst nicht den Guttenberg spielen ?

Da ich vermutlich begründen muss, was ich wie und warum gemacht habe, 
bin ich über jede Lösung glücklich, die von mir stammt, da ich dabei das 
Vorgehen zu 100% nachvollziehen kann.
Mein Ziel ist es leider nicht nur, eine funktionsfähige Anwendung 
bereitzustellen.

MaWin schrieb:
> Wenn man einen Taster in halbwegs regelmässigen Zeitabständen, die ÜBER
> der Prellzeit des Tasters liegen und UNTER der Reaktionszeit die der
> Benutzer wünscht (also meist ein sehr weiter Bereich) abfragt, bekommt
> man automatisch und ohne jeglichen Programmcode entprellte
> Tastensignale, bei denen man dann in Ruhe das Drücken, das Loslassen,
> die Zeitdauer erkennen kann.

D.h. wenn ich das Ganze besser machen will, installier ich einen Timer, 
der alle 10 ms einen Interrupt auslöst. In der Interrupt Routine frage 
ich ab, ob der Pin auf low steht. Um nun noch Fehler auszuschließen füge 
ich eine Schleife ein, sodass das Signal "Taster gedrückt" erst gesendet 
wird, nachdem der Taster 5 mal hintereinander auf low stand.

Damit müsste ich doch die Lösung aus dem Tutorial haben, oder?!

Ina schrieb:
> Ja, und die Leute die das machen, posten hier regelmäßig, daß es so
> gerade nicht funktioniert, weil sie irgend etwas falsch gemacht haben.
> Wie schon Mawin gesagt hat, solltest du die Interruptvariante sein
> lassen. Poll alle 10msec die Tastereingänge und verwende das Verfahren
> von Peter Dannegger mit den vertikalen Zählern. Damit kannst du 8 Taster
> auf einen Schlag entprellen. Schau in den Tutorials nach.

Damit ich es nachvollziehen kann, würde mich aber noch interessieren, wo 
genau das Problem bei meiner Lösung liegt?!

Mir bekannt ist das Problem, dass ich aufgrund der gesperrten Interrupts 
eine "Mindestdrückdauer" vorgebe.
Ist es realistisch, dass evtl. negative Flanken erzeugt/ erkannt werden, 
ohne, dass der Taster betätigt wird?!

@ Jens:

Wenn ich deiner Beschreibung richtig folge, gibst du die Interrupts nach 
ca. 2,5 ms schon wieder frei - ist das nicht relativ kurz?


Gruß

von Ina (Gast)


Lesenswert?

>Damit ich es nachvollziehen kann, würde mich aber noch interessieren, wo
>genau das Problem bei meiner Lösung liegt?!

Naja, oft ist das nicht die einzige ISR im Programm und du bekommst 
schnell Prioritätskonflikte. Außerdem wird gerne zuviel in die ISR 
hineingepackt.

Wenn du Meßsignale verarbeitest, hast du oft eine gewisse Periodizität, 
die sich gut nutzen läßt, um die Tasten zu lesen. Wenn du beispielsweise 
alle 10msec eine Meßsignalverarbeitung startest, kannst du bequem im 
Anschluß die Tasten lesen. Mit der Routine von Peter Dannegger, bei der 
eine Taste als gedrückt oder als losgelassen erkannt wird, wenn sie 4 
mal hintereinander den gleichen Status hatte, hast du dann auch 
automatisch eine Tastenentprellung vorgenommen, ohne daß das 
Hauptprogramm in irgendeiner Weise mit einer zusätzlichen ISR gestört 
wurde.

von Jens (Gast)


Lesenswert?

Michael G. schrieb:
> Wenn ich deiner Beschreibung richtig folge, gibst du die Interrupts nach
> ca. 2,5 ms schon wieder frei - ist das nicht relativ kurz?

Wie gesagt, das musst du dann am Taster ausprobieren, ist ja 
letztenendes eine mechanische Fragestellung. Manche Taster prellen 
extrem, manche fast gar nicht. Daher: trial and error. ;-)

Gruß
Jens

von Michael G. (magoo)


Angehängte Dateien:

Lesenswert?

Ah ok, das Argument Interrupts nach Möglichkeit zu vermeiden habe ich 
nicht bedacht, macht aber natürlich Sinn.

Ich habe nun einmal probiert eine Tasterentprellung nach dem Vorbild des 
Tutorials zu entwerfen. Ich möchte jeden, der sich dazu in der Lage 
fühlt, bitten, sich meine entworfenen Textzeilen einmal anzuschauen und 
mir zu sagen, ob es auf diese Art und Weise funktionieren kann oder ob 
sich noch Fehler eingeschlichen haben.


Mit freundlichen Grüßen

von Uwe (de0508)


Lesenswert?

Hallo,

kennst Du schon diesen Super Code von Peter (PeDa) ?

Beitrag "Re: Universelle Tastenabfrage"

Damit solltest Du arbeiten und es gibt keine Probleme mehr !

von ♪Geit (Gast)


Lesenswert?

Wie wäre es mit einer Hardware-Entprellung?

>Um nun das Tasterprellen nicht zu
>berücksichtigen, möchte ich einfach in der Interrupt Routine, den
>Interrupt deaktivieren und gleichzeitig einen Timer starten. Nach Ablauf
>des Timers (ca. 10 - 50 ms) wird der Interrupt wieder freigeschalten.

Das ist wirklich nicht die optimale Lösung. Besser ist, in der IRS 
wartest du deine 50ms ab, guckst ob der Zustand noch low ist und gut 
ist...

von Michael G. (magoo)


Lesenswert?

Danke für die Rückmeldungen :)

♪Geit schrieb:
> Wie wäre es mit einer Hardware-Entprellung?

Natürlich möglich, aber ich würde eine funktionierende Software-Lösung 
bevorzugen.

♪Geit schrieb:
> Das ist wirklich nicht die optimale Lösung. Besser ist, in der IRS
> wartest du deine 50ms ab, guckst ob der Zustand noch low ist und gut
> ist...

Diese Funktionsweise habe ich ja in dem oben geposteten C-Code probiert 
zu erreichen ;)
Ich bin mir nur nicht sicher, ob meine Lösung so funktionieren kann.


Uwe S. schrieb:
> Hallo,
> kennst Du schon diesen Super Code von Peter (PeDa) ?

Ja auf den Code bin ich auch schon gestoßen, da ist ja sogar deutlich 
mehr enthalten, als das, was ich benötige.
Den Teil der einfachen Tastenentprellung habe ich oben probiert 
"nachzumachen".


Gruß

von Michael G. (magoo)


Angehängte Dateien:

Lesenswert?

Ich habe selbst schon einen Fehler in meinem Programmtext entdeckt. 
Deswegen hier der aktualisierte Code, der meiner Meinung nach 
funktionieren sollte.


Gruß

von Uwe (Gast)


Lesenswert?

Also ich fand RS FlipFlops immer gut. z.B. Taster über RC-kombi an Set 
und von alle FF den Reset an einen IO zum zurücksetzen. Ist zwar 
aufwänduig dafür aber sehr schön. Heute mach ich sowieso auf jede µC 
Platine nen PLD rauf oder man kann die Funktion des FlipFlops auch in 
Software machen.

von 51ghki76 (Gast)


Lesenswert?

Natürlich nutzt man keine Hardware-Entprellung, wenn ein µC mit 
ausreichend freier Performance vorhanden ist und es zeitlich (zyklisch) 
damit sinnvoll durchführbar ist.

Die Flexibilität/Konfigurierbarkeit des Moduls, Wiederverwendung dieses 
Entprellmoduls usw. spricht fast immer gegen eine Hardwarelösung.

Zum Thema Software und Interrupts. Interrupts sollten wirklich nur dann 
genutzt werden, wenn es unbedingt notwendig ist. Und das ist bei einer 
Tastenentprellung wirklich nicht der Fall.

Als Software-Architekt hat man gerne ein deterministisches System. 
Bedeutet: Ich habe lieber ein Stückchen Sourcecode, was mir pauschal 1% 
meiner Performance frisst, als ein Interrupt, der mir irgendwann 
(absolut gesehen unvorhersagbar, und meistens auch aufeinanderfolgend 
unvorhersagbar) in meine Abarbeitung haut. Denn mit 1% kann ich schön 
rechnen (im professionellen Umfeld würde man dann noch eine 
Worst-Case-Analyse machen, wie lange das Modul maximal den µC belastet 
(RAM, Flash, Laufzeit, usw.).

Eine Entprellroutine ist wirklich eines der Standardmodule, das 
irgendwie in jeder µC-Software zu finden ist. Heutzutage sind die µCs 
auch so perfomant, dass die Belastung durch eine vermeindlich zu "große" 
Entprellroutine immer noch "im Rauschen untergeht".
Für das Entprellmodul braucht man übrigens überhaupt keinen eigenen 
Interrupt - den hat man nämlich quasi schon, wenn man sich ein kleines 
Zeitscheibenmodell selber aufsetzt.

Ein Tip: Es ist sowieso nebenbei immer eine gute Idee, jedes 
Eingangssignal 1. zu filtern (analog -> filtern, digital -> entprellen) 
und 2. zu plausibilisieren.

Das ganze lässt sich auch auf Ausgangssignale übertragen - aber dazu 
nach der nächsten Maus :-))

von Michael G. (magoo)


Lesenswert?

Ist der oben von mir angehängte Code als Entprellroutine nutzbar?

von 51ghki76 (Gast)


Lesenswert?

Blöde Antwort: Wenn Du mir vorher erklärst, wie die Routine genau 
funktionieren soll, kann ich Deinen Code reviewen.

1.: Du solltest aber mehr Kommentare benutzen.
2.: Die Init-Routine scheint nicht vollständig zu sein. Wo wird der 
Timer initialisiert?
3.: Warum wird das Eingangsregister (das Bit) P1.0 von Dir mit 1 
beschrieben?
4.: Du solltest Deinen 10ms-Timer einen globalen Tick erzeugen lassen. 
Den kannst Du dann mittels Zeitscheiben innerhalb Deiner while(1) (in 
der main) behandeln.

von Ina (Gast)


Lesenswert?

>Ein Tip: Es ist sowieso nebenbei immer eine gute Idee, jedes
>Eingangssignal 1. zu filtern (analog -> filtern, digital -> entprellen)
>und 2. zu plausibilisieren.

Völlig richtig! ESD an einem Port-Pin ist immer kritisch. Und gerade die 
Tasten können da Einiges einfangen...

von Peter D. (peda)


Lesenswert?

♪Geit schrieb:
> Besser ist, in der IRS
> wartest du deine 50ms ab, guckst ob der Zustand noch low ist und gut
> ist...

Köstlicher Witz, ich bin vor Lachen fast gestorben.

Also Du drückst ne Taste und schon kackt die UART ab, die gerade Daten 
empfängt.
Und die gemultiplexte LED-Anzeige flackert auch jedesmal, wie blöd.

Wie willst Du mit diesem kruden Ansatz größere Programme schreiben?


Peter

von Michael G. (magoo)


Angehängte Dateien:

Lesenswert?

Oh...i-wie hatte ich in Erinnerung mehr Kommentare hinterlassen zu haben 
- aber jetzt fällt mir auf, wie schwer verständlich mein Code ist.

Ich habe nochmal komplett neu angefangen, da mir der Code selbst viel zu 
kompliziert erschienen ist. Diesmal erscheint mir das Ergebnis deutlich 
sinnvoller und es ist alles ausführlich kommentiert :)


Kurz zur Funktion:

Es soll ein Taster (active low geschalten) eingelesen werden. Um das 
Tasterprellen herauszufiltern wird ein Tastendruck erst akzeptiert, 
nachdem der Taster 4 Mal hintereinander einen veränderten Zustand hatte.
Nachdem ein erfolgreicher Tastendruck registriert wurde, soll die 
Variable "Taster_gedrueckt" inkrementiert werden (Hier werde ich dann 
die entsprechende Funktion einsetzen).



Ich hoffe so ist das Ganze besser, bzw. überhaupt zu verstehen ;)


Gruß

von MaWin (Gast)


Lesenswert?

> In der Interrupt Routine frage ich ab, ob der Pin auf low steht.

Nö.

Du fragst ab, ob es sich im Vergleich zu zuletzt GEÄNDERT hat.

> Um nun noch Fehler auszuschließen füge ich eine Schleife ein,
> sodass das Signal "Taster gedrückt" erst gesendet wird,
> nachdem der Taster 5 mal hintereinander auf low stand

Nö.

Niemals nur auf einen Zustand ("Taster gedrückt") entstören,
sondern wenn, dann immer auf beide, also gucken ob es 5 mal
nacheinander DENSELBEN Zustand hatte (um Störungen die
kürzer als 50ms waren zu unterdrücken. Allerdings: Solche
Störungen sind selten, um nicht zu sagen bei brauchbarem
Aufbau gibt es sie nicht, eine Entstörung ist nicht nötig.

von Ina (Gast)


Lesenswert?

Neue, gute Tasten prellen, wenn überhaupt, nur für wenige Millisekunden. 
Ältere oder solche mit ungeeigneten Kontaktströmen, können aber 
erheblich länger prellen! Von daher ist es schon sinnvoll, zu fordern, 
daß eine Taste, wenn sie alle 10msec abgefragt wird, für mindestens 
4...5 mal hintereinander den gleichen Zustand hatte, bevor eine Änderung 
des Zustands akzeptiert wird.

Nimmt man für das Drücken und Loslassen eine Prellzeit von jeweils 
50msec an, dann sind immer noch 5 Tastendrücke pro Sekunde auflösbar, 
was für normale Anwendungen völlig ausreicht.

Hardware-Entprellungen sind diesbezüglich übrigens nicht immer 
vorteilhaft, vor allem dann nicht, wenn der zugehörige Schmitt-Trigger 
nur eine kleine Hysterese aufweist. Die erforderliche Zeitkonstante für 
wirkungsvolles Entprellen kann dann zu einer deutlich wahrnehmbaren 
Verzögerung der Tasteneingabe führen.

von Ai (Gast)


Lesenswert?

du kannst Optokoppler verwenden
Ai

von Michael G. (magoo)


Lesenswert?

51ghki76 schrieb:
> Blöde Antwort: Wenn Du mir vorher erklärst, wie die Routine genau
> funktionieren soll, kann ich Deinen Code reviewen.

--> Sind immer noch Fragen offen oder ist der Code und meine gewünschte 
Vorgehensweise mittlerweile verständlich dargestellt?

51ghki76 schrieb:
> 4.: Du solltest Deinen 10ms-Timer einen globalen Tick erzeugen lassen.
> Den kannst Du dann mittels Zeitscheiben innerhalb Deiner while(1) (in
> der main) behandeln.

Dem kann ich leider nicht ganz folgen :(
Aber es klingt ganz nützlich, denn momentan scheint es mir so, dass in 
meiner ISR sehr viel zu tun ist?!


Gruß

von Knut (Gast)


Lesenswert?

Ich machs offensichtlich noch anderster:

Ich habe einen Timer, der alle 1ms einen Interrupt auslöst.

Dann weiter zwei Varianten. Entwerder mit Interrupts den Tester 
überwachen, oder polln. Wenn ich den polle, dann:
1
if ((taster gedrückt) && !(tasterx)) 
2
{
3
 Das was zu tun ist, wenn der Taster betätigt wurde;
4
 tasterx = 100;
5
}

In der Timer ISR dann:
1
if (tasterx) {tasterx -= 1;

Funktioniert wunderbar, auch bei mehrern tastern. So kann man auch die 
Entprellzeit sehr genau einstellen, hier 100ms


Knut

von 51ghki76 (Gast)


Lesenswert?

Michael G. schrieb:
> 51ghki76 schrieb:
>> Blöde Antwort: Wenn Du mir vorher erklärst, wie die Routine genau
>> funktionieren soll, kann ich Deinen Code reviewen.
>
> --> Sind immer noch Fragen offen oder ist der Code und meine gewünschte
> Vorgehensweise mittlerweile verständlich dargestellt?

habe ich noch keine Zeit für gehabt, das anzusehen. Sind Dir denn
beim Kommentieren eventuell schon Fehler aufgefallen?

> 51ghki76 schrieb:
>> 4.: Du solltest Deinen 10ms-Timer einen globalen Tick erzeugen lassen.
>> Den kannst Du dann mittels Zeitscheiben innerhalb Deiner while(1) (in
>> der main) behandeln.
>
> Dem kann ich leider nicht ganz folgen :(
> Aber es klingt ganz nützlich, denn momentan scheint es mir so, dass in
> meiner ISR sehr viel zu tun ist?!

Du pollst in Deiner while-Schleife in main auf das zu erzeugende 
Timer-Tick und startest von dort Deine Applikationen.

Die ISR besteht dann nur noch aus dem Setzen des Timer-Ticks, das wäre 
dann ein Einzeiler.

von Michael G. (magoo)


Lesenswert?

@ Knut:

Wenn ich das richtig verstehe, frägst du jedes Mal in der ISR ab, ob der 
Taster gedrückt ist, und zählst, wenn er gedrückt ist, die Variable um 1 
runter. In der main Funktion frägst du nun ab, ob der Taster gedrückt 
ist - also taster gedrückt = 0, bzw. 1 - und taster x bei 1, bzw. 0 
ist?!

Das Prinzip ist ja ganz ähnlich wie das, das ich verwenden will - nur 
wollte ich probieren, mit einzubauen, dass der Taster zwangsläufig 4 Mal 
hintereinander als gedrückt registriert wurde, der Zaehler also jedes 
Mal, wenn der Taster als nicht gedrückt registriert wurde wieder hoch 
gesetzt wird.

Bei deiner Lösung musst du in der ISR aber nach
1
 if (taster gedrueckt)...
 fragen, oder?


Gruß

von Michael G. (magoo)


Lesenswert?

51ghki76 schrieb:
> habe ich noch keine Zeit für gehabt, das anzusehen. Sind Dir denn
> beim Kommentieren eventuell schon Fehler aufgefallen?

Eigentlich nicht - es sollte eigentlich meiner Meinung nach 
funktionieren - es fehlt ausschließlich die Timer intitialisierung, dazu 
muss ich aber ja "nur" die entsprechenden Bits aus dem Manual 
raussuchen, das sollte klappen ;)

--> Wäre schön wenn jm bei Gelegenheit mal kurz drüber schaut ;)

51ghki76 schrieb:
> Du pollst in Deiner while-Schleife in main auf das zu erzeugende
> Timer-Tick und startest von dort Deine Applikationen.
> Die ISR besteht dann nur noch aus dem Setzen des Timer-Ticks, das wäre
> dann ein Einzeiler.

D.h. ich setze in der ISR eine Variable auf einen bestimmten Wert und 
setze in die Main Funktion eine if- Abfrage auf den entsprechenden Wert.
In der ISR wird die Variable dann jedes Mal gesetzt und in der 
if-Abfrage in der main Funktion wieder zurück gesetzt.

Damit sollte die ISR dann ja sehr kurz ausfallen, sodass die 
Hauptfunktion nicht "unnötig" lange unterbrochen wird?!


Gruß

von 51ghki76 (Gast)


Lesenswert?

Michael G. schrieb:
> 51ghki76 schrieb:
>> habe ich noch keine Zeit für gehabt, das anzusehen. Sind Dir denn
>> beim Kommentieren eventuell schon Fehler aufgefallen?
>
> Eigentlich nicht - es sollte eigentlich meiner Meinung nach
> funktionieren - es fehlt ausschließlich die Timer intitialisierung, dazu
> muss ich aber ja "nur" die entsprechenden Bits aus dem Manual
> raussuchen, das sollte klappen ;)
>
> --> Wäre schön wenn jm bei Gelegenheit mal kurz drüber schaut ;)
>
> 51ghki76 schrieb:
>> Du pollst in Deiner while-Schleife in main auf das zu erzeugende
>> Timer-Tick und startest von dort Deine Applikationen.
>> Die ISR besteht dann nur noch aus dem Setzen des Timer-Ticks, das wäre
>> dann ein Einzeiler.
>
> D.h. ich setze in der ISR eine Variable auf einen bestimmten Wert und
> setze in die Main Funktion eine if- Abfrage auf den entsprechenden Wert.
> In der ISR wird die Variable dann jedes Mal gesetzt und in der
> if-Abfrage in der main Funktion wieder zurück gesetzt.
>
> Damit sollte die ISR dann ja sehr kurz ausfallen, sodass die
> Hauptfunktion nicht "unnötig" lange unterbrochen wird?!

Hört sich sehr brauchbar an :-))

Mach mal so.

Ich schau dann mal über den Quelltext, wenn Du das soweit hast.

von Michael G. (magoo)


Angehängte Dateien:

Lesenswert?

So, ich habe die Tasterentprellung nochmal umgeschrieben, sodass die 
vesch. Abfragen in der main Funktion sind und in der ISR des Timers 
ausschließlich eine Variable gesetzt wird.

Die Abfragen in der main Funktion sollen sicherstellen, dass der Timer 
erst als gedrückt registriert wird, nachdem sich der Wert des Eingangs, 
an dem der Taster angeschlossen ist gegenüber der letzten Abfrage 
verändert hat und dies 4 mal direkt hintereinander registriert wurde. 
Anschließend unterscheidet eine weitere Abfrage noch den absoluten Wert 
des Tasters, um die beiden Stellungen (EIN/ Aus) zu unterscheiden.


Gruß

von 51ghki76 (Gast)


Lesenswert?

Das geht in die richtige Richtung.

Wenn Du jetzt noch Deine Entprellroutine komplett aus main() auslagerst,
hast Du Deine erste Low-Level-Applikation :-), die alle 10ms aufgerufen 
wird.

Nenne bitte den Timer-Tick nicht einfach "timer".

Du solltest Dich in Deiner Entprell-Routine darauf reduzieren, eine
Entprellung durchzuführen, keine weiteren Aktionen.
Die gehören dann nämlich eigentlich in eine andere Applikation,
nennen wir sie doch einfach mal "High-Level-Applikation".
Da gehört dann auch das Abfragen auf einen gedrückten Taster hin.
Das ist nämlich schon die Auswertung, was die Entprellroutine gar nichts 
angeht. Hier wird lediglich entprellt.

Kommuniziere über Schnittstellen zwischen diesen Applikationen
und nehme damit eine zeitliche Kopplung heraus.

Und als Hausaufgabe für den Abend: Wo geht das "timer = 0" hin:
An den Anfang oder den Schluss, was wäre der Unterschied und ganz
wichtig: Was wäre, wenn du timer zurücksetzt, direkt nach "if(timer == 
0)"
und timer nach Abarbeitung Deiner Applikation schon wieder gesetzt ist?
Du kannst ja mal nach "Echtzeit" in Wikipedia googeln, bzw. "harte" und 
"weiche" Echtzeit.

Achso. Die Entprellroutine mag so funktionieren. Du kannst Dir aber noch
nach dem Aufräumen (s.o.) mal überlegen, mehrere konfigurierbare 
Entprellkanäle zu implementieren, die Du auf diverse Ein- und 
Ausgangssignale routen kannst.

Du solltest Dir auch ein einheitliches Namensschema deiner Variablen 
ausdenken. Enumerations helfen hier auch schön weiter. Einen Quelltext 
sollte man gut lesen können. Im Idealfall sollte schon meine Mutti das 
halbwegs verstehen können (also Quelltext direkt mit Kommentaren zur 
Hilfe).

Um zu jedem C-Compiler der Welt kompatibel zu sein, wäre es sinnvoll,
Kommentare nicht mit Doppelslash zu beginnen. Auch Umlaute sollte man 
vermeiden (ä, ü, ö, ß, usw.).

von Michael G. (magoo)


Lesenswert?

Danke für die ausführliche Rückmeldung :)

51ghki76 schrieb:
> Wenn Du jetzt noch Deine Entprellroutine komplett aus main() auslagerst,
> hast Du Deine erste Low-Level-Applikation :-), die alle 10ms aufgerufen
> wird.

Das "Zeug" in eine separate Funktion zu packen sollte möglich sein --> 
wird gemacht ;)

51ghki76 schrieb:
> Und als Hausaufgabe für den Abend: Wo geht das "timer = 0" hin:
> An den Anfang oder den Schluss, was wäre der Unterschied und ganz
> wichtig: Was wäre, wenn du timer zurücksetzt, direkt nach "if(timer ==
> 0)"
> und timer nach Abarbeitung Deiner Applikation schon wieder gesetzt ist?

Ich denke, da sollte sich kein Unterschied ergeben?! Sobald die 
Bedingung erfüllt ist, wird der Anweisungsblock abgearbeitet. 
Anschließend sollte die main Funktion fortgesetzt werden - unabhängig 
davon, ob die if-Bedingung schon wieder gesetzt ist. Dementsprechend 
sollte es auch egal sein, wann die Variable timer zurückgesetzt wird, 
oder nicht?


51ghki76 schrieb:
> Du solltest Dir auch ein einheitliches Namensschema deiner Variablen
> ausdenken. Enumerations helfen hier auch schön weiter. Einen Quelltext
> sollte man gut lesen können. Im Idealfall sollte schon meine Mutti das
> halbwegs verstehen können (also Quelltext direkt mit Kommentaren zur
> Hilfe).

Das mit den Namen stimmt - wobei ich zu meiner Entschuldigung sagen 
muss, dass ich in meinem eigentlichen Programm auch ein "Schema" habe. 
Bei dem Code hier habe ich weniger darauf geachtet, da ich nur kurz die 
Funktion der Tasterentprellung darstellen wollte.
Kommentare sollten doch ausreichend vorhanden sein, oder ist meine Art 
der Darstellung so unkonventionell?!

51ghki76 schrieb:
> Um zu jedem C-Compiler der Welt kompatibel zu sein, wäre es sinnvoll,
> Kommentare nicht mit Doppelslash zu beginnen. Auch Umlaute sollte man
> vermeiden (ä, ü, ö, ß, usw.).

--> Stattdessen /* Kommentar */ ?
Oder gibt es noch andere Möglichkeiten?
Umlaute versuche ich schon zu vermeiden - es ist nur sehr umstaendlich 
sich da umzustellen ;)



Ich aendere das Ganze noch Mal ab, sodass es besser gegliedert ist und 
in einzelnen Schritten abläuft.
Ich hoffe ich habe das auch soweit richtig verstanden :)


Gruß

von 51ghki76 (Gast)


Lesenswert?

Michael G. schrieb:
> Danke für die ausführliche Rückmeldung :)
>
> 51ghki76 schrieb:
>> Wenn Du jetzt noch Deine Entprellroutine komplett aus main() auslagerst,
>> hast Du Deine erste Low-Level-Applikation :-), die alle 10ms aufgerufen
>> wird.
>
> Das "Zeug" in eine separate Funktion zu packen sollte möglich sein -->
> wird gemacht ;)
>
> 51ghki76 schrieb:
>> Und als Hausaufgabe für den Abend: Wo geht das "timer = 0" hin:
>> An den Anfang oder den Schluss, was wäre der Unterschied und ganz
>> wichtig: Was wäre, wenn du timer zurücksetzt, direkt nach "if(timer ==
>> 0)"
>> und timer nach Abarbeitung Deiner Applikation schon wieder gesetzt ist?
>
> Ich denke, da sollte sich kein Unterschied ergeben?! Sobald die
> Bedingung erfüllt ist, wird der Anweisungsblock abgearbeitet.
> Anschließend sollte die main Funktion fortgesetzt werden - unabhängig
> davon, ob die if-Bedingung schon wieder gesetzt ist. Dementsprechend
> sollte es auch egal sein, wann die Variable timer zurückgesetzt wird,
> oder nicht?

Jein. Man kann das ganze noch mal schön abrunden, wenn man den 
Timer-Tick
sofort nach Einsprung in die 10ms-Zeitscheibe zurücksetzt und am Ende
der Abarbeitung der 10ms-Zeitscheibe noch mal gegenprüft.

Wurde der Timer-Tick wieder gesetzt, weißt Du, dass Deine Abarbeitung
der Applikationen länger als 10ms gedauert hat (in der Zwischenzeit 
wurdest Du wieder still und heimlich vom Timer-Interrupt unterbrochen, 
der dem Timer-Tick gesetzt hat).

Du willst doch eigentlich auch, dass Deine Daten im 10ms-Raster 
entprellt werden und es mag auch Applikationen geben, die es gar nicht 
lustig finden würden, wenn die Abarbeitung immer weiter "nach hinten" 
geschoben wird.

Ich persönlich lasse dann im Zeitscheibenüberlaufs-Fall eine Debug-LED 
permanent aktivieren, die mir diesen Fehler anzeigt. Willst Du harte 
Echtzeit, musste Du dann auch gleich noch einen Software-Reset ausführen 
und Deinen Code korrigieren, dass das nicht (noch einmal) passiert.

Du könntest auch noch ganz wunderbar beim Start der Zeitscheibe das 
Register des Timers sichern und am Schluss noch einmal mit dem aktuellen 
Timer-Register gegenrechnen. Dann weißt Du auch mal ungefähr, wie lange
Deine Applikationen überhaupt den µC zeitlich belasten. Auch eine nette 
Nebeninfo.
[Daher auch nicht sinnvoll, zur 10ms-Erzeugung z.B. mit einem Timer-Takt 
von 1ms zu arbeiten, mit Timer-Register auf 10. Würde man ja nur in 
10%-Auflösung die Belastung berechnen können. Den Timer-Register-Range 
bitte immer gut ausnutzen. Hast Du z.B. einen 16-Bit-Timer um 10ms zu 
erzeugen, dann sollte der Reload-Wert so nahe wie möglich an den 65535 
liegen.]

von Michael G. (magoo)


Angehängte Dateien:

Lesenswert?

Das Gegenprüfen am Ende macht natürlich Sinn - aber ich denke, da meine 
Anwendung relativ simpel bleibt, sollte ich ohne auskommen. Ich denke, 
ich werde es, sobald der Code Sinn macht erst einmal ausprobieren und 
anschließend prüfen, ob die Funktionalität erfüllt ist ;)

Ich habe den Code schon mal verändert, sodass die Entprellung und die 
Flankenerkennung in einzelnen Funktionen ablaufen. Ich finde, es wirkt 
dadurch übersichtlicher :)

von 51ghki76 (Gast)


Lesenswert?

Wenn Du jetzt das ganze in der Interrupt-Routine gemacht hättest, 
könntest Du nicht so elegant die Zeitscheiben-Verletzung herausfinden. 
Nebenbei würde die ISR extrem lange dauern. Noch mal zur Erinnerung: Ein 
Interrupt kann nicht durch sich selber unterbrochen werden. Würdest Du 
einen Timer programmieren, der jede 10ms einen Interrupt auslöst und Du 
in der ISR 15ms für die Abarbeitung brauchen, würde die ISR praktisch 
unendlich hintereinander wiederholen, zumindest wäre das beim C166 so. 
Der löscht nämlich direkt beim Einsprung in die ISR das Interrupt-Flag. 
Mag aber sein, dass Du mehrere Timer benutzt und dann je nach Prio erst 
eine andere ISR dran wäre. Wie auch immer: Du würdest Deine 
Applikationen in der main() praktisch nicht mehr ausführen können. Daher 
sind die Änderungen (s. vorherige Beiträge) schon sehr sinnvoll.

Und nochmal: Du bist nicht alleine auf dem µC unterwegs. Jetzt hast Du 
eine Entprellroutine und eine Applikation, die den Taster auswertet.
Morgen hast Du 25 Low-Level-Applikationen und 100 
High-Level-Applikationen, die alle quasi "gleichzeitig" laufen wollen. 
Und da kannst Du wirklich keinen Überblick behalten, wenn die nicht 
sauber voneinander entkoppelt sind.

von 51ghki76 (Gast)


Lesenswert?

Momentan hast Du gar keine Flankenerkennung, Du fragst nur auf 0 ab.
Das wäre dann eine "Pegelerkennung", Zustandserkennung.

Willst Du eine Flanke erkennen, müsstest Du auch immer den alten Zustand 
merken, um einen Pegelwechsel (= Flanke) detektieren zu können.

Was aber für Deine Taster-Applikation nicht notwendig ist.

Die prüft nämlich auch nur zyklisch aufgerufen, ob der der Taster
gedrückt wurde, um eine bestimmte Aktion auszuführen.

Ich hab' das Gefühl, der Groschen ist bei Dir noch nicht 100%ig 
gefallen,
was ich meine :-))

von Michael G. (magoo)


Lesenswert?

Ich denke, der aktuelle Stand sollte die Aenderungen enthalten.
Sobald der Code für die Tasterentprellung einigermaßen brauchbar ist 
werde ich das Ganze für mein gesamtes Programm uebernehmen. Da sollte 
sich eine saubere Gliederung noch viel deutlicher bemerkbar machen :)


Gruß

von 51ghki76 (Gast)


Lesenswert?

Brr.....

Bitte nicht in der Entprellroutine die "Flankenerkennung" aufrufen.

Bitte entkopple die Applikationen voneinander!

Die Flankenerkennung gehört auch in main() in die While-Schleife
in eine der Zeitscheiben (Du hast ja nur eine, die 10ms-Zeitscheibe).

Die Zustände usw. übergibst Du z.B. über einen globalen Speicher.
Willst Du es sauber machen, gehören hier zwei Funktionen hin: Eine Set-
und eine Get-Funktion, wobei nur diese beiden Funktionen auf die Daten 
zugreifen können (Sichtbarkeit von Daten, Zugriff so weit es geht 
einschränken).

von Michael G. (magoo)


Angehängte Dateien:

Lesenswert?

51ghki76 schrieb:
> Momentan hast Du gar keine Flankenerkennung, Du fragst nur auf 0 ab.
> Das wäre dann eine "Pegelerkennung", Zustandserkennung.
>
> Willst Du eine Flanke erkennen, müsstest Du auch immer den alten Zustand
> merken, um einen Pegelwechsel (= Flanke) detektieren zu können.

Naja ok - da ist meine Wortwahl nicht ganz eindeutig.
Dass eine Ueberpruefung des Pegels erfolgt ist mir klar - mit 
Flankenerkennung meinte ich die Unterscheidung welche Flanke vorher 
detektiert wurde und die kann ja am aktuellen Zustand abgelesen werden.

Die eigentliche Erkennung der Flanke erfolgt natuerlich vorher ueber die 
Abfrage ...
1
if (var != Taster1_IN)

Anschließend werte ich in meiner Funktion "Flankenerkennung" ja nur noch 
aus, um welche Flanke es sich gehandelt hat, um nicht auf jede Aenderung 
des Zustands des Tasters zu reagieren.


51ghki76 schrieb:
> Die prüft nämlich auch nur zyklisch aufgerufen, ob der der Taster
> gedrückt wurde, um eine bestimmte Aktion auszuführen.

--> Diese zyklicher Ueberpruefung ist doch nichts anderes, als der Test, 
ob es eine Flanke gab, oder nicht?!


51ghki76 schrieb:
> Ich hab' das Gefühl, der Groschen ist bei Dir noch nicht 100%ig
> gefallen,
> was ich meine :-))

--> Vl konnte dieser Post verdeutlichen, was ich wirklich meine - die 
Wortwahl ist wirklich nicht eindeutig?!
Ansonsten fehlt mir wirklich noch das Verstaendnis :(

von Michael G. (magoo)


Lesenswert?

51ghki76 schrieb:
> Brr.....
>
> Bitte nicht in der Entprellroutine die "Flankenerkennung" aufrufen.
> Bitte entkopple die Applikationen voneinander!
> Die Flankenerkennung gehört auch in main() in die While-Schleife
> in eine der Zeitscheiben (Du hast ja nur eine, die 10ms-Zeitscheibe).

Ah ok...dann habe ich das Ganze wirklich nicht richtig verstanden :(

Meinst du, dass bei jedem "Tick" des Timers eine andere Aktion 
ausgeführt werden soll?!

Hier quasi zuerst die Entprellung und anschließend, nach dem nächsten 
Tick die Ueberpruefung des Zustands?!

Gruß

von 51ghki76 (Gast)


Lesenswert?

Im Allgemeinen solltest Du Dir überlegen, welche Applikationen wie 
häufig durchlaufen werden sollen (zeitlich betrachtet).

Du kannst ja mittels Timer-Tick weitere Zeitscheiben ableiten, z.B. 
100ms und 1s.

Sortiere die Applikationen da rein.

Die Informationen werden dann zyklisch (Entprellroutine z.B.) 
eingelesen, entprellt und dem System zur Verfügung gestellt. Wer dann 
wann, wo und wie auf diese Informationen zugreift, ist doch erst mal 
egal. Einfach zugänglich machen.

Ob jetzt zyklisch auf einen Tastendruck immer wieder reagiert wird, oder 
nur beim ersten Drücken (= Flanke), kannst Du ja immer noch in der 
entsprechenden Applikation entscheiden.

Das mit dem zeitlich entkoppelt und dem Austausch der Informationen 
musst Du Dir noch ein wenig einhämmern :-)

Glaub mir, wenn das Projekt größer wird, würdest Du sonst ganz schnell 
die Übersicht verlieren, wenn Du kreuz und quer harte Abhängigkeiten 
hättest.

von Peter D. (peda)


Lesenswert?

51ghki76 schrieb:
> Bitte nicht in der Entprellroutine die "Flankenerkennung" aufrufen.
>
> Bitte entkopple die Applikationen voneinander!

Ich mache Entprellung und Flankenerkennung zusammen, aus gutem Grund:

In der Entprellroutine habe ich ja schon das Ereignis der 
Flankenänderung.
Ich muß also nur noch eine UND-Verknüpfung mit dem entprellten Zustand 
machen und schon habe ich das gedrückt-Bit.
Woanders müßte ich mir die Flankenänderung mit ner zusätzlichen Variable 
nochmal neu erzeugen.

Außerdem habe ich den Vorteil, wenn ich einen Timerinterrupt verwende, 
daß das gedrückt-Bit erzeugt wird, auch wenn das Main gerade keine Zeit 
hat.
Das Main kann den Druck auch später behandeln, wenn man vielleicht schon 
wieder losgelassen hat.

Beitrag "Universelle Tastenabfrage"

Variablen:
key_state: entprellter Zustand
key_press: gedrückt-Bits

Funktion:
get_key_press: Abholen und Rücksetzen des gedrückt-Bits


Peter

von 51ghki76 (Gast)


Lesenswert?

"Das Main kann den Druck auch später behandeln, wenn man vielleicht 
schon
wieder losgelassen hat.".

Interessanter Aspekt. Danke.

Was man noch erwähnen sollte, ist, dass so eine Entprellroutine nicht
nur Taster entprellen kann, sondern praktisch jedes digitale Signal.
Daher macht es auch Sinn, die Eingangs- und Ausgangskanäle der 
Entprellroutine frei auf die Quellen und Senken "verschaltbar" zu 
machen.

Wenn man dann noch das ganze für lange Zeiträume einstellen kann man 
sich z.B. auch einen Aus- oder Einschalttimer konfigurieren.

Schon schön, was man mit einer flexiblen Entprellroutine alles machen 
kann.

Und nebenbei auch ein schönes Einsteigerprojekt für µC-Neulinge, weil 
man sich so auch über die SW-Architektur Gedanken machen muss.

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.