Moin,
ich habe ein kleines Verständnisproblem mit der Drehencoder-Routine von
Peter Dannegger
http://www.mikrocontroller.net/articles/Drehgeber#Solide_L.C3.B6sung:_Beispielcode_in_C
Ich verwende einen Encoder mit Rastung aller zwei Schritte, daher wollte
ich die encode_read2()-Funktion verwenden. Das funktioniert wunderbar,
wenn man nach oben dreht, nach unten passiert allerdings schlicht gar
nix.
1
ISR(TIMER0_COMP_vect)// 1ms for manual movement
2
{
3
int8_tnew,diff;
4
5
new=0;
6
if(PHASE_A)
7
new=3;
8
if(PHASE_B)
9
new^=1;// convert gray to binary
10
diff=last-new;// difference last - new
11
if(diff&1){// bit 0 = value (1)
12
last=new;// store new as next last
13
enc_delta+=(diff&2)-1;// bit 1 = direction (+/-)
14
}
15
}
16
17
int8_tencode_read2(void)// read two step encoders
18
{
19
int8_tval;
20
21
cli();
22
val=enc_delta;
23
enc_delta=val&1;
24
sei();
25
returnval>>1;
26
}
Statt dass enc_delta bei jeder Abfrage gelöscht wird wie in
encode_read1(), wird ja in encode_read2() - falls ich das richtig
verstehe - ein Einzelschritt behalten und im nächsten Durchlauf dann der
zweite Einzelschritt im Timer-ISR aufaddiert.
In negativer Richtung wird durch
1
enc_delta=val&1
aber enc_delta jedesmal gelöscht (-1 & 1 = 0), der nächste Teilschritt
setzt enc_delta im Timer-ISR wieder auf -1, und so wiederholt sich das.
Ich habe schon einen Knoten im Vorderhirn. Wie löst man das geschickt,
dass auch in negativer Richtung zwei Schritte aufaddiert werden? Oder
falls ich das alles missinterpretiere, wo liegt mein Fehler?
Gruß,
/Hannes
Edit: hm, irgendwie in der falschen Rubrik gelandet, bitte mal
verschieben. Danke. ;-)
@ Johannes S. (johannes_s94)
>Ich verwende einen Encoder mit Rastung aller zwei Schritte, daher wollte>ich die encode_read2()-Funktion verwenden.
Passt.
>Das funktioniert wunderbar,>wenn man nach oben dreht, nach unten passiert allerdings schlicht gar>nix.
Dann ist möglicherweise dein Drehgeber falsch angeschlossen oder defekt.
Prüfe, ob die beiden Signale wie erwartet phasenverschoben ankommen. Im
einfachsten Fall mit zwei LEDs mit Vorwiderständen, welche an deine
Signale und VCC geklemmt werden. Wenn man dann den Drehgeber festhält
und langsam dreht, muss in beide Richtungen das phasenverschobene
Schalten der Signale sichtbar sein.
>verstehe - ein Einzelschritt behalten und im nächsten Durchlauf dann der>zweite Einzelschritt im Timer-ISR aufaddiert.>In negativer Richtung wird durch
Genau.
>Ich habe schon einen Knoten im Vorderhirn. Wie löst man das geschickt,>dass auch in negativer Richtung zwei Schritte aufaddiert werden?
Indem man den Code einfach so lässt und benutzt. Er funktioniert ;-)
Das Problem liegt woanders.
aber
> enc_delta jedesmal gelöscht (-1 & 1 = 0), der nächste Teilschritt setzt> enc_delta im Timer-ISR wieder auf -1, und so wiederholt sich das.
Nur wenn wirklich jeder Schritt auch abgefragt wird und enc_delta damit
quasi gelöscht wird.
> Ich habe schon einen Knoten im Vorderhirn. Wie löst man das geschickt,> dass auch in negativer Richtung zwei Schritte aufaddiert werden? Oder> falls ich das alles missinterpretiere, wo liegt mein Fehler?
Soweit ich gerade durchblicke, hast Du da im Prinzip schon recht. Der
eine Teilschritt wird zwischengespeichert und die Richtungsinformation
ist weg. Im praktischen Betrieb wird das aber kaum auffallen.
Allerdings leide ich auch gerade unter akuter Hirnverknotung, ist schon
eine Zeit her das ich mir das genau angesehen habe. :o)
Grüße
Klaus
Hallo,
die Ausleseroutine |encode_read2()|ruft man einmal auf und addiert den
Rückgabewert zu einer Variable vom Type int8_t.
1
'indermain-loop
2
staticint8_tencoder_step+=encode_read2();
Ist |encoder_step <> 0|, dann führt man seine Menüfunction oder was auch
immer aus.
Hat man den |encoder_step| abgearbeitet, setzt man den Wert in
|encoder_step| neu.
Also bei mir encoder_step += (encoder_step< 0 ? +1 : -1).
Falk Brunner schrieb:> Dann ist möglicherweise dein Drehgeber falsch angeschlossen oder defekt.> Prüfe, ob die beiden Signale wie erwartet phasenverschoben ankommen. Im
Wenn ich Einzelschritte mit encode_read1() auslese, klappt es
einwandfrei. Ich werde mir das aber mit den LEDs nochmal angucken,
obwohl ich mir eigentlich kaum vorstellen kann, wie man einen
Drehencoder falsch anschließen will. Möglich ist freilich alles, bei mir
sowieso. ;-)
> Indem man den Code einfach so lässt und benutzt. Er funktioniert ;-)> Das Problem liegt woanders.
Das vermute ich ja auch, z.B. hier:
Klaus I. schrieb:> Nur wenn wirklich jeder Schritt auch abgefragt wird und enc_delta damit> quasi gelöscht wird.
...und genau das tue ich nämlich der Faulheit halber. Aller 1ms wird die
ISR gezündet und auch gleich encode_read2() (bzw. read1()) mit
abgearbeitet. Genau genommen ist das bei mir keine ISR, sondern ein
Abschnitt in der Mainloop, der bei einem gesetzten 1ms-Flag, welches in
der Timer-ISR gesetzt wird, einmal durchlaufen wird, aber das tut ja
nichts zur eigentlichen Sache.
Uwe S. schrieb:> die Ausleseroutine |encode_read2()|ruft man einmal auf und addiert den> Rückgabewert zu einer Variable vom Type int8_t.>
1
>staticint8_tencoder_step+=encode_read2();
2
>
Das ist ja das Problem. Wenn ich rückwärts drehe, kommt nie etwas
anderes als 0 zurück.
Gruß,
/Hannes
@ Johannes S. (johannes_s94)
>Wenn ich Einzelschritte mit encode_read1() auslese, klappt es>einwandfrei.
D.h. es kommt bei Rückwärtsdrehung auch -1 zurück?
>> Nur wenn wirklich jeder Schritt auch abgefragt wird und enc_delta damit>> quasi gelöscht wird.>...und genau das tue ich nämlich der Faulheit halber.
???
>Das ist ja das Problem. Wenn ich rückwärts drehe, kommt nie etwas>anderes als 0 zurück.
Lass dir einfach mal die Variable enc_delta irgendwo ausgeben, wenn du
nur ein paar LEDs hast, reichen die unteren 4 Bit. Also DIREKT die
Variable, ohne Nutzung von encode_read()!
Dann muss man bei Vor/Zurück drehen das Bitmustr entsprechend laufen
sehen.
Falk Brunner schrieb:> D.h. es kommt bei Rückwärtsdrehung auch -1 zurück?
Genau.
>>...und genau das tue ich nämlich der Faulheit halber.> ???
Der Timer-ISR läuft mit 1kHz, und das Auslesen von Enc_delta mittels
encode_read2() auch. Damit hat das Delta gar keine Chance,
aufzuaddieren.
> Lass dir einfach mal die Variable enc_delta irgendwo ausgeben, wenn du> nur ein paar LEDs hast, reichen die unteren 4 Bit. Also DIREKT die> Variable, ohne Nutzung von encode_read()!>> Dann muss man bei Vor/Zurück drehen das Bitmustr entsprechend laufen> sehen.
Hab ich gemacht, deswegen komme ich ja drauf, dass das nicht
funktioniert. ;-)
Enc_delta wird beim Rückwärtsdrehen -1 (0b11111111), dann in read2() mit
1 ver-AND-et (das Ergebnis wird für die Rückgabe rechtsgeschoben und es
bleibt 0, ergo gibt dieser Durchlauf wie gewünscht nichts zurück), und
auf die resultierende +1 wird im nächsten Timer-ISR wieder -1
draufaddiert. Das wird zu 0 (encode_read2 gibt freilich nix zurück und
"merkt" sich auch nix), und in der nächsten Drehung wird enc_delta
wieder zu -1. Damit beginnt das Spiel von vorn.
Denkfehler?
/Hannes
So,
ich habe es jetzt dahingehend korrigiert, dass es auch dann
funktioniert, wenn der Wert zu oft mit read2() ausgelesen wird. Genau
daran liegt es nämlich.
Begründung nochmal:
Wenn man mit großer Geschwindigkeit den Encoder ausliest, d.h. wenn man
ihn schon ausliest, sobald enc_delta auf -1 springt, dann macht
encode_read2() aus enc_delta = -1 nun enc_delta = +1 (durch das & 1).
Der Rückgabewert von read2() ist korrekterweise null, denn der
Drehzyklus ist noch nicht abgeschlossen.
Dreht man weiter, so wird enc_delta wieder -1, und zusammen mit der
falschen "gemerkten" +1 gibt das null.
Dreht man wieder weiter, kommt wieder -1, und das Spiel beginnt von
vorn.
Ich habe das jetzt wie folgt abgeändert:
1
int8_tencode_read2(void)// read two step encoders
2
{
3
int8_tval;
4
5
cli();
6
val=enc_delta;
7
ASRval,1// arithmetic shift right
8
if(val)
9
enc_delta=0;
10
sei();
11
12
returnval;
13
}
Keine Ahnung, ob es in C ein ASR gibt, ich beherrsche C nicht und hab
das nur adaptiert. Jedenfalls wird val erst durch 2 geteilt, und wenn
dort was über bleibt, wird enc_delta wieder auf null gesetzt, sonst wird
der bestehende enc_delta behalten. Bei mir geht das so bisher, wenn das
noch irgendwo falsch ist, bitte ich um Hinweis, damit ich das bei mir
noch ändern kann. ;-)
/Hannes
@ Johannes S. (johannes_s94)
>ich habe es jetzt dahingehend korrigiert, dass es auch dann>funktioniert, wenn der Wert zu oft mit read2() ausgelesen wird. Genau>daran liegt es nämlich.
Glaub ich nicht ;-)
Ich hab den Code auch schon merhfach benutzt und dieses Problem nicht
bemerkt.
>ihn schon ausliest, sobald enc_delta auf -1 springt, dann macht>encode_read2() aus enc_delta = -1 nun enc_delta = +1 (durch das & 1).
Ja.
>Der Rückgabewert von read2() ist korrekterweise null, denn der>Drehzyklus ist noch nicht abgeschlossen.
NEIN!
-1 einmal ARITHMETISCH nach recht schieben ergibt -1! Klingt komisch,
ist aber so! Damit ist das KORREKT.
>Dreht man weiter, so wird enc_delta wieder -1,
Nein, es wird um 1 verringert.
> und zusammen mit der>falschen "gemerkten" +1 gibt das null.
Genau. Aber das ist NICHT falsch gemerkt sondern OK, wenn gleich ein
Trick.
D.h. negative Zahlen werden durch & 1 modulo gerechnet und ins positive
gedreht. Das ist aber für die Gesamtwirkung egal.
>Dreht man wieder weiter, kommt wieder -1, und das Spiel beginnt von>vorn.
Was aber OK ist, denn du bekommts nach je zwei Codewechslen in
Rückwärtsrichtung einmal eine -1 aus encode_read2(). Das passt.
>Keine Ahnung, ob es in C ein ASR gibt,
Sicher, ganz einfach schieben. Wenn die Variable vorzeichenbehaftet ist,
macht das der Compiler richtig. Und genau das ist im bestehenden Code
drin.
>der bestehende enc_delta behalten. Bei mir geht das so bisher, wenn das
Aber nicht mit dem oben gezeigten Code. ASR gibt es so als Befehl nicht
in C.
Also ich vermute eher, dass du, warum auch immer, ein Problem mit dem
Vorzeichen hast. Im Code vom Wiki sind sowohl enc_delta als auch val in
der Funktion als int8_t definiert, also VORZEICHENBEHAFTET. Damit geht
das. Möglicherweise hast du irgendwelche wilden Compilerschalter
aktiviert, die das ignorieren oder aushebeln. Oder du hast den Code
verändert. Mit welcher Entwicklungsumgebung arbeitest du? Welcher
Compiler mit welcher Version?
Poste vollständigen Code im Anhang, dann kann man das Problem vielleicht
finden.
Falk Brunner schrieb:> -1 einmal ARITHMETISCH nach recht schieben ergibt -1! Klingt komisch,> ist aber so! Damit ist das KORREKT.
Ha! Das ist der Punkt.
Normales Shift right (>>) ist laut
http://de.wikipedia.org/wiki/Bitweiser_Operator#C_und_C.2B.2B in C/C++
undefiniert, sofern man negative Werte rechts schiebt, und darum bin ich
drauf reingefallen. Laut dem englischen Artikel
http://en.wikipedia.org/wiki/Arithmetic_shift#Non-equivalence_of_arithmetic_right_shift_and_division
soll ein arithmetisches Shift right genau das machen, was der gcc
offenbar tut, nämlich abrunden und nicht gegen Null runden (also -1 >>
1 = -1).
> Aber nicht mit dem oben gezeigten Code. ASR gibt es so als Befehl nicht> in C.
Klar, sagte ich ja. Das war nur symbolisch eingesetzt, ich kann wie
gesagt kein C.
Der Fehler liegt also eindeutig im fehlenden Verständnis des
arithmetischen Shift right bei negativen Zahlen, und da bin ich offenbar
in guter Gesellschaft. :-D
Es läuft also genau anders herum:
- read2() gibt direkt beim ersten Dreher -1 zurück, drückt aber +1 in
den Skat (weil mit & 1 verknispelt)
- beim zweiten Dreher kommt 0 zurück, weil auf +1 ein -1 addiert wird.
- beim dritten Dreher sind wir wieder am Start
Hab ich das jetzt gecheckt?
/Hannes
@ Johannes S. (johannes_s94)
>Normales Shift right (>>) ist laut>http://de.wikipedia.org/wiki/Bitweiser_Operator#C_... in C/C++>undefiniert, sofern man negative Werte rechts schiebt, und darum bin ich>drauf reingefallen. Laut dem englischen Artikel
Ich kenn den C-Standard nicht, glaub das aber nicht so recht.
>http://en.wikipedia.org/wiki/Arithmetic_shift#Non-...>soll ein arithmetisches Shift right genau das machen, was der gcc>offenbar tut, nämlich abrunden und nicht gegen Null runden (also -1 >>>1 = -1).
Beim Schieben wird nie gerundet, nur abgeschnitten. Also IMMER
abgerundet, wenn man so will.
>- read2() gibt direkt beim ersten Dreher -1 zurück, drückt aber +1 in>den Skat (weil mit & 1 verknispelt)>- beim zweiten Dreher kommt 0 zurück, weil auf +1 ein -1 addiert wird.>- beim dritten Dreher sind wir wieder am Start>Hab ich das jetzt gecheckt?
Yo, Man!
Aber was ist nun mit deiner Anwendnung? Funktioniert die oder nicht?
Falk Brunner schrieb:> Aber was ist nun mit deiner Anwendnung? Funktioniert die oder nicht?
Ja, tut sie. Das war eine Verbindung zweier Fehler, die ich nicht
korrekt durchschaut habe. Jetzt, wo ich das eine begriffen habe, habe
ich auch den anderen gefunden.
Danke.
/Hannes
@ Johannes S. (johannes_s94)
>Ja, tut sie. Das war eine Verbindung zweier Fehler, die ich nicht>korrekt durchschaut habe. Jetzt, wo ich das eine begriffen habe, habe>ich auch den anderen gefunden.
Aber wie machst du das, wenn du kein C kannst? :-0
@Johannes S. (johannes_s94)
>Das ist kein C, sondern LunaAVR. :-D
Ach so.
>Ich portiere nur laufend hin und her, damit ich hier überhaupt Antworten>bekommen kann.
OMG! Dann poste doch lieber das ORIGINAL! Damit kann man was anfangen
und du machst keine weiteren Fehler beim "Umschreiben".
Der Fehler ist nicht beim Umschreiben passiert, sondern liegt im
Verhalten des Compilers. Wenn ich mir nicht sicher wäre, dass ich das
Portieren kann, hätte ich es sicher auch direkt als Luna-Code gepostet
(dass ich behaupte, kein C zu können, heißt nur, dass ich mir nicht
zutraue, direkt aus dem Stand in C zu programmieren, aber lesen kann ich
es schon ein wenig).
Ich nehme mal an, dass du genau wie ich auf die Nase gefallen wärst,
weil du nicht damit gerechnet hättest, dass Luna (derzeit) bei
Verwendung des arithmetischen Schiebeoperators ASR aus -1 eine 0
rechtsschiebt, so wie ich nicht drauf gekommen bin, dass gcc bei >>
gleich arithmetisch schiebt statt stumpf binär links Nullen
reinzudrücken.
BTW: OMG ist wenigstens die richtige Anrede. :-P
Aber auf jeden Fall danke nochmal, ohne dich wäre ich nicht drauf
gekommen.
/Hannes
@ Johannes S. (johannes_s94)
>weil du nicht damit gerechnet hättest, dass Luna (derzeit) bei>Verwendung des arithmetischen Schiebeoperators ASR aus -1 eine 0>rechtsschiebt,
Na dann schreib mal einen schönen Bugreport an den Mann im Mond . . .
;-)