Forum: Mikrocontroller und Digitale Elektronik Dynamische "Beschleunigung" bei Encoder-Eingabe


von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Ich habe einen Dreh-Encoder, mit dem die Frequenz eines DDS-Generators
eingestellt werden soll.  Nun soll man diese Frequenz einerseits auf
1 Hz genau eingeben können, andererseits ist das Teil so im Bereich
100 kHz bis 30 MHz benutzbar, man sollte also beim schnellen Drehen
am Rädchen schon auch maximal in 1-MHz-Schritten hüpfen können.

Meine ersten Versuche, sowas basierend auf Peters Encoder-Routinen
aufzusetzen, sehen erstmal nicht so glücklich aus.  Hat jemand
eventuell sowas schon einmal gemacht und wäre gewillt, mir entweder
ein paar Zeilen Code dafür zu schenken oder kurz den gewählten
Algorithmus zu erläutern?  Wenn sich's vermeiden lässt, würde ich
dafür ungern das Fahrrad nochmal erfinden.

von Frank N. (arm-fan)


Lesenswert?

Hallo Jörg!

Hab ich schonmal gemacht.
Ist aber schon ein paar Tage her und hab ich grad nicht zur Hand.
Schlummert in der Quellcodeverwaltung auf Arbeit.

Aber du kannst wie folgt vorgehen:

Du beachtest sowohl die Zählimpulse als auch die Drehgeschwindigkeit.
Also Impulse/Zeit.

Für kleine Drehgeschwindigkeiten des Enkoders gibst du für eine
präzise Sollwersteuerung die Impulse 1:1 weiter.

Ab einer bestimmten Schwelle gehst du zur Geschwindigkeitssteuerung
über. Die gewünschte Progression kannst du dann über eine Formel
oder lookup table einbringen.

Hoffe, das war einigermaßen verständlich rekapituliert... ;-)

von brain (Gast)


Lesenswert?

Hi.
Ist nicht 100% was du suchst, aber du könntest auch einen Drehencoder 
mit eingebautem Taster nutzen. Wenn man bei gedrücktem Taster dreht 
gehts zb in 10er Schritten, sonst in einern...

Gruß

von Frank N. (arm-fan)


Lesenswert?

>Wenn man bei gedrücktem Taster dreht
>gehts zb in 10er Schritten, sonst in einern...

Das reicht nicht bei so einem großen "Dynamikumfang" über
mehrere 10er Potenzen hinweg.

Eine progressiv arbeitende Steuerung ist da schon eine gute
Lösung, aber eben nicht einfach zu implementieren wenn sie
intuitiv bedienbar sein soll.

Ich denke mal nicht umsonst sieht man bei vielen Fertiggeräten
wie Frequenzgeneratoren oder "digitalen" Labornetzteilen eine
Sollwertvorgabe über 10er Tastatur oder Enkoder PLUS Tasten,
mit denen man die zu verändernde Vor- oder Nachkommastelle
auswählt.

von Andreas K. (derandi)


Lesenswert?

Encoder mit Taste wär natürlich am einfachsten, davon abgesehen: Je 
länger man in eine Richtung dreht, desto größer werden die Schritte wär 
jetzt mein Alternativ-Vorschlag.
Kriegt man auch durch rumspielen schnell raus, man braucht keinerlei 
weitere Taster und lässt sich leicht Programmieren.
Könnte man eventuell auch mit der Drehgeschwindigkeit des Encoders 
kombinieren, wenn man schnell genug dreht gehts nochmal ums 50-fache 
schneller.

von Frank N. (arm-fan)


Lesenswert?

>Je länger man in eine Richtung dreht, desto größer werden die
>Schritte wär jetzt mein Alternativ-Vorschlag.

Wenn es sich nicht gerade um einen leicht laufenden Enkoder mit einem
Bedienrad mit Fingermulde handelt, das man ohne abzusetzen immer im
Kreis drehen kann, wird das auch schwierig.

Wenn du absetzen/umgreifen mußt, dann mußt du alles innerhalb einer
halben Umdrehung des Enkoders abhandeln.

von Nico E. (masta79)


Lesenswert?

Naja, vom Ansatz her würde ich es so machen:

1.) Timer mitlaufen lassen der einen Wert X (angefangen z.B. bei 6) 
runterzählt bis 0
2.) Bei Betätigung des Encoder um 10^X hochzählen und X wieder auf den 
Startwert setzen.

von Gruenschnabel (Gast)


Lesenswert?

Um diese Dynamik zu bekommen, muß man dem Drehgeber eine Schwungmasse 
verpassen. Dazu muß die Drehgeschwindigkeit ermittelt und auch gemittelt 
werden, sodaß die Stellschritte nicht sofort kräftig erhöht werden, 
sondern erst nach ca. 0,3 bis 0,5 Sekunden. Die Schwungmasse sorgt 
dafür, dass auch mehrere Umdrehungen des Drehgebers in einem Rutsch 
passieren und die Auswertung der Geschwindigkeit einen Sinn ergibt.
Optimal sind optische Drehgeber, die minimale Reibung aufweisen. Die 
Auflösung sollte nicht zu klein. Für Einzelschritte werden die Impulse 
z.B. durch 10 geteilt und für höhere Drehgeschwindigkeit vervielfacht.

