Forum: Analoge Elektronik und Schaltungstechnik Regler fuer Drehgeber


von Manuel K. (manuel_k690)


Lesenswert?

Hallo Freunde,
ich bin etwas ratlos, deshalb schreibe ich euch jetzt hier.

An meiner Stereoanlage ist ein Poti zur Lautstärkeregelung. Wenn ich 
schnell am Poti drehe steigt die Lautstärke überproportional schnell an. 
Wenn ich langsam drehe dann ist der Anstieg vermutlich nahezu linear 
über der Drehgeschwindigkeit.

Ich möchte die gleiche Funktionalität in einer Microcontrollerschaltung 
realisieren. Allerdings sollen damit Parameter im Microcontroller 
eingestellt werden können. Ein entsprechendes Display zur Anzeige ist 
vorhanden.

Leider habe ich trotz Maschinenbaustudium nur noch rudimentäre 
Kenntnisse in Regelungstechnik und kann mir grade auch keine Reim draus 
machen, wie ich an einen solchen Regelansatz herangehe.

Außerdem habe ich folgenden Drehencoder zu Hause:
KY040

Kann jemand sagen ob solch ein "Regelverhalten" mit einem solchen 
Encoder möglich ist und wie ich am besten vorgehe?

Grüße Manuel

von Harald W. (wilhelms)


Lesenswert?

Manuel K. schrieb:

> Ich möchte die gleiche Funktionalität in einer Microcontrollerschaltung
> realisieren. Allerdings sollen damit Parameter im Microcontroller
> eingestellt werden können. Ein entsprechendes Display zur Anzeige ist
> vorhanden.
>
> Leider habe ich trotz Maschinenbaustudium nur noch rudimentäre
> Kenntnisse in Regelungstechnik und kann mir grade auch keine Reim draus
> machen, wie ich an einen solchen Regelansatz herangehe.

Als "Masch-Ing" solltest Du zumindestens den Unterschied zwischen
Regelung und Steuerung kennen. Hier handelt es sich um eine reine
Steuerung. Der Rest ist nur richtige Programmierung Deines µCs.
Kannst Du denn µCs prorammieren? Wie man Drehencoder richtig aus-
wertet, wird u.a. in den DSE-FAQ beschrieben. Die unterschiedliche
Auswertung je nach Drehgeschwindigkeit kann man einfach erreichen,
indem man die Drehgeschwindigkeit mit Hilfe des µC misst.

von MaWin (Gast)


Lesenswert?

Das hat nichts mit Tegelungstechnik zu tun, fas idt simpeldzes 
programmieten.

Man fragt den Drehencoder

int table[4][4]={{0,1,-1,0},{-1,0,0,1},{1,0,0,-1},{0,-1,1,0}};
  int position=0,steps=0;
  volatile int quadrature_input; // bit 0 und bit 1 sind 
Quadratureingaenge
  int new_quadrature_value, last_quadrature_value=quadrature_input;

ab, z.B. jede Millisekunde:

new_quadrature_value=quadrature_input;
  steps+=table[last_quadrature_value][new_quadrature_value];
  last_quadrature_value=new_quadrature_value;

https://dse-faq.elektronik-kompendium.de/dse-faq.htm#F.29

und überprüft nach 100 solcher Durchläufe, wieviel zusammengekommen ist,

"steps"

können ja nur maximal 100 sein die vielleicht aber zu 10000 führen 
sollen. Das macht dann eine exponentielle Formel

position += 1.095^steps;
steps=0;

von Maxim B. (max182)


Lesenswert?

Manuel K. schrieb:
> Kann jemand sagen ob solch ein "Regelverhalten" mit einem solchen
> Encoder möglich ist
Warum nicht möglich? Das ist ja nur ein Programm. Was einprogrammiert 
wird, so wird man auch bekommen.

Manuel K. schrieb:
> und wie ich am besten vorgehe?

Es gibt viele Möglichkeiten. Z.B. diese:
1. jede ca.1ms Encoder überprüfen, eine Variable entsprechend ändern.
2. jede z.B. 100 ms Variable prüfen. Wenn sie stärker geändert wurde (im 
Vergleich mit dem früheren Wert), dann sollte Lautstärke-Zähler 
überproportional incrementiert oder decrementiert werden. Sonst 
proportional.

von Achim M. (minifloat)


Lesenswert?

Manuel K. schrieb:
> rudimentäre Kenntnisse in Regelungstechnik

Der Drehgeber an der Stereoanlage ist genau genommen auch eine 
Steuerung. Eine Regelung wäre es, wenn abgeleitet von einer 
Schallpegelmessung der Verstärkungsfaktor nachgestellt würde.
Genug klug geschi##en.

Idee zu einem Ansatz:
Der Drehgeber wird zyklisch (sagen wir Mal alle 20msec), hierbei wird 
das Quadratursignal in eine Zahl in zyklischem Zahlensystem umgewandelt. 
Also 0, 1, 2, ..., 254, 255, 0, 1, ...

Davon wird ebenso zyklisch (sagen wir Mal 200msec) die Ableitung 
berechnet, also
Delta = Zähler_vorher - Zähler_jetzt
Zähler_vorher = Zähler_jetzt

Abhängig vom Betrag des Delta kann man jetzt die eigentliche Stellgröße 
schneller oder langsamer zählen lassen, z.B.

if (abs(Delta) > 20)
{
   Stellgröße += Delta * 10
}
else
{
  Stellgröße += Delta
}

mfg mf

von Manuel K. (manuel_k690)


Lesenswert?

