Even wat inleiding: Voor school ben ik bezig met mijn stage, een CD opstelling bouwen in het laserlab van de VU. In deze opstelling komt ook een monochromator voor, dat is een apparaat die licht van maar 1 golflengte naar keuze doorlaat. Deze monochromator moet worden aangestuurd met de computer, hiervoor heb ik een stappen motor eraan laten maken.

Het resterende probleem was dus de aansturing van de stappenmotor. Gezien ik een opleiding Analytische Chemie doe zit dit totaal niet in mijn lespakket.
Ik had hiervoor een stukje code gevonden op het internet, geschreven in Bascom AVR, afkomstig van dit project.

Nu weten meerdere mensen hier dat ik niets van uC's afweet, maar ik heb toch eens wat zitten rommelen in het programma.

Ten eerste wilde ik de snelheid regelbaar hebben, ik wilde een ready terug hebben als het ding klaar is met draaien, en ik wilde een hardware busy LEDje hebben. Het was de bedoelding dat het geheel gaat draaien in een ATtiny2313.
Na wat googlen en wijzigen ben ik hier op uitgekomen:


    $crystal = 4000000
    $baud = 9600

    Dim Wa As String * 2 , Wb As Byte
    Dim Stap As Integer , A As Integer , Dir As String * 3 , Value As String * 8
    Dim Step1 As Byte , Step2 As Byte , Step3 As Byte , Step4 As Byte

    Config Portb = Output
    Config Portd = Input

    Print "Serial Port Stepper Motor Driver"
    Print "input mmdsxxxxx in which mm=m0/m1, d=l/r, s=1-9, xxxxx=1-32000"

      Lus:
      Input "Give input: " , Value
      Dir = Left(value , 3)
      Wa = Mid(value , 4 , 1)
      Wb = Val(wa)
      Value = Mid(value , 5 , 5)
      Stap = Val(value)

      If Dir = "m0l" Then
      Step1 = 48
      Step2 = 96
      Step3 = 192
      Step4 = 144
      Gosub Steps
      Print "ready"
      End If

      If Dir = "m0r" Then
      Step1 = 144
      Step2 = 192
      Step3 = 96
      Step4 = 48
      Gosub Steps
      Print "ready"
      End If

      If Dir = "m1l" Then
      Step1 = 3
      Step2 = 6
      Step3 = 12
      Step4 = 9
      Gosub Steps
      Print "ready"
      End If

      If Dir = "m1r" Then
      Step1 = 9
      Step2 = 12
      Step3 = 6
      Step4 = 3
      Gosub Steps
      Print "ready"
      End If

      Goto Lus

Steps:
      Portd.2 = 1

      For A = 1 To Stap

      Portb = Step1
      Waitms Wb

      Portb = Step2
      Waitms Wb

      Portb = Step3
      Waitms Wb

      Portb = Step4
      Waitms Wb
      Portb = 0

      Next A

      Portd.2 = 0
      Return

Nu ziet het er in de simulatie van Bascom AVR wel allemaal werkend uit, maar kan iemand mij uitsluitsel geven of dit werkt, of anders tips hoe dit beter kan?

Ik stel me vragen bij je stappen. Activeer jij telkens 2 wikkelingen van je motor(s)? Dat is toch nergens voor nodig? Of miskijk ik me op je code (waar ik geenszins vertrouwd mee ben)?

Er worden inderdaad steeds twee wikkelingen tegelijk geactiveerd.
Zoveel ervaring heb ik ook niet met stappenmotoren, dus ik had maar even aangenomen dat die hele sub voor 1 stap goed was en heb de waardes van portb overgenomen.

Als het een unipolaire motor (4 wikkelingen, 6 aansluitdraden) betreft, kan je het zo doen:


Wikkeling 1: A en B
Wikkeling 2: C en D

A - C - B - D - A - C - ... (of andersom)
of
A - AC - C - CB - B - BD - D - DA - A - AC ... (of andersom)

In het 2e geval draait de motor trager, maar zijn de stappen half zo groot.

[Bericht gewijzigd door pros op ]

Maar is dan zo'n cyclus van
A - C - B - D
of
A - AC - C - CB - B - BD - D - DA
Nog steeds maar 1 stap?

Het eerste zijn 4 stappen, het tweede ook, maar dan met "tussenstapjes".

Ah. Nu snap ik waarom er "For A = 1 To Stap Step 4" in de orginele code staat, hij loopt met stappen van 4 lang door de waardes van A..