Ganz wichtig ist, dem Drehgeber eine Hysterese zu verpassen, die 
verzögert beim Stillstand einsetzt. Damit verhindert man ein Springen um 
den letzten Schaltpunkt.
Meine Routinen würden Deinen Dynamikbereich nicht abdecken.

von Benedikt K. (benedikt)


Lesenswert?

Hier mal etwas Code den ich immer verwende:
Die Beschleunigung erfolgt zwar nur etwa um den Faktor 200, aber das 
kann man ja alles anpassen. Weiterhin ist der Code aus Sicht einiger 
Leute hier im Forum ein schlimmes Verbrechen. Ich habe dagegen die 
Erfahrung mit dem Pollin Panasonic Inkrementalgeber gemacht, dass der 
Coder sehr viel besser funktioniert als der von Danegger. Bei dessen 
Code hatte ich Sprünge, die ich mit diesem Code nicht habe. Dafür 
erfordert dieser Code aber eine richtige Belegung der beiden Phasen, 
damit der Interupt nicht um den Einrastpunkt herum erfolgt, sondern auf 
dem "Hügel". Weiterhin sollte man eine Entprellung aus 10k 10nF 
vorsehen.

Man benötigt dazu einen Timerinterrupt mit etwa 1kHz in dem die 
Beschleunigungswerte berechnet werden (eventuell könnte man auch hier 
den Inkrementalgeber samplen und auswerten, so wie in Daneggers Code, 
bei mir hat es mit dem Pollin Inkrementalgeber aber jedenfalls nicht 
funktioniert):
1
   if (inkrem_speed>1)
2
     inkrem_speed-=1+inkrem_speed/16;

und dann noch die eigentliche Routine, die je nach Typ auf 1 oder 2 
flankigem Interrupt eingestellt ist:
1
ISR(SIG_INTERRUPT0)
2
{
3
  if (inkrem_speed>200)    //Entprellung
4
    return;
5
6
  if (((INKREM1)&&(INKREM2))||((INKREM1==0)&&(INKREM2==0)))
7
    inkrement-=inkrem_speed;
8
  else
9
    inkrement+=inkrem_speed;
10
  inkrem_speed=255;
11
  GIFR=(1<<INTF0);
12
}

Das Hauptproblem von Daneggers Code dürfte daran liegen, dass er erstmal 
die volle Auflösung einliest und am Ende teilt, was für die 
Beschleunigung eher ungünstig ist, da sich das Prellen stark auswirkt.

PS: Bitte jetzt keine Diskussion zu diesem leidigen Thema dass man es so 
nicht machen sollte. Falls doch jemand anfängt, dann soll er erstmal 
eine "ordentliche Version" mit dynamischer Beschleunigung vorlegen die 
mit dem Pollin Inkrementalgeber funktioniert.

von Peter D. (peda)


Lesenswert?

Das sollte eigentlich ganz einfach sein.
Man darf dann nicht jeden einzelnen Schritt übernehmen, sondern muß sie 
über eine Zeit aufaddieren, z.B. 200ms.
Das kann man mit im Encoderinterrupt (1ms) machen und dann je nach Wert 
mit nem Faktor multiplizieren.


Peter

von Stephan H. (stephan-)


Lesenswert?

@Jörg,

der Hannes Lux hat das mit dem taster gemacht.
Bei Drehung mit gedrücktem Taster hat der die 10fache Auflösung.
Sollte sich Softwaretechnisch am einfachsten machen.
Der Port wird ja eh begragt. Ist halt nur ein Bit mehr.

von Benedikt K. (benedikt)


Lesenswert?

@ Peter
Hast du das zufällig schonmal ausprobiert?
Falls du irgendwann mal dazu kommst, wäre eine Erweiterung von deinen 
Routinen mit einer Beschleunigung echt nett. Ich hatte es schon mehrmals 
mit deinen Routinen Versucht, bin aber immer gescheitert und habe daher 
wieder meine alten Routinen verwendet. Ein Problem liegt u.a. daran, 
dass die Phasenverschiebung der beiden Signale bei dem Pollin Encoder 
nicht 90° sondern viel weniger beträgt, was dazu führt, dass die Signale 
stark asymmetrisch sind, weshalb man die Samplerate sehr hoch (>10kHz) 
drehen muss, damit das ganze sicher funktioniert.

von Peter D. (peda)


Lesenswert?

Benedikt K. wrote:
> Die Beschleunigung erfolgt zwar nur etwa um den Faktor 200, aber das
> kann man ja alles anpassen. Weiterhin ist der Code aus Sicht einiger
> Leute hier im Forum ein schlimmes Verbrechen.

