Shop OBEX P1 Docs P2 Docs Learn Events
Trouble with COG launching — Parallax Forums

Trouble with COG launching

JBWolfJBWolf Posts: 405
edited 2011-10-01 14:43 in Propeller 1
Hello,
I am trying to write a stepper control program but having trouble launching them into new cogs.

I have 2 steppers going through a ULN 2803.
The goal is to have each stepper running in a cog awaiting variables for distance, direction and speed.
I can move the motors no problem with a simple loop going through the phases... but I cannot get this code to work.

The LCD just returns failure, not sure why though.
I sincerely appreciate any help.
{{
  Dual cog control for ULN 2003/2803 & 2 unipolar steppers
  motor 1 = pins 8-11
  motor 2 = pins 12-15

  1 revolution = 200 steps

}}

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[3]                            ' Cog ID
  
  byte testctr                            ' Counter for LCD
  byte session                            ' Variable for LCD


PUB Main | Success

LCD.start(TX_PIN, TX_PIN, %1000, 19_200)           ' initialize LCD
  waitcnt(clkfreq / 100 + cnt)
BKLon                                              ' Turn on LCD BackLight    

 
    

  Success := StartGalvo                             'initialize outputs and start motors in cogs
  LCD.str(string("Start Galvo:" ))
   if success == 1
     LCD.str(string("Success" ))
   else
     LCD.str(string("Failure" ))      

  Success := Calibrate                             ' Move galvo to home position  
  NewLine
  LCD.str(string("Calibrate:" ))
   if success == 1
     NewLine
     LCD.str(string("Success, on cog#" ))
     LCD.dec(cog[1])
     LCD.str(string(" and cog#" ))
     LCD.dec(cog[2])

   else
     LCD.str(string("Failure" ))                           


 


PUB StartGalvo : success 

cog[1] := cognew(Xaxis(0,0,0), @stack[40])     ' Start X-Axis control
cog[2] := cognew(Yaxis(0,0,0), @stack[80])     ' Start Y-Axis control

PUB Stop

cogstop(cog[1])                                              ' Stop X-axis
cogstop(cog[2])                                              ' Stop Y-Axis



PUB Calibrate : Success | stp                                         ' 280deg home positioning
dira[8..15]~~
stp := 5
 repeat 155
   stp := stp - 1
   if stp < 1
     stp := 4

   outa[8..11] := WStep[stp]
   outa[12..15] := WStep[stp]
   waitcnt(clkfreq/600 + cnt)


PUB Xaxis (dirX, distX, delayX) | index, stp, distX2      ' dir/dist 1=up 2=down

dira[8..11]~~                           ' Set pins 8-11 output    X-Axis 
repeat
  distX2 := distX 
  repeat index from 0 to distX2         ' cycle phase distX many times
    if dirX == 1                        ' increment if direction = up
       stp := stp + 1
       if stp == 5                      ' reset increment at 5 to 1
         stp := 1

    if dirX == 2                        ' decrement if direction = down
        stp := stp - 1    
        if stp := 0                     ' reset decrement at 0 to 4
          stp := 4
    
    
        
    outa[8..11] := FStep[stp]          ' send phase pulse
                                                              
    waitcnt(clkfreq/delayX + cnt)      ' set speed, max speed = 600

 








PUB Yaxis (dirY, distY, delayY) | index, stp, distY2


dira[12..15]~~                                     ' Set pins 12-15 output   Y-Axis

repeat
  distY2 := distY 
  repeat index from 0 to distY2          ' cycle phase distY many times
    if dirY == 1                        ' increment if direction = up
       stp := stp + 1
       if stp == 5                      ' reset increment at 5 to 1
         stp := 1

    if dirY == 2                        ' decrement if direction = down
        stp := stp - 1    
        if stp := 0                     ' reset decrement at 0 to 4
          stp := 4
    
    
        
    outa[12..15] := FStep[stp]          ' send phase pulse
                                                              
    waitcnt(clkfreq/delayY + cnt)       ' set speed, max speed = 600

  






PUB Newline                            ' Start LCD Commands
   LCD.tx(13)

pub NextLine
   LCD.tx(10)

PUB CLS
   LCD.tx(12)
   waitcnt(clkfreq /500 + cnt) 

PUB Back (n)
   repeat n
     LCD.tx(8)

PUB BKLon
   LCD.tx(17)

PUB BKLoff
   LCD.tx(18)

PUB BlinkON
   LCD.tx(23)

PUB Default
   LCD.tx(24)

PUB NoCursor
   LCD.tx(22)

PUB LCDoff                              ' End LCD Commands
   LCD.tx(21)


    
DAT

HStep   byte   %0000, %1001, %1000, %1100, %0100, %0110, %0010, %0011, %0001   ' Half Step - best resolution
FStep   byte   %0000, %1001, %1100, %0110, %0011                               ' Full Step - max torque
WStep   byte   %0000, %1000, %0100, %0010, %0001                               ' Wave Step - weak torque