* klein is fijn opent prullebak en schuift code erin.

Ik heb dus iets nodig waarmee je 1 stap per keer kan nemen, niet 4 tegelijk.

Waarom niet met een paar losse chippies? Da's ook heel gemakkelijk hoor. Neem zo'n up-downcount IC'tje met 4 bits. (in 2 bits heb je ze niet) en dan ff met EN/xor poortjes en invertortjes klooien. Dan heb je t ook.
In 3 IC's heb je de complete sturing, een 4029, een 4*XOR IC, en een uln2003 (500mA invertors, voor direct op de motor)

Das allemaal geen probleem, maar dat hangt zo rot aan de seriele poort. Het is de bedoeling dat het ding straks met staptjes van 1nm een heel spectrum gaat doorlopen. Aan de andere kant hangt de detector ook aan de computer en van die twee wordt een leuk grafiekje in elkaar gezet.

l297+l298+paralelle poort.

Ik doe meestal zoiets:


#define RECHTS 1
#define LINKS 2
unsigned char motor1[4] = { 48, 96, 192, 144 };
char step1 = 0;

void stappen_maar(unsigned char richting, unsigned int aantal)
{
    while (aantal > 0) {
	if (richting == RECHTS) {
	    step1++;
	} else {
	    step1--;
	}
	step1 &= 3;    // we gebruiken van "step1" enkel bits 0 en 1
	PORTB = motor1[step1];
	aantal--;
    }
}

// code...

stappen_maar(RECHTS, 12);

// enz...

Voor een 2e motor maak je een 2e array (motor2) aan, en gebruik je uiteraard een 2e variabele (step2) om de motorstand bij te houden. Wil je met halve stappen werken, dat heb je per motor een array van 8 stappen nodig, en doe je "step1 &= 7".
Het bovenstaande zal wel om te zetten zijn naar bascom, hoop ik...

Als het principe van pros inderdaad opgaat voor jouw motor, zou ik het zo doen. Heb je geen datasheet van die stepper? Als het bekend is hoe je het spul aan moet sturen, is het helemaal appeltje-eitje. Ik kan je met bascom niet helpen. Ben meer van de assembly...

Ik ben wat in elkaar aan het prutsen in bascom, maar ik kom erachter dat het behoorlijk behelpen is.
De plek bijhouden waar de stepper staat is geen probleem, maar die plek omzetten naar wat anders is wat lastig.
Ook schijn ik niet in 1 commando 8 dingen in een array te kunnen proppen, daar zijn weer 8 regels voor nodig..

Het probleem waar ik nu ook weer mee zit is het feit dat er twee stappenmotoren op 1 poort hangen en ik toch wel graag nu meteen 1/2 step wil gaan werken. Er moet dus spanning op blijven staan, maar hoe verander ik de eerste of laatste 4 bits van een poort zonder de andere te veranderen?

* klein is fijn vraagt zich even voorzichtig af of hier geen betere taal voor is dan bascom.

Dat voorbeeldje stond toevallig in bascom, maar volgens mij zijn er veel handigere manier om dit te schrijven.

Tsja... ik blijf erbij dat je uC's gewoon in assembly programmeert. Punt. Maar goed...