Nö, sie haben eben diesen Code als sehr zuverlässig erfahren und haben 
daher keine Lust, Probleme mit anderen Codes zu lösen. Warum etwas 
lösen, wofür es schon eine Lösung gibt?

Ein "Verbrechen" ist es erst, wenn Du damit Geräte für teuer Geld 
verkaufst und dann der Kunde damit Probleme hat.


> Ich habe dagegen die
> Erfahrung mit dem Pollin Panasonic Inkrementalgeber gemacht, dass der
> Coder sehr viel besser funktioniert als der von Danegger. Bei dessen
> Code hatte ich Sprünge, die ich mit diesem Code nicht habe.

Generell ist eine schlechte Idee, Codebeispiele als Blackbox zu 
betrachten. Wenn Du Probleme hast, überlege, woran das liegen könnte.

Wenn Du Schritte verlierst, verkürze das Abtastintervall. Wenn Du zu 
selten im Main einliest, daß +/-127 Schritte nicht ausreichen, 
vergrößere die Variable auf 16 Bit.


Peter

von Peter D. (peda)


Lesenswert?

Benedikt K. wrote:

> Ein Problem liegt u.a. daran,
> dass die Phasenverschiebung der beiden Signale bei dem Pollin Encoder
> nicht 90° sondern viel weniger beträgt, was dazu führt, dass die Signale
> stark asymmetrisch sind, weshalb man die Samplerate sehr hoch (>10kHz)
> drehen muss, damit das ganze sicher funktioniert.

Du kannst auch den Pin-Change-Interrupt nehmen und auf beide Pins 
aktivieren.
Was sind denn das für Drehgeber (Piher, Alps)?


Peter

von Benedikt K. (benedikt)


Lesenswert?

Peter Dannegger wrote:

> Du kannst auch den Pin-Change-Interrupt nehmen und auf beide Pins
> aktivieren.

Ich dachte genau das sei verboten:
http://www.mikrocontroller.net/articles/Drehgeber#Auswertung_mit_Interrupt_durch_Pegelwechsel

> Was sind denn das für Drehgeber (Piher, Alps)?

http://www.pollin.de/shop/detail.php?pg=NQ==&a=Njg2OTU3OTk=

Das Datenblatt passt aber nicht ganz, u.a. ist die Anzahl an Schritten 
unterschiedlich.

von Hannes Lux (Gast)


Lesenswert?

> der Hannes Lux hat das mit dem taster gemacht.

Das ist für Jörgs Anwendung aber keine gute Idee.

Nun weiß ich auch nicht, welches Codebeispiel Du meinst, ich habe da 
(aus heutiger Sicht gesehen) auch viel Suboptimales veröffentlicht.

Inzwischen lese ich Drehgeber nur noch durch zyklische Abfrage (1ms) und 
4-Bit-LUT (2 alte und 2 neue Stati als Index) ein.

Hier könnte aber ein Zähler angesetzt werden, der herunter zählt, wenn 
der Drehgeber nicht betätigt wurde und bei Betätigung auf Startwert 
gesetzt wird (ob betätigt oder nicht, erkennt man am Inc-Wert, den man 
aus der LUT ausliest). Bei langsamem Drehen ist der Zähler jeweils 
abgelaufen, bei schnellem Drehen noch nicht. Somit kann der Zählerstand 
(evtl auch als Index für weitere LUT) als Maß für die Dynamisierung 
genutzt werden.

Dies nur so als Idee, ist mangels Bedarf nicht getestet.

...

von Paul Baumann (Gast)


Lesenswert?

Für Jemanden, der (wie ich) aus dem Hut nicht weiß, was ein "LUT" ist:

Eine Look-up-Tabelle.

MfG Paul

von Hannes Lux (Gast)


Lesenswert?

> Eine Look-up-Tabelle.

Ich habe den Begriff auch nur über das Forum kennen gelernt, steter 
Tropfen höhlt bekanntlich den Stein (oder: wenn man lange genug mit 
Sch.. beschmissen wird, fängt man eines Tages an zu stinken...).

;-)

...

von Peter D. (peda)


Lesenswert?

Benedikt K. wrote:
>> Du kannst auch den Pin-Change-Interrupt nehmen und auf beide Pins
>> aktivieren.
>
> Ich dachte genau das sei verboten:

Verbieten kann Dir niemand was.
Man sollte aber um die Probleme bescheid wissen, wenn man eine bestimmte 
Methode verwendet.

Theoretisch besteht beim PCI die Möglichkeit, sich mit Interrupts zu 
überlasten.
Praktisch ist das bei manuell betätigten Kontaktencodern nicht möglich, 
die maximale Prellfrequenz ist durch den AVR verkraftbar.
Die Möglichkeit besteht nur bei nicht rastenden optischen Encodern, z.B. 
durch hochfrequente Vibrationen.

