Forum: Mikrocontroller und Digitale Elektronik Zwei Inkrementalgeber ansteuern


von TM F. (p_richner)


Lesenswert?

Hallo zusammen

Ich möchte zwei Inkrementalgeber ansteuern und auswerten. Mein Problem 
ist, dass ich nur detektieren kann, wenn etwas ändert, jedoch nicht, auf 
welche Seite gedreht wird. Die Detektion erfolgt über einen Interrupt, 
der den ganzen Port in einen Ringpuffer schreibt. Am Port sind noch 4 
Taster. Ich benutze einen Attiny48.
An die Funktion übergebe ich den ganzen Port aus dem Puffer.

Hier mein Code:
1
#define PHASE_1_A               !(PIND & 1<<PIND2)
2
#define PHASE_1_B               !(PIND & 1<<PIND3)
3
#define PHASE_2_A               !(PIND & 1<<PIND5)
4
#define PHASE_2_B               !(PIND & 1<<PIND6)
5
6
#define ALL_ENCODER    0x6C
7
#define ENCODER_1      0x0C
8
#define ENCODER_2      0x60
9
10
// the first digit stands for the encoder and the second for left(2) or right(1)
11
#define ENCODER_1_RIGHT 31
12
#define ENCODER_1_LEFT  32
13
#define ENCODER_2_RIGHT 41
14
#define ENCODER_2_LEFT  42
15
16
#define ENCODER_CHANGED 1
17
#define ENCODER_CONSTANT 0
18
19
uint8_t encoder_1_old = 0;
20
uint8_t encoder_1_new = 0;
21
22
void encoder_init(void)
23
{
24
    encoder_1_new = encoder_1_old = PIND & ENCODER_1;
25
    encoder_2_new = encoder_2_old = PIND & ENCODER_2;
26
}
27
28
uint8_t get_encoder_1(uint8_t data)
29
{
30
    encoder_1_new = data & ENCODER_1;
31
    if(encoder_1_new != encoder_1_old)
32
    {
33
        if((encoder_1_new & PHASE_1_A) == (encoder_1_old & PHASE_1_B))
34
        {
35
            encoder_1_old = encoder_1_new;
36
            return ENCODER_1_RIGHT;
37
        }
38
39
        else if((encoder_1_new & PHASE_1_B) == (encoder_1_old & PHASE_1_A))
40
        {
41
            encoder_1_old = encoder_1_new;
42
            return ENCODER_1_LEFT;
43
        }
44
    }
45
    return ENCODER_CONSTANT;
46
}

Das ganze habe ich auch für den zweiten Encoder, wegen Platzeinsparung 
poste ich dieses jedoch nicht.

Danke für Hilfen

MfG

von Peter (Gast)


Lesenswert?

TM F. schrieb:
> auf welche Seite gedreht wird

Ich nehme an, das soll heißen "in welche Richtung gedreht wird"?

Das kommt aus Deiner Routine doch heraus? Wenn eine Änderung 
festgestellt wird, wird entweder wird ENCODER_1_RIGHT oder 
ENCODER_1_LEFT zurückgegeben, je nach Richtung.

von TM F. (p_richner)


Lesenswert?

Peter schrieb:
> wird entweder wird ENCODER_1_RIGHT oder
> ENCODER_1_LEFT zurückgegeben, je nach Richtung.

wenn ich die Werte jedoch auswerte, kommt jedes mal das gleiche Resultat 
heraus, egal auf welche Seite ich drehe

von MaWin (Gast)


Lesenswert?

TM F. schrieb:
> Problem ist, dass ich nur detektieren kann, wenn etwas ändert,
> jedoch nicht, auf welche Seite gedreht wird.
> Die Detektion erfolgt über einen Interrupt

Tja, das nennt man dann wohl 'falscher Ansatz'.

Deinem code fehlt die Interruptzuweisung.

Hättest du deine Routine an einen ausreichend oft aufgerufenen 
TIMERinterrupt gehängt, würde sie fast funktionieren (man müsste nur 
statt ENCODER_1_LEFT, ENCODER_1_RIGHT, ENCODER_1_CONSTANT eien volative 
Zähler +1, -1 oder 0 zählen).

