stappenmotor van A naar B ?

trix

Golden Member

stel je voor, ik heb een beweging die aangedreven word met een stappenmotor (m.b.v. een driver dus puls, dir en enable).
die gaat van A naar B.
A = 200 en B = 4800 (als voorbeeld).

hoe hoor je dit te doen ?

nu maak ik een "for loop" en begin op 200 en spring uit de loop op 4800 (simpel uitgelegd).

nadeel daarvan is dat dit een "blocking" code is ik kan tijdens de beweging niet zien of dat b.v. het touchpanel word bediend. is wel weer op te lossen met een interrupt natuurlijk.
maar volgens mij is de door mij toegepaste "for loop" constructie niet de juiste manier om een stappen motor te sturen.

hoe dan wel ?

eigenwijs = ook wijs
Arco

Special Member

For loop is niks mis mee...

In een interrupt is natuurlijk wel zo netjes. Maar je kunt toch ook in die For loop na iedere stap kijken of er iets is ingedrukt?

Arco - "Simplicity is a prerequisite for reliability" - hard-, firm-, en software ontwikkeling: www.arcovox.com
trix

Golden Member

ik gebruik de arduino IDE, en die heeft een instructie "serial_available" (of zoiets) wellicht is die te gebruiken (HMI gaat over UART).
maar zo'n loop beperkt ook weer het aansturen van 2 stappen motoren tegelijk, (mocht ik dat ooit willen).

eigenwijs = ook wijs

Ik zou juist het geven van stapjes in een timer interrupt laten gebeuren, want dat moet op tijd.

[edit]

c code:

 
#include "TimerInterrupt.h"
#define TIMER1_INTERVAL_MS    1

void setup() {
if (ITimer1.attachInterruptInterval(TIMER1_INTERVAL_MS, TimerHandler)) {
    Serial.print(F("Starting  ITimer1 OK, millis() = ")); Serial.println(millis());
  } else {
    Serial.println(F("Can't set ITimer1. Select another freq. or timer"));
  }
}

void TimerHandler() {
 // Loopt iedere ms
 // Stapperdestap.. 
}

[Bericht gewijzigd door Aart op vrijdag 31 maart 2023 19:09:37 (70%)

Als je de arduino IDE gebruikt dan is er volgens mij een redelijk bruikbare bibliotheek voor steppers.

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

Golden Member

goeie, die heb ik nog niet bekeken, kijken of ik hem kan vinden. tnx.

eigenwijs = ook wijs

Ik heb zelf ooit eens eens ISR gestuurde stappenmotor software geschreven. Acceleratie en decelleratie waren volgens het algoritme geschreven door David Austin (Echt geniale PDF) Daarmee kon it tot ongever 30.000 ISR's per seconde afhandelen op een ATMEGA8. (En dat was dan de maximale stap snelheid). Elke keer dat een interrupt werdt uitgevoerd, zette hij ook een nieuwe waarde in de timer voor wanneer het tijd wordt om de volgende stap uit te voeren. Als hij niets te doen had, dan bleef de ISR op een laag tempo (paar honderd ISR's per seconde) op de achtergrond lopen.

"Arduino" heeft ook een aantal stappen motor bibliotheken, die varieren in functionaliteit.

Je kunt ook eens naar GRBL kijken (of een van de andere open source stappenmotor projecten) De code hiervoor kun je vrijwel ongemodificeerd gebruiken. Je moet alleen uitzoeken in welk buffer je een paar regels "G-Code" moet injecteren. Dat uitzoeken kan overigens wel een flinke uitzoek klus zijn...

Marlin kan ongeveer 3 stappen per ISR doen, voor zover ik weet. Klipper nog meer stappen/sec doen, maar die besteed het uitrekenen van wanneer welke stap te doen uit aan een andere processor.

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

In dit topic: https://www.circuitsonline.net/forum/view/142445/last

heb ik code geschreven voor exact dit probleem; die werkt op basis van timer interrupts, heeft een ramp-up en ramp-down, en zet een bepaald aantal stappen.

