Shop OBEX P1 Docs P2 Docs Learn Events
Better Way in Spin? — Parallax Forums

Better Way in Spin?

davidsaundersdavidsaunders Posts: 1,559
edited 2017-02-28 21:39 in Propeller 1
The actual step routine in my stepper motor controller seems to have to many operations to me. I have tried factoring out by assigning partial values to variables, though that creates even more operations in total.

The routine in question is:
'*** DoStep(m,v) Step one step. ****************************************
' m is the motor number, 0 for A, 1 for B, 2 for C, and 3 for D.
' v is the step value:
'                     -1 for half step reverse.
'                      1 for half step forward.
'                     -2 for full step reverse.
'                      2 for full step forward.
'It is recommended not to mix full and half steps, it can through off
'  your count if you are not carefull.  If you must mix full and half
'  steps make sure that the half step count is a multiple of 2 before
'  returning to full steps, other wise your count will be wrong, unless
'  you compensate for it.
PUB DoStep(m,v)
   outa[0..17]:=PinVals:=(Step[7&((StpI[m]+=v)&!(||v-1))]<<(m<<2))|(PinVals&!($0F<<(m<<2)))

DAT
  Step   byte %0011, %0010, %0110, %0100
         byte %1100, %1000, %1001, %0001


Needless to say part of it is making sure that for full steps the index into Step is even, that is the &!(||v-1) part, the rest is fairly self explanitory. It does not check to see if the values passed in are in range, as it will only be called from known code I did not see the use in worrying about that check.

Is there a way to accomplish this in fewer operations, before I wire up four steppers to a propeller and test it?