Die sinnvolle Auswertung von Encodern ist hinlänglich bekannt:
http://www.dse-faq.elektronik-kompendium.de/dse-faq.htm#F.29

von drehrichtung (Gast)


Lesenswert?

>wenn ich die Werte jedoch auswerte, kommt jedes mal das gleiche Resultat
>heraus, egal auf welche Seite ich drehe

Die Werte sind doch als untershiedlich definiert. Was gibt es da 
"auszuwerten".

Was machst du wirklich?

von Klaus (Gast)


Lesenswert?

Der Fehler wird sein, dass er immer auf die andere Seite dreht und nicht 
die Richtung ändert. Das ist wie bei der alten Schallplatte. Die 
Richtung ist für beide Seiten gleich. :-)

von W.S. (Gast)


Lesenswert?

TM F. schrieb:
> Ich möchte zwei Inkrementalgeber ansteuern und auswerten.

Ansteuern?
Sicherlich meinst du Abfragen.

Aber dein Quelle ist mir ein Graus, man kann kaum darin erkennen, was du 
dir da gedacht hast und was das Ganze tatsächlich macht.

Solche Drehencoder haben 2 Ausgänge bzw. liefern 2 Signale, von denen 
eines dem anderen vor- bzw. nacheilt. Dein Speichern eines vorherigen 
Zustandes des einen Signals ist deshalb wohl sinnlos, denn es erfaßt das 
eigentliche Signal überhaupt nicht.

TM F. schrieb:
> Die Detektion erfolgt über einen Interrupt,
> der den ganzen Port in einen Ringpuffer schreibt.

Hmm.. kann man denn bei diesen ATtiny-Dingern denn nicht festlegen, 
welche Pins einen Interrupt generieren dürfen und welche nicht? Oder 
wenigstens anhand eines Interrupt-Registers herauskriegen, welcher Pin 
es denn gewesen ist, der den Interrupt ausgelöst hat?

Ich schreib dir mal ne symbolische Quelle:

procedure Drehgeber; interrupt(PortD.1);
var
 R : boolean;
begin
 // Annahme: Signal A auf PortD.1 und Signal B auf PortD.2
 R:= (((PortD shr 1) xor (PortD shr 2)) and 1) <> 0;
 if R
 then AddEvent(DG1_ist_rechtsgedreht)
 else AddEvent(DG1_ist_linksgedreht);
end;

W.S.

von MaWin (Gast)


Lesenswert?

W.S. schrieb:
> procedure Drehgeber; interrupt(PortD.1);
> var
>  R : boolean;
> begin
>  // Annahme: Signal A auf PortD.1 und Signal B auf PortD.2
>  R:= (((PortD shr 1) xor (PortD shr 2)) and 1) <> 0;
>  if R
>  then AddEvent(DG1_ist_rechtsgedreht)
>  else AddEvent(DG1_ist_linksgedreht);
> end

Autsch, kaum ist alles gesagt, kommt immer noch einer mit einem nie 
ausprobierten aber grundfalschen Vorschlag daher, der nicht mal die 
grundlegenden Notwendigkeiten verstanden hat:

> Dein Speichern eines vorherigen
> Zustandes des einen Signals ist deshalb wohl sinnlos

von TM F. (p_richner)


Lesenswert?

Zuerst mal danke für die Antworten.
Den Code versuchte ich zusammen zu setzen aus mehreren Quellen, die ich 
gefunden habe.

W.S. schrieb:
> Hmm.. kann man denn bei diesen ATtiny-Dingern denn nicht festlegen,
> welche Pins einen Interrupt generieren dürfen und welche nicht? Oder
> wenigstens anhand eines Interrupt-Registers herauskriegen, welcher Pin
> es denn gewesen ist, der den Interrupt ausgelöst hat?

Ich kann sagen, welche Pins einen Interrupt generieren, da jedoch der 
ganze Port mit Tastern/Inkrementalgeber belegt ist, müssen alle Pins 
einen Interrupt generieren. Einen Pin-eigenen Interrupt gibt es nicht.

