Forum: Mikrocontroller und Digitale Elektronik Schrittmotor-Routine - hätte gerne Tipps (C)


von Ich B. (ichbin)


Lesenswert?

Hallo,

ich verzweifele gerade ein wenig an meiner Unfähigkeit. Ich habe eine
ISR, die für den Schrittmotor zuständig ist. Sie soll den Motor
entweder anhand einer Zielposition in Bewegung setzen, oder aber in
eine Richtung schicken.
Folgender Code hält den Motor nie mehr an, selbst wenn ich alle
Variablen auf 0 setze (stepper.target, stepper.position, stepper_on).
Aber das Problem ist wohl eher meine Un-Logik denn die Variablen.

Kann mir bitte jemand zum AHA-Erlebnis verhelfen? :-)

von Ich B. (ichbin)


Lesenswert?

ach mist:

if(stepper.on || (stepper.position !=stepper.target)) {

// so there's a reason to be moving
// just .. which way?
// either go straight to specified position
// or go some direction

  if((stepper.on&&stepper.dir) || (stepper.target>stepper.position))
{  // go down
    stepper.step --;
    stepper.position --;
  }
  if((stepper.on&&(!stepper.dir)) || (stepper.target<stepper.position))
{
    stepper.step++;
    stepper.position ++;
  }


  STEPPER_PORT=(STEPPER_PIN & ~STEP_MASK) | step[stepper.step];    //
update stepper outputs


}
else {  /* todo: turn off or hold properly*/
  STEPPER_PORT=(STEPPER_PIN & ~STEP_MASK) | 0;    // turn off /
  stepper.on=0;
}

von Profi (Gast)


Lesenswert?

Es ist recht schwirig zu erraten, was die verschiedenen Variablen
bedeuten und welche Wertebereiche sie annehmen können.

Aber: die beiden folgenden Zeilen sollen Bits setzen und zurücksetzen.

  STEPPER_PORT=(STEPPER_PIN & ~STEP_MASK) | step[stepper.step];    //
ok


  STEPPER_PORT=(STEPPER_PIN & ~STEP_MASK) | 0;    // turn off /
da wird nichts zurückgesetzt.
vermute mal eher:
  STEPPER_PORT=(STEPPER_PIN & STEP_MASK);    // turn off /

Oder anders herum.

von Ich B. (ichbin)


Lesenswert?

Hi Profi,

danke für Deine Antwort! Es ging mir eher um die Struktur des ganzen,
ich kann anscheinend keinen klaren Gedanken fassen.
Bis ich versucht habe, auch die Zielvorgabe zu berücksichtigen, hat
alles (soweit) problemlos funktioniert. Rauf, runter. Er gibt fleißig
die richtigen Bitmuster aus.
Nur jetzt fängt der Motor sofort an, sich zu bewegen, wenn die ISR
durchlaufen wird..

