steppermotor timing

trix

Golden Member

hallo,
nog steeds voor het zelfde "grote" project.
ik kies er voor om een nieuwe topic te starten, omdat wanneer ik deze vraag in het vorige topic stel de verschillende disciplines gaan mixen en daarmee de bijbehorende reacties ook, wat snel onoverzichtelijk word (laat staan als er b.v. 6 of meer disciplines zijn)

ik heb mijn stepper motors draaien met een trapezium profiel, en dat loopt super, kan eigenlijk niet beter.
maar ik heb nu een probleem dat ik eigenlijk al lang zag aan komen, maar nu is het moment daar dat ik dit moet gaan oplossen.
de manier hoe ik de trapezium heb geprogrammeerd is eigenlijk heel simpel, te simpel waarschijnlijk.

voor het vlakke stuk in de trapezium:
- een for lus, met het totaal aantal gewenste stappen (is de gewenste afstand).
- puls naar stepper driver: "hoog" - delay 5uS - "laag".
- preload timer met een waarde die de snelheid v/d motor bepaald.
- wacht op overflow.
- einde lus

voor het acc.& dec. gedeelte dezelfde opzet.
probleem natuurlijk in deze opzet is dat ik in tussentijd niets anders kan doen. en dat moet ik wel. niet een andere steppermotor sturen maar elke 100 pulsen (is ca. 100 mS) moet ik een signaal reeks over de uart versturen, en die duurt ca. 80 mS. daarbij moet de stepper motor natuurlijk soepel door blijven lopen.

ik zoek niet naar een high-tech oplosing, waarbij meerdere motoren met verschillende snelheden en verschillende richtingen d.m.v. van een S-curve worden gestuurd. maar liefts zo simpel mogelijk.
hoe kan ik dit nu het best doen ?
bedankt alvast.

eigenwijs = ook wijs

Over wat voor microcontroller hebben we het? Ik neem aan een Arduino of andere 8-bitter?

Om te beginnen zou ik die UART via interrupts doen; je zet de data klaar in een buffertje, zet de TX buffer empty interrupt aan, en steeds als die interrupt komt schrijf je steeds het volgende byte, tot ze op zijn, en dan zet je de interrupt uit.

Hoeveel motoren moet je nu besturen, en in hoeverre moeten die onafhankelijk zijn? Wat is je maximale stapfrequentie?

Je kunt een timer interrupt op een hoge frequentie laten lopen, en steeds bepalen welke motor of motoren een stapje moeten krijgen, door bij te houden wat hun huidige interval is, en wanneer ze voor het laatst een stap hebben gezet. Ik heb dit wel eens gemaakt (in een FPGA) met het Bressenham lijn algoritme, dan kan volledig met integers, en je krijgt automatisch dittering, zodat je geen cumulatieve fout krijgt als de gewenste frequentie geen mooie deler is van de interrupt frequentie.

Een manager is iemand die denkt dat negen vrouwen in één maand een kind kunnen maken
trix

Golden Member

atmega 2560 (8 bitter dus). draait op een 8MHz X-tal.

het zijn in totaal 10 stepper motoren, die nooit tegelijk worden gestuurd.
max. snelheid = 5500 Hz.

eigenwijs = ook wijs

In dat geval is het eigenlijk heel eenvoudig, in plaats van te wachten tot de timer afloopt, zet je de timer interrupt aan, en elke keer dat die afloopt, geef je de motor een stapje en stel je de timer in voor de volgende stap. In de tussentijd loopt je main loop en andere interrupts (zoals de UART communicatie) gewoon door.

Ik heb ooit eerder iets soortgelijks gepost: https://www.circuitsonline.net/forum/view/message/2024863#2024863