Man könnte dann in den PCI einen Zähler einbauen, wenn der überläuft, 
sperrt er weitere Interrupts und ein Timer setzt diesen Zähler zyklisch 
zurück. Damit ist eine hohe Peakfrequenz möglich, aber die mittlere 
Interruptrate wird begrenzt.


Peter

von Peter D. (peda)


Lesenswert?

Paul Baumann wrote:
> Für Jemanden, der (wie ich) aus dem Hut nicht weiß, was ein "LUT" ist:

Das kennen wohl nur noch ältere Semester aus den PC-Anfängen.
Da waren pro Bildpunkt nur 4 Bits verfügbar, d.h. 16 Farben gleichzeitig 
darstellbar. Und damit wurde eine LUT auf der Grafikkarte adressiert, 
die daraus 3*6Bit Farben ausgewählt hat.

Fotos sahen daher auf dem PC etwa so aus:

http://www.poster.de/Warhol-Andy/Warhol-Andy-Marilyn-Monroe-1967-2108274.html


Peter

von Paul Baumann (Gast)


Lesenswert?

@Peter
Aha, -Danke für den Hinweis.

MfG Paul

von Läubi .. (laeubi) Benutzerseite


Lesenswert?

Du könntest es auch einfach so machen das sich der Multiplikator nur 
erhöht (mehr als X Schritte / 100ms = Multiplikator mal 2) und man den 
Multiplikator per Knopfdruck zurücksetzen kann... Oder halt das man per 
knopfdruck das Erhöhungsintervall druchschalten kanna (Hz, khz, Mhz)...

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Vielen Dank erstmal für die rege Anteilnahme. ;-)

Ja, rein vom Algorithmus ist mir schon ungefähr klar, wie's geht, nur
die tatsächliche Implementierung hatte erstmal nur Mist gemacht.
Ich nehme Peters Code, und ja, es sind die Pollin-Drehgeber (sowohl
Panasonic als auch ALPS kommen in Frage, da bin ich noch nicht ganz
sicher, welcher es wird).  Wie ich schon mal in einem anderen Thread
geschrieben habe, prellen die Teile erstaunlich wenig.  Der Panasonic
prellt im Bereich von einigen 100 ns, der ALPS maximal so < 5 µs.
Im Moment ist die Interruptfrequenz zur Abfrage 800 Hz.  Ich werd
dann mal sehen, wie ich Benedikts Vorschlag in die Poll-Routine
eingearbeitet bekomme.

Die Benutzung des Tasters habe ich auch schon in Erwägung gezogen,
aber nur eine Dekade umschalten hat wenig Sinn.  Wenn, dann würde
ich wohl nach Tastendruck von initial 1-Hz-Schritten auf 1-kHz-
Schritte schalten, und dann aber von da aus noch weiter dynamisch
beschleunigen.  Wenn 1 s lang nichts mehr geändert worden ist, kann
man dann ja wieder auf 1-Hz-Schritte zurück fallen für die Feinein-
stellung.

Eine Tastatur kommt vielleicht trotzdem noch dran für die genaue
Einstellung der Frequenz, aber so'n richtiger Knopf zum Drehen ist
halt was anderes, den möchte ich als Alternative nicht missen.

von *.* (Gast)


Lesenswert?

Öhm... sowas hatte ich doch letztens mal!
1
//Rotary-Encoder-Handling
2
    new = 0;
3
4
    if( PHASE_A )
5
      new = 3;
6
7
    if( PHASE_B )
8
      new ^= 1;                      // convert gray to binary
9
10
    diff = last - new;               // difference last - new
11
12
    if( diff & 1 ) {                 // bit 0 = value (1)
13
      last = new;                    // store new as next last
14
      enc_delta += (diff & 2) - 1;   // bit 1 = direction (+/-)
15
    }
16
17
//Beschleunigung Drehgeber
18
    if(!(prescaler % (DEBOUNCE / 8))) {
19
      accelerateCounter = FALSE;
20
21
      if(volume != lastcnt) {
22
        if(((volume - lastcnt) > 5) || ((volume - lastcnt) < -5)) {
23
          accelerateCounter = TRUE;
24
        }
25
26
        lastcnt = volume;
27
      }
28
    }
Das steht im Timerinterrupt.
In der Hauptschleife kommt dann irgendwann mal sowas...
1
//## Rotary-Encoder #############################################################
2
      if((b = encode_read2())) {          // read a single step encoder
3
        if(accelerateCounter == TRUE) {
4
          b *= 4;
5
          accelerateCounter = FALSE;
6
        }
7
      ...
8
      }

... habe brav P. Dannegger's Encoder-Routine benutzt.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

*.* wrote:
> Öhm... sowas hatte ich doch letztens mal!

Danke!  Verstehe ich noch nicht ganz.