MaWin schrieb:
> Deinem code fehlt die Interruptzuweisung.

Hier ist das Problem, dass ich keinen freien Timer mehr habe. Den einen 
brauche ich für eine UART-Übertragung, den anderen für die 
Tasterentprellung.

von Klaus (Gast)


Lesenswert?

TM F. schrieb:
> den anderen für die Tasterentprellung.

Da passt das doch hervorragend hin. Ich brauch meist nur einen Timer und 
pack da alles rein, was getimed werden muß.

MfG Klaus

von TM F. (p_richner)


Lesenswert?

Klaus schrieb:
> Da passt das doch hervorragend hin. Ich brauch meist nur einen Timer und
> pack da alles rein, was getimed werden muß.

Machst du das dann per Softtimer?
Ich wollte zuerst  ausprobieren, mit den beiden 
Interrupt-Match-Register. Dazu für die Inkgeber einen Timer von 1ms und 
für die Taster 20ms. Ich erhielt jedoch den Hinweis, dass ich später eh 
alles mit dem Softtimer machen muss und sich diesen Aufwand nicht lohnt.

von Thorsten O. (Firma: mechapro GmbH) (ostermann) Benutzerseite


Lesenswert?

Eine Abfrage der Drehgeber alle Millisekunde halte ich für deutlich zu 
langsam. Unabhängig davon ist es aber gängige Praxis, Funktionen, die 
deutlich seltener aufgerufen werden müssen, bei jedem x-ten Durchlauf 
des Timers aufzurufen. Falls die jeweilige Funktion zu lange dauert und 
den Timer blockieren würde, kann man auch ein Flag setzen, was zur 
Ausführung der Funktion in der Mainschleife führt.

Mit freundlichen Grüßen
Thorsten Ostermann

von Klaus (Gast)


Lesenswert?

TM F. schrieb:
> Machst du das dann per Softtimer?

z.B. so:

für Delay wird eine Variable aufgesetzt, die bei jedem Timertick 
runtergezählt wird. Ist sie 0, ist die Zeit um. Das kann dann, wer will, 
abfragen. Davon kann es natürlich auch mehrere geben.

Dann wird bei jedem Tick der Encoder angesehen und bearbeitet. Nix mit 
Matchregister ..., einfach die passenden Portpins einlesen und die 
Encoderstatemaschine abarbeiten. MaWin hat da irgendwo einen Dreizeiler 
parat.

Bei z.B jedem 10. Timerinterrupt wird nach den Tasten geschaut. Und da 
auch einfach die Portpins einlesen und die Entprellstatemachine 
abarbeiten. Ist ja fast das gleiche wie beim Encoder.

Bei jedem x. (je nach Baudrate) wird geschaut, ob was seriell zu senden 
und der Transmitter leer ist und das UART gefüllt. Den Empfang könnte 
man auch so lösen: schauen ob was im UART ist und auslesen und 
einqueuen.

Dabei entstehen dann einige kleine Statemschinen, die in sich recht 
übersichtlich sind.

MfG Klaus

von Klaus (Gast)


Lesenswert?

Thorsten O. schrieb:
> bei jedem x-ten Durchlauf
> des Timers aufzurufen. Falls die jeweilige Funktion zu lange dauert und
> den Timer blockieren würde, kann man auch ein Flag setzen, was zur
> Ausführung der Funktion in der Mainschleife führt.

Oder den Interrupt wieder freigeben. An diesen Programmteil kommt ja 
erst der x-te Interrupt wieder, dann erst muß der lange Interrupt fertig 
sein.

MfG Klaus

von incr. (Gast)


Lesenswert?

>Eine Abfrage der Drehgeber alle Millisekunde halte ich für deutlich zu
>langsam.

Das hängt doch immer vom Drehgeber und von der Bedienung ab.

Incrementalgeber an Motoren - Millisekunde zu langsam
Incrementalgeber zur Menüführung - kann locker reichen

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


Lesenswert?

