Ich hab jetzt nochmal den Code schön gemacht und hier reingestellt, weil
der Ursprungsthread doch recht unübersichtlich ist.
Und auch, weil regelmäßig Threads zu Problemen mit anderen Codes im
Forum auftauchen. Das Thema ist also immer noch sehr aktuell.
Auch wenn dieser Code sehr kurz ist, ist das Thema keineswegs trivial.
Besonders habe ich nun das Problem der 2- und 4-schrittigen Drehgeber
behandelt.
Es sind jetzt 3 Auslesefunktionen drin, brauchen tut man aber nur die
eine zu Deinem Drehgeber passende.
Man hätte das auch per Defines machen können, aber ich wollte auch
Anfänger nicht zu sehr verwirren.
Der besondere Kniff an den Mehrschritt-Routinen ist, daß die nicht
verwendeten 1 oder 2 unteren Bits erhalten bleiben müssen. Nur dadurch
ist weiterhin die Entprellung gewährleistet.
Das Prinzip ist immer noch das gleiche, also Abtastung im
Timerinterrupt. Damit ergibt sich eine automatische Entprellung.
Eine Entprellung in Hardware mit RC-Gliedern hat demgegenüber den
Nachteil, daß sie schwer zu dimensionieren ist.
Wählt man die Zeitkonstante zu kurz, können Preller durchkommen, wählt
man sie zu lang, kommt es zu Fehlern bei schnellem Drehen.
Ich habe das Programm mit nem Drehgeber mit 96 Stellungen und 15mm
Drehknopf getestet. Man muß schon sehr schnell drehen, damit Schritte
verloren gehen.
Wichtig ist, daß die Auslesefunktion "encode_read*()" so oft aufgerufen
wird, daß nicht mehr als 127 Schritte dazwischen liegen.
Wenn das nicht gehen sollte, kann man aber die Schrittvariable und die
Auslesefunktion einfach auf 16 Bit erweitern (int16_t), dann sinds 32767
Schritte, das sollte dann reichen.
Peter
Moin Peter,
ich nutze deinen Code um einen 2-schrittigen Drehgeber auszulesen. Das
klappt auch hervorragend, wenn ich zum Beispiel einen Wert hoch- und
herunterzähle. Nun möchte ich aber den Drehgeber zur Navigation in einem
Menu einsetzen. Die Hauptschleife in meiner main.c sieht wie folgt aus:
1
while(1){
2
if(input_read_encoder()>0)
3
menu_next_entry(&menu_context);
4
elseif(input_read_encoder()<0)
5
menu_prev_entry(&menu_context);
6
elseif(input_get_key())
7
menu_select(&menu_context);
8
}
Es wird also immer die Funktion, die bei dir encode_read2() heißt
aufgerufen und überprüft ob man mit oder gegen den Uhrzeigersinn gedreht
hat. Das Ganze klappt aber nicht wirklich zuverlässig. Theoretisch soll
bei jedem Einrasten entweder die Funktion menu_next_entry() oder
menu_prev_entry() aufgerufen werden. Dem ist aber nicht so, denn
manchmal muss ich den Drehgeber 4-5 Rastungen weit drehen, damit auf den
nächsten Menüpunkt gesprungen wird. Manchmal reicht aber auch eine
Rastung. Setze ich deinen Code möglicherweise falsch ein?
Vielen Dank, Sven
Wenn Du die Funktion nicht an mehreren Stellen gleichzeitig aufrufst,
sollte es funktionieren.
Gib dochmal die Variable "val" aus meinem Beispiel auf das LCD aus, ob
sie richtig zählt.
Ich hab den Code noch leicht verbessert:
http://www.mikrocontroller.net/articles/Drehgeber#Beispielcode_in_C
damit er schon nach der Initialisierung richtig zählt und das "volatile"
hinzugefügt, damit er auch bei Inlining funktioniert.
Peter
Hallo Peter,
der Tipp mit dem mehrmaligen Aufrufen war schonmal Gold wert, viel
besser, aber dennoch brauche ich manchmal noch 2-3 Dreher, um zu
springen. Woran könnte es jetzt noch liegen?
1
while(1){
2
val=input_read_encoder();
3
4
if(val>0)
5
menu_next_entry(&menu_context);
6
elseif(val<0)
7
menu_prev_entry(&menu_context);
8
elseif(input_get_key())
9
menu_select(&menu_context);
10
}
Und ja, wenn ich val einfach hoch- und runterzählen lasse, klappts
perfekt.
mfG, Sven
Ich glaube ich habe jetzt die Geschichte gelöst: Vorher war die
Abtastfrequenz bei 100Hz, ich dachte das reicht bei langsamen Drehen.
Nun habe ich 1kHz und es läuft problemlos. Tja da dachte ich, ich könnte
das Interruptaufkommen reduzieren, geht aber anscheinend nicht. Egal.
Danke nochmal.
@Peter ( danni )
wenn ich Deinen Code im µVision compilieren will, hagelt es Fehler ohne
Ende.
Ich meine die aus dem Ursprungsthread.
Wo Du beide Varianten ( Tabelle und Umwandlung ) darstellst.
Fehlermeldung von Keil:
compiling Gray.c51...
GRAY.C51(25): error C141: syntax error near 'INT_T0', expected 'const'
GRAY.C51(26): error C132: 'INT_T0': not in formal parameter list
GRAY.C51(26): error C141: syntax error near '{'
Target not created
etc.pp.
Ich habe keine Ahnung von "C" . Wo liegt das Problem ???
Hintergrund:
Ich habe mit meiner ASM Routine einige Sorgen.
Beitrag "Probleme mit dem drehgebr 8051 in ASM"
Daher will ich mal sehen was Deine Routine draus macht.
Danke
Stephan Henning wrote:
> Ich meine die aus dem Ursprungsthread.
Dann poste nächstes mal auch in dem richtigen Thread.
Denke mal an Deine Mitmenschen, die vielleicht auch Anfänger sind und
dann völlig konfusioniert sind, weil Du was im falschen Thread postest
:(
> GRAY.C51(25): error C141: syntax error near 'INT_T0', expected 'const'
Heißt, er kennt 'INT_T0' nicht.
Ich hab mir die Interrupt-Nummern in verstehbare Symbole definiert:
1
#define INT_EX0 0
2
#define INT_T0 1
3
#define INT_EX1 2
4
#define INT_T1 3
5
#define INT_UART 4
Einfügen, dann gehts.
> Ich habe keine Ahnung von "C" . Wo liegt das Problem ???
Das ist in der Tat ein Problem.
Ohne C-Kenntnisse wirst Du ja nicht verstehen, was Du machst.
Ich kann Dich auch nicht ans Händchen nehmen und Dir alles beibringen.
Ich würde Dir erstmal raten, C aufm PC zu lernen, z.B. mit men alten
Borland-C im DOS-Fenster, damit man nicht den ganzen Windows-Schrunz
mitmachen muß.
Peter
Gast
wrote:
> muss ich bei zwei Drehgebern auch zweimal die gleiche Routine verwenden?> Gruß
Ja, Du mußt die benötigten Routinen kopieren und umbenennen, natürlich
mit verschiedenen Pins und Variablen.
Peter
Hallo Peter,
In deinem Ursprungsthread hast du den Pin Change Interrupt erwähnt, ganz
am Schluss.
Ich kenne die AVRs nicht so gut aber damit meinst du einen pin Interrupt
der auf beide Flanken reagiert oder.
=> Also statt über einen Timer IRQ zu pollen - verwendet man einfach die
pin change Interrupts => aber dann halt für die A-Spur und B-Spur sonst
wird es ja nicht gehen - oder?
... also so pseudo code:
IRQ(A pin-schange)
{
dein_enocde_code();
}
IRQ(B pin-change)
{
dein_enocde_code();
}
Ich möchte auf einem 80MHz Controller ein Drehgeber damit auslesen.
Maximale Drehfrequenz 70kHz.
Ich hoffe das es geht ohne die CPU zu sehr zu belasten.
sg
Danke
Ins Wiki
https://www.mikrocontroller.net/articles/Drehgeber#Solide_L.C3.B6sung:_Beispielcode_in_C
sollte unbedingt ein Link zur Erklärung in
Beitrag "Re: Drehgeber auslesen"
mit rein.
Leider habe ich ein Problem mit meinem Vier-Schritt-Encoder: es geht nur
aufwärts. Abwärts wird sofort (d.h. bereits bei der ersten Flanke) in
encode_read(4) ein Wert zurückgegeben.
Ersetze ich
case 4: enc_delta = val & 3; val >>= 2; break;
durch
steps = val % 4;
val /= 4;
funktioniert es.
Kann es sein, dass das Maskieren vom int8_t (Zweierkomplement) die
Ursache ist (Vorzeichen wird entfernt)? Oder wird das durch C++ anders
behandelt?
Ich habe das Beispiel von Peter Dannegger mal mit einem neuen
Algorithmus versehen. Bitte nicht als Kritik verstehen, sondern als
mögliche alternative, da es ein paar Vorteile hat.
Um es vergleichen zu können, war ich so frei und habe den Rahmen von
Peter übernommen. Leider steht mir kein ATmega16 zu Verfügung. Das
Beispiel ist auf ein ATtiny2313, mit nur leichten Anpassungen (Timer und
PINs), portiert worden.
Vorgestellt wurde der Algorithmus schon im Beitrag
Beitrag "Drehgeber (Rotary Encoder) hochauflösend und prellfrei decodieren"
Im angehängten Beispiel wurde mein Algorithmus integriert. Damit sind
beide Varianten leicht vergleichbar.
Vorteile:
- Der Algorithmus ist immer prellfrei auch in der höchsten Auflösung.
- Drehgeber mit wackeligen Rastpunkten machen keine Probleme.
- Die Position der Schaltpunkte bei Einzel- und Doppelschritt Encoders
ist besser.
- Alle Eigenschaften des ursprünglichen Codes bleiben erhalten.
- Es ist trotzdem sehr einfach aufgebaut und leicht verständlich.
Da es definitv Prellfrei ist, kann man auch ein Interrupt für beide PINs
nehmen. Der Timer ist aber vorzuziehen und man sollte wissen was man
tut. Eine bei Bedarf höhere Abtastrate gefährdet die Entprellung beim
Drehgeber nicht.
Eine Anpassung für mehrere Drehgeber stelle ich gerne bei Bedarf zur
Verfügung. Eine zweite Kopie der "encode" Funktion ist dann nicht
notwendig.
Hallo,
schön das du dir Gedanken zum Encoder gemacht hast. Nur schlägt deine
Beschreibung fehl. Alle genannten Vorteile hat der Code von Peter schon.
Demnach kann es kein Vorteil gegenüber Peters Code sein. Du müsstest
beschreiben was dein Code anders macht gegenüber Peters Code und welchen
Vorteil du genau darin siehst. Sonst verläuft das ins Leere. Aktuell
liefert er wohl das gleiche Ergebnis, gut, hat damit aber noch keinen
Vorteil. Verstehste?
Übrigens was mir gar nicht gefällt ist ein uint8_t Rückgabeparameter von
encode() und return +1 und -1. Und warum ist dgA signed und dgB
unsigned? Sieht in allen etwas fraglich aus, sprich nicht
Vertrauenswürdig. Getestet ist der Code aber? ;-)
Erstmal danke für deine konstruktive Kritik.
Du hast vollkommen recht, dass die Wahl der Typen falsch ist. Richtig
muss es so aussehen:
1
int8_tencode(uint8_tdgA,uint8_tdgB)
Aber ja, es ist getestet! Sonst hätte ich es nicht für den ATtiny2313
umschreiben müssen. In diesem Fall ist das kompilierte Ergebnis
identisch.
> ... Alle genannten Vorteile hat der Code von Peter schon.
Da kann ich dir nicht zustimmen. Die genannten Vorteile existieren
wirklich. Ich beschreibe es mal ausführlicher:
- Der Algorithmus ist immer prellfrei auch in der höchsten Auflösung.
Das Entprellen in Original entsteht durch den Abtastintervall, der
idealerweise länger als das Prellen ist. In einem Datenblatt habe ich
eine Prellzeit von max. 3ms gefunden. Hier wird mit 1ms abgetastet. Das
ist zu schnell, und lässt den einen oder anderen Preller durch. Da das
Prellen dennoch sehr stark reduziert wird, ist es in der Praxis nicht
von Bedeutung.
Prellen beim Drehgeber erzeugt immer ein Richtungswechsel, da sich immer
nur ein PIN ändert (Gray-Code). Dieser Richtungswechsel wird bei mir
herausgefiltert. Fast alle Lösungen basieren auf das Speichern des
vorherigen Zustands. Damit kann man zwar die Richtung, aber nicht den
Richtungswechsel erkennen. Dieser Lösung speichert ZWEI vorherige
Zustände, und kann damit den Richtungswechsel erkennen. Es wird nur eine
Sequenz von ZWEI Änderungen in die gleiche Richtung zugelassen. Dadurch
wird jedes Prellen ignoriert. Das Abtastintervall darf deutlich kürzer
sein als die maximal zu erwartende Prellzeit, da es für das Entprellen
nicht notwendig ist.
- Drehgeber mit wackeligen Rastpunkten machen keine Probleme.
Das ist ein signifikanter Vorteil. Das Problem tritt beim Original
nachgewiesen auf und wird im Artikel auch behandelt.
https://www.mikrocontroller.net/articles/Drehgeber#Dekoder_f%C3%BCr_Drehgeber_mit_wackeligen_Rastpunkten
Hier wird auch darauf hingewiesen, dass die Spur A und B nicht beliebig
vertauschen kann und man die Auflösung halbieren muss.
In meinem Programm ist das keine Problem. Auch der wackelnde Rastpunkt
ist ein einzelner PIN-Wechsel, wie es beim Prellen auftritt. Und dieser
wird immer herausgefiltert. Auch in der höchsten Auflösung. Der PIN muss
nicht beachtet werden und eine Halbierung der Auflösung ist nicht
notwendig.
- Die Positionen der Schaltpunkte bei Einzel- und Doppelschritt Encoder
sind besser.
Ich versuche es mal grafisch darzustellen. Die halbe Sequenz:
Vorwärts:
1
Rastung | | | | |
2
A ---|___|___|---|---|___|___|---|---|
3
B ---|---|___|___|---|---|___|___|---|
4
Peter | | | |
5
Uwe | | | |
Das ist identisch und sieht gut aus. Jetzt rückwärts:
1
Rastung | | | | |
2
B ---|---|___|___|---|---|___|___|---|
3
A ---|___|___|---|---|___|___|---|---|
4
Peter | | | |
5
Uwe | | | |
Hier sieht man, dass bei Peter der Schritt schon beim ersten Impuls
gemacht wird. Idealerweise sollte es beim zweiten Impuls erfolgen. Damit
reduziert man die Wahrscheinlichkeit beim Drücken des Drehgebers weiter
zu drehen.
Jetzt das ganze bei einer ganzen Sequenz:
Vorwärts:
1
Rastung | | |
2
A ---|___|___|---|---|___|___|---|---|
3
B ---|---|___|___|---|---|___|___|---|
4
Peter | |
5
Uwe | |
Hier wird bei Peter etwas später geschaltet. Das ist unproblematisch.
Und rückwärts:
1
Rastung | | |
2
B ---|---|___|___|---|---|___|___|---|
3
A ---|___|___|---|---|___|___|---|---|
4
Peter | |
5
Uwe | |
Es wird bei Peter sehr früh geschaltet. Wehe man hat einen wackeligen
Drehgeber. Und die Gefahr des versehentlichen Drehens beim Drücken ist
hoch.
Idealerweise schaltet man mit dem Impuls nach dem Durchschreiten der
Mitte. Das ist bei mir der Fall.
- Alle Eigenschaften des ursprünglichen Codes bleiben erhalten.
OK. Das ist kein Vorteil. Aber auch kein Nachteil.
- Es ist trotzdem sehr einfach aufgebaut und leicht verständlich.
Technisch kein Vorteil. Für Einsteiger ist es aber leichter zu verstehen
als der Code von Peter. Das hat Peter doch tief in die Trickkiste
gegriffen, was für seine hervorragenden Fähigkeiten spricht. Für ein
Einsteiger ist es aber nicht so einfach zu verstehen (schon genial, wie
aus einem Gray-Code eine Binärzahl wird). Die Portierung in andere
Sprachen ist bei mir etwas einfacher.
Welche Lösung man bevorzugt, kann natürlich jeder für sich entscheiden.
Die Lösung von Peter ist "gut abgehangen" und hat sich über Jahre
bewährt. Es schadet aber nicht meine Lösung zu verstehen und in die
Auswahl mit einzubeziehen. Vorteile sind durchaus vorhanden.
Hallo,
ich versuche es gerade zu verstehen und habs getestet. Ich versuche es
einmal zu beschreiben.
> mit 1ms Leseraster:
Dein Code unterdrückt das pendeln des aktuellen Wertes, was man beim
Code von Peter im Ergebnis ganz selten sieht. Beschreibt Peter ja auch
in seiner Doku. Peters Code wertet alles aus und korrigiert sich solange
selbst bis die Kontakte nicht mehr prellen. Das ist Peters Trick damit
trotz prellen am Ende, wenn es sich ausgeprellt hat, der korrekte Wert
erhalten bleibt. Dieses seltene sichtbare "pendeln" unterdrückt dein
Code. Ich weiß noch nicht genau wie, aber das ist meine Beobachtung. So
wie du es beschreibst.
> ohne 1ms Zeitraster:
Maximale Aufrufgeschwindigkeit. Das ist der Härtetest. Hier sieht man
ein zurückspringen eines Wertes deutlicher trotz weiterdrehen am
Encoder. Das ist bei Peters Code ausgeprägter zu beobachten. Entspricht
seiner Beschreibung. Bei deinem Code ist das weniger der Fall, es ist
sozusagen seltener und weniger der Fall.
Also aktuell zählt dein Code sichtbar schöner mit erforderlichen 1ms
Zeitraster, weil man das "auspendeln" nicht mehr sieht. Dafür bekommste
ein Bienchen. :-)
Aktuell noch ein Kommentar zum Code.
Bitte die Kommentare "1:1" und "1:4" vertauschen und dein enc_delta muss
volatile sein.
Ich bin gerade am überlegen wie man die 3 case switch wegbekommt ...
Hallo,
habe etwas Kosmetik betrieben und einen Raster Parameter eingesetzt.
Jetzt könnte man noch überlegen die Arbeit in der ISR zu reduzieren.
Muss das alles in der ISR erfolgen? Man könnte doch nach dem auslesen
der Phasen und der letzten digitVal Zuweisung die "Rechenlast" der ISR
beenden und alles weitere in der mainloop berechnen lassen? Abhängig
davon ob sich digitVal geändert hat oder nicht?
Die unteren Leerzeilen im Code sind nicht von mir. ;-) Muss irgendwie
die Forumssoftware einfügen.
Die drei Case alternativen wurden nicht willkürlich gewählt.
Die Masken sind speziell abgestimmt, um den Schaltpunkt zu wählen.
Speziell
1
value=absolutCounter>>(encoderRaster/2);
zerstört die Schaltpunkte. Jetzt hast Du wieder die gleichen wie bei
Peter.
Wähle immer das richtige CASE Konstrukt für deine Anforderung. Hier ist
eine bedingte Kompilierung nützlich. Ich habe meine Aktuelle h-Datei
angehängt. Diese enthält noch einen Parameter für bis zu 8 Encoder.
> Muss das alles in der ISR erfolgen?
Ein klares JA. Das muss sein. "encDelta" muss im Interrupt aktualisiert
werden. Die Dauer der Main-Loop ist unbestimmt. So könnten dir
Änderungen verloren gehen.
Uwe K. schrieb:> Die drei Case alternativen wurden nicht willkürlich gewählt.> Die Masken sind speziell abgestimmt, um den Schaltpunkt zu wählen.>> Speziell>
1
>value=absolutCounter>>(encoderRaster/2);
2
>
> zerstört die Schaltpunkte. Jetzt hast Du wieder die gleichen wie bei> Peter.
Okay. Funktioniert jedoch weiterhin so gut wie vor meiner Änderung. :-)
> Wähle immer das richtige CASE Konstrukt für deine Anforderung. Hier ist> eine bedingte Kompilierung nützlich. Ich habe meine Aktuelle h-Datei> angehängt. Diese enthält noch einen Parameter für bis zu 8 Encoder.
Gut, dass würde ich dann mittels Lib und angelegten Objekten erschlagen.
>> Muss das alles in der ISR erfolgen?> Ein klares JA. Das muss sein. "encDelta" muss im Interrupt aktualisiert> werden. Die Dauer der Main-Loop ist unbestimmt. So könnten dir> Änderungen verloren gehen.
Nicht das wir uns hier falsch verstehen. Ich wollte nicht die gesamte
ISR abschaffen. Ich wollte nur das Pin auslesen bis
1
if((digitHist&0b11)==digitVal)return0;// no change --> return
in der ISR lassen. Alles danach könnte doch außerhalb der ISR erfolgen?
Der dritte Parameter ist die Encoder Nummer und kann von 0 bis 7 gehen.
Praktisch, wenn man mehrere Encoder angeschlossen hat.
> ... Ich wollte nur das Pin auslesen bis>
1
>if((digitHist&0b11)==digitVal)return0;// no change --> return
2
>
> in der ISR lassen. Alles danach könnte doch außerhalb der ISR erfolgen?
So viel kommt dann nicht mehr. Ein klares nein.
P.S.: Meine erste Erfahrung mit Arduino. Ich benutze sonst C mit
Atmel-Studio (nicht C++). Das ist nicht meins... 🙄
>Alles danach könnte doch außerhalb der ISR erfolgen?
Ja, natürlich. Die einzige Aktion, die unverhandelbar in der ISR
stattfinden muss, ist das Abspeichern der aktuellen Pegel der
Encoder-Pins ins SRAM.
Zwiespältig wird die Sache auch erst, wenn die Encoder-ISR öfter (z. B.
16 mal so oft) aufgerufen wird als die Main. Dann gibt es zwei
grundsätzliche Möglichkeiten:
(1) Man möchte die ISR so kurz wie möglich halten. Dann ist man
gezwungen, für das Abspeichern einen (hier 16-fach-) Puffer
einzurichten, der von der ISR beschrieben und von der Main ausgelesen
wird. Nachteil: Puffer = mehr Code, mehr CPU-Zeit, mehr Gehirnschmalz,
mehr Fehlerquelle.
(2) Man möchte sich das Geraffel mit dem Puffer sparen. Dann ist man
gezwungen, direkt in der ISR zu rechnen. Nachteil: Die ISR braucht
länger.
Auf die Frage, welche dieser beiden Optionen "besser" ist, gibt es keine
allgemeingültige Antwort. Das muss man für einen konkreten
Anwendungsfall individuell entscheiden.
Hallo,
@ Uwe:
denke bitte daran das dein enc_delta volatile sein muss.
Und diese Zeile fiel mir noch auf.
> TCCR0B = 1<<CS01^1<<CS00;
Das sollte
1
2
TCCR0B=(1<<CS01)|(1<<CS00);
3
oder
4
TCCR0B=_BV(CS01)|_BV(CS00);
lauten.
Weil ich gerade bei Kritik bin. Habe bei mir auch was wichtiges
vergessen.
Nach dem
const uint8_t saveSREG {SREG};
muss natürlich
cli() aufgerufen werden, sonst ist das sinnlos. :-)
> P.S.: Meine erste Erfahrung mit Arduino. Ich benutze sonst C mit> Atmel-Studio (nicht C++). Das ist nicht meins...
Ist kein Problem. Solange wir verstehen was der andere sagen möchte ist
alles i.O., egal ob C, C++ oder andere Sprachen.
Danke dir für den Encoder Code und Erklärungen, auch Danke an
LostInMusic für die Hinweise. Ich werde das weiter verfolgen und
umsetzen.
> @ Uwe:> denke bitte daran das dein enc_delta volatile sein muss.> Und diese Zeile fiel mir noch auf.>> TCCR0B = 1<<CS01^1<<CS00;> Das sollte>
1
>TCCR0B=(1<<CS01)|(1<<CS00);
2
>oder
3
>TCCR0B=_BV(CS01)|_BV(CS00);
4
>
> lauten.
Die Intension mich an diese Diskussion anzuhängen, war es meinen
Algorithmus in eine bekannte Umgebung zu bringen. Damit wird die
Funktion vergleichbar.
Meinen Teil habe ich dort nochmal separat angehängt.
Beitrag "Re: Drehgeber/Encoder 1-, 2- oder 4-schrittig"
Beide Anmerkungen sind nicht Teil meines Algorithmus, sondern eine Kopie
des Originals. Aber deine Hinweise sind zu empfehlen. Wahrscheinlich
wird es auch ohne die Anpassung problemlos funktionieren.
Es geht mir nicht darum, den Code von Peter zu optimieren. Sondern
meinen Algorithmus vorzustellen.
Hallo,
okay
TCCR0B = 1<<CS01^1<<CS00;
ist nur eine ungewöhnliche Schreibweise zum darstellen wie man die
Wertigkeiten der Bits miteinander verknüpft. Schreibt kaum jemand so.
Aber das enc_delta volatile sein muss sollte jeden klar sein. Solche
Fehler sind absolut schwer zu finden, weil sie sporadisch auftreten.
Hier ist es von Peter richtig. Diese Doku ist das eigentliche Original.
https://www.mikrocontroller.net/articles/Drehgeber
In dem verlinkten Thread sicherlich nur ein Flüchtigkeitsfehler.
Veit D. schrieb:> ist nur eine ungewöhnliche Schreibweise
So kann man das auch nennen. ^ ist EXOR, das hier anstelle eines OR zu
verwende, funktioniert zwar, ist aber wirklich ... sehr schräg.
DerEinzigeBernd schrieb:> Das geht übersichtlicher:> switch (digitHist) // 1:1 four step encoders, wird noch laut Rastung> passend geschiftet> {> case 0b111000:> case 0b100001:> case 0b000111:> case 0b011110:> returnValue = 1;> break;> case 0b110100:> case 0b010010:> case 0b001011:> case 0b101101:> returnValue = -1;> break;> }
one step further towards simplification :-)
switch (digitHist) {
case 0b111000: case 0b100001:
case 0b000111: case 0b011110:
return 1;
case 0b110100: case 0b010010:
case 0b001011: case 0b101101:
return -1;
}
Reine Kosmetik. Dein erster Vorschlag, in dem man alles "CASE"
untereinander scheibt ist leichter zu lesen. Aber stimmt, ein BREAK
braucht es nicht, wenn man direkt RETUNed.
Und die Variable returnValue finde ich nicht gut. Man braucht sie nicht.
Ist in meinen Code auch nicht enthalten.
Ich bleibe auch dabei, dass es die 1:4 Abfrage ist. Schließlich werden 4
Änderungen gezählt für ein Zyklus.
Schon alles richtig so:
Beitrag "Re: Drehgeber/Encoder 1-, 2- oder 4-schrittig"
Einfach einbinden und Finger weg.
Warum werden eigentlich die Pegel der Leitungen A und B vom Encoder
nicht einfach gleich in eine Variable "EncPosition" verrechnet?
Also ohne "EncDelta" und immer sofort, nachdem auf A oder B irgendeine
Änderung festgestellt wurde. EncPosition enthält dann schlicht jederzeit
die korrekte Position des Encoders. Handelt man sich damit irgendein
Problem
ein? Kann mich mal jemand darüber aufklären, wozu der Umweg über das
EncDelta gut sein soll?
LostInMusic schrieb:> Warum werden eigentlich die Pegel der Leitungen A und B vom Encoder> nicht einfach gleich in eine Variable "EncPosition" verrechnet?
EncPosition ist eine absolute Position. Die Position ist sehr abhängig
von der Anwendung. Wenn es z.B. 10 Menu-Eintrage, gibt, ist 10 die
höchste Zahl. Es darf nicht weiter zählen. Deshalb ist es besser die
Position vom individuellen Programm zu setzen. Es geht auch die
Information der Drehrichtung verloren. Das Problem ist gerade aktuell:
Beitrag "STM32 Encoder als +/- Wert auswerten"
EncDelta ist eine relative Position. Dort sind alle Änderungen des
Drehgebers seit der letzten Abfrage addiert. Dadurch ist man nicht
gezwungen jede Bewegung sofort zu verarbeiten, da diese im EncDelta
summiert wird. Die Information der Drehrichtung bleibt erhalten. Um
Limits im unteren und oberen Bereich muss man sich noch nicht kümmern.
Manchmal benötigt man den absoluten Wert nicht.
EncDelta ist viel flexibler zu verarbeiten.
>Die Kontakte des Encoders prellen - wie jeder Schaltkontakt.>Das willst Du nicht in dem Zähler haben.
Ach so. Was da aber eigentlich passiert, ist eine simple Unterabtastung,
und die ist über das "EncDelta" nur unnötig kompliziert programmiert.
Könnte man ja genausogut erreichen, indem man gleich EncPosition
berechnet und den Wert zu Beginn der Main einfach cacht, also mit "=" in
eine zweite Positionsvariable kopiert.
>EncDelta ist viel flexibler zu verarbeiten.
OK, ist ein Argument. Man kann tatsächlich fragen, wozu man die
Absolutposition des Gebers berechnen soll, wenn die nirgendwo benötigt
wird.
Allerdings: Mit Entprellen hat das nichts zu tun. Wenn der Geber um 1
wackelt (mechanischer Kontakt prellt oder Maschine mit optischem Geber
vibriert), dann wackelt auch jedes EncPosition, das man durch eine
Unterabtastung daraus gewinnt, um 1, nur weniger oft.
Danke fürs Feedback.