[Bericht gewijzigd door SparkyGSX op zondag 2 april 2023 13:46:38 (37%)

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

Ik vond in dat topic deze posting van jou.

Jou code switched van bijvoorbeeld 19 cycles naar 10 cycles per pulse in de laatste stap voordat het "vaste snelheid" stuk begint.

Als je het in mm/sec van het aangedreven ding uitdrukt dan is dat van bijvoorbeeld 10.5 naar 20mm/sec in 1 stap.

De mechanica dicteert echter dat je bijvoorbeeld wel instantaan van 0 naar 1mm/sec kan gaan, maar niet van 19 naar 20, omdat dan al een deel van het beschikbare koppel gebruikt moet worden om de beweging in stand te houden.

De meeste stepper-sturing-libraries doen welliswaar een lineaire accelleratie die daar geen rekening mee houdt, maar jou code doet het kwa versnellingen verkeerdom.

Wat ik doe, is ik werk in "nanosteps". Pas als die zeg 2^13 bereiken doe ik een een microstap (i.e. pulse naar de motor).

Door nu de snelheid te maximeren op zeg 2^13 per tijdstap, en een versnelling van zeg 100 of 200 per tijdstap te doen, dan kan je dus iedere tijdstap (timer ISR)

c code:

 snelheid = snelheid + accelleratie;
 nanopos = nanopos + snelheid;
 
 while (nanopos > 8192) {
    send_pulse (1);
    nanopos -= 8192;    
 }
 while (nanopos < -8192) {
    send_pulse (0);
    nanopos += 8192;    
 }

waarbij send_pulse dus de DIR pin op de waarde in het argument zet en dan een pulsje geeft.

De wiskunde is eenvoudig: Tijdens een accelleratie-fase is de afgelegde afstand 1/2 a * t2. Tijdens het decelereren ook. Samen a * t2. Tijdens het "constante snelheid" bewegen is de beweging v * t.

De complexiteit zit hem er in dat die twee keer "t" discreet moet zijn. Als je het uitrekent kom je met t = remaining_constant_speed_distance / vmax; bijvoorbeeld op 400.5 uit. Tja. Dan kom je dus "vmax/2" te ver of niet ver genoeg. Daarvoor corrigeren is TRICKY. Er is een artikel over geschreven door een stelletje studenten uit eindhoven. Ik heb geprobeerd hun code te implementeren, maar dat wilde maar niet. Uiteindelijk maar gewoon zelf gedaan.

Maar "goed in de richting" is vaak al goed genoeg. Dan is het eenvoudig.

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

Golden Member

Op 2 april 2023 13:37:59 schreef SparkyGSX:
In dit topic: https://www.circuitsonline.net/forum/view/142445/last

heb ik code geschreven voor exact dit probleem; die werkt op basis van timer interrupts, heeft een ramp-up en ramp-down, en zet een bepaald aantal stappen.

klopt, die code heb ik ook gebruikt en werkte zeer goed, nog bedankt daar voor.
die gebruik ik dus in een "for loop"
de vraag was ook, of dat de juiste manier was om een stappenmotor te sturen (de "for loop")

ik gebruik trouwens een raspberry pi pico met een arduino IDE.

eigenwijs = ook wijs

Kennelijk kijken we naar verschillende stukken code, want Sparky z'n code gebruikt een interrupt en niet de for-loop waar jij het over hebt.

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

Golden Member

nee we kijken naar hetzelfde :)
er word inderdaad een interrupt gebruikt voor het zetten v/d stappen.

maar als ik b.v. van positie 200 mm. naar positie 4800 mm. moet met een beweging, dan tel ik in een "for loop" van 200 tot 4800.

eigenwijs = ook wijs

Je hebt zeker gelijk dat het absoluut niet ideaal is (daarom stond er ook "may need improvement" bij denk ik). Bij de laatste stap wordt de interval altijd gehalveerd.

Uiteindelijk is het een afweging tussen de complexiteit, interrupt rate, etc. Bij jouw methode moet de timer een stuk harder lopen dan de maximale pulse rate bij de constante snelheid, anders kom je alsnog niet goed uit bij de laatste stap. Daarbij gebruik ik de timer niet alleen voor de interrupt, maar ook om de pulsen te maken. Waarschijnlijk is het wel mogelijk om het al dan niet maken van een puls door de timer te schakelen.