Zu den Bitoperationen: das "| 0" hat tatsächlich keine Auswirkung.
Die Negierung hat aber ihren Sinn
(http://www.mikrocontroller.net/articles/Bitmanipulation#Bestimmte_Bits_l.F6schen_.28Standard_C.29)

von Profi (Gast)


Lesenswert?

Jetzt habe ich es auch gerade gesehen, wie Du es mit den Bits gemeint
hast.

zu Zielvorgabe: brauchst Du nicht eher 4 Fall-Unterscheidungen?
2 für die Richtung und 2 für < und >

von Ich B. (ichbin)


Lesenswert?

Ha!
Das hab ich gestern auch gedacht, und seitdem verstehe ich nicht, wieso
ich mit 2*1 Bit weniger sinnvoll Informationen darstellen kann als mit
1*2 Bit.

"Off, Hold, Up, Down" sollte alles berücksichtigen, zumal damit mit
>1 das Schrittmuster ausgeführt werden kann, und andernfalls höchstens
noch der Strom abgeschaltet werden soll.

Allerdings hat er gestern auch damit immer noch Mist gebaut, ich werde
mich gleich nochmal in Ruhe darum kümmern.

von Ich B. (ichbin)


Lesenswert?

Das meinte ich mit 'meine Logik':

Wenn der Schrittmotor sich durch den Zustand 'rauf' bewegt, wird ja
seine Position verändert (!= Soll). Die ISR steuert gegen, der Motor
bleibt unter Strom stehen. Argh!

Also entweder führe ich jetzt noch einen anderen Zustand ein, oder
aktualisiere den Soll-Wert... Ich glaube, ein weiterer Zustand ist
verständlicher..

von Ich B. (ichbin)


Lesenswert?

So, jetzt funktioniert es endlich.
Man sollte berücksichtigen:
 - Stimmt die Zählrichtung mit der Drehrichtung überein?
 - Falls vom Zählerstand 0 rückwärts gedreht wird, gibt es einen
Überlauf (Motor dreht dann einmal durch den Wertebereich, bis
Ist=Soll).

von Profi (Gast)


Lesenswert?

- Stimmt die Zählrichtung mit der Drehrichtung überein?

genau das meinte ich mit den 4 Fallunterscheidungen: 2 für die Richtung
...


Falls vom Zählerstand 0 rückwärts gedreht wird, gibt es einen
Überlauf.

das könnte man umgehen, indem man alle Variablen um 1 (oder 128) höher
ansetzt. Bei 128 kann man dann statt des Carry-Flags das
Vorzeichen-(S)-Flag abfragen.


na, Hauptsache es geht.  Kannst Du mal bitte den gesamten Code
anhängen, würde mich interessieren.
Danke!

von Thomas F. (thomas-hn) Benutzerseite


Lesenswert?

Hallo,

ich habe letztens vergebens nach bestehenden Schrittmotorroutinen
gesucht, welche auch eine Beschleunigungs-/Bremsrampe enthalten.
Durch Zufall fand ich dann einen Artikel in der CT, mit einem
Beispielcode...welchen ich garnicht mal so schlecht finde.
Vielleicht ist das ja auch was für Dich.
Der Softlink der CT: ftp://ftp.heise.de/pub/ct/listings/0405-214.zip

Gruss,

Thomas

von Profi (Gast)


Lesenswert?

@Thomas: als ich obigen Post tippte, überlegte ich noch, ob ich was zu
den Rampen schreiben soll. Jetzt hast Du damit angefangen.

Von einem CT-Artikel (Heft5/2004 S.214) hätte ich mir etwas mehr Detail
gewünscht als "An dieser Stelle wollen wir nicht verhehlen, dass das
Ermitteln des optimalen Verlaufs mit viel Experimentieraufwand
verbunden sein kann.  ... Für die Beispielanwendung haben wir eine
lineare Verringerung der Taktzeiten von Schritt zu Schritt angesetzt.
... Manchmal ist eine Exponentielle Beschleunigung günstiger, weil
Resonanzbereiche schneller durchfahren werden und der Motor schneller
die Drehzahl erreicht."

Dazu muss ich aus 25-jähriger Stepper-Erfahrung sagen, das reicht für
einfache Experimente aus. Will man einen Stepper ordentlich (ernsthaft)
betreiben, muss man seine Gehirnwindungen etwas mehr strapazieren.
Siehe meine Ausführungen im wiki:
http://www.mikrocontroller.net/articles/Schrittmotoren

von Michael (Gast)


Lesenswert?

@Stepper-Profi

Bezüglich der neuronalen Wicklungen kann ich Dir nur zustimmen. Meines
Erachtens ist das, was in dem o.g. Programm als Beschleunigung gemacht
wird, genau verkehrt herum. Im unteren Drehzahlbereich wird die
Geschwindigkeit langsam verändert und im oberen viel zu schnell.
Außerdem löst man mit einer 8 stufigen Rampe kein Problem. Mit der von
Dir angeführten feinen Frequenzänderung bei hohen Drehzahlen kommt man
locker auf 200-500 Rampenschritte von Start-Stop-Frequenz bis auf
Solldrehzahl für schnelle Bewegung.

Für eine 'gute' Rampe braucht man daher eine entsprechend große,
zuvor berechnete Tabelle, oder man hat einen schnellen µC, der die
Rampe in Echtzeit berechnet. Per µC kann man dann auch
Gechwindigkeitsänderungen während der Fahrt vornehmen.

von Ich B. (ichbin)


Angehängte Dateien:

Lesenswert?

Hallo,

es hat wohl jemand meine Verbindung gekappt, erst jetzt habe wieder
Zugang zur Interwelt. Ist ja fast lebenswichtig!

Leider habe ich einen herben Rückschlag einstecken müssen, nachdem es
so erfolgreich aussah. Ich weiß auch nicht, wo der Fehler liegt. Ich
habe wohl bei der Programmiererei eine Änderung zuviel vergessen, die
einen neuen Fehler verursacht hat, den ich dann wiederum falsch
bekämpft habe. Oder beim Flashen ist was schiefgelaufen, ich vertraue
AVRdude irgendwie nicht so richtig.

Zwischenzeitlich hat es gereicht, daß ich meinen Finger in die Nähe
eines Tasters gehalten habe, damit das Programm ihn als geschaltet
erkannt hat (active-low, internal pull-up an).

Danach habe ich einige structs weggelassen und die volatile-variablen
für die ISR 'normal' deklariert.

Möglicherweise überlaste ich meine ISR aber schon wieder.

Habe ein bißchen die Schnauze voll, wäre schön, wenn Ihr den
entscheidenden Tip habt!

Rampen etc. brauche ich vorerst nicht, wird vermutlich eh nicht in den
Tiny2313 passen. Aber danke, ich guck's mir mal an.

Anbei die ISR mit Peter Dannegers Entprellroutine, die
Abfragefunktionen (da mag der Fehler liegen, weil eine Abfrage den
Status ändert?), und die eigentliche Initialisierungsroutine für den
Schrittmotor, mit Endschalter.

Funktion:
Schalter offen? -> Rauf, bis er schließt
Runterfahren, bis er öffnet.

Der erste Zustand wird (wurde mal) erkannt, aber danach geht nix mehr
Ich habe versuchshalber noch einen Handtaster in der Abfrage, aber auch
da passiert nicht das, was ich gerne hätte.

Hier noch ein paar Variablen:

// debounce inputs
volatile uint8_t key_state, key_press, key_release;

// stepper motor step patterns
const uint8_t step[4] = {STEP_PATTERN};
volatile uint8_t stepper_mode, stepper_speed;
volatile int16_t stepper_position, stepper_target;

Vielleicht verstehe ich auch das 'volatile' miß, Peter Danneger hat
es teilweise nicht in seinem Code.. Das sollte ich mal ausprobieren,
aber habe erst nachher wieder Zeit.

Danke schonmal für Euer Interesse!

von Ich B. (ichbin)


Lesenswert?

Vielleicht sollte ich noch schreiben, daß F_CPU 4MHz ist, der
ISR-Prescaler 64, das OCR ist mit 255 - also Aufruf alle 4ms..

von Ich B. (ichbin)


Lesenswert?

Ich drängele nur ungerne, aber da ich leider nicht weiterkomme hätte ich
gerne Anregungen zur Problemfindung und -lösung..

Jetziges Verhalten (Entprell-Variablen als volatile deklariert):

Die Schalterabfrage läuft irgendwie falsch.

stepper_init();

(!switch_state(1<<ZEROPOS)) wird richtig geprüft, aber die
darauffolgenden Abfragen (while(switch_state()))funktionieren nicht wie
gedacht, das Programm blockiert nicht, sondern läuft einfach weiter.
Das ist mir schleierhaft, denn es wird ja durch switch_state() nichts
verändert, weshalb folgende Abfragen ebenfalls gültig sein sollten.

Ich hoffe, einer von Euch weiß Rat..

von Profi (Gast)


Lesenswert?

@Michael: genau das meine ich auch.
Allerdings ist die Geschwindigkeit der CPU gar nicht nötig, man kann
die Berechnungen vereinfachen. Wenn ich mal Zeit habe, gibt es im
Wiki-Artikel eine Erweiterung mit Code.

Mit Tabelle: ja, momentan mache ich es so, dass jeder Schritt beim
Beschl/Bremsen einen Tabelleneintrag braucht. Allerdings kann man die
Tabelle wesentlich verkürzen, wenn man zwischen den Einträgen
interpoliert.

@ichbin:
wenn beim Annähern schon was ausgelöst wird, tippe ich auch auf ein
Hardwareproblem: externe 1k-Pullups schon eingebaut (die Internen sind
evtl. zu schwach)?

Die Variablen key_xxx würde ich static machen.

In so einem Fall baue ich gerne Debug-Ausgaben über die Serielle ein,
die man am PC betrachten kann.

von Michael (Gast)


Lesenswert?

@Profi

Hast Du schon 'mal eine automatische Umschaltung - z.B. zwischen 1/8
Schritt auf 1/2 Schritt - bei höheren Drehzahlen realisiert ?
Die hohe Auflösung macht langsame Fahrten ruhiger und die gröbere
Auflösung, reduziert die Interruptbelastung bei hohen Drehzahlen, wo
1/8 Schritt sowieso dummy ist.

von Ich B. (ichbin)


Lesenswert?

Hi Profi, danke für deine Antwort. Diese Variablen sind wie in Peter
Dannegers Codebeispielen global definiert, ein static macht doch daher
nicht so viel Sinn, oder?
Ich greife in einer ISR und in Funktionen darauf zu.

Kann man denn die Entprell-Abfrage-Routinen überhaupt so einsetzen, wie
ich es hier versuche (in einer while-Schleife pollen)?

von Ich B. (ichbin)


Lesenswert?

Die ISR dauert gerade mal 18µs, überladen habe ich sie also auch nicht.

...

Ich hatte noch eine Reihe von Variablen (structs, volatile) deklariert,
die ich jetzt mal rausgeschmissen habe - schon bekomme ich sinnvolle
Werte aus den Abfragefunktionen für die Entprellroutine.

Ich kann nicht beurteilen, was da schiefläuft, aber - sollte der
Compiler einen nicht warnen?

von Profi (Gast)


Lesenswert?

@Michael:
bin ein Freund hoher Drehzahlen, deshalb nur 1/2 und 1/1-Schritt
Umschalten sollte problemlos möglich sein, etwa bei 1-2 kHz.

@ichbin:
Du kannst den Warning-Level einstellen. Ich stelle immer alle Warnings
an und code so, dass keine Warnings kommen.

Aber in C kann man problemlos korrekten Code schreiben, der völlig
unsinnig ist, denke an verschattete Variablen, Pointer und andere fiese
Seiteneffekte.

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.