Hallo,
Ich wollte nach einem Feedback fragen ob mein Programm so gut
geschrieben ist.
Undzwar ist ein Rotary Encoder LPD3806 mit 600 Schritten an einem
PIC16F15244 verbunden.
Der Encoder wird als Positionsbestimmung eines Pendels benutzt.
Die zwei Phasen des Encoders sind an RA4 und RA5 angeschlossen. Dabei
werden für beide Pins jeweils ein Interrupt On Change benutzt.
Ist das so gut? Besteht das Risiko dass ich Schritte "verpasse"?
Das ist die ISR Config:
RoterKater schrieb:> beide Pins jeweils ein Interrupt On Change benutzt
Ich hoffe, der Interrupt ist Flankengetriffert, sonst müsstest Du in der
ISR beide Pegel abfragen.
Zudem scheinst Du mit float oder double dort zu rechnen. Vermutlich wäre
Schritte besser, aber dafür müsste man Dein Programm kennen.
Und ich hoffe, Du weisst, dass Du pro Vollschritt 2 Incremente
berechnest.
RoterKater schrieb:> if(ENC_degree<0)> ENC_degree=360;
das ist vermutlich ein Tippfehler, weil Du es davor richtig gemacht
hast.
Kannst Du Sicherstellen, dass die Interrupts schnell genug sicher
verarbeitet werden? also in 1/4 der maximalen Signalzeit?
Andreas B. schrieb:> Dieses Thema hat mittlerweile einen recht langen Bart.> https://www.mikrocontroller.net/articles/Drehgeber
Ja, sieht nach einem Troll aus. Der will natürlich vor allem die
üblichen Idioten rauslocken, die steif und fest behaupten, dass eine
interruptgesteuerte Auswertung garnicht möglich wäre.
Und macht vermutlich genau deshalb genau die Fehler, die es tatsächlich
unmöglich machen, das zuverlässig hinzubekommen. Obwohl in einschlägigen
Threads längst erschöpfend behandelt wurde, wie man es richtig macht...
Andreas B. schrieb:> Dieses Thema hat mittlerweile einen recht langen Bart.
Aber es wird nie aufhören. Sieht ja auch verlockend einfach aus wenn man
die idealisierten Pegelverläufe sieht :-)
A. S. schrieb:> sorry, noch vergessen: Beim Flattern um ein Wert entstehen ggf.> Fehlmessungen (mit beliebiger Akkumulation trotz Stillstand)
Nicht, wenn man es richtig macht. Denn dann flattert da rein garnix.
Höchstens genau ein mal und der dadurch verursachte Fehler wird beim
nächsten Schritt kompensiert.
Also im Prinzip genauso wie beim Polling. Denn auch da könnte natürlich
ein Eingang flattern...
c-hater schrieb:> Ja, sieht nach einem Troll aus. Der will natürlich vor allem die> üblichen Idioten rauslocken, die steif und fest behaupten, dass eine> interruptgesteuerte Auswertung garnicht möglich wäre.
Ich denke wer so einen Code (TO) schreibt, ist nicht in der Lage, die
Threads oder Beiträge dazu zu verstehen. Manche Dinge muss man einfach
mal selber gemacht haben (und in die Fehler laufen), um die Problematik
überhaupt zu erfassen.
A. S. schrieb:> Manche Dinge muss man einfach> mal selber gemacht haben (und in die Fehler laufen), um die Problematik> überhaupt zu erfassen.
Tja, Erfahrungen sammeln heißt, es selber zu tun und dabei etwas zu
merken. Und wenn ich mich nicht verkehrt erinnere, dann sollte man zu
allererst die Portänderung in eine lokale Variable retten und dann
sofort den Port selbst ebenso retten. Aber - wie geschrieben - selber
erfahren, was da abgeht oder nicht. Die Pinchange-Interrupts sind ein
bissel speziell.
W.S.
RoterKater schrieb:> Besteht das Risiko dass ich Schritte "verpasse"?
Das kommt drauf an, wie schnell dein Prozessor läuft und wie schnell
sich die Achse dreht.
c-hater schrieb:> Nicht, wenn man es richtig macht. Denn dann flattert da rein garnix.> Höchstens genau ein mal und der dadurch verursachte Fehler wird beim> nächsten Schritt kompensiert.
Nein.
Wie in den thread auf den schon hingewiesen wurde ausreichend
dokumentiert, führt flankenbasierte Auswertung zu einem Zählfehler bei
Richtungsänderung.
Wenn vorwärts eine Stelle bei Increment 10 ist, könnte sie rückwärts bei
11 sein.
> Also im Prinzip genauso wie beim Polling. Denn auch da könnte natürlich> ein Eingang flattern...
Dort tritt die Ungenauigkeit nur genau am Übergang auf und ist danach
wieder korrigiert.
Es ist SOOO einfach es richtig zu machen, dass dieser ganze Flankenkram
einfach nur dumm ist.
https://dse-faq.elektronik-kompendium.de/dse-faq.htm#F.29
MaWin schrieb:> Wie in den thread auf den schon hingewiesen wurde ausreichend> dokumentiert, führt flankenbasierte Auswertung zu einem Zählfehler bei> Richtungsänderung.> Wenn vorwärts eine Stelle bei Increment 10 ist, könnte sie rückwärts bei> 11 sein.
Das ist doch auch alles durchgekaut: Ja, eine naive Implementierung wie
beim TO fällt darauf rein. Aber bei gleicher Sorgfalt geht es mit Pollen
genauso sicher wie mit Interrupts.
Der Wertebereich ist auf jeden Fall falsch. Der valide Wertebereich geht
von 0-359 und nicht von 0-360 (weil 0 das gleiche ist, wie 360). Somit
ist einmal der Wert im if falsch und einmal im else die Zuweisung.
So, wie der Code jetzt ist, würde bei mehrfachen Umdrehungen in eine
Richtung die Abweichung immer größer.
MagIO schrieb:> Der valide Wertebereich geht von 0-359
Er geht im Grunde von 0,0000000 bis 359,99999999 sodass natürlich >=360
richtig wäre. Aber letztlich macht die fehlerhafte Abfrage auf >360 den
Kohl nicht mehr fett, weil es den floatwert 360,00000000 vermutlich
sowieso gar nicht gibt.
Wobei ich eh' niemals in ° mit float rechnen würde, sondern einfach die
600 (bzw 2400 bei Quadraturauswertung) Schritte mitzählen. Und erst
dann, wenn die ° Angabe wirklich nötig ist (nämlich nur dann, wenn ein
Mensch meint, es sehen zu müssen) diesen Zähler zu diesem Zeitpunkt mit
einer einfachen Formel in ° umrechnen würde:
grad = (steps%600)*360.0/600.0
Dank des modulo könnte ich mir den ganzen Über- und Unterlaufklimbim mit
>360 und <0 sparen.RoterKater schrieb:> Besteht das Risiko dass ich Schritte "verpasse"?
In einer Interruptroutine rechnet man nur dann mit float, wenn der
Prozessor eine FPU hat. Denn dann musst du dir Gedanken machen: wie
schnell dreht der Geber? Wie schnell kommen die Interrupts? Wie lange
dauern diese vielen float-Rechnungen pro Interrupt?
> Besteht das Risiko dass ich Schritte "verpasse"?
Mal angenommen, da kommt ein kurzer ESD-Spike (ja, das Leben ist kein
Ponyhof) und triggert irgendeinen Interrupt. Was macht deine Auswertung?
A. S. schrieb:> Das ist doch auch alles durchgekaut: Ja, eine naive Implementierung wie> beim TO fällt darauf rein. Aber bei gleicher Sorgfalt geht es mit Pollen> genauso sicher wie mit Interrupts.
Ja, alles durchgekaut
Das Resultat war: nein, geht nicht.
Lothar M. schrieb:> Dank des modulo könnte ich mir den ganzen Über- und Unterlaufklimbim mit>>360 und <0 sparen.
Naja, es kommt drauf an, ob man die GK-Funktionen überhaupt bereits
verwendet und sie deshalb ohnehin eingebunden sind. Ansonsten sind zwei
Vergleiche in Integer weitaus weniger aufwendig als eine komplette
GK-Division, die ja dem Modulo zugrunde liegt.
MaWin schrieb:> Es ist SOOO einfach es richtig zu machen, dass dieser ganze Flankenkram> einfach nur dumm ist.
Ach du mal wieder mit deiner ewigen Leier vom Polling. Ich sag's mal
ganz direkt: Entprellt werden muß sowas immer - entweder in Hardware
oder in Software. Ist generell also wurscht, aber was man in Hardware
mit einem simplen RC-Glied machen kann, ist in Software schwieriger,
denn da fehlt immer der Amplitudenwert, weil es hinter der
Schaltschwelle eines gesampleten Eingangsgatters ist. Also quasi hinter
dem ADC (der hier nur ein 1 Bit breites Ergebnis liefert)
W.S.
W.S. schrieb:> Ich sag's mal ganz direkt: Entprellt werden muß sowas immer - entweder> in Hardware oder in Software.
Ein vernünftiger Decoder für AB-Signale leistet das ohne zusätzliche
Maßnahmen.
W.S. schrieb:> Ach du mal wieder mit deiner ewigen Leier vom Polling. Ich sag's mal> ganz direkt: Entprellt werden muß sowas immer -
Du hast das Polling-Verfahren nicht verstanden.
Kein Wunder, dass du keinen Unterschied zum fehlerträchtigen
Flankeninterrupt erkennen kannst.
Aber musst du den Rest deines Lebens so dumm bleiben, oder besteht doch
noch eine Chance zur Weiterbildung ?
Das Polling-Verfahren scheitert aber auch, wenn ein Kontakt gar nicht
mehr zur Ruhe kommt, sondern die ganze Kontaktbahn lang kratzt. Gegen
beim Umschalten prellende Kontakte ist es aber ganz ohne Entprellung
immun.
Noch die Links, warum auch 'korrekt versuchter' Flankeninterrupt falsch
zählt
Beitrag "Re: Drehgeber an Arduino, external interrupt ISR wird doppelt ausgeführt"
und bei kratzenden Drehgebern erst recht:
Beitrag "Re: Drehgeber an Arduino, external interrupt ISR wird doppelt ausgeführt"
Der code von TO ist jedoch nichtmal 'korrekt versucht' sondern völlig
debil. Da wird nicht der jeweils gegenüberliegende Interrupt gesperrt
und es wird nach dem Interrupt auf das Port zugegriffen in der abstrusen
Hoffnung dort käme immer noch derselbe Zustand raus und dann am Ende des
Interrupts das Ereignis rückgesetzt als ob nicht inzwischen mehr
Ereignisse hätten eintreten können. Also wirklich alles verrissen was
nur geht, aus totaler Ahnungslosigkeit bei der erneuten Erfindung des
Rades und der Weigerung nachzuschlagen wie man es richtig macht.
https://dse-faq.elektronik-kompendium.de/dse-faq.htm#F.29
>Das Polling-Verfahren scheitert aber auch, wenn ein Kontakt gar nicht>mehr zur Ruhe kommt, sondern die ganze Kontaktbahn lang kratzt.
Ich versuch mal, etwas Systematik reinzubringen.
Für die von einem Encoder oder auch einem normalen Taster gelieferten
Signale gibt es grundsätzlich vier Möglichkeiten (siehe auch die
Grafik):
(1) Sie sind perfekt sauber, ohne jedes Prellen und sonstige Störungen;
(2) Sie prellen an den Umschaltpunkten, sind aber "dazwischen" stabil;
(3) Sie prellen an den Umschaltpunkten und sind außerdem noch
"dazwischen" mit Störspikes kontaminiert; dabei ist ihr Pegel aber noch
klar erkennbar.
(4) Sie sind so stark gestört, dass ihr Pegel nicht erkennbar ist.
Fall (4) klammere ich aus den weiteren Betrachtungen aus.
Fall (1) liegt bei optischen Encodern vor, bei Quecksilberschaltern und
bei allem, wo schon irgendeine interne oder zwischengeschaltete Hardware
für Entprellung/Entstörung gesorgt hat. (2) liegt bei mechanischen
Encodern vor, die nicht auf der Bahn kratzen, oder bei hochqualitativen
mechanischen Tastern (beides in Bezug auf die Rohsignale). (3) liegt bei
mechanischen Encodern vor, deren Schleifer während der Bewegung keinen
sicheren Kontakt auf der Bahn haben, oder deren Bahn abgenutzt ist, oder
bei miserablen mechanischen Tastern, wenn man darauf im gedrückten
Zustand mit dem Finger herumwackelt.
Solche Signale (oder Signalpaare) will man nun in einem µC verarbeiten.
Dafür gibt es zwei prinzipielle Möglichkeiten:
(I) Interruptbasiert;
(P) Pollingbasiert.
Weitere Optionen existieren nicht. Wenn eine Interrupt-Lösung aus
welchen Gründen auch immer ausscheidet, hat man keine andere (sinnvolle)
Alternative, als die Eingänge periodisch abzufragen. Was sollte man denn
sonst tun?
So weit so gut. Aus den beschriebenen Alternativen ergeben sich
logischerweise 2 · 3 = 6 Kombinationen:
(1-I) und (1-P),
(2-I) und (2-P), sowie
(3-I) und (3-P).
Also der Reihe nach.
(1-I) und (1-P) sind schnell abgehandelt: Bei garantiert störungsfreien
Signalen gibt es keine Probleme. Auch nicht bei Verwendung von
Interrupts. Letzteres kann sogar vorteilhaft sein, weil die Interrupts
nur auftreten, wenn tatsächlich ein Flankenwechsel stattgefunden hat.
Der Controller muss den ISR-Code dann nur schneller abarbeiten können
wie zwei Flanken zeitlich aufeinanderfolgen, nicht öfter und innerhalb
eines kürzeren Pollingintervalls.
Interessanter sind die Fälle (2-I) und (2-P). Bei einem Taster verbietet
sich (2-I) aus einleuchtendem Grund, womit nur das Polling übrigbleibt.
Das Abtastintervall muss dabei größer gewählt werden als die maximale
Prellzeit. Darüberhinaus sind keine weiteren Maßnahmen nötig, d. h. es
reicht, den Taster nur genügend langsam zu pollen, um von den
Prellvorgängen nichts mitzubekommen. Was man aber eventuell bemerkt, ist
die zeitliche Verzögerung zwischen der Tasterbetätigung und der Reaktion
der Software, wenn das Pollingintervall übermäßig groß ist (größer als
ungefähr 50 ms).
Bei einem Drehencoder sieht es anders aus: Da steht (2-I) durchaus zur
Wahl! Das ist kein Widerspruch, weil es eine bestimmte Bedingung gibt:
Man muss die beiden Encoderspuren wechselseitig sperren (andernfalls
erhielte man völligen Murks). Man schaltet also und wartet dann solange,
bis auf der anderen Spur eine Flanke auftritt. Das funktioniert und es
ist eine Lösung, der ein interessanter Nebeneffekt anhaftet: Man handelt
sich eine Hysterese von genau einem Schritt bzgl. der mechanischen
Position ein, die sich bei jeder Richtungsänderung bemerkbar macht: Die
Positionsvariable hängt danach immer um 1 nach. Klingt unerfreulich, ist
aber zu relativieren: Zum einen ist es nur eine Hysterese, kein sich
akkumulierender Fehler. Zum anderen bewirkt der Effekt auch, dass bei
einer Vibration um einen Umschaltpunkt die Schrittvariable statt
"mitzuzittern" stabil auf einem Wert bleibt, weil die entsprechende Spur
gesperrt ist. Diese positive Eigenschaft kann Grund genug sein, diese
Lösung zu wählen (beides zusammen, also "hysteresefrei" und "stabil bei
Vibration" geht leider nicht - es schließt sich logisch aus).
(2-P) geht natürlich ebenfalls und auch das wechselseitige Sperren lässt
sich damit realisieren.
Bleiben die Fälle (3-I) und (3-P) übrig. Die Antwort darauf ist kurz:
Hier ist man gezwungen, die Signale zu entstören = zu denoisen = zu
glätten = tiefpasszufiltern. Dafür gibt es zwei Möglichkeiten:
(a) durch Hardware;
(b) durch Software.
Die einfachste Hardware, die das Gewünschte leistet, ist ein RC-Tiefpass
plus nachgeschaltetem Schmitt-Trigger mit kräftiger Hysterese. Man kann
eine solche Schaltung aber auch einfach in Software simulieren, was die
Platine vereinfacht (weniger Bauteile = weniger Fläche, weniger
Fehlerquellen, weniger Arbeit bei Layout und Bestückung) sowie die
Möglichkeit bietet, sowohl die Zeitkonstante des RC-Glieds als auch die
Umschaltschwellen des Schmitt-Triggers anpassen und jederzeit ändern zu
können. Bei richtiger Dimensionierung ist der Output des Filters ein
sauberes Signal, für das der Schmitt-Trigger dann sogar schon die
Flankenerkennung erledigt hat.
Apropos "Flankenerkennung": Bei solchen Signalen müssen immer die
Flanken erkannt werden und es findet auch immer eine Flankenauswertung
statt. Man hat nur die Wahl, das automatisch erledigen zu lassen
(Interrupt) oder dafür Programmcode schreiben zu müssen (Polling). Sage
ich deshalb, weil sich mir manchmal der Eindruck aufdrängt, dass Leute
"Flankenerkennung" mit "Interruptbasierte Lösung" gleichsetzen. Das sind
ja eigentlich verschiedene Sachen.
Martin J. schrieb:> signale gibt es grundsätzlich vier Möglichkeiten (siehe auch die Grafik):
Wichtig ist noch der reale 5. Fall:
Wenn der Kontakt offen ist, ist high.
Wenn der Kontakt geschlossen ist, ist low.
Wenn der Kontakt uber die Schleifbahn bewegt wird, gibt es ständig
Aussetzer, das low Signal geht also immer nur kurz auf high, aber das
high Signal nie auf low.
Das ist behebbar wie dein Fall 4.
Martin J. schrieb:> Apropos "Flankenerkennung": Bei solchen Signalen müssen immer die> Flanken erkannt werden und es findet auch immer eine Flankenauswertung> statt. Man hat nur die Wahl, das automatisch erledigen zu lassen> (Interrupt) oder dafür Programmcode schreiben zu müssen (Polling).
Nein, es ist ein prinzipieller Unterschied zwischen beiden Verfahren.
Polling bezieht sich nur auf die Zustände, nicht auf die Übergänge, die
Übergänge werden geschlussfolgert.
Flankenerkennung bezieht sich nur auf die Ubergänge, nicht auf die
Zustände, die werden daraus geschlussfolgert. Blöd, wenn dabei Flanken
übersehen werden, dann stimmt die Schlussfolgerung nicht mehr.
>Wenn der Kontakt uber die Schleifbahn bewegt wird, gibt es ständig>Aussetzer, das low Signal geht also immer nur kurz auf high, aber das>high Signal nie auf low.
Ist doch ein Unterfall von meinem Punkt (3). Ob jetzt nur der H-Pegel
durch L-Spikes gestört ist, oder nur der L-Pegel durch H-Spikes, oder
gar beide Pegel, ist irrelevant, denn man muss in all diesen Fällen
Signalformung machen. Deshalb sehe/sah ich in einer weiteren
Ausdifferenzierung von (3) keinen Sinn.
>Flankenerkennung bezieht sich nur auf die Ubergänge, nicht auf die>Zustände, die werden daraus geschlussfolgert.
Beim Schlussfolgern von Flanken aus Zuständen geh ich ja mit, aber warum
sollte man jemals Zustände schlussfolgern müssen? Man kann doch
jederzeit einfach den betreffenden Port abfragen, wenn man die
Information braucht? Ist die Zeitverzögerung zwischen der Flanke und der
Port-Lese-Instruktion in der ISR der Punkt?