In tegenstelling tot bijvoorbeeld een 8051, kun je op AVRs redelijk veilig read-modify-write doen. Ofwel: je doet een read van die port (niet, niet van PINx maar van PORTx), AND het resultaat met een bitmask met 1-en op de plaatsen van 'de andere motor' (alles van 'jouw' motor is nu 0) en ORed daar de nieuwe waarde (van 4 bits) overheen (yep, even 4 bits shiften voor de hoogste 4...). Vervolgens schrijf je het spul weer. Dat moet ook in basic nog wel lukken.
Alternatief is bitjes stuk voor stuk setten (weet niet op die motor dat leuk vindt; aan de andere kant... als je dat @10MHz doet en bascom compileert je code wat handig, gaat het waarschijnliujk zo snel dat die motor het niet eens door heeft. Geen idee hoe je in bascom 1 pin set / cleared.
De 'netste' en meest 'hufter-proof' manier is een shadow register bijhouden: je hebt een byte waar de status van je output pins in staan. Die kun je als boven omschreven manipuleren en schrijf je dan naar je poort. Voordeel van read-modify-write is dat er niet per ongeluk bitjes 'om kunnen vallen'. Gezien de opbouw van ports op een 8051 ontkom je niet aan deze manier, tenzij je bit-voor-bit zet.

gewoon in assembly pin voor pin programmeren. Dat gaat heel eenvoudig. Je kan binnen 10 cycles alle pinnen goed gezet hebben. Da's ruim snel genoeg.

Hoeveel motoren moet je nou eigenlijk aansturen, en hoeveel andere dingen moet de microcontroller eigenlijk bijhouden?

Ook schijn ik niet in 1 commando 8 dingen in een array te kunnen proppen, daar zijn weer 8 regels voor nodig..

statement data gebruiken ...

http://web.inter.nl.net/users/cpotma/bascom%20and%20avr/arrays%20and%2…

Ik ben gisteravond wat aan het prutsen geweest en ik heb wat leuks (hoop ik) in elkaar gedraaid:


    $crystal = 4000000
    $baud = 9600

    Dim A As Integer
    Dim Dir As String * 3 , Value As String * 8 , Stap As Integer
    Dim Wa As String * 2 , Wb As Byte , Stepdirection As Integer
    Dim Stepm1 As Byte , Stepm2 As Byte
    Dim Posm1 As Integer , Posm2 As Integer
    Dim Stepsm1(8) As Byte , Stepsm2(8) As Byte
    Dim Portbm1 As Byte , Portbm2 As Byte

    Config Portb = Output
    Config Portd = Input

    Stepsm1(1) = 8
    Stepsm1(2) = 10
    Stepsm1(3) = 2
    Stepsm1(4) = 6
    Stepsm1(5) = 4
    Stepsm1(6) = 5
    Stepsm1(7) = 1
    Stepsm1(8) = 9

    Stepsm2(1) = 128
    Stepsm2(2) = 160
    Stepsm2(3) = 32
    Stepsm2(4) = 96
    Stepsm2(5) = 64
    Stepsm2(6) = 80
    Stepsm2(7) = 16
    Stepsm2(8) = 144

    Portbm1 = 8
    Portbm2 = 128

    Portb = 136

    Print "testprogram"
    Print "input mmdsxxxxx in which mm=m0/m1, d=l/r, s=1-9, xxxxx=1-32000"

Lus:
      Input "Give input: " , Value
      Dir = Left(value , 3)
      Wa = Mid(value , 4 , 1)
      Wb = Val(wa)
      Value = Mid(value , 5 , 5)
      Stap = Val(value)

      If Dir = "m1l" Then
      Stepdirection = -32
      Gosub Subm1
      End If

      If Dir = "m1r" Then
      Stepdirection = 32
      Gosub Subm1
      End If

      If Dir = "m2l" Then
      Stepdirection = -32
      Gosub Subm2
      End If

      If Dir = "m2r" Then
      Stepdirection = 32
      Gosub Subm2
      End If

      Goto Lus


Subm1:
      Portd.4 = 1
      For A = 1 To Stap
      Stepm1 = Stepm1 + Stepdirection
      Posm1 = Stepm1 / 32
      Posm1 = Posm1 + 1
      Portbm1 = Stepsm1(posm1)
      Portb = Portbm1 + Portbm2
      Waitms Wb
      Next A
      Portd.4 = 0
      Print "ready"
      Return


Subm2:
      Portd.5 = 1
      For A = 1 To Stap
      Stepm2 = Stepm2 + Stepdirection
      Posm2 = Stepm2 / 32
      Posm2 = Posm2 + 1
      Portbm2 = Stepsm2(posm2)
      Portb = Portbm1 + Portbm2
      Waitms Wb
      Next A
      Portd.5 = 0
      Print "ready"
      Return

Portbm1 en Portbm2 zijn de schaduwbytes die de poortstand bijhouden van beide steppers.
Aan Portd.4 & Portd.5 hangen twee busy LED's.

In de simulatie werkt het allemaal prima, maar ik toch nog wat vragen.
Hoe kan je berekenen hoelang 1 keer zo'n sub doorlopen kost? Stel het kost 20ms om die sub te doorlopen dan heeft die 3ms wachttijd ook geen zin.

De Config van Portd staat op Input, maar Portd.4 & Portd.5 worden gebruikt als output. Nu werkt dat allemaal wel leuk in de simulatie, maar moet ik per se als &B00001100 configgen als ik wil dat het ook in het echt werkt?

Ik heb die array's op een andere manier proberen te vullen, maar dit blijkt minder ruimte in te nemen dan een stel commando's.

Wel een leuk feit, deze code is even groot als het voorbeeld waar ik mee begon. :)

