Shop OBEX P1 Docs P2 Docs Learn Events
unipolar stepper control — Parallax Forums

unipolar stepper control

JBWolfJBWolf Posts: 405
edited 2011-10-09 23:35 in Propeller 1
Hello,
I am having trouble controlling uni-polar stepper motors with the propeller.
I have been working the past week on a simple x/y axis scanner for making my own laser show system.
I have tried several different programming methods but I always end up with overshoots, lost movements and cannot create a circle at all.

Wiring: Two 12v, 1amp steppers wired directly to 8 pins on the propeller with an ULN2803.
I am using simple out commands to create a phase position as such:
outa[12..15] := phase[stp]

Here are the phases I am using with the 'phase[stp]'
HStep byte %1001, %1000, %1100, %0100, %0110, %0010, %0011, %0001 ' Half Step
FStep byte %1001, %1100, %0110, %0011 ' Full Step
WStep byte %1000, %0100, %0010, %0001 ' Wave Step


Am I supposed to be sending a frequency rather than a simple outa[12..5] to control movements?
I am using a simple loop to try to get it to move a specific distance:
Say I want a horizontal line.... it moves right 10 steps, then left one, then no movement for 9 sec, then repeats. I cannot figure out why!
I also cannot get both motors to move at the same time.
PUB test1 | dist, delay
 delay := 200 
 dist := 10
 
    repeat 6
       xaxis(right, dist, delay)
       waitcnt(clkfreq + cnt)

       xaxis(left, dist, delay)
       waitcnt(clkfreq + cnt)        


PUB Xaxis (dir, distx, delay)   
repeat distx                          ' cycle phase dist many times

        if dir == 2                      ' decrement if direction = down
           stpx := stpx - 1
             if stpx == -1
                stpx := 7
                
        if dir == 1                        ' increment if direction = up
           stpx := stpx + 1
             if stpx := 8
                stpx := 0
                     
        outa[12..15] := HStep[stpx]          ' send phase pulse                                                              
        waitcnt(clkfreq/delay + cnt)        ' set speed, max speed = H1800 F800 W700
All variables are initialized to 0 before use.