[Bericht gewijzigd door SparkyGSX op zondag 13 september 2020 10:40:32 (16%)

Een manager is iemand die denkt dat negen vrouwen in één maand een kind kunnen maken
trix

Golden Member

dat klinkt inderdaad logisch, ik ga dat eens bekijken. tnx.

eigenwijs = ook wijs
trix

Golden Member

uit die link van jou:
wat gebeurt hier:

c code:


if( !--StepCount ) // done yet?
eigenwijs = ook wijs

Stepcount wordt eerst met één verlaagd, daarna wordt bekeken of het resultaat s daarvan 0 is, en als dat waar is wordt de code onder de if statement uitgevoerd.

Een manager is iemand die denkt dat negen vrouwen in één maand een kind kunnen maken
trix

Golden Member

nou ben ik bezig om die code van jou te implementeren in mijn code, maar ik zie nu dat die code gebruik maakt van de PWM pin op de controller.
hoe kan ik daar het best meerdere (drivers) motoren mee aansturen ? (nooit tegelijk)

edit: die:

c code:


delay(100);

zijn uS (micro sec.) denk ik ?

;

eigenwijs = ook wijs

Ik gebruikte dat inderdaad de timer uitgang, en in de ISR stelde ik alleen de timer in voor de volgende puls. Als je meerdere uitgangen wilt kunnen gebruiken, zul je die in de ISR zelf moeten aansturen; je kunt de betreffende uitgang aan het begin van de ISR aanzetten, dan wat werk doen om te bepalen wat de volgende stap moet worden, en aan het eind de uitgang weer uitzetten. Met een beetje mazzel krijg je dan een puls die lang genoeg is, zonder dat je daar nog iets bijzonders voor hoeft te doen.

Die delay was 100ms, en dat is de frequentie waarmee het hele patroon herhaald wordt; immers, eenmaal gestart loopt die helemaal in de interrupt service routine, en dan moet de main loop hem natuurlijk niet opnieuw starten terwijl hij nog bezig is.

Een manager is iemand die denkt dat negen vrouwen in één maand een kind kunnen maken
trix

Golden Member

als je een delay van 100 mS gebruikt, dan stopt de ISR toch ook ?

eigenwijs = ook wijs

Waarom zou die stoppen? Kijk eens waar die delay staat, die heeft niets met de ISR te maken.

Een manager is iemand die denkt dat negen vrouwen in één maand een kind kunnen maken
trix

Golden Member

ga het nog eens bekijken, ik kan deze week moeilijk reageren i.v.m. buitenland trip.
tnx.

eigenwijs = ook wijs

Een leuke methode om je steppers aan te sturen is hier https://os.mbed.com/users/fablabtruck/code/laoslaser/docs/tip/stepper_… te vinden. Idee erachter is dat je weet hoeveel stappen je wil nemen en vervolgens op een slimme manier gaat versnellen. Slim wil zeggen dat de stap-tijd steeds korter wordt tot de eindsnelheid.
De code is wat lang, maar de kern is relatief eenvoudig. Ik heb het ooit omgeschreven naar Basic (Bascom).
Het artikel van de bedenker vind je hier: https://www.boost.org/doc/libs/1_72_0/libs/safe_numerics/example/stepp…

Niet alles wat op internet staat is waar... Dat geldt ook voor CO.
trix

Golden Member

vandaag getest met de code van SparkyGSX, en het werkt, ik moet hem alleen nog implementeren in mijn code.
ook geprobeerd om in het begin v/d ISR de "pulse" hoog maken en op het eind laag, voor wanneer je een andere pin wil gebruiken dan de timer uitgang, en dit gaat ook goed.

de waardes die zijn gebruikt zijn voor mij echter te laag, ik zoek iets van:
ramp-up = 1000 steps (pulsen)
const. speed = 30000
ramp-down = 1000

hoe kan ik dit het best aanpassen ?
tnx.

eigenwijs = ook wijs

Gewoon de getallen aanpassen. De "50" in de function call is het totaal aantal stappen dat gezet moet worden, de andere parameters staan in het blok constantes bovenaan.

code:

const unsigned short PulseLength = 2; // 2 * prescaler (now set at 256), must be smaller than RunInterval
const unsigned long StartInterval = 100; // interval at start of cycle
const unsigned long RunInterval = 10; // interval at top speed in cycle (MUST be smaller than StartInterval!)
const unsigned long RampSteps = 10; // number of staps in acceleration / deceleration ramps

Als je de timer pin niet gebruikt, is PulseLength niet meer relevant. StartInterval is de tijd tussen stappen bij het beginnen van de beweging, RunInterval is tijd tussen stappen bij de maximale snelheid, en RampSteps bepaald de lengte van de start- en stop ramps. Die laatste kun je dus gewoon op 1000 zetten, en de andere getallen kun je ook aanpassen naar wens, tot maximaal 65535 voor die intervals.

Een manager is iemand die denkt dat negen vrouwen in één maand een kind kunnen maken
trix

Golden Member

ik had dat straks gedaan, "gewoon" de getallen aan gepast, maar dat werkte om een of andere reden niet. ik ga dat morgenvroeg nog eens bekijken.

en inderdaad die pulslength kan weg, ik kwam op een puls lengte van 80-100 uS. de driver wil minimaal 2,5 uS zien dacht ik.

eigenwijs = ook wijs

... en doet het prima met sub-microseconde pulsen.

four NANDS do make a NOR . Kijk ook eens in onze shop: http://www.bitwizard.nl/shop/
trix

Golden Member

wat waardes aangepast, en de code werkt nu prima voor mijn toepassing.
vervolgens de code geimplementeerd in mijn programma, en dat gaat ook goed.

nu heb ik:
ramp-up = 1000 pulsen.
cont. speed = 30000 pulsen.
ramp-down = 1000 pulsen.

nou wil ik in het rechte stuk b.v. iedere 100 pulsen een actie ondernemen.
zonder dat de stappenmotor gaat "haperen" natuurlijk.
waar kan ik dan de benodigde waarde (100, 200, 300, enz. enz.) het beste "aftappen".

zou ik b.v. in de ISR een teller kunnen ophogen en die waarde vervolgens gebruiken, of kan het simpeler ?

jou programeer style is duidelijk voor de wat gevordere programeur (verkorte schrijfwijzes), en ik ben natuurlijk een beginner.
tnx.

eigenwijs = ook wijs

Als het niet heel belangrijk is dat de actie wordt gedaan exact op het moment dat de 100ste stap begint, zou ik in de main loop naar StepCount kijken. Je kunt steeds in een andere variabele opslaan wanneer je de actie voor het laatst gedaan hebt, en stepcount daar van aftrekken, waarbij je kijkt of het verschil groter of gelijk is aan 100. StepCount telt in jouw geval van 30000 omlaag tot 0.

Hiermee wordt ook meteen duidelijk waarom interrupts zo handig zijn; ook als die nieuwe actie relatief lang duurt, blijft de motor gewoon door lopen, want je main loop wordt dan onderbroken door de interrupt, de motor zet een stapje, en de main loop gaat verder waar hij gebleven was, alsof er niets is gebeurd.

Als de actie echt heel kort is (alleen een uitgang veranderen bijvoorbeeld) zou je dat ook nog wel in de ISR kunnen doen.

[Bericht gewijzigd door SparkyGSX op zondag 27 september 2020 13:43:16 (13%)

Een manager is iemand die denkt dat negen vrouwen in één maand een kind kunnen maken
trix

Golden Member

de actie duurt ca. 80 mS, is zeker niet kort, en exacte startmoment komt niet zo nouwkeurig.

[Bericht gewijzigd door trix op zondag 27 september 2020 15:28:35 (34%)

eigenwijs = ook wijs

Dat kan dan prima in de main loop, zoals ik beschreef, en is dan dus een mooi voorbeeld voor het nut van interrupts!

Een manager is iemand die denkt dat negen vrouwen in één maand een kind kunnen maken
trix

Golden Member

ik ben de code verder aan het implementeren, nu ben ik bezig met de limitsw itches. voor alle duidelijkheid: de switsches die de deceleratie moeten starten bij een beweging (beweging kan mechanisch gezien niet verder), mocht de stepper deze om wat voor reden dan ook missen, dan komen pas de mechanische switches die de NS activeren. (ik merk hier dat deze 2 vaak door elkaar worden gehaald, wat veel spraak verwarring geeft)

dus als de stepper beweegt en hij ziet zo'n limitswitch, dan wil ik graag de deceleratie starten.
waarschijnlijk moet ik in die situatie StepCount een waarde geven die in mijn geval 1000 pulsen (is decelaratie lengte) van de gewenste eind positie ligt.
is dit de juiste manier ? of kan dit beter/simpeler ?

nog een zijdelings vraagje: waar is dit voor in je code ?
StepCount = NrSteps; // record number of steps to be done

eigenwijs = ook wijs

StepCount is de globale variabele, waar de ISR (interrupt service routine) ook bij kan, en die constant wordt bijgewerkt, terwijl NrSteps de parameter is die je meegeeft aan de functie; die houdt dus op te bestaan zodra die functie klaar is, en dan is de beweging net gestart.

StepCount wordt afgeteld naar 0, dus als de deceleratie 1000 cycles lang is, kun je die simpelweg naar 1000 forceren om direct de deceleratie te starten. Dit zou alleen mis gaan als de deceleratie al begonnen is; dan zou de motor, zonder nette acceleratie, direct terug moeten naar de nominale snelheid, en dat kan natuurlijk niet.

Een manager is iemand die denkt dat negen vrouwen in één maand een kind kunnen maken
trix

Golden Member

ik ga er mee aan de slag, tnx.

edit: dat van: StepCount = NrSteps; // record number of steps to be done
wist ik eigenlijk wel natuurlijk |:(

eigenwijs = ook wijs