Hoe kan je berekenen hoelang 1 keer zo'n sub doorlopen kost? Stel het kost 20ms om die sub te doorlopen dan heeft die 3ms wachttijd ook geen zin.

Berekenen? Bij een assembler-programma is dat nog te doen, daar weet je precies welke instructies er gebruikt worden. Bij c heb je er het raden naar. Maar je kan de compiler wel opdragen, de assembler-source (die normaal tussendoor aangemaakt en terug verwijderd wordt) te laten staan, en aan de hand daarvan je berekeningen te maken. Hoe dat met basic zit? 'k Zou 't begot niet weten...
Anderzijds denk ik dat je één enkele doorlooptijd van je subroutines eerder in µS moet uitdrukken.

Toch nog een (misschien onterechte) opmerking:


For A = 1 To Stap

Moet dat niet zo:


For A = 0 To Stap

Op 19 februari 2007 21:00:59 schreef pros:
Berekenen? Bij een assembler-programma is dat nog te doen, daar weet je precies welke instructies er gebruikt worden. Bij c heb je er het raden naar. Maar je kan de compiler wel opdragen, de assembler-source (die normaal tussendoor aangemaakt en terug verwijderd wordt) te laten staan, en aan de hand daarvan je berekeningen te maken. Hoe dat met basic zit? 'k Zou 't begot niet weten...
Anderzijds denk ik dat je één enkele doorlooptijd van je subroutines eerder in µS moet uitdrukken.

Oke, dat zit dus wel goed.

Toch nog een (misschien onterechte) opmerking:


For A = 1 To Stap

Moet dat niet zo:


For A = 0 To Stap

Nee, ik heb het dubbel gecheckt en het moet echt 1 zijn. Stel Stap is ook 1, dan doorloopt ie van 1 tot en met 1, 1x dus.

* klein is fijn schopt dit topic nog even omhoog omdat hij met iets zit waar hij niet uit komt.

Ik verwijs naar deze sub voor het stappen:

Subm1:
      Portd.4 = 1
      For A = 1 To Stap
      Stepm1 = Stepm1 + Stepdirection
      Posm1 = Stepm1 / 32
      Posm1 = Posm1 + 1
      Portbm1 = Stepsm1(posm1)
      Portb = Portbm1 + Portbm2
      Waitms Wb
      Next A
      Portd.4 = 0
      Print "ready"
      Return

Nu gaat dat prima, alleen het LEDje dat aan Portd.4 hangt gaat amper aan. Ik zie een heel zwak lichtpuntje.
Dus ik had even een knipperlicht programma erin geschoven om het te testen en de LED brand gewoon normaal.

Waarom brandt mijn LED zo zwak als die stepper code draai?

Ik neem aan dat je die LED zo aangesloten hebt dat het ding met een '1' brandt (ik zou het zelf andersom doen omdat die port beter sinkt dan sourced en ik zo geen zooi erachter nodig heb). Als dat niet zo is, zou je meer over de routine die dit stuk code aanrept moeten weten om er wat zinnigs over te zeggen.

Geen idee hoe snel die routine is. Ik weet wel dat de LED intensiteit zoals je die waarneemt niet lineair verloopt met de duty-cycle.

Zou het kunnen dat je voeding bij elke stap wat instort waardoor de stroom door de LED een stuk lager wordt dan dat je denkt?

Het geheel is de code hierboven die ik al eerder had neergezet.

De sub wordt 1 keer aangeroepen waarna ie "stap" aantal keer in die sub rondrent. Het LEDje gaat dus maar 1 keer aan en een keer uit, daartussen blijft het constant branden. De voeding heb ik ook al gemeten, die blijft gewoon 5V ten alle tijden.

De poort laten sinken is natuurlijk een idee en misschien wel wat netter, maar toch vind ik het vreemd dat sourcen niet werkt met deze code.

Das inderdaad wat laat, en over een totaal ander iets. Wat jij post dat gaat over stappenmotoren aansturen via de parallele poort en Visual Basic, ik ben stappenmotoren aan het aansturen via een microcontroller.

Projectjes is overgens bijna af, ben momenteel een behuizing aan het ontwerpen / maken. Print is al bijna af, moet alleen nog in elkaar gesoldeerd worden (het minste werk).