Thorsten O. schrieb:
> Eine Abfrage der Drehgeber alle Millisekunde halte ich für deutlich zu
> langsam.
Es kommt darauf an, was für Drehgeber das sind: wenn es Eingabegeräte 
für die manuelle Bedienung sind, dann reicht das locker.
Wenn es Encoder an Motoren sind, dann muss man mit der spitzen Feder 
nachrechnen und Drehzahl sowie Strichzahl des Gebers kennen.

TM F. schrieb:
> Hier ist das Problem, dass ich keinen freien Timer mehr habe. Den einen
> brauche ich für eine UART-Übertragung, den anderen für die
> Tasterentprellung.
Hast du auch zum Aufstehen eine Uhr, zum Zähneputzen eine eigene Uhr, 
zum Losfahren zur Arbeit, für die Frühstückspause eine und für die 
Mittagspause, für den Feierabend usw...?
Oder nimmst du nicht ein und die selbe Uhr für alle diese Dinge?

Eines ist klar: der UART ist Hardware, da kannst du nicht viel dran 
drehen. Aber der verbleibende Timer kann doch einfach jede ms einen 
Zähler hochzählen und dann hast du deine Uhr...

von TM F. (p_richner)


Lesenswert?

incr. schrieb:
> Das hängt doch immer vom Drehgeber und von der Bedienung ab.

Es ist ein Drehgeber, mit dem ein Menu gesteuert wird, also nichts 
zeitkritisches.

von MaWin (Gast)


Lesenswert?

TM F. schrieb:
> MaWin schrieb:
>> Deinem code fehlt die Interruptzuweisung.
>
> Hier ist das Problem, dass ich keinen freien Timer mehr habe.

Wie schrecklich.

Mein PC hat übrigens nur 1 Timer.

Daahhheerr prrellllen meeinee Tassstten.

Es ist also eher eine Frage,
wie erfahren man mit dem Programmieren ist.

Natürlich reicht 1 Timer für Tastenentprellen und Drehencoder 
gleichzeitig.

Man muss nicht auf nicht-funktioniernde Flankenauswertung zurückgreifen.

von W.S. (Gast)


Lesenswert?

TM F. schrieb:
> Ich kann sagen, welche Pins einen Interrupt generieren, da jedoch der
> ganze Port mit Tastern/Inkrementalgeber belegt ist, müssen alle Pins
> einen Interrupt generieren. Einen Pin-eigenen Interrupt gibt es nicht.

Das ist zum Teil schade und zum Teil ungünstig designed.

Also:
gewöhnliche Tasten brauchen gar keinen Interrupt zu erzeugen. Sowas 
fragt man so alle 10 ms im System-Uhr-Interrupt ab (für MaWin: per 
Polling).

Bei Drehgebern sieht das anders aus, da ist ja nicht nur gefragt, ob 
sich ein Signal geändert hat (ich nenn das hier mal "A") - was man per 
Polling ausreichend gut abfragen könnte - sondern es ist der Zustand des 
anderen Signals (hier mal "R" für Richtung) direktemang zum Zeitpunkt 
der Änderung des "A"-Signals zu erfassen, denn nur darin liegt die 
Richtungsinformation.

Klaro jetzt?

Deshalb müßte man bei Drehgebern dramatisch häufiger pollen als es so 
ein oller Drehgeber eigentlich verdient hätte oder eben das "A"-Signal 
(das, was sich zwischen den Rastpunkten ändert) per HW entprellen und 
dann damit nen Interrupt auslösen, der es einem gestattet, zeitnah zur 
Flanke den Zustand des anderen Signals und damit die Drehrichtung zu 
erfassen.

Das ist der eigentliche Knackpunkt.
Allerdings begreifen Leute wie MaWin sowas aus innerer Überzeugung 
partout nicht, sondern plädieren für häufiges Pollen.

So. Und wenn du bei deiner HW nicht unterscheiden kannst, welches Pin 
den Interrupt ausgelöst hat, dann bleibt dir nix anderes übrig, als 
alle in Frage kommenden Pins per HW zu entprellen (damit du nicht in 
sinnlosen Interrupts ersäufst) und in der Interruptroutine eine 
Fallunterscheidung zu treffen.

