Hallo Forum,
ich versuche einen Glassmasstab per STM32 auszulesen.
Der Glassmastab hat eine Auflösung von 1 um,
und sollte also auf 5 cm 50.000 Impulse liefern.
Er hat einen 0815 Quadratur-Encoder eingebaut.
Ich bekomme aber leider nur 25.000 Impulse im Counter gezählt,
obwohl ich dem STM32F103C8T6 (Bluepill...) sage er soll
beide Kanäle zählen und die Doku sagt, dass immer beide Flanken
ausgewertet werden. Ich bekomme also keine '4x' Auflösung hin.
Bevor ich also heute Abend den Glassmasstab durch zwei Schalter ersetzt
und das wirklich auf Flankenebene durchspiele,
dachte ich vielleicht sieht ja jemand den Fehler per Glaskugel.
Auswerte Schmema ist Standard:
Timer2 ist als Encoder definiert und zaehlt von 0..16000, und wird auf
8000 gesetzt. Impulse zählen hier also hoch und runter (unsigned Wert,
deswegen der Shift um 8000).
Timer1 kommt jede Millisekunde, addiert Timer2->CNT - 8000 zu einem long
und setzt wieder auf 8000.
Die Schleife gibt dann alle Sekunden den long per Serieller
Schnittstelle aus...
Gruss
FloF
Unter Verwendung der Standard Peripheral Lib hab ich das mit genau
diesem µC schonmal erfolgreich umgesetzt.
Anbei der Code dazu. Welche Register genau die Funktionen setzen kannst
du in der entpsrechenden Source von ST nachschauen. (stm32f10x_tim.c)
Ich möchte dieses File aus Copyright-Gründen jetzt hier nicht hochladen.
Das Zurücksetzen des Encoder-Counters halt ich übrigens für keine gute
Idee. Wenn zwischen Auslesen und Zurücksetzen ein Encoder-Puls
reinkommt, verlierst du den. Ein sehr seltener Fall, aber irgendwann
wird das passieren und du wunderst dich warum die Position nicht stimmt.
Lass den Counter stattdessen einfach die vollen 16bit laufen und bilde
immer die Differenz zwischen den jeweiligen Zaehlerständen (Codebeispiel
siehe unten). Ein Überlauf macht da nichts, da die Differenz trotzdem
stimmt (so lange du öft genug aufrufst, dass nicht 2^16 Pulse zwischen
zweimal Auslesen reinkommen).
hi,
ich verstehe nicht warum dein auswertschema standard ist, für mich ist
das eher "esoterisch".
standard ist für mich ein interrupt bei jeder flanke an den 2 pins und
dann auswertung.
das sollte ohne zweifel auch immer funktionieren unter der voraussetzung
die maschine ist schnell genug mit bezug zum timing des external events
und das passt ja wohl hier!
das habe ich schon x-mal so implementiert mit 8-bit herzschlag und einer
function die beide isr benutzen.
erleuchte mich mal bitte, weil ich neige bereits dazu an murcks zu
denken.
dk4ug
apollo2mond schrieb:> "esoterisch"
Die meisten (alle?) STM32 haben einen Quadratur-Decoder in Hardware.
Damit kann ohne CPU-Beteiligung Frequenzen bis hin zum CPU-Takt
erfassen.
Da ist also nix esoterisch dran, sondern es ist - sofern die Hardware
vorhanden ist - der Standardweg.
Soft-UART oder Soft-SPI implementiert man ja i.d.R. auch nur, wenn keine
Hardware dazu vorhanden (oder frei) ist.
Florian F. schrieb:> r.gen->CCMR1 |= TIMER_CCMR1_CC1S;> r.gen->CCMR1 |= TIMER_CCMR1_CC2S;
sind diese Zeilen richtig? Laut manual sollte das so initialisiert
werden:
• CC1S= ‘01’ (TIMx_CCMR1 register, TI1FP1 mapped on TI1)
• CC2S= ‘01’ (TIMx_CCMR2 register, TI2FP2 mapped on TI2)
Florian F. schrieb:> Bevor ich also heute Abend den Glassmasstab durch zwei Schalter ersetzt> und das wirklich auf Flankenebene durchspiele, ...
Das wäre doch wohl das naheliegendste. Den Debugger kannst du dann
gleich mitlaufen lassen.
> Ich bekomme aber leider nur 25.000 Impulse im Counter gezählt,...
Wieso "nur". Wenn immer 25.000 raus kommt, hast du immerhin nicht die
Maximalgeschwindigkeit bei der Bewegung überschritten und der Fehler
läßt sich reproduzieren - beste Bedingungen zum Debuggen.
Du bist sicher, dass du das Datenblatt deines Geber richtig
interpretiert hast oder hast die 1µm Auflösung durch eine Messung
verifiziert?
ok, der hw decoder macht sinn, wenn du die maschine entlassten
musst/willst und ein anspruchvolles external timing hast. beides wäre
hier nicht der fall.
interessantes beispiel, weil mein sw decoder funktioniert und braucht
augenscheinlich weniger hw resourcen - ich denke, noch kompakter geht es
nicht!? hat aber grenzen, wenn die interrupts nicht verarbeitet werden
können ... und darum gibt es ja das eventsystem.
ich kenne halbwegs die stm32 familie und das event system, aber daraus
folgt für mich nicht dein standard als ansage.
aber natürlich muss auch ein hw encoder funktionieren und rund laufen.
ich zieh mir mal jetzt die details der doku und lib rein ...
keep going
...
Das scheint mir der wahre Pfusch, siehe
https://www.mikrocontroller.net/articles/Drehgeber...
gleich lockere starke worte?!
diesen guten beitrag kenne ich.
mein gewählter ansatz ist ok, wenn du das in dem !bewusstsein! machst,
das pendeln/prellen die maschine durch "unnötige" interrupts belastet,
führt aber nie zu fehlerhaften werten und das ist der entscheidene
punkt.
copy/past ohne verstehen/bewusstwerdung sehe ich als problematischer an,
darum kenne ich immer gut die hw plattform und lese auch die details.
ich benutze für jede hw plattform einen emulator/debugger, um beobachten
zu können was da abgeht.
die arduino plattform taugt nur zum quick and dirty start ... und
printf's als debug tool verdient keine erwähnung.
ich freue mich schon auf die auflösung und lass mal nur aus interesse
die stopuhr laufen, um zu realisieren wie lange wir brauchen.
parallel hole ich mal mein entwicklungssystem raus um auf registerebene
deinen code durchzufahren ...
Ihr müsst auch darauf achten das eure Positions Variable nicht
überläuft! Dreht sich das Rad immer in die gleiche Richtung läuft die
Variable über.
Das Zurücksetzen an sich macht schon Sinn wenn man sich auf die Position
der letzten Auswertung bzw. Den Mechanischen Überlauf der Hardware
bezieht.
Der Weg das Register zurückzusetzen ist in der Tat wage, man kann aber
das overflow Bit in der Hardware auswerten und die Absolute Position
berechnen.
Marco H. schrieb:> Ihr müsst auch darauf achten das eure Positions Variable nicht> überläuft!
Die Positionsvariable ist unsigned 16 Bit. Ein Überlauf macht ihr
überhaupt nichts, weil man ohnehin immer mit Differenzen arbeitet. Die
ist dann 16 Bit vorzeichenbehaftet.
Bei meinem Projektchen läuft die Positionsvariable alle 0,8 Sekunden
über und fühlt sich Pudelwohl dabei.
apollo2mond schrieb:> gleich lockere starke worte?!
Wer die Methode aus dem Reference Manual als 'esoterisch' und 'Murks'
bezeichnet darf auch mal ein "Pfusch" für seine Variante, vor der im
Wiki explizit gewarnt wird, einstecken.
Glashaus und in den Wald rufen und und so.
apollo2mond schrieb:> ok, der hw decoder macht sinn, wenn du die maschine entlassten> musst/willst und ein anspruchvolles external timing hast. beides wäre> hier nicht der fall.
Mit Verlaub - Du kannst nicht beurteilen, was sonst noch so außerhalb
des obigen Minimalbeispiels anliegt.
----
Dein Ansatz hat neben dem Interrupts-lasten-die-CPU-vollständig aus noch
ein Problem: Seine Fehlerfreiheit kommt daher, dass er nur jede 2.
Flanke zählt (2X Auflösung).
Gefragt war aber gerade 4X.
4X geht nicht ohne Jitter von +-1, wie man auch in Abbildung 93, Seite
330 des STM32F103 Reference Manuals sehen kann. Aber das stört mich hier
nicht.
Die Hardwarelösung des STM32 ist ja nichts anderes als deine
State-Maschine, aber eben ohne das Worst Case Problem.
------
Mein Debuggen per Schalter hat eine interessante Sache geliefert, die
nicht zur Tabelle 81 im Reference Manual passen möchte:
Und zwar zählt er mir immer nur die 2. rising und die 2. falling Edge
Taster gegen GND, Pullup an, A=1,B=1:
drücke A -> A=0, B=1 -> keine Veränderung.
drücke B -> A=0, B=0 -> +1
A los -> A=1, B=0 -> keine Veränderung
B los -> A=1, B=1 -> +1
und umgekehrt
drücke B -> A=1, B=0 -> keine Veränderung
drücke A -> A=0, B=0 -> -1
B los -> A=0, B=1 -> keine Veränderung
A los -> A=1, B=1 -> -1
Das passt aber weder zu 'Counting on TI1' (oder TI2) only, noch zu
'counting on TI1 and TI2'. Im ersteren Fall sollte er bei rising &
falling nur von A oder nur von B zählen, und im zweiten bei jeder
Veränderung.
Also entweder 'input filter', oder 'prescaler', oder die Addition
versaut. Oder hab ich was übersehen?
Marco H. schrieb:> Ihr müsst auch darauf achten das eure Positions Variable nicht> überläuft! Dreht sich das Rad immer in die gleiche Richtung läuft die> Variable über.>> Das Zurücksetzen an sich macht schon Sinn wenn man sich auf die Position> der letzten Auswertung bzw. Den Mechanischen Überlauf der Hardware> bezieht.>> Der Weg das Register zurückzusetzen ist in der Tat wage, man kann aber> das overflow Bit in der Hardware auswerten und die Absolute Position> berechnen.
Man benötigt kein Rücksetzen, wenn sichergestellt ist, dass man den
Zähler so abfragt, dass maximal die halbe Zählerbreite hochgezählt
werden konnte, hier also 32767 Impulse. Erfolgt das Auslesen vorher, so
kann man immer entscheiden, ob der Zähler herauf- oder heruntergezählt
wurde. Die ausgelesene Position speichert man ab und das Spiel beginnt
von neuem.
So kann auch kein Impuls verloren gehen (eben weil der Zähler nur
ausgelesen wird).
Die zyklische Abfrage selbst kann dann idealerweise in einem
Timerinterrupt stattfinden, also bspw. alle 1ms. Das führt zu einer
maximalen Zählfrequenz von 32767*1000 > 32MHz! - ohne großartige
Controllerbelastung.
Im Moment sehe ich Deinen Fehler auch noch nicht. Hier mein
(funktionierender) Code für Timer 3 eines STM32F0DISCOVERY:
Florian F. schrieb:> 4X geht nicht ohne Jitter von +-1, wie man auch in Abbildung 93, Seite> 330 des STM32F103 Reference Manuals sehen kann. Aber das stört mich hier> nicht.
Da bauen die extra eine Hardware für diese Sache ein und dann kann die
nicht einmal die Umwandlung des "Jitter" in eine
1-Schritt-Umkehrhysterese leisten (zumindest optional)? Das würde
immerhin nur sehr wenige zusätzliche Gatter kosten.
Wenn das wirklich so wäre (was ich kaum zu glauben vermag): eine
absolute Frechheit...
c-hater schrieb:> und dann kann die> nicht einmal die Umwandlung des "Jitter" in eine> 1-Schritt-Umkehrhysterese leisten (zumindest optional)? Das würde> immerhin nur sehr wenige zusätzliche Gatter kosten.
Eine Umkehrhysterese? Dann würde ich aber hysterisch. Die Funktion des
Zählers ist sauber und wie erwartet: Bei jeder Flanke wird herauf- oder
herabgezählt.
... weiter gehts, ich denke du must mal die dinge "sortieren"
- 4x encoder und jitter haben nichts miteinander zwingend zu tun!
- das beispiel im manual zeigt nur, das bei prellen/jitter (realer
encoder) der zähler immer +-1 zählt und daher kein fehler ensteht
- ein idealer encoder wäre ohne jitter
- der stm32 encoder mode macht alles richtig und hat auch kein problem
mit x4!
- dein schaltertest erscheint mir unpassend weil die schalter natürlich
prellen und daher muss auch nicht immer +1/-1 gezählt werden, wie ja das
beispiel dir zeigt
- die regelmäßigkeit der testergebnisse erklärt sich mir nicht
- somit bleibt offen, ob die sw überhaupt die baustelle ist!
- entprell die schalter mit d-ff oder schalte port lines direkt und
wiederhol den test
- ich denke, das ergebnis wird sich ändern
- was dein glasstab da so macht solltest du mal mit dem oszi anschauen!
- nur am rand, meine encoderanwendung ist auch 4x
keep going!
- 1-Schritt-Umkehrhysterese ... immer diese spinner, nix gelernt dafür
doof und glücklich!?
MERKER:
Manaual Seite 329 ...
Figure 93 gives an example of counter operation, showing count signal
generation and direction control.
!!!
It also shows how input jitter is compensated where both edges are
selected.
!!!
total logisch ... both edges selected
nur aus dem grund zählt auch meine sw ohne fehler, weil beide flanker
ausgewertet werden.
... und könnte/wird auch erklären dein unstimmiges testergebnis,
schaun wir mal wie es weitergeht!
passend zum hw testen, sei die frage erlaubt:
ist der unterschied zwischen verifizieren und validieren oder
falsifizieren geläufig?
apollo2mond schrieb:> - ein idealer encoder wäre ohne jitter
Jitter ist doch völlig egal. Es spielt für die Funktion einen
Dreh-Encoders überhaupt keine Rolle, ob eine Flanke etwas früh oder
später kommt, solange die Reihenfolge eingehalten wird.
https://de.wikipedia.org/wiki/Jitter
Was meinst du mit "Jitter"?
Wolfgang schrieb:> apollo2mond schrieb:>> - ein idealer encoder wäre ohne jitter>> Jitter ist doch völlig egal.
Ich glaube, Du sprichst nicht den Urheber des Jitter-Problems an, denn
apollo2mond schrieb:> - 4x encoder und jitter haben nichts miteinander zwingend zu tun!
Bei diesem Thema kann aus Hysterie schnell eine Hysterese werden, sowie
andere Leute von Pfusch reden, nur weil sie sich bei jedem Interrupt in
die Hose machen. Der µC würde überlastet und könne jederzeit
explodieren!
Da wird dann "Pendeln, Prellen, Surren, Zirren" usw. erfunden, um eine
vermeintliche Universallösung zu propagieren, die allein in
Schneckenhausen funktionieren würde.
Wenn ich einen Hardwaredekoder auf dem Chip habe, wird natürlich dieser
verwendet. Aber bei einem 1 µm Glasmaßstab reicht auch ein ATmega48 für
Verfahrgeschwindigkeiten <= 200 - 400 mm/s. Gerade bei einem Lineargeber
ist dank beidseitigen Anschlages die Interruptlast begrenzt.
Mich würde ja mal interessieren, um welchen Geber es sich überhaupt
handelt (genaue Typenbezeichnung). Hat er ggf. nur 2 µm Auflösung?
... in der tat, den begriff jitter hätte ich hier auch nie selber
gewählt!
ich denke, denverwendet st hier irreführend, weil kontaktprellen damit
beschrieben wird.
ABER jitter bei encodern extistiert (je nach bauart)und ist auch ein
echtes problem, weil daraus eine ungenauigkeit oder ein
reproduzierbakeitsproblem für eine gesuchte/detektierte position folgen
kann.
richtig, jitter in unserem diskutierten fall würden wir garnicht
bemerken, ausser wir messen immmer die strecke nach, die mit der
possitionsangabe korrespondiert
meine erfahrung ...
wann auch immer ich nur copy/past mache, egal ob hw oder sw, das endete
immer in mehr arbeit als wenn ich es gleich richtig selber gemacht hätte
und war nie eine abkürzung, aber dafür oft viel frust.
cyberhermit
Es handelt sich um GCS898-K5, und ja, sie sind für 1um spezifiziert.
Schalter waren natürlich mit RC-Glied zur Vermeidung von Prellen dran.
Wenn ich einen Encoder über Pins simuliere (und entsprechend verbinde)
zählt er immer noch falsch:
1
void step_left()
2
{
3
digitalWrite(PA2, 0);
4
delay(1);
5
digitalWrite(PA3, 0);
6
delay(1);
7
digitalWrite(PA2, 1);
8
delay(1);
9
digitalWrite(PA3, 1);
10
delay(1);
11
}
führt zu einem Count von 200, und nicht zu 400.
Aber: Mit STM32CubeMX geht es!
-> Entweder es ist doch ein Fehler in meiner Registerdefinition,
oder das STM32Duino Framework setzt irgendwo was was nachher stört.
Mal schaun, wann ich dazu komme das zu debuggen.