Hallo,
ich versuche den Pollin Drehencoder von ALPS auszuwerten. Ich habe mich
genau nach dem C-Code im Wiki hier auf der Website gehalten.
http://www.mikrocontroller.net/articles/Drehgeber
Die Auswertung läuft in folgender Funktion ab:
Das Problem ist, dass manchmal Drehungen nicht erkannt werden, oder ich
erst zig mal drehen muss bis sich was auf dem Display tut. Auch werden
manchmal Zeichen übersprungen.
:-(
Ich habe die Abtastrate auf 0,5ms verringert und auf 4 bzw. 8ms erhöht.
Hat irgendwie nichts gebracht. Ich bin jetzt etwas ratlos.
Hat jemand mit dem Pollin ALPS Drehencoder Erfahrungen? Wer kann helfen?
danke.
> Ich habe mich genau nach dem C-Code im Wiki hier auf der Website gehalten.
Gut. Der Code funktioniert super.
> Das Problem ist, dass manchmal Drehungen nicht erkannt werden
Dann zeig mal deinen restlichen Code. Bei dem, was du schon gepostet
hast, erwarte ich Schlimmes... :-/
Wurzel schrieb:
> Hat jemand mit dem Pollin ALPS Drehencoder Erfahrungen?
Welcher ist es denn genau?
(Hintergrund: Ich habe hier auch Drehgeber, die verhalten sich aber ganz
anders. Die geben beim Drehen von einer Rastposition zur nächsten nur
Pulse aus)
int8_tencode_read1(void)// read single step encoders
5
{
6
int8_tval;
7
8
timer0_comp_irx(DISABLE);
9
val=enc_delta;
10
enc_delta=0;
11
timtimer0_comp_irx(ENABLE);
12
returnval;// counts since last call
13
};
14
15
16
int8_tencode_read2(void)// read two step encoders
17
{
18
int8_tval;
19
20
timer0_comp_irx(DISABLE);
21
val=enc_delta;
22
enc_delta=val&1;
23
timer0_comp_irx(ENABLE);
24
returnval>>1;
25
};
26
27
28
int8_tencode_read4(void)// read four step encoders
29
{
30
int8_tval;
31
32
timer0_comp_irxDISABLE);
33
val=enc_delta;
34
enc_delta=val&3;
35
timer0_comp_irxENABLE);
36
returnval>>2;
37
};
Folgendes muss ich noch sagen:
Die Funktion decoder_getc(..) sieht etwas chaotisch aus, weil die
Auswertung nicht über den Timerinterrupt geschehen ist. Die zwei
fehlenden Parameter min_char und max_char wurden zu testzwecken
entfernt.
Außerdem Timer0 Overflow Interrupt läuft noch ein Interrupt vom Timer0
ca. alle 4ms.
In der main wird nach Initalisierung (Ports, Timer) die Funktion
decoder_getc() aufgerufen und das ASCII Zeichen auf dem Display
ausgegeben. Es handelt sich bei der Funktion nur um eine Testroutine.
@Karl heinz
ALPS 11mm Size Metal Shaft Encoder EC11 Series
Model No. EC11E15244B2
Ich kann jetzt aus dem Kopf nicht sagen, welchen ALPS Typ ich genau
habe. Aber wenn ich abends nach Hause komme, werd ich das mal eruieren
und meine Codebasis rausgeben.
Hmm.
Der letzte Encoder, den ich noch nicht verbaut hatte, versteckt sich
hartnäckig.
Jetzt kann ich nicht sagen, was das genau für ein Typ ist. Aber ich weiß
noch, wie ich ihn das erste mal ausprobiert hatte: Ich hab einen Kanal
(A) an den Durchgangspiepser gehängt und war erstaunt, dass derbeim
Drehen von einer Rastposition in die nächste nur ganz kurz gepiepst hat.
Erwartet hatte ich eigentlich, dass sich mit dem Drehen von einer
Rastposition in die nächste ein Piepsen/nicht_piepsen ergibt.
Wie auch immer.
Ich habe die Auswertung in die Tastenentprellroutine vom PeDa
eingebunden.
D.h. Timer-Overflow der alle paar Millisekunden ausgelöst wird.
1
#define ENCODER_DDR DDRA
2
#define ENCODER_PORT PORTA
3
#define ENCODER_PIN PINA
4
#define ENCODER_A (1<<PA5)
5
#define ENCODER_B (1<<PA4)
6
7
8
uint8_tprevEncoderA=ENCODER_A;
9
uint8_tprevEncoderB=ENCODER_B;
10
uint8_tencoderA=ENCODER_A;
11
uint8_tencoderB=ENCODER_B;
12
13
ISR(TIMER0_OVF_vect)// every 10ms
14
{
15
...
16
17
// Drehendcoder
18
prevEncoderA=encoderA;
19
encoderA=ENCODER_PIN&ENCODER_A;
20
encoderB=ENCODER_PIN&ENCODER_B;
21
22
if(encoderA!=prevEncoderA){// Pegelwechsel A
23
if(!encoderA)// 1 -> 0 Startflanke
24
prevEncoderB=encoderB;
25
else{// 0 -> 1 Endflanke
26
if(prevEncoderB!=encoderB){// gilt nur, wenn es an B ebenfalls einen Pegelwechsel gab
27
// da die Wechsel phasenverschoben sind
28
// ist das gleichzeitig eine Entprellung
29
30
if(encoderB)
31
key_press|=KEY_NEXT;
32
else
33
key_press|=KEY_PREV;
34
35
}
36
}
37
}
38
}
Das Ergebnis wird in key_press abgeliefert und wird mit den normalen
Entprellroutinen für die Tasten mit abgefragt.
Alle Alps Drehencoder sind anders.
Auch die mit derselben Partnummer.
Insbesondere die Phase B kann bei der Rastung stabil 1 oder 0 sein oder
- meistens- undefiniert irgendwas sein.
Auch kann es 2 oder 1 Impuls zwischen Rastungen geben. Eine allgemein
definierte Aussage zu diesem Murks ist nicht möglich.
Ich habe leider kein Oszi und kann daher nur den Zustand in den
Rasterstellungen erfassen. Beide "Phasen" wechseln zwischen 11 und 00.
Ich probiere mal meinen Code an deine Vorlage anzupassen.
Wurzel schrieb:
> Ich habe leider kein Oszi und kann daher nur den Zustand in den> Rasterstellungen erfassen.
Wenn Du langsam genug drehst, dann siehst Du auch an LEDs, wie die
beiden Phasen schalten.
> Beide "Phasen" wechseln zwischen 11 und 00.
So ist es bei meinem Alps von Pollin (Bestellnummer 240399, vor einigen
Jahren gekauft) auch, ist eine Ausführung mit kurzer geriffelter 1/4
Zoll-Welle, 30 Rastungen pro Umdrehung (15 mal 00 und 15 mal 11) und
sehr sauberen Rastpunkten ohne Wackler an einer der beiden Phasen (im
Gegensatz zum Panasonic-Drehgeber Bestellnummer 240313, dessen Phase B
genau im Rastpunkt umschaltet).
>> Ich probiere mal meinen Code an deine Vorlage anzupassen.
Ich frage die Dinger im Timer-synchronisierten Job im Abstand von 1 ms
ab, wobei ich aus (um 2 Bit geshiftetem) Alt-Wert und Neuwert einen
Index bilde, über den ich den Increment-Wert (-1, 0, +1) aus einem
Flash-Array hole (LUT). Bei Drehgebern mit anderen Eigenschaften wird
einfach nur die LUT angepasst.
Mit C-Code kann ich nicht dienen, ich werkele in ASM.
...
Wurzel schrieb:
> Ich habe leider kein Oszi und kann daher nur den Zustand in den> Rasterstellungen erfassen. Beide "Phasen" wechseln zwischen 11 und 00.>> Ich probiere mal meinen Code an deine Vorlage anzupassen.
Jep ist richtig so, er schaltet nicht direkt 11 und 00 sondern schaltet
in die eine Richtung
P1: 0-1-1 / 1-0-0
P2: 0-0-1 / 1-1-0
in die andere Richtung
P1: 0-0-1 / 1-1-0
P2: 0-1-1 / 1-0-0
@ alpshasser (Gast)
>Alle Alps Drehencoder sind anders.
Oh wie schön.
>Insbesondere die Phase B kann bei der Rastung stabil 1 oder 0 sein oder>- meistens- undefiniert irgendwas sein.
Es ist ja auch selten dämlich, die Rastpunkte genau auf den
Flankenwechsel eines Kanals zu legen.
MfG
Falk
Falk Brunner schrieb:
> @ alpshasser (Gast)>>>Alle Alps Drehencoder sind anders.>> Oh wie schön.>>>Insbesondere die Phase B kann bei der Rastung stabil 1 oder 0 sein oder>>- meistens- undefiniert irgendwas sein.>> Es ist ja auch selten dämlich, die Rastpunkte genau auf den> Flankenwechsel eines Kanals zu legen.
Das weißt Du und das weiß ich, aber Panasonic weiß das nicht...
http://www.pollin.de/shop/downloads/D240313D.PDF
Schau Dir das Impulsdiagramm an, Spur B hat die Flanke (den
Pegelwechsel) genau auf dem Rastpunkt.
@Wurzel:
Beim Alps ist das nicht der Fall, siehe Datenblatt im Anhang.
>> MfG> Falk
...
Wurzel schrieb:
> @Lux>> Doch!
Der Alps-Drehencoder, den Pollin unter Bestellnummer 240339 verkauft
hat, hat seine 30 Rastungen allesamt im stabilen Bereich beider Spuren.
Und dies nicht nur laut Datenblatt, sondern auch real. Um dies zu
überprüfen, habe ich gestern Abend extra mein Steckbrett hervorgeholt
und das dort verbaute Exemplar (unten ganz rechts) mit LEDs
durchgespielt.
http://www.hanneslux.de/avr/tipps/brett/index.html> Zumindest bei den Typen EC09E/EC11E/EC11J/EC11K. Schau mal auf> Seite 9 des Datenblatts. ;-)> http://www.alps.com/products/WebObjects/catalog.woa/E/PDF/Switch/Encoder/EC11/EC11.PDF
Ja, das ist wie bei dem Panasonic-Drehgeber (Bestellnummer 240313), den
Pollin derzeit noch vertreibt und dessen Datenblatt ich oben verlinkt
habe.
Wenn schon (mindestens) zwei Hersteller solche Drehgeber anbieten, dann
muss es doch auch Kunden geben, die so "selten dämlich" (TM Falk) sind,
solche Konstrukte in Auftrag zu geben.
Diese "selten dämliche" Konstruktion ist zwar nicht gerade vorteilhaft,
lässt sich aber auch zuverlässig und sauber auswerten, ich habe in
verschiedenen Basteleien die Panasonic-Drehgeber (Pollin 240313) im
Einsatz.
Bei Drehgebern, die ihre Rastung auf 00 und 11 haben, gibt es pro
Rastung auf jeder Spur eine Flanke. Um diese Drehgeber auszuwerten,
prüft man ja eine Spur auf Flanke und die andere Spur auf Zustand. Bei
symmetrischen Drehgebern ist es egal, welche Spur man auf Flanke prüft.
Bei diesen "selten dämlichen" Drehgebern prüft man Spur A (also die
Spur, die im eingerasteten Zustand stabilen Pegel liefert) auf Flanke
und Spur B auf Zustand. Da der Zustand nur relevant ist, wenn eine
Flanke erkannt wurde, spielt der (eingerastet) undefinierte Zustand der
Spur B keine Rolle.
Zum Abtasten des Drehgebers wird das Bitmuster (der Zustand der beiden
Spuren) eingelesen und auf die beteiligten Bits maskiert. Dies wird dann
zu dem "gemerkten" und um 2 Bits verschobenen Bitmuster der letzten
Abtastung geORT. Es entsteht eine 4-Bit-Zahl, die als Index auf die LUT
genutzt wird. Von diesen 16 möglichen Zuständen sind bei diesen
Drehgebern aber nur 4 Zustände relevant.
In die LUT werden also nur dort Incremente eingetragen, wo Spur A den
Pegel wechselt (also eine Flanke hat).
Eine Drehrichtung:
Rastung alt neu
A B A B
-------
--> 0 0 0 1 1
| > 0 1 1 1 7 Flanke an A
| 1 1 1 0 14
| > 1 0 0 0 8 Flanke an A
| 0 0 0 1 1
| > 0 1 1 1 7 Flanke an A
| 1 1 1 0 14
| > 1 0 0 0 8 Flanke an A
--< 0 0 0 1 1
Andere Drehrichtung:
Rastung alt neu
A B A B
-------
--> 0 1 0 0 4
| > 0 0 1 0 2 Flanke an A
| 1 0 1 1 11
| > 1 1 0 1 13 Flanke an A
| 0 1 0 0 4
| > 0 0 1 0 2 Flanke an A
| 1 0 1 1 11
| > 1 1 0 1 13 Flanke an A
--< 0 1 0 0 4
Die eine Drehrichtung ergibt also bei Flanken an Spur A die Zahlenwerte
(als Index auf die LUT) 7 und 8, die andere Drehrichtung 2 und 13.
Daraus ergibt sich, dass bei Index 7 und 8 der Wert +1 (als Increment)
in die LUT eingetragen wird und bei Index 2 und 13 der Increment-Wert
-1. Alle anderen Elemente des Arrays (der LUT) werden mit dem Wert 0
aufgefüllt.
Wird Spur A und B vertauscht, so ergeben sich andere Index-Werte. Bei
symmetrischen Drehgebern ist das egal, die "selten dämlichen" spinnen
dann aber.
Somit wird der Zählerstand nur verändert, wenn eine Flanke an Spur A
erkannt wurde. Der Zustand von B ist in diesem Zeitpunkt ja stabil.
Dies alles läuft im Timer-Int oder einem per Timer synchronisierten Job
ab. Die Aufruf-Frequenz ist ein Kompromiss zwischen CPU-Last und maximal
möglicher Drehgeschwindigkeit. Bei Verwendung des Drehgebers als
manuelles Eingabegerät hat sich bei mir eine Abtastfrequenz von 1 kHz
bewährt.
Die Mainloop (bzw. ein Job davon) addiert nun diesen Zählerstand auf den
zur Bearbeitung anstehenden Wert und löscht ihn danach. Somit gehen
keine Drehbewegungen verloren, wenn es in Main mal länger dauert.
C-Code kann ich Dir nicht geben, ich mach' sowas in ASM.
...
@ Hannes Lux (hannes)
>Wenn schon (mindestens) zwei Hersteller solche Drehgeber anbieten, dann>muss es doch auch Kunden geben, die so "selten dämlich" (TM Falk) sind,>solche Konstrukte in Auftrag zu geben.
Sicher, was aber nicht automatisch bedeutet, dass das sonderlich klug
ist. Wie es scheint, ist der Drehgeber auch heutzutage noch sehr mit
mystischen Vorstellungen behaftet.
>Diese "selten dämliche" Konstruktion ist zwar nicht gerade vorteilhaft,>lässt sich aber auch zuverlässig und sauber auswerten,
Nöö, das ist mehr oder minder Glückssache, jenachdem wie "klapprig" der
individuelle Drehgeber ist. Warum das so ist, steht im Artikel
Drehgeber.
>Rastung auf jeder Spur eine Flanke. Um diese Drehgeber auszuwerten,>prüft man ja eine Spur auf Flanke und die andere Spur auf Zustand.
Genau DAS ist Billigmurks, der aber leider selbst bei Markenherstellern
zu finden ist. Kann jeder leicht prüfen. Einfach einen Drehknopf fest
anfassten und immer auf einem Rastpunkt leicht hin- und herdrehen.
Mechanisch dreht man immer vor und zurück, ein Menu darf dabei nicht
kontinuierlich weiterblättern bzw. wenn es eine Zahleneinstellung ist,
muss sie vor und zurück springen. Tut sie das nciht, ist die Auswertung
Schrott.
MfG
Falk
Falk Brunner schrieb:
> @ Hannes Lux (hannes)>>>Wenn schon (mindestens) zwei Hersteller solche Drehgeber anbieten, dann>>muss es doch auch Kunden geben, die so "selten dämlich" (TM Falk) sind,>>solche Konstrukte in Auftrag zu geben.>> Sicher, was aber nicht automatisch bedeutet, dass das sonderlich klug> ist.
Da gebe ich Dir schon mal unbestritten recht. Aber...
Nicht alles, was ich nicht (auf Anhieb) verstehe, ist von vornherein
Schrott.
> Wie es scheint, ist der Drehgeber auch heutzutage noch sehr mit> mystischen Vorstellungen behaftet.
Bei mir nicht.
>>>Diese "selten dämliche" Konstruktion ist zwar nicht gerade vorteilhaft,>>lässt sich aber auch zuverlässig und sauber auswerten,>> Nöö, das ist mehr oder minder Glückssache,
Nöö, ist es nicht. Es sei denn, Du meinst mit Glücksache das Vertauschen
der Spuren, dann hättest Du natürlich recht. Aber dann ist auch der
(richtig gepolte) Anschluss der Versorgungsspannung Glücksache... ;-)
> jenachdem wie "klapprig" der> individuelle Drehgeber ist.
Wenn er wirklich "klapperig" (auf beiden Spuren) ist, dann gehört er in
den Schrott.
> Warum das so ist, steht im Artikel> Drehgeber.
In diesem Artikel steht eine Menge theoretisches Blabla, aber nichts
Konkretes praktisch Verwertbares (zumindest vor einiger Zeit, aktuell
nachgesehen habe ich nicht).
>>>Rastung auf jeder Spur eine Flanke. Um diese Drehgeber auszuwerten,>>prüft man ja eine Spur auf Flanke und die andere Spur auf Zustand.>> Genau DAS ist Billigmurks,
Du musst es ja wissen...
> der aber leider selbst bei Markenherstellern> zu finden ist. Kann jeder leicht prüfen. Einfach einen Drehknopf fest> anfassten und immer auf einem Rastpunkt leicht hin- und herdrehen.> Mechanisch dreht man immer vor und zurück, ein Menu darf dabei nicht> kontinuierlich weiterblättern bzw. wenn es eine Zahleneinstellung ist,> muss sie vor und zurück springen. Tut sie das nciht, ist die Auswertung> Schrott.
Diesem Test hält meine Auswertung locker stand.
Anscheinend hast Du meinen Text nicht richtig gelesen und bist auf
"Flanke" angesprungen. Mit "Auswertung der Flanke" meinte ich natürlich
keinen externen Interrupt, sondern einen Vergleich zwischen Neuwert und
Altwert bei zyklischer Abfrage (per Timer-Interrupt).
>> MfG> Falk
...
Falk Brunner schrieb:
> anfassten und immer auf einem Rastpunkt leicht hin- und herdrehen.> Mechanisch dreht man immer vor und zurück, ein Menu darf dabei nicht> kontinuierlich weiterblättern bzw. wenn es eine Zahleneinstellung ist,> muss sie vor und zurück springen.
Noch nicht mal das.
Eine saubere Auswertung erkennt, dass nicht beide Flanken in einer
korrekten Reihenfolge gekommen sind und tut nichts.
Karl heinz Buchegger schrieb:
> Falk Brunner schrieb:>>> anfassten und immer auf einem Rastpunkt leicht hin- und herdrehen.>> Mechanisch dreht man immer vor und zurück, ein Menu darf dabei nicht>> kontinuierlich weiterblättern bzw. wenn es eine Zahleneinstellung ist,>> muss sie vor und zurück springen.>> Noch nicht mal das.> Eine saubere Auswertung erkennt, dass nicht beide Flanken in einer> korrekten Reihenfolge gekommen sind und tut nichts.
Auch das funktioniert ohne Probleme, solange man den Drehgeber nicht zu
schnell dreht, was bei Benutzung als manuelles Eingabegerät aber egal
ist.
Von den 16 möglichen Kombinationen aus Alt und Neu werden ja 12
ignoriert (0 in der LUT) und nur 4 genutzt (2 ma sorum und 2 mal rosum
(TM Paul)).
...
@Hannes Lux
Es funktioniert!
Ich habe den Code von Karl Heinz Buchegger probiert, aber auch kein
brauchbares Ergebnis erhalten. Ich weiss nicht wieso.
Dann habe ich die Funktion mit Hilfe der LUT getestet und damit
funktioniert es jetzt schon viel besser. :-)
Nur ganz selten wenn ich den Knopf ganz langsam zwischen zwei
Rastpunkten drehe zählt er manchmal eine Stellung falsch (+1 oder -1).
Aber kein Vergleich zu meinen vorherigen Versuchen, da ging ja nichts
zuverlässig. Das Problem was ich noch habe ist, dass Phase A und Phase B
vertauscht sind. D.h. Phase A am Pin des Encoders ist Phase B im
Programm. Also müsste die LUT noch angepasst werden.
Hier der Code in C, Verbesserungsvorschläge nehme ich gerne entgegen.
ATmega16 @ 8Mhz
Initialisierung Timer0
Die LUTs für die beiden möglichen Polungen sehen in ASM so aus:
1
/*
2
dgtab: ;Tabelle mit Drehgeber-Werten (alt-alt-neu-neu als Index)
3
;aa nn, aa nn
4
.db 0, 1 ;00 00, 00 01
5
.db 0, 0 ;00 10, 00 11
6
.db -1, 0 ;01 00, 01 01
7
.db 0, 0 ;01 10, 01 11
8
.db 0, 0 ;10 00, 10 01
9
.db 0,-1 ;10 10, 10 11
10
.db 0, 0 ;11 00, 11 01
11
.db 1, 0 ;11 10, 11 11
12
*/
13
14
dgtab: ;Tabelle mit Drehgeber-Werten (alt-alt-neu-neu als Index)
15
;aa nn, aa nn
16
.db 0, 0 ;00 00, 00 01
17
.db 1, 0 ;00 10, 00 11
18
.db 0, 0 ;01 00, 01 01
19
.db 0,-1 ;01 10, 01 11
20
.db -1, 0 ;10 00, 10 01
21
.db 0, 0 ;10 10, 10 11
22
.db 0, 1 ;11 00, 11 01
23
.db 0, 0 ;11 10, 11 11
Eine ist auskommentiert. Es stehen immer 2 Bytes in einer Zeile, weil
die ASM-Direktive ".db" das bei Flash-Daten (die ja wordadressiert sind)
so verlangt (geradzahlig).
...
@Hannes Lux (hannes)
>Wenn er wirklich "klapperig" (auf beiden Spuren) ist, dann gehört er in>den Schrott.
Nöö, auch wenn er keinen Wackelkontakt hat, ist das Ding nicht
wasserdicht. Und nur weil das Ding bei dir in Einzelstückzahlen auf dem
Steckbrett läuft, heisst das nicht automatisch, dass die Lösung
wasserdicht ist.
>> Warum das so ist, steht im Artikel>> Drehgeber.>In diesem Artikel steht eine Menge theoretisches Blabla, aber nichts>Konkretes praktisch Verwertbares (zumindest vor einiger Zeit, aktuell>nachgesehen habe ich nicht).
Klasse, so kann man sich natürlich auch rausreden. Und nur weil du was
nicht (auf Anhieb) verstehst, ist es noch lange kein theoretisches
BlaBla . . . ;-)
>Anscheinend hast Du meinen Text nicht richtig gelesen und bist auf>"Flanke" angesprungen. Mit "Auswertung der Flanke" meinte ich natürlich>keinen externen Interrupt, sondern einen Vergleich zwischen Neuwert und>Altwert bei zyklischer Abfrage (per Timer-Interrupt).
Ok, hab deinen Post nochmal genau gelesen.
Hast Recht ;-)
Das passt in dem Fall. Vielleicht sollte man das in den Artikel
Drehgeber aufnehmen. Denn die ALPS & Co "Murksdrehgeber" sind ja nun mal
recht verbreitet. Und der Verlust/Halbierung der Auflösung ist in diesem
Fall sogar positiv, denn man erhält nur einen Puls pro Rastung, was im
allgemeinen erwünscht ist.
MfG
Falk
So ich habe die LUT und die ISR noch etwas angepasst. Aber manchmal
"springt" der Encoder vor und zurück wenn man in eine Richtung bewegt.
Also macht er quasi 1,-1 vielleicht auch 1,0,-1 beim drehen in ein und
die selbe Richtung.
Wo ist hier noch der Fehler? **ratlos**
1
#define PHASE_A ((PIND & 1<<PD2)>>PD2)
2
#define PHASE_B ((PIND & 1<<PD7)>>PD7)
3
#define KEY ((PIND & 1<<PD6)>>PD6)
4
#define BOUNCE 20 //BOUNCE*Abtastrate der ISR=Prellzeit
Nimm mal als LUT:
0, 0, 1, 0, 0, 0, 0,-1,-1, 0, 0, 0, 0, 1, 0, 0
bzw. das Gegenstück:
0, 0,-1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0,-1, 0, 0
oder bei anderer Polung:
0, 1, 0, 0,-1, 0, 0, 0, 0, 0, 0,-1, 0, 0, 1, 0
bzw. das Gegenstück:
0,-1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0,-1, 0
In Deinem oberen Beispiel hast Du Dich vermutlich beim Index verhaspelt,
Du musst von 0 bis 15 zählen, Deinen Kommentaren nach hast Du von 1 bis
16 gezählt... ;-)
Beitrag "Re: Hilfe zu Drehencoder-Auswertung nach Wiki"
Dein unteres Beispiel entspricht ja meinem unteren Vorschlag.
Beitrag "Re: Hilfe zu Drehencoder-Auswertung nach Wiki"
Wenn der Murks ist, dann müsstest Du mal meinen oberen Vorschlag
umsetzen.
...
ähm oben, unten... ich bin jetzt etwas durcheinander.
Ja ich habe den Code und die LUT geändert.
enc_delta += decoder_lut[index*+1*];
Die Erhöhung des Index um 1 war ja falsch, weil ich ja dadurch nie das
Element 0 aus der LUT erhalten kann (und 16te Element existiert ja nicht
wie du schon erwähnt hast).
Also ich gehe jetzt mal davon aus, dass ich die vier LUTs in der letzten
Codevariante ausprobieren soll.
Beitrag "Re: Hilfe zu Drehencoder-Auswertung nach Wiki"
Also ich habe jetzt alle LUTs getestet, keine Besserung. :-(
P.S. ich meine natürlich das Element mit dem Index 16
>(und 16te Element existiert ja nicht wie du schon erwähnt hast)
Also wenn ich mir den Assembler-Code ansehe, entspricht dies genau
meiner Vorgehensweise im C-Code. :-/
Vielleicht liegt's doch am Drehencoder selbst. ???
Hallo Hannes,
ich habe deinen Assemblercode nach Bascom portiert. Das Ergebnis
arbeitet sehr sauber. Nur mit viel Mühe geht da mal eine Rastung
verloren. Das hat mir so gut gefallen, das ich den Bascom-Quelltext dann
hier (http://rn-wissen.de/index.php/Drehencoder) veröffentlicht habe.
Danke.
Auch der C-Code von Peter Dannegger hat mit diesen hier per Datenblatt
vorgestellten "wackligen" Drehgebern, die in der Raststellung
Signalwechsel haben, keine Probleme. Diese "Wackler" aber auch das
sogenannte Pendeln wird durch die zyklich aufzurufende Anpassungsroutine
@ screwdriver (Gast)
>Signalwechsel haben, keine Probleme. Diese "Wackler" aber auch das>sogenannte Pendeln wird durch die zyklich aufzurufende Anpassungsroutine
Nöö, das ist in deinem Fall Zufall. Denn die Umschaltung zwischen zwei
Zuständen kann nämlich auch genau auf der Flanke liegen, auch bei read2
oder read4.
MfG
Falk
Falk Brunner schrieb:
> Denn die Umschaltung zwischen zweiZuständen kann nämlich auch genau auf> der Flanke liegen, auch bei read2 oder read4.
Das spielt keine Rolle.
Durch einen Pegelwechsel eines Encoder-Signals wechselt in PeDas
Timer-ISR wohlwahr die Variable enc_delta zwischen +1 und -1. Und eine
Auswertung des Encoders mit der Routine encode_read1 im Hauptprogramm
würde dann auch dieses Pendelverhalten in der Variable val zeigen.
Die o.a. Encoder sind jedoch mit der Routine encode_read2 auszuwerten.
Hier muß die Variable enc_delta jedoch größer 1 oder kleiner -1 sein,
damit sich die Variable val im Hauptprogramm ändert.
Hi,
ich bin's nochmal. Mit einer neuen Problemstellung:
Ich verwende den integrierten Taster des Drehencoders. Das Entprellen
wie in meinem obigen funktioniert auch. Aber ich möchte jetzt
unterscheiden zwischen kurz und lang gedrückt. Leider komme ich da nicht
weiter. Den Code von PeDa
http://www.mikrocontroller.net/articles/Entprellung#Komfortroutine_.28C_f.C3.BCr_AVR.29
kapiere ich irgendwie wie nicht und ich bräuchte den ja auch nur für
einen Taster und nicht einen ganzen port. :-(
Kann mir jemand helfen wie ich vorgehen sollte. Es muss kein fertiger
Code sein, ich möchte ja auch noch was lernen. Danke euch im Vorraus.
Wurzel schrieb:
> Hi,>> ich bin's nochmal. Mit einer neuen Problemstellung:>> Ich verwende den integrierten Taster des Drehencoders. Das Entprellen> wie in meinem obigen funktioniert auch. Aber ich möchte jetzt> unterscheiden zwischen kurz und lang gedrückt. Leider komme ich da nicht> weiter.
So, Unterschied kurz/lang. Den erfährt man also erst beim Loslassen,
denn aus kurz könnte ja noch lang werden... ;-)
Man erhöht also bei gedrückter einen Zähler, sorgt dafür, dass er nicht
überläuft und wertet ihn beim Loslassen aus. Nach der Auswertung wird er
natürlich wieder auf 0 gesetzt.
Betrachtet man jetzt die Zeiten und Wertebereiche so stellt man
Folgendes fest:
Der Drehgeber wird im Zeitabstand von 1 ms abgefragt. Fragt man im
selben Raster auch den Taster ab, so kann man mit einem Byte als Zähler
maximal 255 ms als Tastendruckzeit darstellen. Das ist zuwenig. Man
könnte nun 16-bittig zählen, dann müsste man aber auch 16-bittig
auswerten. Das ist zwar kein Akt, aber nicht nötig. Denn man kann auch
einen Vorteiler laufen lassen, der z.B. nur jede 16. Runde zuschlägt.
Dann reicht das Byte des Zeitzählers bis zu 4 Sekunden.
Also etwa:
1
inc vorteiler
2
andi vorteiler,15
3
brne weg_hier
4
5
;Tastenabfrage...
weg_hier:
Damit wird die Tastenabfrage dann nur noch alle 16 ms aufgerufen.
Beim Abfragen des Tasters gibt es zwei Zustände, Taster betätigt (L) und
Taster unbetätigt (H). Die Tastenabfrage läuft ja auch im Interrupt. Um
dem Hauptprogramm einen erfolgreichen Tastendruck mitzuteilen, werden
noch zwei Bit als Merker gebraucht, das eine signalisiert den langen
Tastendruck, das andere den kurzen. Gelöscht werden die Bits dann von
der Mainloop, wenn zu zu dem Programmteil verzweigt, der den Tastendruck
(den zugehörigen Job) abarbeitet.
1
;Tastenabfrage ;Teil der Timer-ISR, Aufruf alle 16 ms
rjmp Tastenabfrage_end ;ja, weg hier, denn Zählen u. Begrenzen ist
11
;ja fertig...
12
cpi tastenzeit,taste_lang ;Zeit für lang überschritten?
13
brlo Tastenabfrage2 ;nein, weiter...
14
sbr merker,1<<t_lang ;ja, Merker für langen Tastendruck setzen
15
rjmp Tastenabfrage3 ;und weiter...
16
Tastenabfrage2:
17
cpi tastenzeit,taste_kurz ;Zeit für kurz (Entprellzeit) überschritten?
18
brlo Tastenabfrage2 ;nein, weiter...
19
sbr merker,1<<t_kurz ;ja, Merker für kurzen Tastendruck setzen
20
Tastenabfrage3:
21
clr tastenzeit ;Tastenzeit löschen
22
Tastenabfrage_end:
Der Zähler "tastenzeit" wird also bei gedrückter Taste erhöht, wobei ein
Überlauf verhindert wird. Bei ungedrückter Taste wird er zwar auch
erhöht, fällt aber durch die Lang- und Entprell-Prüfung und wird wieder
gelöscht. War zuvor die Taste lang genug gedrückt, so wird vor dem
Löschen noch der zugehörige Merker aktiviert. Dieser wird vom
Hauptprogramm geprüft und gelöscht.
> Den Code von PeDa> http://www.mikrocontroller.net/articles/Entprellung#Komfortroutine_.28C_f.C3.BCr_AVR.29> kapiere ich irgendwie wie nicht und ich bräuchte den ja auch nur für> einen Taster und nicht einen ganzen port. :-(
Da der in C ist, ... ;-)
>> Kann mir jemand helfen wie ich vorgehen sollte.
Ich hoffe, es ist verständlich genug erklärt.
> Es muss kein fertiger> Code sein,
Naja, ist einfach so drauflos geschrieben, ist also nicht geprüft, es
können durchaus Tippfehler drin sein.
> ich möchte ja auch noch was lernen. Danke euch im Vorraus.
...
Ich weiss ja das wir nicht die selbe Sprache sprechen, daher umschreibe
ich die Tastenabfrage. ;-)
Die ISR wird alle 1ms aufgerufen. Wenn Taster gedrückt (L) dann wird
count um 1 erhöht. Wenn count den Wert zum Entprellen erreicht hat, wird
der Merker für kurze Tastenbetätigung gesetzt, count wieder null, und
gleichzeitig der zweite Zähler count2 um 1 erhöht. Erreicht count2 den
Wert für langes Drücken, wird auch hier der Merker gesetzt und count
zurückgesetzt. Falls zwischendurch der Taster losgelassen wird, werden
beide Zähler auf null gesetzt, d.h. um sicher zu gehen, dass die Zeiten
eingehalten werden.
Die Abfrage der Merker erfolgt in der main() über die zwei Funktionen
encoder_keypressed() und encoder_keypressedlong(). innerhalb dieser
Funktionen werden die Merker wieder zurück gesetzt.
Soweit mein Code, aber irgendwie funktioniert dieser noch nicht richtig.
:-/
Wurzel schrieb:
> Also ich denke so wie du beschrieben hast, bin ich vorgegangen.
Den C-Text kommentiere ich nicht...
> Ich weiss ja das wir nicht die selbe Sprache sprechen,
Naja, ich spreche die Sprache, die der AVR auch spricht (ASM ist eine 1
zu 1 Umsetzung in Maschinencode) und die der Architektur des AVRs
entspricht.
> daher umschreibe> ich die Tastenabfrage. ;-)
Das ist gut...
>> Die ISR wird alle 1ms aufgerufen. Wenn Taster gedrückt (L) dann wird> count um 1 erhöht.
Gut.
> Wenn count den Wert zum Entprellen erreicht hat, wird> der Merker für kurze Tastenbetätigung gesetzt, count wieder null, und> gleichzeitig der zweite Zähler count2 um 1 erhöht.
Wenn man sich dabei bewusst ist, dass dies im Interrupt passiert, also
(zeitlich) zwischendurch immer das Hauptprogramm läuft, dann erkennt
man, dass das so nix wird. Denn das Hauptprogramm fragt ja die Merker ab
und erkennt den Merker für kurzen Tastendruck, bevor die Taste wieder
losgelassen wird. Der Merker für den kurzen Tastendruck darf also erst
gesetzt werden, nachdem der Taster losgelassen wurde (also nachdem
sicher ist, dass es kein langer Tastendruck mehr werden kann).
> Erreicht count2 den> Wert für langes Drücken, wird auch hier der Merker gesetzt und count> zurückgesetzt.
Der Count-Merker wurde indessen von Main erkannt, abgearbeitet und
zurückgesetzt.
> Falls zwischendurch der Taster losgelassen wird, werden> beide Zähler auf null gesetzt, d.h. um sicher zu gehen, dass die Zeiten> eingehalten werden.
Zu umständlich, bei strikter Trennung in Vorteiler und Tastenzeit wird
alles viel einfacher und als angenehmen Nebeneffekt funktioniert es
sogar.
> Die Abfrage der Merker erfolgt in der main() über die zwei Funktionen
Ob man das in Funktionen auslagern muss, weiß ich nicht, darüber möchte
ich auch nicht diskutieren. In ASM würde ich die Merker in der Mainloop
abfragen
1
sbrc merker,t_lang ;trat ein langer Tastendruck auf? - nein...
2
rcall langer_tastendruck ;ja, abarbeiten...
3
sbrc merker,t_kurz ;trat ein kurzer Tastendruck auf? - nein...
4
rcall kurzer_tastendruck ;ja, abarbeiten...
(Je nach Struktur des Programms verzweige ich auch mal über "rjmp" und
springe statt mit "ret" mit "rjmp mainloop" zurück. Aber das ist eine
andere Baustelle und muss den C-Programmiierer nicht interessieren.)
... und im Unterprogramm zurücksetzen
1
cbr merker,t_lang ;Jobauftrag löschen, ist ja in Arbeit
und
1
cbr merker,t_kurz ;Jobauftrag löschen, ist ja in Arbeit
> encoder_keypressed() und encoder_keypressedlong(). innerhalb dieser> Funktionen werden die Merker wieder zurück gesetzt.
Nur doof, dass der kurze Tastendruck bereits voreilig abgearbeitet
wurde, ehe der lange Tastendruck überhaupt erkannt werden konnte.
>> Soweit mein Code, aber irgendwie funktioniert dieser noch nicht richtig.> :-/
Das ist auch kein Wunder, Dein Algorithmus entspricht ja nicht dem
meinen und ist irgendwie auch nicht zu Ende gedacht. Sieh es aber bitte
nicht als persönliche Beleidigung, nur irgendwie muss ich es ja (etwas
direkt) formulieren, damit es verständlich wird.
Wenn mit Interrupts gearbeitet wird, sollte man Programme aus einer
anderen Sicht betrachten. Die Tasten/Drehgeberabfrage erfolgt ja nicht
in einer Schleife, in der verweilt wird, bis das Ergebnis vorliegt,
sondern wird durch zyklusches "Vorbeischauen" und Erledigen nur eines
Schrittes (entsprechend eines Schleifendurchlaufes) abgearbeitet.
Zwischendurch ist immer wieder die Mainloop aktiv, bzw. einer ihrer
Jobs. Gut, es wäre vermessen, dies bereits als "Multitasking" zu
bezeichnen, aber es ist schon ein kleiner Schritt in diese Richtung...
...
Hallo Hannes,
ich nehme deine Antwort nicht als beleidigend auf. ;-) Ich bin mir ja im
klaren, dass mein Code fehlerhaft und wahrscheinlich Murks ist.
Ich möchte daher nochmal von vorne beginnen und meine Erkenntnisse aus
deinen Beiträgen in einen Pseudocode zusammen fassen.
1
vorteiler = 8Bit
2
tastzeit = 8Bit
3
4
ISR alle 1ms:
5
{
6
vorteiler um eins erhöhen;
7
wenn vorteiler gleich 16:
8
dann tastenzeit um eins erhöhen
9
und danach prüfen ob tastzeit = 255:
10
falls ja: von tastzeit eins abziehen;
11
12
...
13
}
Bis hier hin bin ich noch gekommen, aber danach blicke ich bei deinem
Assemblercode nicht mehr ganz durch. Wo kommt jetzt der Zustand
(gedrückt/nicht gedrückt) des Tasters ins Spiel?
Sorry aber ich muss mir das Stück für Stück herleiten.
Wurzel schrieb:
> Hallo Hannes,>> ich nehme deine Antwort nicht als beleidigend auf. ;-) Ich bin mir ja im> klaren, dass mein Code fehlerhaft und wahrscheinlich Murks ist.>> Ich möchte daher nochmal von vorne beginnen und meine Erkenntnisse aus> deinen Beiträgen in einen Pseudocode zusammen fassen.>
1
> vorteiler = 8Bit
2
> tastzeit = 8Bit
3
>
4
> ISR alle 1ms:
5
> {
6
> vorteiler um eins erhöhen;
7
> wenn vorteiler gleich 16:
8
> dann tastenzeit um eins erhöhen
9
> und danach prüfen ob tastzeit = 255:
10
> falls ja: von tastzeit eins abziehen;
11
>
12
> ...
13
> }
14
>
> Bis hier hin bin ich noch gekommen, aber danach blicke ich bei deinem> Assemblercode nicht mehr ganz durch.
Du hast recht, da ist auch noch (mindestens) ein logischer Fehler drin.
Ich hätte nur dann hochzählen dürfen, wenn der Taster betätigt ist. Das
passiert nunmal, wenn man mal schnell etwas Code aus dem Hut schreibt
und nicht vorher testet. - Sorry...
> Wo kommt jetzt der Zustand> (gedrückt/nicht gedrückt) des Tasters ins Spiel?
Durch die Abfrage des Tastenpins:
sbis tastenpin,tastenbit ;ist Taste betätigt? - nein, auswerten...
rjmp Tastenabfrage_end ;ja, weg hier, denn Zählen u. Begrenzen ist
;ja fertig...
SBIS überspringt den RJMP dann, wenn der Tastenpin H-Pegel hat, also der
Taster nicht betätigt ist. Ist er betätigt, wirkt der Sprung (RJMP) zum
Ende der Routine.
> Sorry aber ich muss mir das Stück für Stück herleiten.
In BASIC würde es in etwa so aussehen:
1
vorteiler = vorteiler + 1
2
if vorteiler >= 16 then 'nur jedes 16. mal
3
vorteiler = 0
4
if taste = 0 then 'ist Taste betätigt?
5
tastenzeit = tastenzeit + 1 'ja, hochzählen und
6
if tastenzeit = 255 then tastenzeit = 254 'begrenzen
7
else 'Taste ist unbetätigt
8
if tastenzeit > lang then 'war Taste lange betätigt?
9
merker = set merker_lang 'ja, Merker "lang" setzen
10
elseif tastenzeit > kurz then 'nein, nicht lang, war dann kurz?
11
merker = set merker_kurz 'ja, Merker "kurz" setzen
Ich habe den Code umgeschrieben nach C.
Allerdings klappt's noch nicht. Nutzt du nur einen merker dem du
unterschiedliche werte (kurz und lang) zuweist?
Der merker wird ja bei der Abfrage vom Hauptprogramm wieder gelöscht,
oder?
Rainier schrieb:
Wer nun? Rainer, Rambo oder Wurzel?
> Ich habe den Code umgeschrieben nach C.> Allerdings klappt's noch nicht.
Dann hast Du etwas verändert. Ich vermute, Du hast das ELSEIF falsch
interpretiert und setzt bei langem Tastendruck beide Merker...
> Nutzt du nur einen merker dem du> unterschiedliche werte (kurz und lang) zuweist?
Ich nutze derzeit keinen Merker, da ich die Kurz/Lang-Unterscheidung
bisher noch nicht brauchte. ;-)
Natürlich sollen für lang und kurz unterschiedliche Merker
(Bitvariablen) genutzt werden, ansonsten könnte man sie ja nicht
unterscheiden. In ASM nutze ich dazu ein Register, deren 8 Bits
unterschiedliche Funktion haben. Jeder dieser Bits bekommt einen eigenen
Namen und wird separat gesetzt und gelöscht. In C wird das nicht viel
anders sein. Natürlich muss man sie so deklarieren, dass sowohl
Hauptprogramm als auch ISR darauf zugreifen können.
>> Der merker wird ja bei der Abfrage vom Hauptprogramm wieder gelöscht,> oder?
Ja sicher doch. Die ISR setzt bei "Auftreten des Ereignisses" den zum
Ereignis gehörenden Merker, das Hauptprogramm prüft und löscht dann die
Merker bei der Ausführung des zugehörigen Jobs. Man kann die Merker in
diesem Falle auch als "Jobflag" sehen, also ein Flag (Semaphore, Merker,
Bitvariable, Schalter, RS-Flipflop), das anzeigt, das ein bestimmter Job
zu erledigen ist (das Gegenstück dazu sind Merker, die einen Status
anzeigen, der darüber entscheidet, wie ein Job erledigt werden soll).
...
Sry, ich wurschtel hier an mehreren Rechnern rum. -> Wurzel
>Dann hast Du etwas verändert. Ich vermute, Du hast das ELSEIF falsch>interpretiert und setzt bei langem Tastendruck beide Merker...
Nein, mache ich nicht:
1
prescaler++;
2
if(prescaler>=16)
3
{
4
prescaler=0;
5
if(!KEY)
6
{
7
button_down++;
8
if(button_down==255)
9
button_down=254;
10
}
11
else
12
{
13
if(button_down>=LONG)
14
key_pressed_long=1;
15
elseif(button_down>=SHORT)
16
key_pressed=1;
17
18
button_down=0;
19
}
20
}
Ich verwende natürlich auch zwei Merker: key_pressed_long, key_pressed.
zurückgesetzt werden sie nachdem sie abgefragt wurden.
Hm, irgendwas ist noch fehlerhaft.
Wurzel schrieb:
> prescaler und button_down sind static variablen in der ISR deklariert.> Die Marker key_pressed_long und key_pressed sind global definiert.
Volatile?
Achnee, ich nehm's zurück, ich habe ja keine Ahnung von C...
...
@hannes
Dein Code arbeitet wirklich gut. Er hat aber bekanntermaßen den
Nachteil, das die Signalzuordnung nicht beliebig ist.
Wie wärs denn, wenn du nicht nur zwei Signalzustände auswertest, sondern
drei. Bei drei Zuständen muß ja mindestens einmal der stabile
dabeigewesen sein. Somit wäre deine Sprungtabelle um 2bit länger, also
4mal so lang, aber was solls, die Abarbeitungszeit bleibt ja gleich. Das
Wackeln und Zappeln hätte dann auch bei falscher Signalzuordnung ein
Ende! Dann sollte doch die Auswertung unabhängig von der Signalzuordnung
sein, oder?
mfg
screwdriver
Hannes Lux schrieb:> lsl dgalt ;altes Drehgeber-Bitmuster> lsl dgalt ;nach oben schieben
Ich habe mit sehr gutem Erfolg deine Routine verwendet.
Ich verwende jedoch 2x lsr, da dann "urold" von selbst im Nirvana
verschwindet. Die 2. Tabelle habe ich noch selbst abgeleitet.
Sie verwendet ja Übergänge zwischen den Rastpunkten und funktioniert
deshalb so perfekt.
Siehe attachments
LG Rudi
Rudi D. schrieb:> Ich verwende jedoch 2x lsr, da dann "urold" von selbst im Nirvana> verschwindet.
Da bei mir der Drehgeber an den Bits 0 und 1 liegt, wäre Rechtsschieben
recht sinnfrei. Bei Dir liegt der Drehgeber an Bit 2 und 3, da ist
Rechtsschieben natürlich besser.
> Die 2. Tabelle habe ich noch selbst abgeleitet.> Sie verwendet ja Übergänge zwischen den Rastpunkten und funktioniert> deshalb so perfekt.
Es gibt viele Varianten, wie man die Drehgeberbits anordnen kann. Jede
Variante erfordert natürlich eine eigene Tabelle. Für
Copy&Paste-Programmierer ist das natürlich nix, aber wenn man die
Funktion verstanden hat, dann ist das ja kein Problem. Anschlussbelegung
und Tabelle ist für mich kein Dogma, ich habe da auch verschiedene
Varianten im Einsatz. Auch Varianten, die mit nur einem Shift auskommen
(Bit 0 und 2 oder 1 und 3).
Es gibt da auch eine Variante, bei der zwei Drehgeber angeschlossen sind
(Bit 0 und 1, sowie 4 und 5). Jedes Nibble enthält die Bits "seines"
Drehgebers. Die Überträge beim Schieben werden vor dem ORen der neuen
Bits ausgeANDet.
>> Siehe attachments>> LG Rudi
Noch 'n Tipp: Wenn Du gute preiswerte Alps-Drehgeber suchst, dann schau
mal hier vorbei:
http://stores.ebay.de/Logo-s-Elektronik-Kiste_Encoder-inkremental_W0QQfsubZ366805719
Dagegen ist der Pollin-Drehgeber Wucher... ;-)
...
Hannes Lux schrieb:> Auch Varianten, die mit nur einem Shift auskommen> (Bit 0 und 2 oder 1 und 3).
Verstehe ich in der Eile nicht.
Danke für die Alps Quelle.
Da ist die Anwendung mit 2x t2313.
LG Rudi
Rudi D. schrieb:> Verstehe ich in der Eile nicht.
Anschluss von Spur A an Bit 0 und Spur B an Bit 2:
Bit 0: Spur A neu
Bit 1: Spur A alt
Bit 2: Spur B neu
Bit 3: Spur B alt
...
Hannes Lux schrieb:> Rudi D. schrieb:>>> Verstehe ich in der Eile nicht.>> Anschluss von Spur A an Bit 0 und Spur B an Bit 2:>> Bit 0: Spur A neu> Bit 1: Spur A alt> Bit 2: Spur B neu> Bit 3: Spur B alt>> ...
Danke, bis zum nächsten mal
LD Rudi