Hallo Liebe Forengemeinde Da hier schon sehr sehr oft über das Thema Schrittmotoren usw. Lamentiert wurde möchte ich euch garnicht lange mit einem Roman belasten und schmeisse meine Frage einfach mal in den raum ... Ich Zerbreche mir seit Tagen den Kopf wie es doch Teschnisch am besten umzusetzen wäre einen ATMEGA32 oder 644 über Serielle Schnittstelle vernünftig zu steuern... Was ich bereits habe und mit welchem Aufbau ich Arbeite. Ich habe eine voll Funktionsfähige 3D-Step Schrittmotor Steuerkarte mit L297 + L298 IC's in Betrieb und eine selbstgebaute ATMEGA Testplatine.. Jetzt habe ich durch ein sehr Simples Bascom Programm ( SIEHE ANHANG ) die momentan 2 Schrittmotoren mit ca 1 Khz auf Tastendruch angesteuert ... ist ja soweit keine hexerei ... Mein Verständnisproblem ist nun aber folgendes ... Wie geht man bei einem Atmega am besten her wenn ich ihm über die Serielle schnittstelle eie befehlskette übergeben möchte um ihm Geschwindigkeit und Steps der achsten mitzuteilen. Und daraufhin müsste er ja jeden Motor Separat anlaufen lassen ( beschleunigungskurve ) und ihn nach "X" steps wieder Bremsen . Wie macht man sowas Programmierteschnisch ? da der AVR ja nur einen Thread hat indem er alles abarbeiten muss ... Das geht irgendwie nicht in meine Birne, da er Ja wenn er den einen Motor z.B. Grade beschleunigt und den anderen Bremsen soll einen der beiden Arbeiten unterbrechen müsste ... Ich weiss das hört sich echt wirr an .. Aber vieleicht greift ihr mir ja etwas unter die Arme oder gebt mir den richtigen Denkanstoß ... Vielen Dank an euch .... PS: Ihr müsst mir keinen Quellcode vorkauen ( wenn ihr wollt werde ich aber nicht Schlagen ;-) ) aber für ein paar Logische erklärungen wäre ich euch sehr Dankbar ....
An und für sich ist es einfach: zwischen zwei Takten für den einen Motor hat der Prozessor nichts zu tun. Und in der Zeit bearbeitet er die anderen und macht die Kommunikation. Wenn z.B. eine Schrittfrequenz von 1kHz an einer Achse eingestellt ist, dann macht der Prozessor für diese Achse bei z.B. 1 Million Operationen/sec jede Millisekunde (1/1kHz) vielleicht 200 Operationen, das sind dann 200us. Und damit hat diese eine Achse gerade mal 20% der Rechenleistung verbraucht. Die 2. und 3.Achse dann auch noch jeweils 20%, sind in der Summe 60% der Rechenleistung für die 3 Achsen. Bleiben noch 40% übrig für die Kommunikation. Sodele. In Assember und C ist das klar: Timer loslaufen lassen. Dann 3 Capture-Register mit zugehörigem Interrupt einrichten und (fast) fertig. Dann noch die serielle Schnitte mit Interrupt bedienen und ein wenig drumherum. Und weil aktuelle uCs noch wesentlich schneller rennen als mit 1 Mops bleibt da ausreichend Luft. Jetzt hast du 1 kleines Problem: wie geht das mit Bascom? Aber du wolltest ja nur die Idee ;-)
Hallo Nico, also ich wuerde da folgendermassen rangehen: Timer 0 toggelt Portc.1 Timer 2 toggelt portc.4 beide timer prescale=1024 dimensioniere: speed-c1 als byte, speed-c4 als byte, gaspedal-c1 und gaspedal-c4 als byte der timer Toggelt den Port immer bei Ueberlauf, also dem entsprechenden Timer Interrupt. der Timer muss gucken ob speed=0 ist und dann den timer wieder auf 0 setzen ohne zu toggeln. ist speed nicht null (0 < speed*) dann soll er toggeln und den Timer auf den Wert "speed*" setzen. Zweck ist das die Timer immer gleichmaessig den Pin wackeln lassen (bei 0 aber nicht) und speed* sagt wie lange es dauert bis der timer wieder neu ausloest, also der naechste schritt kommt. ist speed*=50 laeuft der Timer dann bei 50 los und toggelt wieder bei dem naechsten ueberlauf. speed kann man entweder mit dem gleichen timer hoch- oder runterzaehlen (die Rampe), das waere dann z.b. (gaspedal=0 ist bremsen, 1 kein gas, 2 gas geben) select case gaspedal case = 0: if speed<>0 then decr(speed) endif case = 1: 'nix machen case = 2: if speed<240 then (!) incr(speed) endif else end select (Steilere Rampe bekommst du durch addition bzw subtraktion von 2, 3 oder mehr, aber aufpassen das speed* nicht ueberlaeuft) ein geringerer wert ist sicherlich erstmal anschaulicher. 240 als maximalwert fuer den timer heisst, er muss mindesten 15 schritte machen (bis 255) um enen neuen schritt auszuloesen (entspricht dann ungefaehr deinen 1000Hz) (18MHz prescaler 1024 mind.15 schritte) das bedeutet er laeuft mit mindestens 68Hz (255 Timerschritte) oder ist aus, wenn es eben nicht toggelt. Damit gibst du nur vor ob der bei jedem schritt schneller werden soll, oder nicht. du kannst den wert fuer speed auch direkt vorgeben und kein gas geben (gaspedal* = 1), dann hast du halt keine rampe. Oder du zaehlst die Rampe mit dem Timer1. (wuerde ich jetzt nicht machen) Jedenfalls kann dein Hauptprogramm nun in ruhe gucken in welche richtung er drehen soll, dazu vielleicht zuerst bremsen, die pins umsetzen, und wieder gas geben und die serielle und das LCD bearbeiten. der eigentliche takt kommt immer vom timer, abhaengig davon ob Gaspedal 0, 1 oder 2 ist. alle werte werden vom hauptprogramm an globale variablen uebergeben die die Timer dann benutzen. in der Hoffnung das das so funktioniert ;o))) ich hab mir das bis genau dahin ueberlegt, hier bastle ich gerade (d.h. das buegeleisen wird gerade warm ;o)) bye uwe
Hallo, ich würde einen Timer einrichten, der mit einigen kHz einen Interrupt auslöst. In diesem würde ich dann entscheiden, ob ein Motor einen Step erhält oder nicht. Nach wievielen Interrupts der Motor gesteppt wird hängt dann von der gewünschten Geschwindigkeit ab. Dafür könnte man in arrays definierte Rampen fahren, wobei man vorher berechnen muss, wann die "down-Rampe" startet, dass bei der Zielposition gestoppt wird. Die Kommunikation würde ich asynchron über interrupts lösen, so dass eine Verfahranforderung (absolute Sollposition aller Achsen) global zur Verfügung gestellt wird. Sollposition - Istposition ist dann der gewünschte Verfahrweg. Wenn er lang genug ist, beschleunigt man die "up-Rampe" bis zur maximalen Geschwindigkeit und rechnet aus, wann die Bremsrampe starten muss (Zielposition - n Ticks). Ist der Verfharweg kurz, darf eben nicht voll beschleunigt werden, man muss vorher beginnen zu bremsen. Bei geschickter Programmierung geht das ohne Fallunterschwidung. Grüße, Peter
Hey Super Finde ich klasse das ich heute morgen direkt 3 gute Antworten bekommen habe, das hat mir den weg über die Timer aufgezeigt ... Ich danke euch drei bis hier her erstmal für eure mühe mir bei meinem Problem zu helfen ... Ich werde heute Nachmittag zuhause mal versuchen die lösung von UWE umzusetzten da mir dieser beitrag in meiner momentanen situation am besten weiter hilft ... Ich werde dann heute Abend oder morgen berichten wie es gelaufen ist ... PS: Aber bitte lasst euch nicht abhalten wenn ihr noch ein paar ideen habt immer her damit. Bekanntlich denken kommen die Besten Ideen immer in der Summe zusammen ... Ich werde regelmäßig hier rein schauen .. Vielen Dank nochmal an euch drei .... bis Später !
Also, ich würde das so lösen: der Timer "gehört" dem Motor, der momentan am schnellsten dreht, d.h. der insgesamt am meisten Schritte machen muss. Die langsameren werden über eine DDS = Bresenham's Linienalgo gesteppt. z.B. Motor0: 10 Schritte, Motor1: 5 Schritte, Motor2: 4 Schritte M0 M1 M2 m0sum m1sum m2sum 0 0 0 0 1 32768 s 16384 13107 2 1 32768 s 32768 s 26214 3 1 32768 s 16384 39321 s -> 6553 4 2 32768 s 32768 s 19660 5 2 32768 s 16384 32767 6 3 32768 s 32768 s 45874 s -> 13106 7 32768 s 16384 26213 8 4 3 32768 s 32768 s 39320 s -> 6552 9 32768 s 16384 19659 10 5 4 32768 s 32768 s 32766 //m2-Schritt wird wg. Abrunden geskippt Wenn Du es optimal machen willst (auch nicht sooo schwer, aber eigentlich unnötig), muss M2 "sich den Timer kurz ausleihen", um genau zwischen den M0-Schritten 2 und 3 seinen M2-Schritt 1 reinzulegen. Wie funktioniert's? Du brauchst 3 16bit-Zählvariablen, auf diese addierst Du bei jedem Schritt des schnellsten Motors eine (FixedPoint)-Variable: in C würde das so aussehen: uint16_t m0, m1, m2; uint16_t m0steps, m1steps, m2steps; uint16_t m1sum=0, m2sum=0; Wobei hier noch eine "Umleitung" stattfindet: m0 ist nicht zwangsweise Motor0, sondern die Bitmaske des Pins des momentan schnellsten MotorX. m0=1;//Bitmaske des schnellsten Motors m1=2;//Bitmaske eines anderen Motors m2=4;//Bitmaske eines anderen Motors m0steps=10; m1steps=5; m2steps=4;//Beispiel //folgende 2 Zeilen müssen 32bit-Berechnungen sein: int16_t m1add=(32768L * m1steps) / m0steps;//=16384 int16_t m2add=(32768L * m2steps) / m0steps;//=13107,2 void timerISR(void){ do1step(m0);//schnellster Motor steppt auf jeden Fall if((m1sum+=m1add)>=32768){m1sum-=32768;do1step(m1);} if((m2sum+=m2add)>=32768){m2sum-=32768;do1step(m2);} } die Routine do1step sieht also folgendermassen aus: void do1step(char mx){ //könnte man natürlich auch inlinen motorport &= ~mx; //betreffendes Pin low (mache Schritt) delay(1); motorport |= mx; //betreffendes Pin wieder hoch } Wg. des Abrundens (13107,2 -> 13107) muss man noch eine Lösung finden (--> Bresenham).
Hallo eProfi Das sieht schon ganzschön heftig aus was du da gepostet hast .. hört sich in jedem fall plausibel an, nur tue ich mich momentan bisschen schwer mit der Umsetzung der hier gegebenen Tips ... Bei der versuchten umsetzung von Uwe's Idee habe ich im moment noch das Problemm das meine Motoren so vor sich hin surren mit ca 60 Hz aber ich kann sie einfach nicht beschleunigen ... Irgendwie mache ich da was falsch bei der umsetzung mit den Timern @ Uwe Hast du schon den Code in Bascon umsetzen können ? hat sich so angehört alsob du auch an sowas arbeitest ... @eProfi Ich nehme an du hältst nicht so viel von Bascon bsw. Basic oder. Das du mir das vieleicht in Basic erklären könntest ? PS: Zwischenfrage Da ich ja nach möglichkeit keine SChritte verlieren möchte muss ich ja gewährleisten das alle Takte an die Motoren übergeben werden... Meine Frage ist jetzt folgende, was passiert eigentlich wenn z.B. 2 Timer gleichzeitig einen interupt auslösen ! Werden dann beide Sprungroutinen nacheinander abgearbeitet oder wird eine der beiden Routinen garnicht abgearbeitet ???
>Da ich ja nach möglichkeit keine SChritte verlieren möchte muss ich ja >gewährleisten das alle Takte an die Motoren übergeben werden... Meine >Frage ist jetzt folgende, was passiert eigentlich wenn z.B. 2 Timer >gleichzeitig einen interupt auslösen ! Werden dann beide Sprungroutinen >nacheinander abgearbeitet oder wird eine der beiden Routinen garnicht >abgearbeitet ??? Nacheinander. In meiner Schrittmotorsteuerung mit ATMega2560 laufen die 4 16bit Timer mit Interrupt. Mit jedem Timerinterrupt wird 1 Motor angesteuert, also 4 insgesamt. Zusätzlich läuft ein 8bit-Timer mit Interrupt im 10ms Abstand als Systemtimer und zur Tastenentprellung, sowie der UART-Interrupt für die Kommunikation mit 115200Baud. Das funktioniert ganz gut. Alle 4 Motoren können gleichzeitig laufen ohne dass es merklich unrunden Lauf gibt. Die Interruptroutinen sind zwar Codemäßig sehr lang, aber effektiv hält der µC sich nur sehr kurz in den einzelnen Interrupts auf, da immer nur ein Teil des Codes ausgeführt wird (verschiedene Motor-Stati). Die Rampen berechne ich übrigens zur Laufzeit im Timer-Interrupt. Lediglich vor dem Losfahren werden diverse Grundparameter für die Rampen vorberechnet. Der Rest halt on the fly.
Kannst du mir denn behilflich sein zumindest einen motor vernünftig zum laufen zu bekommen ? mit Beschleunigung usw. Ich weiss das man sowas nicht gerne macht, schließlich steckt da meist ne menge Gehirnschmalz drin ... Ich würde nur mein Projekt gerne vorran treiben und stecke aber Arbeitstechnisch momentan so tief in Arbeit das ich mich auf mein Hobby nicht voll Konzentrieren kann ... Wir könnten das auch Via E-Mail klären wenn du nicht möchtest das der Code Public wird... Mein Projekt welches ich vorhabe ist weder Komerzieller Natur noch irgendein Auftrag o.ä. sondern schlicht und einfach mein eigener Basteltrieb! Es soll erstmal ein ganz simpler Stiftplotter werden der über mein selbst geschriebenes Programm via koordinaten geführt werden soll .... Mfg: Nico
Code kann ich nicht rausgeben, weil ich dann Ärger mit meinem Boss kriegen könnte. Ich habe mich bei den Rampen im Wesentlichen an diese Beschreibung gehalten. http://www.embedded.com/columns/technicalinsights/56800129?_requestid=309459 Das gleiche ist auch nochmal in der Applikation-Note 446 von Atmel erklärt. http://www.atmel.com/dyn/resources/prod_documents/doc8017.pdf Vom Prinzip her sieht das so aus: - Befehl kommt rein (Zielpunkt, Beschleunigung, Verzögerung, Maximalgeschw.) - In einer Start-Routine werden nun die Grundparameter für die Rampen ausgerechnet (wann erreicht der Motor die Maximalgeschw., wann muß ich anfangen zu bremsen, erreiche ich vieleicht garnicht die Vmax?) - In dieser Routine wird dann auch der erste Timer-Wert berechnet und der Timer gestartet. - Wenn der Timer-Interrupt kommt, wird das Clock-Signal für den Motor erzeugt und der nächste Timer-Wert berechnet, anschließend der Interrupt wieder verlassen. Im Interrupt selbst gibt es verschiedene Stati. 1- Motor STOP -> Hier wird der Timer gestoppt und ggf. der Motor komplett Stromlos gemacht. 2- Beschleunigen -> hier wird immer der nächste Timer-Wert berechnet und überprüft, ob Vmax erreicht ist oder ggf. schon wieder gebremst werden muß. 3- Fahren -> nur einen Impuls generieren und prüfen ob gebremst werden soll. 4- Bremsen -> hier wird immer der nächste Timer-Wert für die Verzögerung berechnet. Bei mir sind im Interrupt noch einige weiter Stati, weil ich noch eine Referenzfahrt (Endschalter) und einen Manuellen Modus mit drinhabe. Das Geheimnis ist nun, dass im Interrupt immer nur der Codeteil ausgeführt wird, dessen Status gerade aktiv ist. Dadurch ist der Interrupt relativ kurz. Sachen die ich immer im Timer-Interrupt mache sind: - Prüfen ob ein Endschalter ausgelöst ist, dann den Schritt nichtmehr machen. - Den Schritt erzeugen. Man kann die Rampen auch über eine Tabelle erzeugen, oder linear, das ist aber nicht so schön (von der Optik her) wie über den Algorithmus der in den obigen Quellen beschrieben ist.
Hallo nico, du scheinst da ja recht fix zu sein, ich habe genau garkeinen handschlag gemacht ;o) evtl hast du vergessen das der timer, wenn er ausgeloest hat, am ende der interruptroutiene natuerlich als aktuellen timerwert die variable speed zugewiesen bekommen muss. also, letzte zeile im interrupt: timer0=speed_c1 sonst laeuft der timer immer wieder bei null los, er soll ja aber z.b. bei 150 loslaufen und nach 105 zaehlschritten (105xprescaler) und dann das pin toggeln, bzw beim naechsten mal eben nur 104 schritte undsoweiter. HTH, bye uwe Die Zeit die vergeht waehrend ein interrupt den anderen blockiert ist nich schlimm, 1024 takte vergehen bei einem zaehlschritt, da kann der uC einiges machen. viele zaehlschritte sind ein motor step. Ein verrueckter tick verzoegert den timer so das die sich beim naechsten mal nicht wieder treffen, ausser die anzahl der schritte aendert sich unterschiedlich (du verstehen? ;o))
Hallo Euch Allen Also nachdem ich mir hier nochmal Alle vorschläge durchgelesen habe und mir paar Kaffee gegönnt habe hat es endlich klick gemacht und ich habe ein kleines Prog Geschrieben ( Siehe Anhang )... Und siehe da er macht genau das was ich möchte !! Es ist zwar noch die Grobe Lineare Art der beschleunigung aber man fängt ja bekanntlich klein an nichtwahr ... :-D Kurze erklärung Beim senden von paar Zahlen über UART passiert folgendes 1 (49) = Beschleunigen 2 (50) = Bremsen, danach setzt er automatosch Gas_x auf 0 bzw. 48 um zu togglen 3 (51) = Bremsen So Die nächsten Kannen Kaffe werden dabei drauf gehen das Ding Bisschen Inteligent zu machen und irgendwie vernünftige Rampen zu Programmieren ... PS: Die UART behandle ich mit Absicht ohne Interrupt, der pollt dann zwar wie ein Irrer aber dadurch bekomme ich wenigstens keine weiteren Unterbrechungen. Weiss nicht ob die Idee jetzt so der knaller ist aber es fühlt sich nicht grade falsch an ... Mfg: Nico
Gut, hier meine Überlegungen: Du hast physikalisch gesehen folgende Größen: Zeit, Weg (Count), Geschwindigkeit (Frequ), Beschleunigung (Accel). Weg ist die Anzahl der Schritte, dafür würde ich mal eine 32bit-Zählervariable reservieren. Geschwindigkeit ist die Frequenz, also Xtal prescaler Timer. Beschleunigung ist die Änderung der Frequenz. in C läßt sich das recht kurz und bündig schreiben: Count+=Frequ+=Accel; oder aufgeröselt (ich weiß: es ist nicht 100% das selbe wg. Reihenfolge) Count=Count+Frequ; Frequ=Frequ+Accel; Wenn der Motor stillsteht und Du loslegen willst, setzt Du einfach Accel auf einen (positiven=eine Drehrichtung oder negativen=andere Drehrichtung) Wert. Dann wird jedes mal die Frequenz erhöht, solange bis die gewünschte Geschwindigkeit erreicht ist (oder Du schon wieder abbremsen (=Accel auf einen negativen Wert setzen) musst). Zum Stehenbleiben setzen wir bei (Frequ == etwa 0) Accel auf 0. Das Richtungssignal ist das Vorzeichen der Frequenz. So, jetzt kommt das größte Problem: diese 2 Berechnungen müssten theoretisch in immer gleichen Zeitabständen durchgeführt werden. Viel günstiger wäre es jedoch, wenn die Berechnung zu jedem Schritt stattfinden würde (zumindest bei hohen Frequenzen). Dazu wenden wir einen genialen Trick an: je öfter diese Berechnung wg. sich erhöhenden Frequenz ausgeführt wird, desto weniger addieren wir jedes mal: Count=Count+Frequ/Frequ; //also um 1 Frequ=Frequ+Accel/Frequ; Timer=Fxtal/Presc/Frequ; //Fxtal/Presc ist eine Konstante Vorteil: die Beschleunigung ist absolut konstant und die Frequenzänderung absolut linear. Um nun richtig in FixedPointarithmetik rechnen zu können, muss man sich davon lösen, dass der Wert in Frequ die echte Frequenz in Hertz ist. Es ist ein zur echten Hertz-Zahl proportionaler Wert, sowas wie milli-Hertz. Jetzt hast Du was zum Knobeln.... (hoffentlich reicht der Kaffee)
Hallo eProfi und natürlich der rest auch So langsam bekomme ich nen durchblick durch die ganze sache, ich werde mich am wochenende mal auf den Hintern setzen und versuchen alle ideen unter einen Hut zu bekommen. Ich bin mal gespannt ob ich eine anständige lösung auf die Beine bekommen kann !!! Aber wie gesagt immer her mit neuen Ideen und Gedanken ! Man kann nie genug davon haben :-D ... Mfg: Nico
Guten Morgen euch allen hier Statusbericht: Ich habe es am wochenede endlich hin bekommen meine Beschleunigungs und Bremsrampen machen genau das was sie sollen ... Ich lasse sie Linear via Bresenham Algo. berechnen, nachdem die beschleunigungskurve nicht das problem war bin ich bald verückt geworden die Bremsrampe berechnen zu lassen wegen formelumstellung ... Naja wie so oft war die Lösung so simpel das man sich selber oft für doof hält ;-) ... Aber manchmal sieht man halt den Wald vor Bäumen nichtmehr .... So momentan fighte ich da dran die Steuerung intelligent zu machen, sprich wann soll wie weit beschleunigt usw ...... Werde hier regelmäßig reinschauen um zu sehen ob es neues gibt ! Mfg: Nico
Hallo Nico, ich habe diesen Thread gefunden, und versuche nun selbst diese Rampen zu programmieren. Ich habe alles gut durchgelesen, komme aber nicht weiter, bzw. finde nicht mal einen Anfang. Kannst Du Deinen Lösungsweg beschreiben? Gruß Christian
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.