Alsnog zou ik de interval van de timer variabel maken, met een slimmere methode om de interval te berekenen.

@trix: als je van positie 200 naar 4800 moet, moet je gewoon StartCycle aanroepen met 4600 als NrSteps parameter. Ik snap niet wat je met die loop aan het doen bent; kun je die code plaatsen?

[Bericht gewijzigd door SparkyGSX op zondag 2 april 2023 19:04:08 (12%)

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

Op 2 april 2023 19:02:09 schreef SparkyGSX:
Alsnog zou ik de interval van de timer variabel maken, met een slimmere methode om de interval te berekenen.

Ik kijk naar "de toekomst", waarbij niet 1 maar meerdere steppers moeten worden aangestuurd. Dan wordt het al snel onoverzichtelijk als je moet gaan uitzoeken welke motor als eerste aan de beurt moet zijn.

Bij jouw methode moet de timer een stuk harder lopen dan de maximale pulse rate bij de constante snelheid, anders kom je alsnog niet goed uit bij de laatste stap.

Ik denk van niet omdat iedereen tegenwoordig microstepping gebruikt is het geen probleem om iets onregelmatig de microsteps te sturen. Dus als je max snelheid 1.5 timer-interrupts per pulsje is, dan zit er steeds 1-2-1-2-1 ... timer-interrupt tussen de individuele pulsjes. Omdat je dit op 10 of 100 kHz doet, zal de motor met een kleine trilling worden aangestuurd die een frequentie heeft van ongeveer 5 of 50 kHz. Dat filtert mechanisch wel er uit....

Je kan de motor zelfs 1.5 pulsjes per timer-IRQ-periode laten lopen, dan krijgt ie opeenvolgende timer interrupts 1-2-1-2-1... pulsjes. Ook geen probleem.

In de specs van de A4988 staat dat een pulsje 1 microseconde moet zijn. Ik hou me daaraan, maar het ding doet het ook prima met VEEL kortere pulsjes. Ik weet niet meer of ik ze ook "te kort" kon maken. Ik vind het acceptabel om in de interrupt 1 microseconde busywaiting te doen. Niet ideaal, maar kan prima.

Idealiter doe je alle motors pulse "aanzetten", dan pas de 1 microseconde wachten, en dan pas alle motoren puls weer uitzetten, En pas DAN ga je voor de eventuele tweede ronde van pulsjes in deze IRQ... Dit is een optimalisatie die onhandig is omdat je de diverse motoren tegelijk moet gaan behandelen. Ik heb nu

for (m=0;m<MAX_MOTORS;m++) if (motor_present(m)) process_motor (m);

waarbij er een array met motor-states is.

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

Golden Member

Op 2 april 2023 19:02:09 schreef SparkyGSX:
@trix: als je van positie 200 naar 4800 moet, moet je gewoon StartCycle aanroepen met 4600 als NrSteps parameter. Ik snap niet wat je met die loop aan het doen bent; kun je die code plaatsen?

moet ik even nog bekijken hoe ik dat ook alweer deed, ik ben het nu een beetje bij beetje aan het omzetten naar de arduino IDE.
de vraag was voor mij ook: hoe doe je dit het best ? voordat ik een verkeerde "gewoonte" doorzet.

eigenwijs = ook wijs

@rew: voor meerdere motoren wordt het inderdaad een ander verhaal, maar dan wordt het toch ook een keer krap op 100kHz, met een 8-bit CPU op een 16MHz clock. Je hebt dan elke 160 cycles een interrupt, en in die tijd moet je de berekeningen voor alle motoren doen, de pulsjes maken, en ook nog tijd overhouden voor de andere taakjes van de CPU.

Edoch, ook op een snellere CPU moet je natuurlijk een bruikbare methode kiezen. Het maakt dat ook nog wel wat uit hoeveel motoren je tegelijk wilt aansturen, en met welke maximale frequentie.

Ik heb het ooit gemaakt met een FPGA, die had per motor een vrij simpel procesje die je met 2 integers een breuk gaf voor de deling vanaf een centrale clock, en op die manier kon een bijna willekeurig groot aantal motoren tegelijk aangestuurd worden. De parameters voor de motoren werden door een microcontroller via SPI ingesteld, en die microcontroller maakte ook de centrale clock, waarmee je dus ramp-up en ramp-down kon doen. Het algoritme in die FPGA was dus bijna gelijk aan wat jij schrijft, behalve dat jij een constante noemer gebruikt in je breuk (8192), en die bij mij dus ook variabel was. Uiteindelijk maakt dat weinig uit, denk ik; het totale aantal bits van de parameters bepaald de nauwkeurigheid.

@trix: Je roept dus StartCycle() aan met het aantal stappen dat je wilt zetten (de andere parameters waren constanten, voor de toepassing van die TS). Vervolgens kun je in de main loop naar StepCount kijken, als die 0 is, is de beweging klaar.

De laatste regel van de functie "ISR" kan nog aangepast worden om het verloop van de interval te verbeteren; ik zal nog eens nadenken over een betere methode. Wil je meerdere motoren tegelijk gaan aansturen?

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

Golden Member

dan kan je ook een boog beschrijven, dus later waarschijnlijk wel.

eigenwijs = ook wijs
Arco

Special Member

Het hangt ook van de implementatie af.

Ik gebruik een 16MHz controller voor een 24 kanaals dimmer. (interrupt iedere 100uS). Dat gaat op z'n sloffen. (iedere 100uS update van alle kanalen)
Veel staat of valt met de code zelf. Bijv. dit:

code:


For i = 0 to 7
  Data[i] = value[i]
Next

kost heel veel meer processortijd als een 'uitgerolde' loop:

code:


  Data[0] = value[0]
  Data[1] = value[1]
  Data[2] = value[2]
  Data[3] = value[3]
  Data[4] = value[4]
  Data[5] = value[5]
  Data[6] = value[6]
  Data[7] = value[7]
Next

Niet zo compact/netjes qua code, maar wel veel efficienter qua processorgebruik.

Arco - "Simplicity is a prerequisite for reliability" - hard-, firm-, en software ontwikkeling: www.arcovox.com
trix

Golden Member

daar moet je een ervaren programeur voor zijn om dat te zien/weten.

eigenwijs = ook wijs

@trix: als je dit soort dingen vaak genoeg doet, en de grenzen opzoekt, wordt je vanzelf een ervaren programmeur!

Wat nog veel sneller gaat, is dat data voorbereid in een buffer zetten, en steeds een byte (of meer) naar de GPIO poort schrijven. Je moet de uitgangen dan wel achter elkaar op één GPIO poort zetten, maar dan is het ook wel heel snel, ten koste van het gebruikte geheugen, natuurlijk.

Als je een enigszins moderne CPU gebruikt (geen oude 8-bitter) heb je waarschijnlijk ook een DMA periferal, en kun je de hele "lees uit geheugen en schrijf naar hardware poort" zelfs in hardware doen, met de trigger van een timer, zonder enige tussenkomst van de CPU.

100us is 10kHz, dat betekend op een 16MHz controller elke 1600 cycles. Als je geen hele gekke dingen doen, moet dat wel haalbaar zijn. De truc is vooral dat je geen andere ISRs mag hebben die te lang duren.

Voor een dimmer die vermoedelijk triacs aanstuurt, met een 50Hz voeding, lijkt 10kHz best een redelijke frequentie; je hebt dan 100 stappen in elke halve sinus. Als je wilt gaan PWM'en op een hogere frequentie is het natuurlijk veel te weinig. Om pulsjes te maken voor een aantal stappenmotoren, wil je dat de stapgrootte "nu of nog 1 cyclus wachten" niet te groot is in verhouding tot de stapfrequentie. Je zou dus met een interrupt op 10kHz wel pulsjes kunnen maken tot 500Hz of misschien 1kHz, maar dan is de afwijking van een cyclus meer of minder dus al 10%.

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

Op 3 april 2023 17:34:12 schreef Arco:Bijv. dit:

code:


For i = 0 to 7
  Data[i] = value[i]
Next

kost heel veel meer processortijd als een 'uitgerolde' loop:

code:


  Data[0] = value[0]
  Data[1] = value[1]
  Data[2] = value[2]
  Data[3] = value[3]
  Data[4] = value[4]
  Data[5] = value[5]
  Data[6] = value[6]
  Data[7] = value[7]
Next

De compiler doet dat voor je als je optimaliseerd voor speed (meestal -Os als extra compiler optie).
En als het bytes zijn maakt ie er zelfs maar 2 instructies van (ieder 32 bits words) als het een 32 bits machine is.

Daar kun je het bij een goede compiler echt niet van winnen door zelf proberen te optimaliseren.

Nog een mooi voorbeeldje:

c code:


uint32_t ntohl(uint32_t a)
{
    uint32_t native = (a & 0x000000ff) << 24;
    native += (a & 0x0000ff00) << 8;
    native += (a & 0x00ff0000) >> 8;
    native += (a & 0xff000000) >> 24;

    return native;
}

Dit zet een big endian 32-bits getal om in native format, onafhankelijk van de cpu endianess. (Veel gebruikt in protocol implementaties zoals TCP/IP.)

Wat denk je dat de assembly code is van het bovenstaande stuk proza?

code:


        mov     eax, edi
        bswap   eax
        ret

Waarbij alleen de "bswap eax" de netto gecompileerde code is. mov + ret zijn de call overhead.
Als je de code dan inlined blijft er precies 1 instructie over. Daar ga je het niet van winnen om met de hand te optimaliseren.

1-st law of Henri: De wet van behoud van ellende. 2-nd law of Henri: Ellende komt nooit alleen.

Op 4 april 2023 16:54:15 schreef SparkyGSX:
Je zou dus met een interrupt op 10kHz wel pulsjes kunnen maken tot 500Hz of misschien 1kHz, maar dan is de afwijking van een cyclus meer of minder dus al 10%.

Dat is dus een issue als je "Fullstepping" doet. Maar niet als je microstepping doet. Dan is het gewoon prima als je op de 1kHz schaal ongeveer genoeg pulsjes hebt gedaan. Zoals ik al eerder schreef kan je dan best 3 pulsjes op de 10kHz 100us interrupt doen, ipv ze netjes 33us uit mekaar te houden.

@Henri62:
-Os is officieel optimaliseer op SIZE. dat gaat dus juist NIET loop-unrolling doen.

Doordat moderne CPUs een cache hebben en omdat die beter werkt naarmate de code compacter is, zal je in benchmarks zien dat -Os even goed of zelfs beter werkt dan "geoptimaliseerd op snelheid" met grappen als loop-unrolling. (als je niet direct ziet dat compactere code sneller is: Een 40k Cache is beter dan een 32k cache. Zal iets in performance schelen. Als nu "op snelheid geoptimaliseerd" 20% groter is dan "op grootte", dan kan je 32k cache dus 40k "op snelheid geoptimaliseerde code" bevatten. Omdat een "cache miss / instruction fetch" al snel vele clocks kost, is het effect van een grotere cache, betere hit-ratio groot t.o.v. de paar cycles die je wint met een loop-unroll).

Maar een AVR heeft geen cache. Dus dan is iedere cycle die je wint er een.

Maar.... "premature optimization is the root of all evil". Ga niet optimaliseren totdat je hebt vastgesteld dat het zonder optimalisatie niet gaat lukken. Voordat je code gaat schrijven kan het helpen om na te denken over de algorithmes en iets te kiezen wat efficient is. Maar de code "lastiger te lezen maken" om het sneller te krijgen werkt vaak averechts.

code:


  Data[0] = value[O]
  Data[1] = value[1]
  Data[2] = value[2]
  Data[3] = value[3]
  Data[4] = value[4]
  Data[5] = value[5]
  Data[6] = value[6]
  Data[7] = value[7]

Wie ziet in 1 oogopslag dat er in dat stukkie code een fout zit? Tuurlijk. De oude rotten zijn daar VEEL beter in dan de mensen die pas beginnen met programmeren.

Dus... Ik ben het met je eens: Beter de compiler z'n werk laten doen en die de boel laten optimaliseren. Kan ie vaak beter dan jij. verder -Os levert vaak snelle code op, maar officieel probeert de compiler dan compacte code te maken....

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