Comments

  • Cluso99Cluso99 Posts: 18,069
    edited 2011-09-24 10:44
    I just had a quick look. Seems you are not waiting long enough for the cogs to start before testing to see if they start. You have to allow time for the cog to be loaded from hub before it begins executing code.
  • JBWolfJBWolf Posts: 405
    edited 2011-09-24 10:57
    I added a 1 sec delay directly after each cognew command... same result = failure
    PUB StartGalvo : success 
    
    cog[1] := cognew(Xaxis(0,0,0), @stack[40])     ' Start X-Axis control
    waitcnt(clkfreq *1 + cnt)
    cog[2] := cognew(Yaxis(0,0,0), @stack[80])     ' Start Y-Axis control
    waitcnt(clkfreq *1 + cnt)
    
  • mindrobotsmindrobots Posts: 6,506
    edited 2011-09-24 11:24
    In the StartGalvo and Calibrate routines, you never set success to 1. I assume success is initialized to 0 when you enter the routine (the default result variable is) - if you don't set it to something, it will remain 0.

    {Should have put in a manual reference: P194 of v1.2 Propeller Manual as supplied with SpinTool V1.3}
  • JBWolfJBWolf Posts: 405
    edited 2011-09-24 11:57
    Ah, your spot on!
    I forgot to power the motors since I was just checking to see if success was returned or not.
    It says failure but the motors do move.

    Changed it to check for 0 instead of 1... it now shows fine and properly returns the new cog id's!
    thanks guys!
  • mindrobotsmindrobots Posts: 6,506
    edited 2011-09-24 12:08
    Glad we could help!
    Welcome to the forum and the Propeller!
  • JBWolfJBWolf Posts: 405
    edited 2011-09-24 12:12
    Any neat math tricks, references or general guidelines for geometry?
    For example a square is pretty simple... up, right, down, left. I'll be able to code that easily.
    But what about a circle or more complex shapes?
    Say I want it to move like a spirograph in an infinite loop, it would be a major task mapping out every movement. I cant think of a math algorithm for it either off the top of my head.

    my routine has the X & Y axis motors moving with 3 variables for each.... direction, distance and speed.
  • mindrobotsmindrobots Posts: 6,506
    edited 2011-09-24 12:25
    I got nothing for you in that area to me, a square is complex! :smile:

    I'd suggest asking that questions in a new thread with a title describing what you want to do. There are a bunch of sharp folks around here that I'm sure are able to give you some helpful ideas for formulas and algorithms for complex movements. They probably won't see the math question under this thread.
  • JBWolfJBWolf Posts: 405
    edited 2011-09-24 12:34
    sorry to jump around so much....

    I'm just a little hazy on the exact amount of stack space needed....
    I believe each cog will need less than 20 stack space?
    I declared way more when i was getting failures to be safe

    Does cog0 have to be considered in there?
  • Mike GreenMike Green Posts: 23,101
    edited 2011-09-24 12:42
    The main cog uses whatever memory is left over once the program is loaded. Usually that's plenty.

    20 longs will usually work for a cog that's only executing a main method that doesn't call anything else or calls something that doesn't call anything else. A call to a method takes about 4 longs plus any parameters plus any local variables. There's also some temporary locations needed for evaluating expressions, subscripts, etc. There's an object in the OBEX that fills a stack area with a unique value, then starts a separate cog to monitor the stack area looking to see how many of those unique values are changed to something else. Your program can display the resulting amount of stack that's actually used during execution.
  • JBWolfJBWolf Posts: 405
    edited 2011-09-26 13:14
    I seem to be having trouble passing 2 cogs data at the same time.
    The revised program is below... the square works as it should, but I cannot get the triangle to work.
    Instead of getting up and right movements at the same time from the 2 seperate motors, they go in order one at a time
    {{
      Dual stepper cog control for ULN 2003/2803 & galvo
      motor 1 = pins 8-11   = Y Axis - DirY 1 = Down, 2 = Up
      motor 2 = pins 12-15  = X Axis - DirX 1 = Left, 2 = Right (bottom motor)
    
      Full Step & Wave = 1 revolution = 200 steps @ 1.8deg per step
      Half Step = 1 revolution = 400 steps @ .9deg per step
    
      Half Step Max Speed = 1800
      Full Step Max Speed = 800
      Weak Step Max Speed = 700
    
      85deg max clearance from home position
      
    }}                                                                
    
    CON
       
      _clkmode = xtal1 + pll16x                  ' System clock &#8594; 80 MHz
      _xinfreq = 5_000_000
    
      TX_PIN        = 2                          ' LCD Pin ID
      BAUD          = 19_200                     ' LCD Baud
      up            = 2
      right         = 2
      down          = 1
      left          = 1
    
    OBJ
      LCD           : "FullDuplexSerial.spin"     ' LCD Object
    
      
    VAR
      long  stack[200]                        ' Cog stack space
      byte  cog[3]                            ' Cog ID
      
      byte testctr                            ' Counter for LCD
      byte session                            ' Variable for LCD
            
    
    PUB Main | Success
    
    
    LCD.start(TX_PIN, TX_PIN, %1000, 19_200)           ' initialize LCD
      waitcnt(clkfreq / 100 + cnt)
    BKLon                                              ' Turn on LCD BackLight    
    CLS                                                ' Clear LCD screen
     
        
    
      Success := StartGalvo                             'initialize outputs and start motors in cogs
      LCD.str(string("Start Galvo:" ))
       if success == 0
         LCD.str(string("Success" ))
       else
         LCD.str(string("Failure" ))      
    
      Success := Calibrate                             ' Move galvo to home position  
      NewLine
      LCD.str(string("Calibrate:" ))
       if success == 0
         
         LCD.str(string("Success!" ))
         NewLine
         LCD.str(string("Cog#:"))
         LCD.dec(cog[1])
         LCD.str(string("and Cog#" ))
         LCD.dec(cog[2])
    
       else
         LCD.str(string("Failure" ))                           
    
    
    waitcnt(clkfreq * 2 + cnt)
    
    
    '*****************************************
    
     cls
      waitcnt(clkfreq / 2 + cnt)
      LCD.tx(153)                       ' move to position 2 x 5 
      LCD.str(string("Movement Routines"))
      Waitcnt(clkfreq / 2 + cnt)
    
      
     square
     triangle
    
    
    
    '***************************************** END ******************************
    
      cls
      waitcnt(clkfreq / 2 + cnt)
      LCD.tx(153)                       ' move to position 2 x 5 
      LCD.str(string("END"))
      Waitcnt(clkfreq / 2 + cnt)
    
      stop                              ' Stop motor cogs
    
    '***************************************** END ******************************
    
    
    
    '****************************** start movement codes **************************************
    PUB Square | dist, speed
    
    dist := 22                         ' 20deg
    speed := 1800
      repeat 50
        xaxis(right, dist, speed)
        yaxis(up, dist, speed)
        xaxis(left, dist, speed)
        yaxis(down, dist, speed)
    
    
    PUB Triangle | dist, speed
    
    dist := 22                        ' 22dist = 20deg
    speed := 1800
      repeat 50
        xaxis(right, (dist/2), speed)
        yaxis(up, dist, speed)
        xaxis(right, (dist/2), speed)
        yaxis(down, dist, speed)
        xaxis(left, dist, speed)    
    
    
    
    
    
    
    '******************************* end movement codes *****************************************
    
    '****************************  Start Galvo Controls *************************************
    PUB StartGalvo : success 
    
    cog[1] := cognew(Xaxis(0,0,0), @stack[40])     ' Start X-Axis control
    cog[2] := cognew(Yaxis(0,0,0), @stack[80])     ' Start Y-Axis control
    
    
    PUB Stop
    
    cogstop(cog[1])                                ' Stop X-axis
    cogstop(cog[2])                                ' Stop Y-Axis
    
    
    
    PUB Calibrate : Success | stp                  ' 90deg back then 45deg fwd home positioning
    dira[8..15]~~
    stp := 5
     repeat 50
       stp := stp - 1
       if stp < 1
         stp := 4
    
       outa[11..8] := WStep[stp]
       outa[15..12] := WStep[stp]
       waitcnt(clkfreq/200 + cnt)
    
     repeat 25
       stp := stp - 1
       if stp < 1
         stp := 4
    
       outa[8..11] := WStep[stp]
       outa[12..15] := WStep[stp]
       waitcnt(clkfreq/200 + cnt)
    
    
    PUB Xaxis (dirX, distX, delayX) | indexX, stpX, distX2      ' 1= Left, 2= Right
    
    distX2 := distX
    distX := 0 
    
      repeat distX2                         ' cycle phase distX many times
        if dirX == 1                        ' increment if direction = up
           stpX := stpX + 1
           if stpX > 8                      ' reset increment counter at 5 to 1
             stpX := 1
    
        if dirX == 2                        ' decrement if direction = down
            stpX := stpX - 1    
            if stpX < 1                     ' reset decrement counter at 0 to 4
              stpX := 8
        
        
            
        outa[12..15] := HStep[stpX]          ' send phase pulse
                                                                  
        waitcnt(clkfreq/delayX + cnt)        ' set speed, max speed = H1800 F800 W700
    
    distX2 := 0
    
    PUB Yaxis (dirY, distY, delayY) | indexY, stpY, distY2        ' 1= Down, 2= Up
    
    distY2 := distY                         ' buffer distance
    distY := 0                              ' clear distance
    
      repeat distY2                         ' cycle phase distY many times
        if dirY == 1                        ' increment if direction = up
           stpY := stpY + 1
           if stpY > 8                      ' reset increment counter at 5 to 1
             stpY := 1
    
        if dirY == 2                        ' decrement if direction = down
            stpY := stpY - 1    
            if stpY < 1                     ' reset decrement counter at 0 to 4
              stpY := 8
        
        
            
        outa[8..11] := HStep[stpY]         ' send phase pulse
                                                                  
        waitcnt(clkfreq/delayY + cnt)       ' set speed, max speed = H1800 F800 W700
    
    distY2 := 0                             ' clear buffer
    
    '*******************************  End Galvo Controls  ***********************************
    
    
    '*******************************  Start LCD Commands ***********************************
    PUB Newline                          
       LCD.tx(13)
    
    pub NextLine
       LCD.tx(10)
    
    PUB CLS
       LCD.tx(12)
       waitcnt(clkfreq /500 + cnt) 
    
    PUB Back (n)
       repeat n
         LCD.tx(8)
    
    PUB BKLon
       LCD.tx(17)
    
    PUB BKLoff
       LCD.tx(18)
    
    PUB BlinkON
       LCD.tx(23)
    
    PUB Default
       LCD.tx(24)
    
    PUB NoCursor
       LCD.tx(22)
    
    PUB LCDoff                              
       LCD.tx(21)
    '*********************************  End LCD Commands  ***********************************
        
    DAT
    
    HStep   byte   %0000, %1001, %1000, %1100, %0100, %0110, %0010, %0011, %0001   ' Half Step - best resolution
    FStep   byte   %0000, %1001, %1100, %0110, %0011                               ' Full Step - max torque
    WStep   byte   %0000, %1000, %0100, %0010, %0001                               ' Wave Step - weak torque
    
  • lanternfishlanternfish Posts: 366
    edited 2011-09-26 20:39
    Hi. I'm no expert on these things so here is my 2cents worth.

    What about combining and then outputting the outa[8..11] := HStep[stpY] & outa[12..15] := HStep[stpX] instructions.

    Cheers
  • JBWolfJBWolf Posts: 405
    edited 2011-09-26 22:02
    If I combine the 2 calls to the Xaxis and Yaxis PUB's with an ampersand... i.e
    xaxis(right, (dist/2), speed) & yaxis(up, dist, speed)

    I get 'error expected end of line'
  • JBWolfJBWolf Posts: 405
    edited 2011-09-27 11:27
    Ok I am getting even more lost now, this doesnt make any sense to me

    Since I cannot make the X and Y axis move at the same time, I tried making it step up 1, over 1, up 1, over 1.....
    I did some debugging in the triangle routine and found something very odd.
    If I send a single call to triangle with the distance set to 1 so it will make the X axis move a single step, it works no prob. now if I tell it to repeat the triangle call more than 2 times, it is showing that the step value starts counting at 114 even with the 'if > 8' condition. stpX should always start at 0 unless already started and in/decremented with a condition to reset stpX to 1 if > 8.
    How does it get 114? it shows this value of 114 if I try to repeat the triangle routine any amount of times more than one.
    there are no other uses of the stpX variable anywhere in the program

    Here is the triangle routine I am trying:
    PUB Triangle | dist, speed
    repeat 10 
        xaxis(2, 1, 2)
    
    here is the xaxis routine:
    PUB Xaxis (dirX, distX, delayX) | indexX, stpX, distX2      ' 1= Left, 2= Right
    
    distX2 := distX
    distX := 0 
    
      repeat distX2                         ' cycle phase distX many times
        if dirX == 1                        ' increment if direction = up
           stpX := stpX + 1
           if stpX >  8                      ' reset increment counter at 5 to 1
             stpX := 1
    
        if dirX == 2                        ' decrement if direction = down
            stpX := stpX - 1    
            if stpX < 1                     ' reset decrement counter at 0 to 4
              stpX := 8
        
        cls
        LCD.str(string("stpX:" ))
        LCD.dec(stpX)
        newline
        LCD.str(string("dirx:" ))
        LCD.dec(dirx)
        newline
        LCD.str(string("distx2:" ))
        LCD.dec(distx2)
        newline
        LCD.str(string("delayx:" ))
        LCD.dec(delayx)
        
            
        outa[12..15] := HStep[stpX]          ' send phase pulse
                                                                  
        waitcnt(clkfreq/delayX + cnt)        ' set speed, max speed = H1800 F800 W700
    
    distX2 := 0
    
  • kuronekokuroneko Posts: 3,623
    edited 2011-09-27 16:38
    stpX is a local variable and not initialised to 0 (like result). It will therefore be whatever is on the stack at the time.
  • lanternfishlanternfish Posts: 366
    edited 2011-09-27 18:36
    JBWolf wrote: »
    If I combine the 2 calls to the Xaxis and Yaxis PUB's with an ampersand... i.e
    xaxis(right, (dist/2), speed) & yaxis(up, dist, speed)

    I get 'error expected end of line'

    I wasn't making myself clear.

    For instance, the xaxis method instead of performing the outa [8..11] command writes the stepper motor data for each step to a table. The Yaxis method does the same for outa [12..15].

    Another method then reads the table (combing both x- and y- data) and performs outa[8..15] effectively synchronising the x- and y- stepping.

    Hope this makes more sense.
  • JBWolfJBWolf Posts: 405
    edited 2011-09-28 00:41
    kuroneko:
    Can I declare it in the con or var section and just use it as a global? Nothing else uses it so.... I'll give that a try!
    I cannot put an stpX := 0 at the beginning of the Xaxis PUB as it would lose track of its current phase position on the next call. I tried this and it resulted in unpredictable movements.

    lanternfish:
    I think I follow your logic, I will most certainly use it in the future for a dedicated movement script.
    I do not find it an easy way to code multiple movement routines however. I believe It would require creating too many of those lines for each and every routine. it would also need counters, which means several nested loops for each.

    Maybe my choice of structure is not as efficient as could be for calling over 100 pre-programmed movement routines? I only started learning spin 2 months ago and invite anyone to please correct my ways :)
    My general idea is to have the 2 steppers running in 2 new cogs, then they await 3 variable to be passed: direction, distance and speed. This way I can make geometric shapes and patterns with simple lines in a repeat loop... such as a square:
      repeat 50
        xaxis(right, dist, speed)
        yaxis(up, dist, speed)
        xaxis(left, dist, speed)
        yaxis(down, dist, speed)
    
    If I could get the 2 cogs to take data at nearly the exact same time, I could code a triangle with 5 simple lines instead of having to repeatedly make a single step up and over.
    {{ easy way}}
    yaxis(up, dist, speed) & xaxis(right, (dist/2) , speed)     ' left side diagonal line
    yaxis(down, dist, speed) & xaxis(right, (dist/2), speed)  ' right side diagonal line
    xaxis(left, dist, speed)                                         ' bottom flat line
    
    But if I cannot get both data to both cogs at nearly the exact same time...
    {{single step up and right, then down and right, then left - the hard way}}
    trak := 0
      dist := 22                        ' 22dist = 20deg
      speed := 180
    
        repeat dist                                                             ' left side diagonal line
           trak ++
           Yaxis(up, 1, speed)
            if trak > 1
              trak := 0
              xaxis(right, 1, speed)
          waitcnt(clkfreq/speed + cnt)
          
      trak := 0
       
        repeat dist                                                              ' right side diagonal line
           trak ++
           Yaxis(down, 1, speed)
            if trak > 1
              trak := 0
              xaxis(right, 1, speed)
          waitcnt(clkfreq/speed + cnt)
        
      xaxis(left, dist, speed)                                             ' bottom flat line
    
  • kuronekokuroneko Posts: 3,623
    edited 2011-09-28 01:03
    JBWolf wrote: »
    Can I declare it in the con or var section?
    The CON section defines constants so you don't really want it in there. VAR is fine.
    JBWolf wrote: »
    My general idea is to have the 2 steppers running in 2 new cogs, then they await 3 variable to be passed: direction, distance and speed.
    Just as an exercise, what happens - based on your posting #11 - when the two cogs you started are finished with their initial method calls (Xaxis(0,0,0) and Yaxis(0,0,0))?
  • JBWolfJBWolf Posts: 405
    edited 2011-09-28 02:27
    I thought they each stay active (in cogs 1 & 2) with an infinite repeat loop, inside that loop they take the global variable 'distance' and copy it to a local variable.... the X and Y axis functions then perform the repeat loop with the local variable.
    While the X and Y axis routines are looping, the global 'distance' variable takes on a new value passed from cog 0... the process then starts over once the last call has completed.
    I added waitcnt's after movement calls to allow the X & Yaxis cogs to finish each set of passed instructions before sending another set.

    I changed stpX & stpY to VAR and removed the local declarations.
    With the following code, I am able to get a triangle... but it does not stop where it starts, not sure yet why
    PUB Triangle | dist, speed, trak
     
      dist := 22                        ' 22dist = 20deg
      speed := 1800
    
      repeat 60
        trak := 0 
          repeat dist
             trak := trak + 1
             Yaxis(up, 1, speed)
              if trak > 1
                trak := 0
                xaxis(right, 1, speed)
            waitcnt(clkfreq / (speed + 1) + cnt)
          
        trak := 0
       
          repeat dist
             trak := trak + 1
             Yaxis(down, 1, speed)
              if trak > 1
                trak := 0
                xaxis(right, 1, speed)
            waitcnt(clkfreq / (speed + 1) + cnt)
        
         xaxis(left, dist, speed)
         waitcnt(clkfreq / (speed + 1) + cnt)    
    
  • kuronekokuroneko Posts: 3,623
    edited 2011-09-28 06:16
    JBWolf wrote: »
    I thought they each stay active (in cogs 1 & 2) with an infinite repeat loop, inside that loop they take the global variable 'distance' and copy it to a local variable....
    Thing is, the code you posted in #11 doesn't use infinite loops (and no global variables are accessed). Which means the cogs stop after the loop is done.
  • Beau SchwabeBeau Schwabe Posts: 6,568
    edited 2011-09-29 08:45
    A few of you have responded and some of you have been right on the money.

    I sent a distilled version of this program to Jeff Martin, just to see what his take was on the situation. After mulling it over he did finally reach a conclusion, but in the process decided to use this as a learning exercise for the rest of us Propeller-Heads within Parallax.

    The real 'initial' problem is that the methods being launched were trying to use their respective local variable without initializing it... and local variables are not initialized at all (except for the Result variable, and the parameters... but those should be obvious/intuitive). So... the local variables contained "random" data that was quite-likely outside of the 0 to 3 range, and thus the condition that resets it to zero failed to do anything the majority of time.

    Also, a curious "red herring" is that the random value that the local variables happened to contain, ended up causing the memory reference to access a few values that had what seemed to be the correct pattern in them... making you think that the routine was working correctly up until the local variable reached the value 4, when in fact it was never really working properly to begin with.

    There are a number of ways to rewrite the code to fix the problem...(also see some of the attached code methods)

    Replace the code similar to this ...
    	idxa := idxa + 1
    	if idxa == 4
    	   idxa := 0
    

    ...using the modulus operator, like this...
    	idxa := ++idxa // 4
    

    ...which has the same intended effect (causing idxa to cycle from 0..3, and then back to 0) but with the added benefit that if idxa is above 4, it will also be reset to 0... so it "corrected" the situation.


    Hope this helps to solve part of your mystery.

    Attached is three versions of the same code. Download them onto the Propeller Demo Board and watch the light pattern in the LEDs.

    The (Bad) code displays unintended patterns on the LEDs. The (Good VAR) and (Good DAT) versions each display the intended patterns. The only difference between them is that the (Bad) uses local variables and the others define those variables in VAR or DAT blocks, respectively. You can open all three and Ctrl+Tab to see the differences between them easily.



    -Thanks Jeff for taking a look at this and confirming my suspicion.
  • Beau SchwabeBeau Schwabe Posts: 6,568
    edited 2011-09-29 09:00
    JBWolf,

    To answer your question in #14...

    The call to xaxis sets dir to 2, which means the stpX := stpX + 1 .... if stpX > 8 .... stpX := 1 will not be executed.

    Instead, your checking for stpX to be less than 1 ... as a result, the value that is returned for stpX is 114 for reasons based on my previous post, and in all likely hood in this situation stpX actually starts out as 115. The line you have that reads ... stpX := stpX - 1 is doing exactly what you are telling it to do ... decrement stpX by 1 so that it becomes 114.
  • JBWolfJBWolf Posts: 405
    edited 2011-09-29 14:36
    Wow thank you very much!
    I'm honored that my stupidity has helped others :D

    I have just gone through the solutions you presented and will take a little while yet for me to take it all in.... but a few questions came to mind.

    idxa := ++idxa // 4 ' if idxa is above 4, it will be reset to 0.
    What about decrementing... if idxa is below 1, it needs to be reset to 9.
    idxa := --idxa \\ 9 ???

    Is there a way to determine if a value is even or odd without having to make a dedicated counter just for that purpose?
    Say I need to repeatedly move up 2, then over 1.
    Id like to simplify:
    index := 0 
    repeat
     ' move up
      index := index + 1
       if index == 2
         index := 0 
         ' move over
    
    a) The 'index' variables in the VAR example are declared as longs, why not use BYTE since it will only retain a single digit decimal value?
    b) I have been wondering about instead of passing the X/Yaxis methods 3 variables, using @ to pass 3 memory locations.... or is this basically the same thing but using HEX?
    c) Assuming the X/Yaxis methods have been launched into new cogs, if I write a value to variable 'distX' from a method running on cog0, will this value also be present in the 'distX' variable running on the other cog (Xaxis method cog). In other words... if a global variable is changed, do all instances of that variable point to the same value, regardless of what cog is trying to use it? Could a method running on a separate cog in an infinite loop, immediately change its course because the value just changed?
    d) I am launching the X&Yaxis methods into two new cogs, then later making a call to those methods from cog0... does this result in the two XY cogs not receiving the data, and instead cog0 runs that method call? This would explain why I cannot make 2 motors move at the exact same time.

    I am thinking about restructuring so that the Xaxis & Yaxis methods are launched into two new cogs with @ pointers to memory locations for receiving the necessary variables. Both X&Yaxis methods/cogs would be in an infinite loop, using a local variable to copy the values pulled from memory locations for use as a condition to compare against the value in the last loop.... i.e. - pull value from memory and write to local variable, write 0 to memory value, nested loop once on condition that the local variable > 0
  • JBWolfJBWolf Posts: 405
    edited 2011-10-01 11:10
    ok, I have come up with what appears to be a solution.
    Thank you again for the help, it did clear up a lot :)

    since I could not find any documentation on decrementing with limit, I used "idxa := ++idxa // 8" and swapped the output.
    so to increment i would use that math function with outa[8..11], and to decrement I used the same math but with outa[11..8]

    I got it to work by using PRI instead of PUB, passing the addresses, and lots of local variables for comparing.
    I used the "addressblinker" example from the PEKit as a template.

    This does create a diagonal line showing that both motors are moving at the same time.

    {{
      Dual stepper cog control for ULN 2003/2803 & galvo
      motor 1 = pins 8-11   = Y Axis - DirY 1 = Down, 2 = Up
      motor 2 = pins 12-15  = X Axis - DirX 1 = Left, 2 = Right (bottom motor)
    
      Full Step & Wave = 1 revolution = 200 steps @ 1.8deg per step
      Half Step = 1 revolution = 400 steps @ .9deg per step
    
      Half Step Max Speed = 1800
      Full Step Max Speed = 800
      Weak Step Max Speed = 700
    
      85deg max clearance from home position
      
    }}                                                                
    
    CON
       
      _clkmode = xtal1 + pll16x                  ' System clock &#8594; 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, right, down, left        ' Directional variables
      long dirX, distX, delayX          ' Xaxis variables
      long dirY, distY, delayY          ' Yaxis variables
    
    PUB Main | Success
    
    
    up    := 2                                         ' initialize variables
    right := 2
    down  := 1
    left  := 1
    
    LCD.start(TX_PIN, TX_PIN, %1000, 19_200)           ' initialize LCD
      waitcnt(clkfreq / 100 + cnt)
    BKLon                                              ' Turn on LCD BackLight    
    CLS                                                ' Clear LCD screen
     
        
    
      success := StartGalvo
     if success == 0                                      'initialize outputs and start motors in cogs
      LCD.str(string("Galvo Started on:" ))                 ' show if initialize was completed or not
         NewLine
         LCD.str(string("Cog#:"))
         LCD.dec(cog[1])
         LCD.str(string(" and Cog#" ))
         LCD.dec(cog[2])    
     else
         LCD.str(string("Failure" ))
         
    waitcnt(clkfreq * 3 + cnt)
    
    
    '*********************$$$$$$$$$$$$$$$$$$$$$$  START MOVEMENT CALLS  $$$$$$$$$$$$$$$$$$$$$$$*********
    
     cls
     waitcnt(clkfreq / 2 + cnt)
     LCD.str(string("Movement Routines"))
     Waitcnt(clkfreq + cnt)
    
      
        diagonal
    
    
    '*********************$$$$$$$$$$$$$$$$$$$$$$  END MOVEMENT CALLS  $$$$$$$$$$$$$$$$$$$$$$$$********
    
    '*****************************************         Start END          ******************************
    
      cls
      waitcnt(clkfreq / 2 + cnt)
      LCD.tx(153)                       ' move to position 2 x 5 
      LCD.str(string("END"))
      Waitcnt(clkfreq * 3 + cnt)
    
      stop                              ' Stop motor cogs
    
    '************************************************     END     ***************************************
    
    
    
    '**********%%%%%%%%%%%%%%%%%%%%  START MOVEMENT ROUTINES  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*****
    PUB Diagonal             ' down & left = 1
    
    delayy := 1500
    delayx := 1500
    
    repeat 2000
    
      diry :=  2
      disty := 20
      
      dirx := 2
      distx := 20
      waitcnt(clkfreq / delayx + cnt)
    
      diry :=  1
      disty := 20
     
      dirx := 1
      distx := 20
      waitcnt(clkfreq / delayx + cnt)
    
    
    
    
    
    '********%%%%%%%%%%%%%%%%%%%%%%%   END MOVEMENT ROUTINES   %%%%%%%%%%%%%%%%%%%%%%%%%%%%*************
    
    '*******************************    Start Galvo Controls    *************************************
    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    
    
    '                             ****** calibrate start *********
    dira[12..15]~~
    stp := 0
    {{ repeat 50
       stp := ++stp // 4
       outa[12..15] := WStep[stp]
       waitcnt(clkfreq/60 + cnt)
    
     repeat 25
       stp := ++stp // 4
       outa[15..12] := WStep[stp]
       waitcnt(clkfreq/60 + cnt)  }} 
    '                               ***** calibrate end ********
    
    
    repeat
      dir := long[dirxaddr]
      dist := long[distxaddr]
      delay := long[delayxaddr]
      chksum1 := (dir + dist + delay)
    
       if chksum1 <> chksum2  
      
        repeat dist                         ' cycle phase dist many times
          if dir == 1                        ' increment if direction = up
             stp := ++stp // 8      
             outa[12..15] := HStep[stp]          ' send phase pulse                                                              
             waitcnt(clkfreq/delay + cnt)        ' set speed, max speed = H1800 F800 W700
             
          if dir == 2                        ' decrement if direction = down
             stp := ++stp // 8      
             outa[15..12] := HStep[stp]          ' send phase pulse
             waitcnt(clkfreq/delay + cnt)        ' set speed, max speed = H1800 F800 W700
          
    
      dirold := dir
      distold := dist 
      delayold := delay
      chksum2 := chksum1
    
    
    PRI Yaxis (dirYaddr, distYaddr, delayYaddr) | stp, dir, dist, delay, dirold, distold, delayold, chksum1, chksum2   
    
    '                             ****** calibrate start *********
    dira[8..11]~~
    stp := 0
    {{ repeat 50
       stp := ++stp // 4     
       outa[8..11] := WStep[stp]
       waitcnt(clkfreq/60 + cnt)
    
     repeat 25
       stp := ++stp // 4
       outa[11..8] := WStep[stp]
       waitcnt(clkfreq/60 + cnt)  }}
    '                               ***** calibrate end ********
    
    
    repeat
      dir := long[diryaddr]
      dist := long[distyaddr]
      delay := long[delayyaddr]
      chksum1 := (dir + dist + delay)
    
       if chksum1 <> chksum2  
      
         repeat dist                         ' cycle phase dist many times
          if dir == 1                        ' increment if direction = up
             stp := ++stp // 8      
             outa[8..11] := HStep[stp]          ' send phase pulse                                                              
             waitcnt(clkfreq/delay + cnt)        ' set speed, max speed = H1800 F800 W700
             
          if dir == 2                        ' decrement if direction = down
             stp := ++stp // 8      
             outa[11..8] := HStep[stp]          ' send phase pulse
             waitcnt(clkfreq/delay + cnt)        ' set speed, max speed = H1800 F800 W700
    
      dirold := dir
      distold := dist 
      delayold := delay
      chksum2 := chksum1
    
    '*******************************  End Galvo Controls  ***********************************
    
    
    '*******************************  Start LCD Commands ***********************************
    PUB Newline                          
       LCD.tx(13)
    
    pub NextLine
       LCD.tx(10)
    
    PUB CLS
       LCD.tx(12)
       waitcnt(clkfreq /500 + cnt) 
    
    PUB Back (n)
       repeat n
         LCD.tx(8)
    
    PUB BKLon
       LCD.tx(17)
    
    PUB BKLoff
       LCD.tx(18)
    
    PUB BlinkON
       LCD.tx(23)
    
    PUB Default
       LCD.tx(24)
    
    PUB NoCursor
       LCD.tx(22)
    
    PUB LCDoff                              
       LCD.tx(21)
    '*********************************  End LCD Commands  ***********************************
        
    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-01 12:04
    can anyone help pin down the exact amount of time needed during a process?

    Lets say using the code from last post, I want to figure out how long to wait for a certain movement to complete before writing the next.
    So if I tell the Xaxis to move a distance of 22 with a delay of 1000.... this Xaxis method will loop 22 times with a delay of 1000. now it seems to me this is pretty simple, 22 * 1000 = 22000 delay = 220ms
    But do I also need to consider clock ticks in that Xaxis method?

    Heres a simple example assuming 80mhz
    PUB method (addr) | new, X
    repeat
     new := long[addr]
     if old <> new
        old := new
        repeat 100
           X := ++X
        waitcnt(clkfreq + cnt)
    
    how long should I wait before writing a new value to addr?
    Is it exactly 100 seconds? or do I need to add 2 ticks for " if old <> new", another 2 for "old := new" and so on?
  • Beau SchwabeBeau Schwabe Posts: 6,568
    edited 2011-10-01 12:53
    JBWolf,

    I think there might be some confusion as to how the Modulus operator works in conjunction with the pre-increment operator and how the Modulus operator might deal with negative numbers. i.e decrementing after initializing to Zero would create a negative result. My experience is that it's easier to deal with the Modulus operator if you can keep it in the positive integer domain.

    Attached is an Example Spin file with 6 different examples that should make things a little clearer. All you need is the Parallax Serial Terminal running (F12 from the IDE) set at 19200 baud.

    Hope this helps.
  • JBWolfJBWolf Posts: 405
    edited 2011-10-01 14:43
    Ah, I did not think of that at all... very simple way to do a decrement... thank you again.
    Is it a bad idea to just switch the output order? i.e [8..11] to [11..8]

    What do you think about the timing issue?
    I would like to make sure I do not write a variable until the Xaxis method is ready, I'm worried that i'm sending them too early.
    Is there a general rule for determining time passed on modulus operators and setting values?
Sign In or Register to comment.