Forum: Mikrocontroller und Digitale Elektronik Ist mein Encoder Programm so gut?


von RoterKater (Gast)


Lesenswert?

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:
1
 // 2 pins als INTERRUPT
2
    INTCONbits.GIE=0;
3
    INTCONbits.PEIE=0; // DISABLE Peripheral ISR
4
    
5
    INTCONbits.INTEDG=0; // Falling Edge
6
    
7
    PIE0bits.IOCIE=1; // Interrupt On Change ON
8
    PIE0bits.INTE=0; // External INT OFF
9
      
10
    // ISR PINS 
11
    // IOC NEGATIVE Edge
12
    IOCANbits.IOCAN4 = 1;
13
    IOCANbits.IOCAN5 = 1;
14
    
15
    PIR0bits.IOCIF=0; // Clear FLAG IOC
16
17
    // Turn ON Interrupts
18
    INTCONbits.GIE=1;

Und die ISR Routine:
1
void __interrupt() ISR_Encoder(){
2
    
3
    // GIE is cleared automatically    
4
    
5
    if(IOCAFbits.IOCAF4==1){ // ISR A4
6
        if(IN_ENC2==1)
7
            ENC_degree+=DegreePerStep;
8
        else if (IN_ENC2==0)
9
            ENC_degree-=DegreePerStep;
10
    }
11
    else if (IOCAFbits.IOCAF5==1){ // ISR A5
12
        if(IN_ENC1==1)
13
            ENC_degree-=DegreePerStep;
14
        else if (IN_ENC1==0)
15
            ENC_degree+=DegreePerStep;        
16
    }  
17
    
18
    // wertebereich von 0-360 
19
    if(ENC_degree>360)
20
            ENC_degree=ENC_degree-360;
21
    else if(ENC_degree<0)
22
        ENC_degree=360;
23
    
24
    
25
    // Clear FLAGS
26
    IOCAFbits.IOCAF4=0;
27
    IOCAFbits.IOCAF5=0;
28
    PIR0bits.IOCIF=0; // Clear FLAG IOC GENERAL
29
}

Freue mich über konstruktives Feedback.
Danke

von Andreas B. (bitverdreher)


Lesenswert?

Dieses Thema hat mittlerweile einen recht langen Bart.
https://www.mikrocontroller.net/articles/Drehgeber

von A. S. (Gast)


Lesenswert?

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?

von c-hater (Gast)


Lesenswert?

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...

von H.Joachim S. (crazyhorse)


Lesenswert?

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 :-)

von A. S. (Gast)


Lesenswert?

sorry, noch vergessen: Beim Flattern um ein Wert entstehen ggf. 
Fehlmessungen (mit beliebiger Akkumulation trotz Stillstand)

von c-hater (Gast)


Lesenswert?

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...

von A. S. (Gast)


Lesenswert?

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.

von W.S. (Gast)


Lesenswert?

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.

von Wolfgang (Gast)


Lesenswert?

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.

von MaWin (Gast)


Lesenswert?

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

von A. S. (Gast)


Lesenswert?

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.

Beitrag #7015990 wurde von einem Moderator gelöscht.
von Einrücker (Gast)


Lesenswert?

Die Klammern sind leider an der falschen Stelle.
Diese gehören in die nächste Zeile jeweils.

Dadurch wird die Lesbarkeit deutlich besser.

von MagIO (Gast)


Lesenswert?

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.

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

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?

: Bearbeitet durch Moderator
Beitrag #7018261 wurde von einem Moderator gelöscht.
von MaWin (Gast)


Lesenswert?

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.

von W.S. (Gast)


Lesenswert?

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.

von Wolfgang (Gast)


Lesenswert?

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.

von Andreas B. (bitverdreher)


Lesenswert?

Wolfgang schrieb:
> Ein vernünftiger Decoder für AB-Signale leistet das ohne zusätzliche
> Maßnahmen.

Nein, ist aber auch schon 100x durchgekaut.

von MaWin (Gast)


Lesenswert?

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.

von MaWin (Gast)


Lesenswert?

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

von Martin J. (martiko)


Lesenswert?

>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.

von Martin J. (martiko)


Angehängte Dateien:

Lesenswert?

Ja, das wär dann auch noch die Grafik :-)

von MaWin (Gast)


Lesenswert?

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.

von LostInMusic (Gast)


Lesenswert?

>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?

von Falk B. (falk)


Lesenswert?

Das theologische Dauerthema Drehgeberauswertung . . .

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.