> //Beschleunigung Drehgeber
>     if(!(prescaler % (DEBOUNCE / 8))) {
>       accelerateCounter = FALSE;

Wofür sind hier `prescaler' und `DEBOUNCE' gut?

>       if(volume != lastcnt) {

Auch hier: wie hängen diese Variablen mit enc_delta bzw. last
zusammen?

von *.* (Gast)


Lesenswert?

>> *.* wrote:
>> > Öhm... sowas hatte ich doch letztens mal!
>>
>> Danke!  Verstehe ich noch nicht ganz.
Soll heißen, ich stand letztens vor einem ähnlichen Problem.

>> > //Beschleunigung Drehgeber
>> >     if(!(prescaler % (DEBOUNCE / 8))) {
>> >       accelerateCounter = FALSE;
>>
>> Wofür sind hier `prescaler' und `DEBOUNCE' gut?
Das habe ich aus P. Dannegger's "Die genaue Sekunde" verwendet.
Hier mal ein paar größere Codefetzen.
1
#define DEBOUNCE    8192                                    // debounce clock 8192Hz
2
3
volatile uint8_t    lastcnt;
1
/************************************************************************/
2
/*          Interrupt Service Routine           */
3
/************************************************************************/
4
ISR(TIMER1_COMPA_vect) {
5
    /*...*/
6
7
#if F_CPU % DEBOUNCE
8
    OCR1A = F_CPU / DEBOUNCE - 1;                           // compare DEBOUNCE - 1 times
9
#endif
10
11
    if( --prescaler == 0 ) {                                // exact one second over
12
        prescaler = DEBOUNCE;
13
14
#if F_CPU % DEBOUNCE                                        // handle remainder
15
        OCR1A = F_CPU / DEBOUNCE + F_CPU % DEBOUNCE - 1;    // compare once per second
16
#endif
17
    }
18
//Rotary-Encoder-Handling
19
    new = 0;
20
21
    if( PHASE_A )
22
        new = 3;
23
24
    if( PHASE_B )
25
        new ^= 1;                                           // convert gray to binary
26
27
    diff = last - new;                                      // difference last - new
28
29
    if( diff & 1 ) {                                        // bit 0 = value (1)
30
        last = new;                                         // store new as next last
31
        enc_delta += (diff & 2) - 1;                        // bit 1 = direction (+/-)
32
    }
33
34
//Beschleunigung Drehgeber, jeder 8. Interrupt
35
    if(!(prescaler % (DEBOUNCE / 8))) {
36
        accelerateCounter = FALSE;
37
38
        if(volume != lastcnt) {
39
            if(((volume - lastcnt) > 5) || ((volume - lastcnt) < -5)) {
40
                accelerateCounter = TRUE;
41
            }
42
43
            lastcnt = volume;
44
        }
45
    }
46
    /*...*/
47
}

>> >       if(volume != lastcnt) {
>>
>> Auch hier: wie hängen diese Variablen mit enc_delta bzw. last
>> zusammen?
Sorry, auch hier etwas mehr Code...
1
/************************************************************************/
2
/*          main loop           */
3
/************************************************************************/
4
int main( void ) {
5
    /*...*/
6
7
    lastcnt = volume;
8
    sei();
9
10
    /*...*/
11
12
    while(1) {
13
14
//## Rotary-Encoder #############################################################
15
        if((b = encode_read2())) {                  // read a single step encoder
16
    
17
            if(accelerateCounter == TRUE) {
18
                b *= 4;
19
                accelerateCounter = FALSE;
20
            }
21
    
22
    
23
        if(b > 0) {
24
            if((volume + b) < 255) volume += b;
25
            else volume = 255;
26
        }
27
    
28
        if(b < 0) {
29
            if((volume + b) >= 0) volume += b;
30
            else volume = 0;
31
        }
32
        /*...*/
33
    }
34
    /*...*/
35
}

In meinem Codebeispiel ist die Beschleunigung lediglich 4-fach, ich muß 
nur zwischen 0 und 255 hin-und herpendeln.
Ich hoffe, nun sieht man die Zusammenhänge etwas besser.

von Simon K. (simon) Benutzerseite


Lesenswert?

Mal ne andere Frage: Warum denn die Beschleunigung messen? Die 
Geschwindigkeit reicht doch auch aus.
Dreht man langsam-> 10^0 Änderung pro Drehschritt
Dreht man schnell-> 10^X Änderungen pro Drehschritt.

von Hannes Lux (Gast)


Lesenswert?

> Warum denn die Beschleunigung messen? Die
> Geschwindigkeit reicht doch auch aus.

Ich glaube kaum, dass es um die (physikalisch korrekte) Beschleunigung 
geht, vielmehr darum, das Zählen bei schnellerem Drehen zu 
"beschleunigen", also mehr Schritte zu zählen als aufgetreten sind. 
Umsonst hat Jörg das nicht in Gänsefüßchen gesetzt.

;-)

...

von Simon K. (simon) Benutzerseite


Lesenswert?

Hannes Lux wrote:
>> Warum denn die Beschleunigung messen? Die
>> Geschwindigkeit reicht doch auch aus.
>
> Ich glaube kaum, dass es um die (physikalisch korrekte) Beschleunigung
> geht, vielmehr darum, das Zählen bei schnellerem Drehen zu
> "beschleunigen", also mehr Schritte zu zählen als aufgetreten sind.
> Umsonst hat Jörg das nicht in Gänsefüßchen gesetzt.
>
> ;-)
>
> ...

Achso. Kann allerdings gut sein. Jaja, a = dv/dt und so ;)

Ich kann mir eigentlich nicht vorstellen, dass Danneggers vorschlagene 
Methode so kompliziert sein soll. Über nen definierten Zeitraum von 
100ms (oder so) immer von Danneggers Drehencoder Code auslesen und 
gucken, wie groß der Betrag in den 100ms geworden ist.

von eProfi (Gast)


Lesenswert?

"Um diese Dynamik zu bekommen, muß man dem Drehgeber eine Schwungmasse 
verpassen. Dazu muß die Drehgeschwindigkeit ermittelt und auch 
gemittelt"

Für die Dynamik nicht sooo wichtig, aber für den Bedienkomfort.

Ich erinnere mich an die alten Röhrenradios, die hatten dicke 
Schwungräder drin, da konnte man mit einem beherzten Dreh ans andere 
Ende der Skala drehen.

Unser HP-MessSender hat auch ein ähnliches Rad mit Schwungmasse und 
magnetischer Rastung. Wenn Du da mal dran gedreht hast, willst Du nichts 
anderes mehr.

Bei den einfacheren würde ich überlegen, ob man entweder
 - durch Drücken der Mitteltaste die Srungweite z.B. in 5-8 Stufen 
ändert
 - bei gedrückter Taste das Rad die Sprungweite einstellt.

Ein wenig Dynamik würde ich trotzdem einbauen.
Bei unserem LeCroy ist das auch nicht optimal gelungen, ist zwar 
dynamisch, aber die Sprungweite kann man nur auf 2 Weiten einstellen 
(grob und fein, genauer gesagt fixed (1-2-5) und variable (mal ca. 
1.01)). Das ist mir zu wenig.

Zum Vorgehen: eigentlich ganz einfach: Zeit für den jeweiligen Schritt 
messen und in einer LUT nachschauen, um wieviel addiert / subtrahiert 
werden soll.
Würde ich mit PinChange-Interrupt lösen.

Viel Erfolg

von flätz (Gast)


Lesenswert?

Ich würde das nicht so kompliziert machen. Bei langsamer Drehung 
Einzelschritte, bei schnellerer Drehung erst 5..15 Einzelschritte, dann 
eine Zehnerpotenz erhöhen und wieder 5..15 Schritte, wieder eine 
Zehnerpotenz erhöhen usw.

von Gruenschnabel (Gast)


Lesenswert?

@eProfi
>Für die Dynamik nicht sooo wichtig, aber für den Bedienkomfort.

Beides geht Hand in Hand. Erst wenn man mit einem Schwung einen Bereich 
von - sagen wir - 20-30% einstellen kann, wird die Bedienung angenehm. 
Wenn aber, wie aus dem Pollin-Link ersichtlich, ein Drehgeber mit 16 
Impulsen/360° verwendet wird, kann das nicht klappen. Der Mensch ist 
keine Maschine und bei zuwenig Feingefühl der Einstellung verkrampft 
sich die Hand (meine Erfahrung mit schlechter Mausanpassung unter 
Linux).

Ohne Schwungmasse wäre noch ein größerer Drehknopf mit Fingermulde 
praktikabel, bei dem man mehrere Umdrehungen ohne abzusetzen machen 
kann. Ab der 2. Umdrehung könnte man die Schrittweite deutlich erhöhen 
und dennoch feinfühlig gekommen. Aber der Drehgeber sollte schon 200-400 
Impulse/Umdrehung liefern.

von Simon K. (simon) Benutzerseite


Lesenswert?

Achso, wenn ihr schon alle mit den Alternativen anfangt:

Ich hatte es damals bei meinem Funktionsgenerator (der leider nie fertig 
geworden ist wegen dem Analogdreck) so gemacht:

Das ganze lief "menüartig" ab. Also: Es gab zwei Eingabeebenen. Man 
wechselte (togglete) mit einem Druck auf den eingebauten Knopf des 
Encoders.
Eingabeebene1: Man konnte durch Drehen des Encoders die Dezimalstellen 
(mit blinkendem Cursor auf dem HD44780 markiert) durchrollen. Man konnte 
also sagen, welche 10er Stelle der Frequenz man verändern wollte.

Eingabeebene2: Durch das Drehen am Encoder konnte man die in 
Eingabeebene1 markierte bzw. gewählte Dezimalstelle verändern.

Um zum Beispiel von 0000001Hz auf 1000000Hz (1Hz auf 1MHz) zu wechseln, 
wählte man zuerst die Dezimalstelle ganz links und drehte um eine 
Rastposition, sodass man 1000001Hz hatte und anschließend verringerte 
man die ganz rechte Stelle um 1.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

flätz wrote:
> Ich würde das nicht so kompliziert machen. Bei langsamer Drehung
> Einzelschritte, bei schnellerer Drehung erst 5..15 Einzelschritte, dann
> eine Zehnerpotenz erhöhen und wieder 5..15 Schritte, wieder eine
> Zehnerpotenz erhöhen usw.

Klappt mit rastenden Drehgebern (wie den beiden Pollin-Typen) nicht:
du musst zu oft nachfassen, und so groß ist die mögliche Variation
der Winkelgeschwindigkeiten nicht, die man erreichen kann.

Sowas wäre wirklich nur für nichtrastende Typen und eine Schwungmasse
machbar.  Bei den rastenden Teilen muss man zumindest ein wenig
,,Erinnerungsvermögen'' implementieren, so in der Art: ,,Hat der
Bediener jetzt nur eine kurze Pause zum Nachfassen gemacht?''

von flätz (Gast)


Lesenswert?

> Klappt mit rastenden Drehgebern (wie den beiden Pollin-Typen) nicht:

Ja, ich dachte eher an einen leichtgängigen Typen, wenigstens mit 
Fingermulde oder Kurbel.

von Benedikt K. (benedikt)


Lesenswert?

Mit dem von mir geposteten Code und dem Pollin Drehgeber ist ein 
Wertebereich von etwa 1000-10000 sinnvoll:
Pro Umdrehung gibt es 32 Rastpunkte, macht zusammen mit der 200x 
Beschleunigung und etwa einer Umdrehung die man auf einmal schafft 
theoretisch etwa 6400 Schritte. In der Praxis kommt man auf rund 
1000-3000 Schritte. Mehr als 3-4x neu Ansetzen für den Kompletten 
Wertebereich macht die Bedienung sehr umständlich.
Von daher baue ich bei größeren Wertebereichen eine Funktion ein, die 
durch Drehen bei gedrücktem Knopf die Stelle verschiebt, so dass man in 
10er, 100er, 1000er usw. Schritten einstellen kann.
Ist nicht wirklich perfekt, aber man kommt mit einem einzelnen Knopf aus 
und kann problemlos einen Wertebereich von >10 Dekaden leicht und 
schnell eingeben.

von Uwe (Gast)


Lesenswert?

Hi!
Wäre es eventuell eine Möglichkeit das Quadrat der Impulse, die über
eine feste Tastzeit reinkommen, zu bilden?
Also Tastzeit: 100ms,
Impulse -> Ausgabe
1            1
2            4
10           100
.
.
.
Könnte ich mir jedenfalls vorstellen

Viel Erfolg, Uwe

von Hannes J. (Firma: _⌨_) (pnuebergang)


Lesenswert?

Ich habe mir gerade mal einen Pollin-Panasonic-Drehgeber aus der 
Ramschkiste rausgesucht. Mehr als vielleicht eine 1/4 bis eine 1/2 
Drehung gelingt mir nicht ohne Umgreifen, wenn es bequem bleiben soll.

Bei 16 Impulsen/360° ergibt das grob 4 bis 8 Impulse. Das halte ich für 
sehr wenig um eine Geschwindigkeitssteigerung zu detektieren, also jenen 
"Kick" den man dem Drehgeber gegen ende der 1/2 oder 1/4 Drehung mitgibt 
um zu sagen, dass man es gerne etwas schneller hätte.

Ich würde in eine andere Richtung gehen: Zwei Drehgeber. Einen um die 
Stelle (Auflösung) einzustellen an der man dreht - dass, was sonst bei 
Geräten mit zwei separaten Tasten gemacht wird. Der andere dient ganz 
normal dazu die gewählte Stelle hoch oder runter zu drehen.

Man könnte den ersten Drehgeber auch einsetzen um die gewünschte 
"Geschwindigkeit" einzustellen, also einen Multiplikationsfaktor, mit 
dem die Schritte vom zweiten Drehgeber multipliziert werden. Vielleicht 
würde sich ein 1-2-5-10-20-... Multiplikationsraster gut eignen.

Das wäre nicht ganz so bequem wie ein einziger Drehgeber, doch immer 
noch etwas bequemer als die normalerweise verwendete Kombination aus 
einem Drehgeber und zwei Tasten.

von Benedikt K. (benedikt)


Lesenswert?

Hannes Jaeger wrote:

> Bei 16 Impulsen/360° ergibt das grob 4 bis 8 Impulse.

Wie zuvor geschrieben: Es sind 32 Rastpunkte was die ganze Sache 
schonmal verbessert. Wenn man beide Phasen abfragt, kommt man sogar auf 
64 Schritte pro Umdrehung.

von Simon K. (simon) Benutzerseite


Lesenswert?

Hannes Jaeger wrote:
> Ich würde in eine andere Richtung gehen: Zwei Drehgeber. Einen um die
> Stelle (Auflösung) einzustellen an der man dreht - dass, was sonst bei
> Geräten mit zwei separaten Tasten gemacht wird. Der andere dient ganz
> normal dazu die gewählte Stelle hoch oder runter zu drehen.

So meinte ich das oben, nur halt über einen Drehencoder, wo man zwischen 
den beiden Modi togglet (durch externe Taste). Aber mit zwei 
Drehencodern geht natürlich auch!

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Benedikt K. wrote:
> Mit dem von mir geposteten Code und dem Pollin Drehgeber ist ein
> Wertebereich von etwa 1000-10000 sinnvoll:

Nun, dann könnte man ja mit der Taste noch einigermaßen arbeiten,
wenn man diese als Faktor 1000 benutzt.  Werd ich mich nochmal
ranmachen.

Für eine exakte Eingabe ist eine richtige Tastatur natürlich allemal
besser, die würde ich dann parallel zum Drehknopf lieber haben wollen
als die Variante, mit dem Encoderknopf die 1er/10er/100er/1000er
durchzuschalten.

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


Lesenswert?

Falls jemand mal noch sowas sucht, hier meine Lösung aufbauend auf Peter 
Danneggers Code:
1
volatile short enc_delta;
2
// ISR wird jede ms ausgeführt
3
ISR(SIG_OUTPUT_COMPARE1A)
4
{
5
#define DYNAMIK 40  
6
    static char  enc_state = 0x01;
7
    static uint8 enc_accel = 0;
8
    signed short ed;
9
    char i=0;
10
11
    if( enc_accel>0 ) enc_accel--;
12
13
    if( PHASE_A ) i = 1;
14
    if( PHASE_B ) i ^= 3;                // convert gray to binary
15
    i -= enc_state;                      // difference new - last
16
    if( i&1 ) {                          // bit 0 = value (1)
17
        ed = enc_delta;
18
        if (enc_accel<(255-DYNAMIK )) enc_accel+=DYNAMIK ; 
19
        enc_state += i;                  // store new as next last
20
        if (i&2) ed += 1+(enc_accel>>6); // bit 1 = direction (+/-)
21
        else     ed -= 1+(enc_accel>>6); 
22
        enc_delta = ed;
23
    }
Mit dem #define Dynamik lässt sich der "Biss", also die Beschleunigung 
einstellen. Leider funktioniert das mit den billigen Pollin-Dingern nur 
eingeschränkt, weil die pro Raste zwei Schritte machen. Also ist da 
etwas Spielen mit dem Wert angesagt.
Mit einem u16 für enc_accel kann der Dynamikbereich wesentlich 
ausgeweitet werden.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Danke, guck' ich mir mal mit an.  Im Moment ist dieses Projekt gerade
im Stack etwas nach unten gerutscht, aber interessieren tut's mich
allemal.

von Peter D. (peda)


Lesenswert?

Ich hab jetzt mal die Beschleunigung programmiert, um nen 16Bit-DAC zu 
setzen.
Funktioniert sehr schön.
Das Beispiel macht 3 Stufen, man sollte aber erstmal 2 Stufen probieren.
Hier nur die Erweiterung zu meinem Code:
1
#include <util\atomic.h>
2
3
4
uint8_t enc_acc_t10ms;                  // count down per timer interrupt 10ms
5
uint8_t enc_delta;
6
uint8_t last;
7
8
9
int8_t encode_read2( void )             // 2 edge per detent, 24 detent
10
{
11
  int8_t val;
12
13
  ATOMIC_BLOCK(ATOMIC_FORCEON){
14
    val = enc_delta;
15
    enc_delta &= 1;
16
  }
17
  return val >> 1;
18
}
19
20
21
#define ACC_TIME        0.05            // 50ms
22
#define ACC_SLOW        3
23
#define ACC_FAST        8
24
#define ACC_BIG         128
25
#define ACC_HUGE        1024
26
27
28
int16_t encode_read_acc( void )         // Acceleration
29
{
30
  int8_t val;
31
32
  if( enc_acc_t10ms )                   // time to accumulate counts
33
    return 0;
34
  enc_acc_t10ms = 1/10e-3 * ACC_TIME;
35
  val = encode_read2();
36
  switch( val ){
37
    case -ACC_SLOW ... ACC_SLOW:    return  val;
38
    case ACC_SLOW+1 ... ACC_FAST:   return  ACC_BIG;
39
    case -ACC_FAST ... -ACC_SLOW-1: return -ACC_BIG;
40
    case ACC_FAST+1 ... 127:        return  ACC_HUGE;
41
    default:                        return -ACC_HUGE;
42
  }
43
}

Dazu wird noch ein 10ms Timer-Interrupt benötigt für die 50ms Meßzeit.

Da die neueren AVRs alle Pin-Change-Interrupts haben, habe ich den 
Encoder-Interrupt als Pin-Change-Interrupt (beide Anschlüsse) 
ausgeführt, anstatt mit 1ms Timerinterrupt. Dadurch entsteht nur beim 
Drehen eine Interruptlast und es gehen keine Schritte verloren.


Peter

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.