Alternative dazu: Pins umverlegen, so daß deine zwei "A" Signale von all 
dem übrigen Zeugs (Tasten, zweimal "R" usw.) separierbar sind und nach 
Möglichkeit separate Interrupts erzeugen können. Dann ersparst du dir 
die ganzen Fallunterscheidungen im Interruptprogramm und kommst mit 
einem simplen "Richtung=A xor R" aus, was auf nen Dreizeiler in der 
Quelle hinausläuft.

Alles andere - vor allem der Gedanke an Polling - läuft darauf hinaus, 
den ganzen Controller mit dem schieren Bedienelemente-Abfragen 
zuzumüllen, da die Abfrageroutine ja nicht simpel ist und obendrein 
häufig stattfinden muß und dein Systemtakt nicht sonderlich hoch ist.

W.S.

von A/B/R (Gast)


Lesenswert?

>Alternative dazu: Pins umverlegen, so daß deine zwei "A" Signale von all
>dem übrigen Zeugs (Tasten, zweimal "R" usw.) separierbar sind und nach
>Möglichkeit separate Interrupts erzeugen können. Dann ersparst du dir
>die ganzen Fallunterscheidungen im Interruptprogramm und kommst mit
>einem simplen "Richtung=A xor R" aus, was auf nen Dreizeiler in der
>Quelle hinausläuft.


Ich könnte wieder einen Glaubenskrieg Interrupt/Polling vom Zaun 
brechen.

Von mir aus werdet alle glücklich mit Interrupts.

Incrementalgeber an schnellen Motoren brauchen eh andere Lösungen.

Wer kein Polling schafft, weil er einen zu langsamen Prozessor, mit 
zuwenigen IO-Pins verwendet, der hat ein Design-Problem.
Das aber wird er mit Interrupts nicht zu lösen.
Der murkst eh gern vor sich hin.

von MaWin (Gast)


Lesenswert?

W.S. schrieb:
> Allerdings begreifen Leute wie MaWin sowas aus innerer Überzeugung
> partout nicht, sondern plädieren für häufiges Pollen.

Muss es wirklich sein dass du deinen Schwachsinn erneut absonderst, in 
jedem Incrementalencoderthread ?

An deinen Sätzen ist so ziemlich jeder Sachverhalt falsch, ich greife 
nur mal einen heraus:

Gerade polling erlaubt im Gegensatz zu deiner abstrusen Behauptung 
problemlos die zeitgleiche Erfassung der 2 Spuren A und R (einfach 2 
Bits im inputstate=PORT; Port), während Pin-Change Interrupts genau 
damit ein Problem haben.

Es ist auch bewiesen, dass polling weit schnellere Drehgeber erlaubt als 
Interrupts http://www.dse-faq.elektronik-kompendium.de/dse-faq.htm#F.29

Ich wäre dafür, deinen Beitrsg voller Lügen einfach zu löschen, denn du 
wirst sowieso nicht mehr verstehen was richtig ist, aber leitest 
unschuldige Leute in die Irre.

von W.A. (Gast)


Lesenswert?

W.S. schrieb:
> Deshalb müßte man bei Drehgebern dramatisch häufiger pollen als es so
> ein oller Drehgeber eigentlich verdient hätte oder eben das "A"-Signal
> (das, was sich zwischen den Rastpunkten ändert) per HW entprellen und
> dann damit nen Interrupt auslösen, der es einem gestattet, zeitnah zur
> Flanke den Zustand des anderen Signals und damit die Drehrichtung zu
> erfassen.

Wie meinen? Du musst A und B genau so häufig auslesen, dass du beim 
Drehen keinen regulären Zustand verpasst. Das Prellen kann dir ziemlich 
egal sein, weil der vom Drehgeber erzeugte Gray-Code dagegen bekanntlich 
immun ist.

von Fehlschritt (Gast)


Lesenswert?