Comments

  • Heater.Heater. Posts: 21,230
    David,
    outa[0..17]:=PinVals:=(Step[7&((StpI[m]+v)&!(||v-1))]<<(m<<2))|(PinVals&!($0F<<(m<<2)))
    
    Sorry. No way am I going to try and decipher that "line noise".

    I suspect what you want to do is more easily written in PASM. And much faster too.

    Surely there are stepper motor drivers in OBEX you could use.
  • davidsaundersdavidsaunders Posts: 1,559
    edited 2017-02-28 21:41
    Heater. wrote: »
    David,
    outa[0..17]:=PinVals:=(Step[7&((StpI[m]+v)&!(||v-1))]<<(m<<2))|(PinVals&!($0F<<(m<<2)))
    
    Sorry. No way am I going to try and decipher that "line noise".

    I suspect what you want to do is more easily written in PASM. And much faster too.

    Surely there are stepper motor drivers in OBEX you could use.

    Just trying to make it simple, a single short expression like that is always easier for me to read, just do not make it to long.

    So to break it down into parts, by order of execution, using the bold sections as what to look at:

    PinVals:=(Step|v-1))[/b<<(m<<2))|(PinVals&!($0F<<(m<<2)))

    This section calculates the index into the array of step output values, defined in the DAT section. Breaking that down more we look at 7&((StpI[m]+=v)&!(||v-1)) this just gives us the next index value, as the StpI array contains the current step index for each motor. Next is 7&((StpI[m]+=v)&!(||v-1)) this just makes sure that if the absolute value of parameter v is 2, then we are indexing an even step number for full stepping. This brings us to (Step|v-1))[b<<(m<<2)) we are indexing into the step array with the result of the above, and taking the result and shifting it by motor number *4 to put it on the correct pins. Once we get this far we need to mask of our local copy of our working output, so we or it with the stored pin values, with the four bits we are changing masked to zero, that is what the section |(PinVals&!($0F<<(m<<2)))


  • OR if it makes it easier yet for you, here is a version a bit broke down:
    PUB DoStep(m,v)
      tidx := StpI[m] := 7 & ((StpI[m] + v) & !(||v - 1))
      t       := m << 2                      'value to shift left by = 4*m.
      outa[0..17] :=  PinVals := (Step[tidx] << t) | (PinVals & !($0F << t))
    
    DAT
      Step   byte %0011, %0010, %0110, %0100
             byte %1100, %1000, %1001, %0001
    
    
  • Heater.Heater. Posts: 21,230
    Getting closer. Although two assignments per line is a no-no.

    Anyway, what is the problem?

    Does it work as you expect or not?
  • As far as I can tell it does the job, would just like to do so with fewer operations involved, not so much for speed, as it is way faster than needed, though more for final object size.
  • davidsaundersdavidsaunders Posts: 1,559
    edited 2017-02-28 22:07
    So is this readable. I do plan on sharing the whole thing once completed, and fully tested. I am still extending the serial command interpreter for it.
    '*** DoStep(m,v) Step one step. ****************************************
    ' m is the motor number, 0 for A, 1 for B, 2 for C, and 3 for D.
    ' v is the step value:
    '                     -1 for half step reverse.
    '                      1 for half step forward.
    '                     -2 for full step reverse.
    '                      2 for full step forward.
    'It is recommended not to mix full and half steps, it can through off
    '  your count if you are not carefull.  If you must mix full and half
    '  steps make sure that the half step count is a multiple of 2 before
    '  returning to full steps, other wise your count will be wrong, unless
    '  you compensate for it.
    '
    ' This Five line procedure is expanded from a one line version I wrote
    '   first.  Expansion is to make it easier for others to read.
    '   PinVals:=(Step[7&((StpI[m]+=)&!(||v-1))]<<(m<<2))|(PinVals&!($0F<<(m<<2)))
    PUB DoStep(m,v)
      tidx := 7 & ((StpI[m] + v) & !(||v - 1))
      StpI[m] := tidx
      t       := m << 2                      'value to shift left by = 4*m.
      PinVals := (Step[tidx] << t) | (PinVals & !($0F << t))
      outa[0..17] :=  PinVals
    
    
    DAT
      Step   byte %0011, %0010, %0110, %0100
             byte %1100, %1000, %1001, %0001
    
  • Heater.Heater. Posts: 21,230
    So how many bytecodes does that take?

    Are you really so pushed for space?
  • Not pushed at all, though if someone someday wants to extend it to include something like a G-Code interpreter or similar, then they may run into space issues, especially for the buffers needed by something along the lines of a G-Code interpreter. Want to leave every byte free that I can while keeping it in spin for now. Once I get it as small as possible it will be time to port it over to PASM, and take almost no hub ram at all.
  • I don't understand why the expression is required in the first place - seems like a lot of work. Why not something like this?
    PUB DoStep(v, mShift)
      mMask := $FFFF_FFFF - ($F << mShift)
      tidx := (tidx + v) & 7
      outa :=  (outa & mMask) | (Step[tidx] << mShift)
    
    DAT
      Step   byte %0011, %0010, %0110, %0100
             byte %1100, %1000, %1001, %0001
    
  • You could even check to see if the absolute value of V is 2 (full step), and if so, mask off the bottom bit of the current tidx value, guaranteeing that the user gets a full step / full torque value.

  • JasonDorie wrote: »
    You could even check to see if the absolute value of V is 2 (full step), and if so, mask off the bottom bit of the current tidx value, guaranteeing that the user gets a full step / full torque value.

    I accomplish the same task by the part that says &!(||v-1)
    JasonDorie wrote: »
    I don't understand why the expression is required in the first place - seems like a lot of work. Why not something like this?
    PUB DoStep(v, mShift)
      mMask := $FFFF_FFFF - ($F << mShift)
      tidx := (tidx + v) & 7
      outa :=  (outa & mMask) | (Step[tidx] << mShift)
    
    DAT
      Step   byte %0011, %0010, %0110, %0100
             byte %1100, %1000, %1001, %0001
    
    I am going to have to look at that a bit more, though it looks like you are just moving some of the stuff out to the caller. IE making the caller do the calculation of the shift. I could be wrong, give me a couple of minutes to fully digest that.

  • davidsaundersdavidsaunders Posts: 1,559
    edited 2017-02-28 23:11
    JasonDorie wrote: »
    I don't understand why the expression is required in the first place - seems like a lot of work. Why not something like this?
    PUB DoStep(v, mShift)
      mMask := $FFFF_FFFF - ($F << mShift)
      tidx := (tidx + v) & 7
      outa :=  (outa & mMask) | (Step[tidx] << mShift)
    
    DAT
      Step   byte %0011, %0010, %0110, %0100
             byte %1100, %1000, %1001, %0001
    

    OH, I see what is missing. The inclusion of the stored values of the pins when doing the output. As there are 4 steppers across 16 pins, and I leave those 16 pins as outputs all the time, thus saving time. Thus your example would change the outputs to the other three stepper motors to invalid values.

    I was wrong, I see you are using outa to get the existing values. Ok that gives me something to think about, and looks good.

    Though I do thank you, that does give me some ideas of how to improve the routine a good bit.
  • JasonDorieJasonDorie Posts: 1,930
    edited 2017-02-28 23:26
    I accomplish the same task by the part that says &!(||v-1)

    Ahh, ok - that expression was pretty dense, so I missed that part. :)

    And to your last point, yes - I'm assuming each output is 4 successive pins, so masking OUTA with & ($FFFF_FFF - ($F<<mShift)) should keep everything but the four pins you're changing, and then you OR in those pins from the step array, shifted into the proper place by mShift. So in my code mShift takes the place of your motor index.

    I would actually create one instance of this object per motor, because then mMask can be computed once and re-used. You could also just pass it in from the higher level code to accomplish the same thing.
  • I do thank you, I will be using some of the ideas you provided to improve my code. It had been a while since I had done any propeller programming, now I am nearly done with a working motor controller, that I may even manage to get to post tonight, if all goes well.

    It did get me thinking about a long since abandoned project of mine, to run a 3D printer from a Prop. Now it would seem more possible as I could have a Raspberry Pi interpret the G-CODE and send my simple motor control commands to the Prop, there are a few other issues, though they are for another thread I will start in a minute.
Sign In or Register to comment.