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
Danke für die schnelle Rückmeldung :) Wie lange sperrst du denn dann die Interrupts, um das Prellen nicht zu registrieren? Gruß
> 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!
>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.
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
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ß
>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.
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
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
Hallo, kennst Du schon diesen Super Code von Peter (PeDa) ? Beitrag "Re: Universelle Tastenabfrage" Damit solltest Du arbeiten und es gibt keine Probleme mehr !
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...
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ß
Ich habe selbst schon einen Fehler in meinem Programmtext entdeckt. Deswegen hier der aktualisierte Code, der meiner Meinung nach funktionieren sollte. Gruß
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.
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 :-))
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.
>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...
♪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
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ß
> 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.
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.
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ß
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
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.
@ 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ß
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ß
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.
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ß
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.).
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ß
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.]
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 :)
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.
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 :-))
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ß
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).
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 :(
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ß
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.
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
"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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.