Hallo
Ich suche nun schon länger nach einer Möglichkeit, einen
Inkrementalgeber "solide" auszulesen.
Leider hab ich das bisher nicht geschafft. Ich arbeite mit Pin interrupt
und nicht mit timerinterrupt (das als "solide" zu bezeichnen finde ich
schon ziemlich gewagt).
Pininterrupt hat/sollte den Vorteil haben, dass erst Schritte verloren
gehen wenn der uC zu langsam ist (in meinem Fall ein Raspberry Pi) und
keine unnötigen Abtastungen erfolgen zudem lässt sich die genauigkeit
steigern.
Nun hab ich schon einiges im Netz gesucht und bisher nur irgendwelche
Bastellösungen mit Timerinterrupt und flankenauswertung von A oder B
gefunden.
Ich arbeite Aktuell mit Flankenauswertung von A und B (positiv und
negativ) somit erreiche ich die 4fache auflösung. Der Geber prellt nicht
(liess sich zumindest nicht messen), Software seitig werden aber die
Signale trotzdem "entprellt". Ausserdem dreht der Schrittmotor an den
der Geber gekoppelt ist ziemlich langsam (geschwindigkeit ist egal).
Wenn ich nun einen testlauf starte, dann gehen ständig Schritte
verloren, so dass die Position teilweise um fast 10° daneben ist nach
100mal hin und her fahren.
Nun meine Frage, hat irgendjemand dass schon eimal gemacht und kann mir
seinen Code oder Teile draus zur Verfügung stellen?
Andere Frage, ist es möglich, dass das GPIO Interface (über wiringPi.h,
C-Programm) zu langsam ist um 1000 bzw. 4000 Inkrements pro Umdrehung
auszuwerten (motor dreht vll mit 10RPM oder so)?
Danke und Gruss Funkuchen
1
voidencoderISR_A(void){
2
if(enableInterrupt==1){//for disabling interrupt after 1 execution (to avoid jitter)
3
setEncoderPosition();
4
enableInterrupt=0;
5
}
6
}
7
8
voidencoderISR_B(void){
9
if(enableInterrupt==0){//for disabling interrupt after 1 execution (to avoid jitter)
Hast du schon kontrolliert, ob es ein mechanisches Problem ist? Wenn
Belastung und Elastizität nicht stimmt, springt ein Schrittmotor bei
einem elektrischen Schritt mehrere mechanische Schritte.
Einfach mal herumprobieren. Weniger Strom oder mit dem Finger leicht
abbremsen. Oder erst mal mit einem primitiven durchsichtigem Programm
100 mal hin und her fahren.
Der Schrittmotor der fährt unbelastet genau die angegebenen Schritte,
das habe ich schon in mehreren Tests ausprobiert, lediglich im
Mikroschrittbetrieb ist die Positionierung, wie zu erwarten, innerhalb
eines Vollschrittes ungenau.
Ich hab auch schon von Hand ausprobiert, ob er den Nullpunkt verliert
und tatsächlich war der Nullpunkt des Inkrementalgebers plötzlich
verschoben, zwar jeweils nur minimal, aber dennoch am falschen Ort.
Patrick B. schrieb:> nur irgendwelche> Bastellösungen mit Timerinterrupt
Hast Du mal versucht, sie zu verstehen, warum sie so solide sind?
Ich habe sie z.B. zur Probenpositionierung auf einem AT89C51CC03 mit
50kHz Abtastinterrupt implementiert, funktioniert einwandfrei.
Wer sagt denn dass das wirklich solide ist? Von mir aus kann man das für
einen Drehknopf nehmen, aber nicht für einen Encoder an einem Motor (der
eigentlich schneller drehen sollte als bisher getestet).
und ja, ich hab verstanden was die Einwände gegen die Pininterrupt
Methode sind, aber hast du schon versucht meinen Code zu verstehen? Der
müsste zumindest theoretisch die Vorteile beider Varianten umsetzten.
- 4fache Auflösung
- entprellung, resistent gegen pendeln
- effiziente CPU nutzung
Ich hab mir hier im Forum einige Beiträge dazu durchgelesen und
anscheinend haben die wenigsten verstanden, wie man es mit Interrupts
machen können sollte. In diesem Abschnitt auf Wikipedia ist eine Idee
die zumindest in der Theorie funktionieren sollte beschrieben:
"Wenn der Inkrementalgeber genau auf der Grenze einer Flanke steht, so
können durch kleinste Erschütterungen oder andere Effekte
(Tastenprellen, elektromagnetische Störungen) zusätzliche Impulse
auftreten. Sogenannte Schrittabellen elimieren Fehler durch
Mehrfachimpulse an den Schaltzeitpunkten: Angenommen B steht auf 1 und A
wechselt von 1 auf 0 und prellt dabei: Amem und Bmem sollen die
gespeicherten Werte vor einer neuen Flanke sein. Beim ersten Wechsel von
A auf 0 wird anschliessend Amem = 0 und Bmem = 1 gespeichert. Wechselt
jetzt A wieder auf 1 so wird kein Impuls gezählt, weil das Aktuelle B =
Bmem ist. Solange sich B nicht verändert hat dürfen Flanken bei A nicht
mehr gezählt werden. Kurz gesagt: Die Logik setzt das Apriori-Wissen,
dass nach einem Wechsel von A erst ein Wechsel von B kommen muss (und
umgekehrt) um. Dies gilt auch bei der Drehrichtungsumkehr."
Die gepostete Version meines Codes müsste dieser Idee entsprechen. Und
ich würde das gerne so umsetzten. Aber irgendetwas funktioniert noch
nicht richtig, aber komplett falsch kann hier wohl nichts mehr sein,
sonst würde der Nullpunkt wohl beliebig hin und her springen.
Ich Vermute mal, dass der Rpi einfach zu lahm ist, bzw. die "Interrupts"
nicht wie Interrupts funktionieren. Deshalb als nächstes test mit einem
"einfachen" uC und tests mit einem Hardware decoder. Mal schauen was die
so zählen.
Patrick B. schrieb:> timerinterrupt (das als "solide" zu bezeichnen finde ich schon ziemlich> gewagt)
Mit Timer-Interrupt gibt es aber Code, der funktioniert ...
> Der Geber prellt nicht (liess sich zumindest nicht messen)
Wie hast du das gemessen? Mit einem Oszilloskop?
> Software seitig werden aber die Signale trotzdem "entprellt"
Ich vermute, dass der Fehler dort liegt.
> ist es möglich, dass das GPIO Interface (über wiringPi.h,> C-Programm) zu langsam ist
Ja, das ist möglich.
Du interessierst dich nicht für den genauen Zeitpunkt der Flanken,
sondern nur für den Zustand. Deshalb ist es egal, ob du die Signale
sofort nach dem Flankenwechsel oder erst beim nächsten Timer-Tick liest.
Also nimm eine funktionierende "Bastel"lösung, verschiebe den Code vom
Timer-Interrupt in die Pin-Interrupts, und es sollte immer noch
funktionieren.
4000 Inkremente bei 10 Umdrehungen/s ergibt fuer mich Interruptfrequenz
von 40kHz oder eine maximale Interruptbehandlungszeit von 25us.
Sportlich!
Benutzt Du einen Linux-Kernel mit RT-Patch? Ohne ist das wohl
hoffnungslos, mit immer noch ambitioniert.
Wenn Du vermutest, dass Interrupts verlorengehen, schnapp Dir ein ftrace
und miss Latenzen.
Danke Konrad, für die erste hilfreiche antwort.
An die anderen : habt ihr den code überhaupt schon verstanden?
@Clemens, du kannst dir ja im code anschauen, wie entprellt wurde!
na ja, zu Konrad^^
Ich benutze ein stink normales Raspbian. Ich bin davon ausgegangen, dass
so ein uC (ach ja übrigens, ich verwende den Pi2) mit 4 kernen schnell
genug sein sollte um das zu schaffen. Aber ja, das bestätigt dann wohl
dass er zu langsam ist.
Ich werde mal versuchen die Priorität meines Threads hoch zu stufen,
evtl. bringt das ja eine Verbesserung.
Mit extrem tiefen drehzahlen hab ich es auch tatsächlich geschafft den
Motor ohne inkrements zu verlieren zu drehen.
Zum Vergleich: ich hatte letztens auf einem 500MHz PPC-Derivat zu tun,
mit RT-Patch, und ich musste kaempfen, um ein Latenzziel von ~100us
einigermassen sauber zu treffen (CAN-Baustein MCP2515 per SPI ansteuern,
oh Graus).
Selbst mit 4 Kernen gibt es immer noch reichlich, was so ein Kernel erst
noch machen muss, bevor Dein Interrupt drankommt. Um so schlimmer, wenn
Deine Interruptbehandlung erst im User-Code stattfindet, dann hast Du
noch Context-Switch-Overhead von sagen wir 10us pro Richtung.
Wenn Du das auch zu Bildungszwecken machst, kann ich nur sehr empfehlen,
mal mit ftrace draufzugucken, was alles passiert, bevor Dein Interrupt
gehandelt wird -- man lernt einiges vom Linux-Kernel kennen.
Der RPi-Prozessor ist mE nicht der richtige Prozessor dafuer. Lieber ein
uC mit einem RTOS, oder ohne OS. Vielleicht auch ein BeagleBone mit
seiner PRU.
Patrick B. schrieb:> habt ihr den code überhaupt schon verstanden?
Warum sollen wir uns die Mühe machen, wenn Du ihn nichtmal kommentierst
und die Funktionsweise beschreibst?
Du willst doch Hilfe, also mußt Du auch ein bischen dafür tun.
Außerdem ist eine Beschreibung nie unnütz, sie hilft Dir selber, den
Code nach ein paar Jahren noch zu verstehen.
Die Timerversion kann übrigens genauso gut im Pin-Change-Interrupt für
die beiden Pins laufen.
Patrick B. schrieb:> aber hast du schon versucht meinen Code zu verstehen? Der> müsste zumindest theoretisch die Vorteile beider Varianten umsetzten.
Ganz toll, abgesehen von der unbedeutenden Kleinigkeit, dass er ja nicht
funktioniert.
Patrick B. schrieb:> An die anderen : habt ihr den code überhaupt schon verstanden?
Es ist schon seltsam, wenn jemand, der das überhaupt noch nicht
hingekriegt hat, alle anderen belehren will, die wissen wie es geht. Und
eine gute Voraussetzung Fehler zu finden ist das auch nicht.
Georg
Nun ja, der RPi soll eben auch noch übers netzwerk mit einem PC
kommunizieren und evtl. soll er auch noch ein Display ansteuern, deshalb
habe ich ihn verwendet. Ich werde noch ein paar versuche mache, um evtl.
hier eine Verbesserung zu erzielen, ansonsten werde ich dann wohl einen
Hardware decoder oder einen anderen uC nehmen (einer ohne Linux ;D ich
programmier lieber direkt auf der Hardware).
Werde mir ftrace mal anschauen, interessiert mich schon, was da so alles
läuft was ich nicht sehe.
Danke und Gruss
Eine beschreibung wie der Code funktioniert hab ich eigentlich schon mal
abgegeben. Da ich ja davon ausgehe, dass ihr die experten seid, solltet
ihr ja eigentlich verstanden haben was ich so beschrieben habe, ihr habt
euch sicher auch schon gedanken zu dem Thema gemacht.
Georg schrieb:> Patrick B. schrieb:>> aber hast du schon versucht meinen Code zu verstehen? Der>> müsste zumindest theoretisch die Vorteile beider Varianten umsetzten.>> Ganz toll, abgesehen von der unbedeutenden Kleinigkeit, dass er ja nicht> funktioniert.>> Patrick B. schrieb:>> An die anderen : habt ihr den code überhaupt schon verstanden?>> Es ist schon seltsam, wenn jemand, der das überhaupt noch nicht> hingekriegt hat, alle anderen belehren will, die wissen wie es geht. Und> eine gute Voraussetzung Fehler zu finden ist das auch nicht.>> Georg
Abgesehen davon, dass du eigentlich keine Ahnung hast oder was? Dass er
nicht funktioniert hat mir noch keiner bewiesen. Mit ziemlich grosser
wahrscheinlichkeit kann er aber nicht funktionieren, da ja scheinbar der
Linux Kernel zu lange braucht.
Patrick B. schrieb:> ansonsten werde ich dann wohl einen> Hardware decoder oder einen anderen uC nehmen (einer ohne Linux ;D ich> programmier lieber direkt auf der Hardware).
Wenn du einen STM32 nimmst kannst du einen Timer die Encoder-Signale
direkt in Hardware auswerten lassen. Dann musst du nichtmal mehr
irgendwas zeitkritisches (Interrupts) programmieren, sondern kannst
einfach nur gemütlich die Timer-Register auslesen um die aktuelle
Encoder-Position zu erhalten. Entprellen ist inklusive. Musst nur
aufpassen dass du die Signale auf Channel 1 und 2 eines Timers legst der
das auch kann - genau im Reference Manual gucken, bei manchen Timern
steht recht versteckt dass sie das nicht können.