Comments

  • frank freedmanfrank freedman Posts: 1,983
    edited 2011-10-08 20:11
    JBWolf wrote: »
    Hello,
    I am having trouble controlling uni-polar stepper motors with the propeller.
    I have been working the past week on a simple x/y axis scanner for making my own laser show system.
    I have tried several different programming methods but I always end up with overshoots, lost movements and cannot create a circle at all.

    Wiring: Two 12v, 1amp steppers wired directly to 8 pins on the propeller with an ULN2803.
    I am using simple out commands to create a phase position as such:
    outa[12..15] := phase[stp]

    Here are the phases I am using with the 'phase[stp]'
    HStep byte %1001, %1000, %1100, %0100, %0110, %0010, %0011, %0001 ' Half Step
    FStep byte %1001, %1100, %0110, %0011 ' Full Step
    WStep byte %1000, %0100, %0010, %0001 ' Wave Step


    Am I supposed to be sending a frequency rather than a simple outa[12..5] to control movements?
    I am using a simple loop to try to get it to move a specific distance:
    Say I want a horizontal line.... it moves right 10 steps, then left one, then no movement for 9 sec, then repeats. I cannot figure out why!
    I also cannot get both motors to move at the same time.
    PUB test1 | dist, delay
     delay := 200 
     dist := 10
     
        repeat 6
           xaxis(right, dist, delay)
           waitcnt(clkfreq + cnt)
    
           xaxis(left, dist, delay)
           waitcnt(clkfreq + cnt)        
    
    
    PUB Xaxis (dir, distx, delay)   
    repeat distx                          ' cycle phase dist many times
    
            if dir == 2                      ' decrement if direction = down
               stpx := stpx - 1
                 if stpx == -1
                    stpx := 7
                    
            if dir == 1                        ' increment if direction = up
               stpx := stpx + 1
                 if stpx := 8
                    stpx := 0
                         
            outa[12..15] := HStep[stpx]          ' send phase pulse                                                              
            waitcnt(clkfreq/delay + cnt)        ' set speed, max speed = H1800 F800 W700
    
    All variables are initialized to 0 before use.

    Maybe slow the clock rate and drop some LEDs on the output to see if the phase seq is was you think that it should be. Down the road a piece, if the surplus gods smile on you, you might try doing the laser thing using a galvo/mirror system for some speed....

    Frank
  • JBWolfJBWolf Posts: 405
    edited 2011-10-08 20:13
    After trying the above coding method, I was convinced the only way to get both motors to move at the same time was to have 2 methods running in 2 new cogs.
    Each method would be in an endless repeat loop to keep it running, each is passed 3 variable addresses with @. The variable addresses hold values for Direction, Distance and Delay respectively.
    The 6 variables are set in the VAR section as longs and initialized before use.

    The 2 methods for controlling the motors are now running in their own cog. The X & Yaxis method repeats endlessly until the direction variable is greater than 0 (1 = left, 2 = right). It then loops 'distance' many times, and simply in/decrements the 'stp' variable for outputting the next proper phase (dont want to jump across phases as it will cause unpredictable movements).

    So now it should be as simple as writing the delay, distance and lastly direction for either axis control method. Once the direction variable is greater than 0, the X or Y method should then start in/decrementing the counter and looping distance many times.
    To avoid sending a new movement command before the last has completed, I first tried using a simple delay:
    waitcnt(clkfreq / (delay / distance) + cnt)
    
    The weird thing is... sometimes this works, sometimes it does not.
    If I send one movement such as writing the Xaxis delay and distance, then write the direction and use the above delay, it works. but if I try to write the X and Yaxis delay and distance, then write both directions... it does not work with that delay all the time.
    Sometimes I have to add another command to write 0 to both direction variables, delay for a very short period, then continue with movement commands, the write 0's, short delay, movements.......

    I cannot find any stable method to program this at all!
    I keep getting varying results using the same code!

    I am right now trying to implement an 'enable' variable.
    This variable is 0 when either the X or Yaxis method is NOT inside the 'if direction > 0' loop, indicating that it is ready for a movement command. When it enters the loop, it writes 1 to the enable variable, and when done it writes 0 to enable again.
    I use a simple "repeat until enable == 0" to act as the delay.
    For some reason, this does not work.
    The enable is declared as long in VAR, Initialized before use, and the address is passed with @ during the Xaxis method launch into a new cog.
    I have tried writing the variable inside the Xaxis method with " enable := 1 " and "long[enable] := 1", but it still wont work.

    I just cannot get anything right. Everything I have tried just leads to more confusion.
    Can someone please point me in the right direction


    Here is the code I am using to launch into cogs and await variable change.
    CON
       
      _clkmode = xtal1 + pll16x                  ' System clock → 80 MHz
      _xinfreq = 5_000_000
    
      TX_PIN        = 2                          ' LCD Pin ID
      BAUD          = 19_200                     ' LCD Baud
      
      
    OBJ
      LCD           : "FullDuplexSerial.spin"     ' LCD Object
    
      
    VAR
      long stack[200]                        ' Cog stack space
      byte cog[2]                            ' Cog ID
      long up
      long right
      long down
      long left        ' Directional variables
      long enX
      long dirX
      long distX
      long delayX      ' Xaxis variables
      long enY
      long dirY
      long distY
      long delayY      ' Yaxis variables
    
    
    PUB Main | Success
    initvars
    StartGalvo   
    
    test
    
    
    PUB test | delay
     initvars
       delay := 400
       delayX := delay
       delayY := delay      
       
    
       '--------------------------------------------- Horizontal Line ----------------
       
       dirX := left                               ' move to start position
       distX := 5
         repeat until enX == 0 
    
       repeat 20
         dirX := right
         distX := 10
            repeat until enx == 0
    
         dirX := left
         distX := 10
            repeat until enX == 0
    
       dirX := right                               ' move to home position
       distX := 5
         repeat until enX == 0 
    
    
    PUB initvars
    up     := 2                                         ' initialize variables
    right  := 2
    down   := 1
    left   := 1
    dirX   := 0
    distX  := 0
    delayX := 0
    dirY   := 0
    distY  := 0
    delayY := 0
    enx := 0
    eny := 0
         
    
    PUB StartGalvo : success 
    
    cog[1] := cognew(Xaxis(@dirX,@distX,@delayX), @stack[40])     ' Start X-Axis control
    cog[2] := cognew(Yaxis(@dirY,@distY,@delayY), @stack[80])     ' Start Y-Axis control
    
    
    PUB Stop
    
    cogstop(cog[1])                                ' Stop X-axis
    cogstop(cog[2])                                ' Stop Y-Axis
    
    
    
    PRI Xaxis (dirXaddr, distXaddr, delayXaddr) | stp, dir, dist, delay, dirold, distold, delayold, chksum1, chksum2   
    
    stp := -1
    dir := long[dirxaddr]
    dist := long[distxaddr]
    delay := long[delayxaddr] 
    dirold := 0
    distold := 0
    delayold := 0
    chksum1 := 0
    chksum2 := 0
    enx := 0
    
    
    '                             ****** calibrate start *********
    dira[12..15]~~
    
     repeat 75
       stp := stp + 1
        if stp == 4
          stp := 0
       outa[12..15] := WStep[stp]
       waitcnt(clkfreq/200 + cnt)
    
     repeat 24
       stp := stp - 1
         if stp == -1
            stp := 3
       outa[12..15] := WStep[stp]
       waitcnt(clkfreq/200 + cnt)
    stp := ((stp * 2) +1)                  ' switch to hstep mode
    '                               ***** calibrate end ********
    
    
    repeat
      dir := long[dirXaddr]
      dist := long[distXaddr]
      delay := long[delayXaddr]
      chksum1 := (dir + dist + delay)
    
       if dir > 0
          
         if chksum1 <> chksum2
             eny := 1  
      
          repeat dist                         ' cycle phase dist many times
            if dir == 1                        ' increment if direction = up
               stp := stp + 1
                 if stp == 8
                    stp := 0     
               
             
            if dir == 2                        ' decrement if direction = down
               stp := stp - 1
                 if stp == -1
                    stp := 7
                    
            outa[12..15] := HStep[stp]          ' send phase pulse                                                              
            waitcnt(clkfreq/delay + cnt)        ' set speed, max speed = H1800 F800 W700
    
      dirold := dir
      distold := dist 
      delayold := delay
      chksum2 := chksum1
      enx := 0         
    
    
    
    PRI Yaxis (dirYaddr, distYaddr, delayYaddr) | outp, stp, dir, dist, delay, dirold, distold, delayold, chksum1, chksum2   
    
    stp := -1
    dir := long[diryaddr]
    dist := long[distyaddr]
    delay := long[delayyaddr] 
    dirold := 0
    distold := 0
    delayold := 0
    chksum1 := 0
    chksum2 := 0
    eny := 0
    
    
    '                             ****** calibrate start *********
    dira[8..11]~~
    
      repeat 75
       stp := stp + 1
         if stp == 4
            stp := 0    
       outa[8..11] := WStep[stp]
       waitcnt(clkfreq/200 + cnt)
    
      repeat 24
       stp := stp - 1
         if stp == -1
            stp := 3
       outa[8..11] := WStep[stp]
       waitcnt(clkfreq/200 + cnt)
    
    stp := ((stp * 2) +1)                 ' switch to hstep mode 
    '                               ***** calibrate end ********
    
    
    repeat
      dir := long[dirYaddr]
      dist := long[distYaddr]
      delay := long[delayYaddr]
      chksum1 := (dir + dist + delay)
    
       if dir > 0
         if chksum1 <> chksum2
            eny := 1
           repeat dist                         ' cycle phase dist many times
             if dir == 1                        ' increment if direction = up
               stp := stp + 1
                 if stp == 8
                    stp := 0     
               
             
             if dir == 2                        ' decrement if direction = down
               stp := stp - 1
                 if stp == -1
                    stp := 7
    
             outa[8..11] := HStep[stp]          ' send phase pulse                                                              
             waitcnt(clkfreq/delay + cnt)        ' set speed, max speed = H1800 F800 W700
        
    
      dirold := dir
      distold := dist 
      delayold := delay
      chksum2 := chksum1
      eny := 0         
    
      
    '*******************************  End Galvo Controls  ***********************************
       
    DAT
    
    HStep   byte   %1001, %1000, %1100, %0100, %0110, %0010, %0011, %0001   ' Half Step - best resolution
    FStep   byte   %1001, %1100, %0110, %0011                               ' Full Step - max torque
    WStep   byte   %1000, %0100, %0010, %0001                               ' Wave Step - weak torque
    
  • JBWolfJBWolf Posts: 405
    edited 2011-10-08 20:15
    I can make a square no prob with the phases.
    It just when I try to use it with multiple movement commands or try to achieve an arc or circle.... I have made an octagon and tried to make the delay 3/4'ths the actual time needed to complete the move so that it will 'round off' the end of the line.. this is the closest I have gotten to a circle.
  • PJAllenPJAllen Banned Posts: 5,065
    edited 2011-10-08 20:16
  • frank freedmanfrank freedman Posts: 1,983
    edited 2011-10-08 21:37
    I was going to suggest separate cogs unless you were going PASM. In either event you will still need some form of syncronizastion between cogs as it is still an x-y plotter with all that implies. You will need to sgtart from a known point for x and y then "draw" the shape the laser on off is actually going to be your z axis.

    Thinking about this, you may be better served using a galvo arrangement the galvo can give ramdom access positioning whereas a stepper will be sequential access to points..

    If sticking with steppers, it may be easier to set the steppers up to continuously scan x and y directions while using the z axis to enable/disable the laser point much like the television does it. Maybe sync the zero detect points of the stepper scans to a slow version of the ntsc tv prop object to paint the image.
    Frank
  • JBWolfJBWolf Posts: 405
    edited 2011-10-09 00:32
    Not sure what you mean by synchronization between the cogs. The way I generally think about it is they are both waiting for a command, i use a delay to make sure I havent written to it again until the last command is done.
    This all happens in real time... the cogs do not communicate to, or share each others data.
    But I have been having alot of timing trouble, can you please fill me in? :)

    Thats interesting, I was just talking with a friend who suggested the same TV idea.
    Only problem with this method I can see, is the motor has a .9 degree half step, which means there will be dots instead of lines.
    So I'm a little worried about resolution.
    But I'll give it a try! I have a TTL laser driver thats optically isolated, I think it should work.
    What advantage would I get out of using the NTSC object? I havent used it before so I know nothing about it.
  • JBWolfJBWolf Posts: 405
    edited 2011-10-09 02:36
    ok so I was playing around trying to get the enable idea to work....
    I simply switched the order in which I was writing the variables and now it works... but only with even delay times, or it moves opposite the direction of what I am sending. I cannot use a delay of ... 1300, 1500, 1700
    weird... really weird


    so this:
    repeat 400
    dirX := right
    distX := dist
    repeat until enx == 0

    Was changed to this:
    repeat 400
    distX := dist
    dirX := right
    repeat until enx == 0
  • RaymanRayman Posts: 14,865
    edited 2011-10-09 05:23
    I wonder if you're giving your cogs enough stack space...
    Typically we give ~100 longs of stack space. It looks like you're only giving 40 longs...

    Whenever strange things like this happen, I try increasing the stack space...
  • frank freedmanfrank freedman Posts: 1,983
    edited 2011-10-09 11:20
    Hey JB,


    Sync first. I mentioned syncronization because you seem to be placing the beam with and x and y position. if you plot the math function of a circle, you will get an x and y position for each point on the circle (Yeah I know, resounding Duh!!), but if you are trying to draw the circle using a continuous list of points, you would need some way of making sure that the intended x and y positions are what the prop believes them to be, either with an index of similar positional indicator. Once syncronized, the prop cores could independently map their x and y as long as they are working from a shared table. So as long as they are singing from the same page, they should be able to position the mirrors independently.

    As for the NTSC object, I have not looked at it in detail, only tried it out for another experiment. The reason I mentioned it is that TV does the electronic equivalent of a mechanical scan. My reasoning for using it would be that at any speed, the timings would still have to maintain the same approximate timing sequence. What you would have to do is adapt the timing for the Horizontal and Vertical timings to the actual stepper position of x and y. After that it should be the same as providing an image in the frame buffer to be painted by the laser.

    You are correct of course regarding the effect of the step angle over distance. On a table I adjusted couple of weeks ago, a 2.3 degree offset from horizontal resulted in over 1cm offset at about 20cm from the hinge. you may need to go with a finer step angle. Or a galvo-mirror where the offset would be dependent on the number of bits in the DAC used to drive the galvo motor.

    Frank
  • JBWolfJBWolf Posts: 405
    edited 2011-10-09 15:25
    "you would need some way of making sure that the intended x and y positions are what the prop believes them to be"
    I do not follow this at all. How would I approach this?
    At the beginning of launching the X & Yaxis cogs, it runs through a 'calibrate' which moves the motors back 45 degrees where they hit a metal bar, then forward 15 degrees. This way they always start in the center.
    So after calibrate, it knows where home is and simply moves from its current position to the next. If I move right 10, then obviously left 10 brings me back home... this is the way I have been keeping them in alignment.
    I do not understand how to do any kind of synchronization.

    Right now If I do not choose a delay time that it 'likes' such as 1000 but not 1100.... the thing goes crazy with missed steps and wrong directions... makes no sense to me.
    If I change to a delay of 1200.... works 100% fine, 1300 goes crazy, 1400 works fine.... no idea whats going on.
    The delay function I am using is : waitcnt(clkfreq / delay + cnt) I have also tried waitcnt((clkfreq / delay) + cnt)
  • frank freedmanfrank freedman Posts: 1,983
    edited 2011-10-09 23:35
    JBWolf wrote: »
    "you would need some way of making sure that the intended x and y positions are what the prop believes them to be"
    I do not follow this at all. How would I approach this?
    At the beginning of launching the X & Yaxis cogs, it runs through a 'calibrate' which moves the motors back 45 degrees where they hit a metal bar, then forward 15 degrees. This way they always start in the center.
    So after calibrate, it knows where home is and simply moves from its current position to the next. If I move right 10, then obviously left 10 brings me back home... this is the way I have been keeping them in alignment.
    I do not understand how to do any kind of synchronization.

    Right now If I do not choose a delay time that it 'likes' such as 1000 but not 1100.... the thing goes crazy with missed steps and wrong directions... makes no sense to me.
    If I change to a delay of 1200.... works 100% fine, 1300 goes crazy, 1400 works fine.... no idea whats going on.
    The delay function I am using is : waitcnt(clkfreq / delay + cnt) I have also tried waitcnt((clkfreq / delay) + cnt)

    Sorry to have confused things.

    Finding zero at some point of both steppers is basically what I meant by syncing the two so that they start at a known position. It looks like it knows where zero is and seems able to track where it is and the octagon shows the ability to do simultaneous movement on x and y to get a diagonal line. I am not sure how you are giving it data to form the circle or octagon. Which make/model of stepper are you using?

    You mention that it has difficulties when trying to do both directions together. What does the power supply look like when doing this (scope?) and what to the outputs to the stepper look like? Can the supply handle both steppers at the same instant? Almost sounds like the unit is slipping from too fast a step rate or too high a load on the motor. Or power supply not able to provide the needed current when stepping. If the steppers are slipping, the prop will loose track of the position unless an init call is done to re-zero the steppers.

    Frank

    Another alternative to the stepper for moving your mirror(s) may be a servo motor. Lots of objects in the OBEX and quite a fine possibly higher resolution than the stepper depending on which you use. Far cheaper than a true galvo motor such as Laser Surplus or similar would sell for.....
Sign In or Register to comment.