Wenn Dir Alles zu viel wird und Du den IIC-Bus benutzen kannst, ein 
ATtiny25 pro Kanal befreit Dich von allen Nöten und uns von den Pollern 
;-)
Beitrag "mini Quadraturdekoder + 32 Bit Zähler + TWI, Attiny25"

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


Lesenswert?

Fehlschritt schrieb:
> ...
Nomen est Omen.

Natürlich könnte ich da auch ein Subsystem mit FPGA aufbauen, das 
würde könnte sogar richgit viele richtig schnelle Inkrementalgeber an 
Motoren mitsamt Oversampling und Entprellung verdauen und auswerten.
Wenn ich aber einen uC habe und zwei solche popelige manuelle Geber, 
dann reicht mir für diese Aufgabe ein (in Zahlen 1) einziger 1ms 
Interrupt aus.

Es ist in heutigen uCs übrigens sowieso so, dass ein Interrupteingang 
von der Hardware einfach nur "gepollt" und anschließend eine 
Flankenerkennung darauf angesetzt wird. Nur muss dieses Pollen einfach 
hinreichend schnell sein...

von Wolfgang (Gast)


Lesenswert?

Lothar M. schrieb:
> Es ist in heutigen uCs übrigens sowieso so, dass ein Interrupteingang
> von der Hardware einfach nur "gepollt" und anschließend eine
> Flankenerkennung darauf angesetzt wird.

Wie soll es denn sonst auch gehen. Irgendwie muss das externe Ereignis 
doch in den µC-Takt reinsynchronisiert werden.

von TM F. (p_richner)


Lesenswert?

Wäre es nicht eine Möglichkeit, beim Interrupt zu überprüfen, ob ein 
Inkgeber oder ein Taster geändert hat. Wenn ein Inkgeber, dann direkt 
auswerten, sonst 20ms warten für die Entprellung?

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


Lesenswert?

Wolfgang schrieb:
> Wie soll es denn sonst auch gehen.
Früher(tm) durfte der Interrupt mit seiner steigenden/fallenden Flanke 
an einem Takteingang eines Flipflops herumzerren. Da reichte dann auch 
ein 10ns langer (Stör-)Impuls für einen Interrupt...

TM F. schrieb:
> beim Interrupt zu überprüfen, ob ein Inkgeber oder ein Taster geändert
> hat. Wenn ein Inkgeber, dann direkt auswerten, sonst 20ms warten für die
> Entprellung?
Pures "Warten" ist i.A eine schlechte Strategie. Besser ist es, wenn ein 
Signal "weiter" aufgenommen und ausgewertet wird. Für eine 
Tasterauswertung würde ich einfach ein paar Pulse lang mitzählen, bis 
eine Schwelle überschritten wird. Wenn das Signal ausreichend lang 
stabil ist, dann wird der neue Pegel verwendet und ausgewertet.

von TM F. (p_richner)


Lesenswert?

Lothar M. schrieb:
> Pures "Warten" ist i.A eine schlechte Strategie.

Ich würde im 1ms Timer eine statische Variable heraufzählen und wenn die 
20 erreicht hat, dann die Taster in meinen Buffer schreiben.

: Bearbeitet durch User
von Fehlschritt (Gast)


Lesenswert?

Lothar M. schrieb:
> Nomen est Omen.

Was sollen die essen?

> Natürlich könnte ich da auch ein Subsystem mit FPGA aufbauen, das
> würde könnte sogar richgit viele richtig schnelle Inkrementalgeber an
> Motoren mitsamt Oversampling und Entprellung verdauen und auswerten.

Mach doch mal, ein-zwei Kanäle im kleinen DIL-Gehäuse würden ja schon 
reichen.

von Wolfgang (Gast)


Lesenswert?

Lothar M. schrieb:
> Früher(tm) durfte der Interrupt mit seiner steigenden/fallenden Flanke
> an einem Takteingang eines Flipflops herumzerren. Da reichte dann auch
> ein 10ns langer (Stör-)Impuls für einen Interrupt...

Das wird heutzutage extra als Glitch-Detect verkauft ;-)

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.