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)
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.
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
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
>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?
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. :-)
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.
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
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.
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
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.
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
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
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
>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
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...
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.
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.
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.
>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.
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.
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.
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...
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.
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?
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.
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.
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.
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 ;-)