huiuiui hier bekommt man ja super schnell eine Antwort.
Vielen Dank für die guten Ideen. Vermutlich habe ich einfach zu 
kompliziert gedacht.
Ich denke das bekomme ich jetzt hin.
Grüße und Danke
Manuel

von Achim M. (minifloat)


Lesenswert?

Manuel K. schrieb:
> Ideen

Ich hatte dieselbe Idee wie Maxim...

Allerdings kann man, statt wie mit dem "if" unstetig, mit z.B. f(x)= x³ 
einen stetigen nichtlinearen Zusammenhang darstellen. Vorteil wäre, dass 
der Übergang von "normal" nach "schnell" weicher und weniger abrupt 
erscheint.

mfg mf

: Bearbeitet durch User
von Maxim B. (max182)


Lesenswert?

Viel leichter wäre es, umgekehrt zu machen: bei Langsamdrehen 
proportional, bei Schnelldrehen unterproportional. Dafür reicht es, 
Kontroller so mit anderen Aufgaben zu belasten, daß nur wenig Zeit für 
Drehgeber-Abtasten bleibt :)

von Axel S. (a-za-z0-9)


Lesenswert?

Manuel K. schrieb:
> An meiner Stereoanlage ist ein Poti zur Lautstärkeregelung. Wenn ich
> schnell am Poti drehe steigt die Lautstärke überproportional schnell an.
> Wenn ich langsam drehe dann ist der Anstieg vermutlich nahezu linear
> über der Drehgeschwindigkeit.

> Kann jemand sagen ob solch ein "Regelverhalten" mit einem solchen
> Encoder möglich ist und wie ich am besten vorgehe?

Das ist eine reine Software-Sache. Die Auswertung schaut darauf, wie 
"schnell" der Encoder gedreht wird und macht beim schnellen Drehen 
größere Sprünge.

Praktisch habe ich das mal so umgesetzt, daß zwischen der Drehzahl des 
Encoders und der "Drehzahl" der zugehörigen Variable ein Faktor 
"Geschwindigkeit" sitzt. Im einfachsten Fall ist der Faktor konstant 1. 
Dann kriegst du das bekannte, langweilige Verhalten.

Interessanter wird es, wenn man den Faktor dynamisch verändert. Sehr 
gute Ergebnisse hatte ich mit diesem Verfahren:

1. der Encoder wird in einem festen Zeitintervall abgetastet. Ein 
zweckmäßiger Wert ist 1ms.

2. die Geschwindigkeit wird als Festkomma-Wert, z.B. in 1/32 Auflösung 
aufgelöst. Ein Wert von 32 enstspricht dann 1.0. 33 wäre 1 + 1/32 = 
1.03125 etc. Damit läßt sich leicht rechnen. Fließkomma würde auch 
gehen, ist aber auf µC ohne Fließkomma-Hardware zu teuer.

3. immer, wenn der Encoder einen Schritt gemacht hat, wird die 
Geschwindigkeit um einen bestimmten Betrag erhöht.

4. bei jeder Abtastung (im festen 1ms Raster!) ohne einen 
Encoder-Schritt wird die Geschwindigkeit um 1 vermindert.

im Ergebnis kriegt man das gewünschte Verhalten. Wird der Encoder nur 
langsam gedreht, ändert sich die Variable in 1-er Schritten. Je 
schneller (und je länger schnell) der Encoder gedreht wird, desto größer 
werden die Schritte.

Die Grundidee wurde schon mehrfach beschrieben, z.B. von Lothar im 
Beitrag "Dynamische "Beschleunigung" bei Encoder-Eingabe"

Meine Variante (mit funktionsfähigem Quellcode für ATTiny2313) findest 
du im Beitrag "Kurzzeittimer (z.B. für Platinenbelichter)". Das ist wie 
beschrieben mit Festkomma-Arithmetik mit Skalierung 1/32 (#define 
SCALE). Die Beschleunigung bei jedem Encoder-Schritt ist 24/32 (#define 
ACCEL). Und die maximale Geschwindigkeit ist 30 (#define VMAX).

Der Code paßt auf eine Bildschirmseite:
1
/* encoder acceleration characteristics */
2
#define ACCEL 24            /* acceleration */
3
#define SCALE 32            /* speed scale, power of 2 recommended */
4
#define VMAX  30            /* max speed */
5
6
/* rotary encoder decoding table */
7
const int8_t enc_table[16] =
8
{
9
    0,  0, -1, 0,
10
    0,  0,  0, 1,
11
    1,  0,  0, 0,
12
    0, -1,  0, 0
13
};
14
15
/* encoder status */
16
volatile int8_t  enc_delta;  /* -128..+127 */
17
18
void debounce_encoder(void)
19
{
20
    static uint8_t last; /* encoder status */
21
    static int16_t spd;  /* encoder speed */
22
23
    last = ((last << 2) | (phase_A ? 2 : 0) | (phase_B ? 1 : 0)) & 0x0F;
24
    int8_t delta = enc_table[last];
25
26
    if (delta) {
27
        /* accelerate, with saturation @ VMAX*SCALE */
28
        spd = (spd < (VMAX*SCALE-ACCEL) ? spd+ACCEL : VMAX*SCALE);
29
30
        if (delta > 0) {
31
            enc_delta += (spd/SCALE);
32
        } else {
33
            enc_delta -= (spd/SCALE);
34
        }
35
36
    } else {
37
        /* decelerate, but not below SCALE */
38
        spd = (spd > SCALE ? spd-1 : SCALE);
39
    }
40
}

Den kompletten Code findest du als ZIP im o.g. Beitrag.

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.