Code to do 1/16th microstepping with a Propeller

pedwardpedward Posts: 1,559
edited 2011-11-21 - 08:42:07 in Propeller 1
I tried to reply to the other post, but decided that this achievement deserves a new post, since I'm trying to contribute something useful and get feedback.

EDIT: Here's a video of a demo using this code:

I have successfully done 1/16th microstepping with a Propeller generating all of the phase commutation in 1 cog. The code uses the CTRA and CTRB to generate sine and cosine PWM waveforms that drive a half h-bridge chip. I haven't done any fancy current control, just the commutation sequences.

There were a couple of tricks in getting this to work, the worst was rounding error when dividing 360 / 64 = 5 on the propeller. I had to generate a table in a spreadsheet to get a proper degree table to generate the sine/cosine tables from. Once I had the degree table, the sine/cosine tables came out at 4pi just fine.

The other major issue was figuring out how to gate the pins, when I've got 4 outputs to drive and 2 counters to drive them. The solution was to detect the zero crossing and update the counter output pin depending upon what phase of the count I was in.

There is a curious issue with this driver, and I wanted some feedback from some of those amazingly, cryptically smart, people. The sine table is not weighted, so the motor accelerates and decelerates throughout the phase changes of the output. This is likely not desirable, as it indicates the steps are non-linear in length. I'd like to hear suggestions on how to improve this.

This code is presently running a stepper motor from an optical drive head assembly at 32,000 steps/s, sitting on my desk.

Without further ado, 1/16th PWM microstepping with the Propeller:
[FONT=courier new]{{
Demonstration of PWM microstepping with the Propeller
To use this code, connect the pins as such:

COS --  CTRB -- 0 --  PHSA
SIN --  CTRA -- 1 -- /PHSA
COS -- /CTRB -- 2 --  PHSB
SIN -- /CTRA -- 3 -- /PHSB


_clkmode = xtal1 + pll16x
_xinfreq = 5_000_000
microsteps = 64


long parameter
long cs[microsteps],sn[microsteps]
word ca,sa

PUB go | x
    repeat x from 0 to microsteps-1
           cs[x] := cos(deg[x],256)
           sn[x] := sin(deg[x],256)

    cognew(@entry, @parameter)
          repeat x from 0 to microsteps-1        '15 deg steps
                 parameter := ca << 16 + sa          'pack cosine and sine
                 waitcnt(2500 + cnt)   'wait a little while before next update

PUB sin(degree, range) : s | c,z,angle
  angle := (degree*91)~>2  ' *22.75
  c := angle & $800
  z := angle & $1000
  if c
    angle := -angle
  angle |= $E000>>1
  angle <<= 1
  s := word[angle]
  if z
    s := -s
  return (s*range)~>16     ' return sin = -range..+range

PUB cos(degree,range)
  return sin(degree+90,range)

'assembly cog which updates the PWM cycle on APIN
'for audio PWM, fundamental freq which must be out of auditory range (period < 50?S)

entry   mov dira, diraval              'set APIN to output
        mov ctra, ctraval1              'establish counter A mode and APIN
        mov ctrb, ctrbval1              'establish counter A mode and APIN
        mov frqa, #1                   'set counter to increment 1 each cycle
        mov frqb, #1                   'set counter to increment 1 each cycle

        mov time, cnt                  'record current time
        add time, period               'establish next period

:loop   rdlong value, par              'get an up to date pulse width
        mov a, value
        shl a, #16  'unpack a
        shr a, #16  'unpack a
        mov b, value
        shr b, #16  'unpack b

        cmp a, #$1FF wc     'test for 16bit signed value
if_nc    or a, mask         'sign extend
        cmp b, #$1FF wc     'test for 16bit signed value
if_nc    or b, mask         'sign extend

        cmps a, #0 wc      'wave change
        muxnc outa, #16    'status display
if_c    movs ctra, #2      'out pin 2
if_c    neg  a, a          'flip sign
if_nc   movs ctra, #0      'out pin 0

        cmps b, #0 wc      'wave change
        muxnc outa, #32    'status display
if_c    movs ctrb, #3      'out pin 3
if_c    neg  b, b          'flip sign
if_nc   movs ctrb, #1      'out pin 1

        neg phsa, a                'back up phsa
        neg phsb, b                'back up phsb
        waitcnt time, period           'wait until next period
        jmp #:loop                     'loop for next cycle

diraval long 1111                      'APIN=0
ctraval1 long 100 << 26 + 0          'NCO/PWM APIN=0
ctrbval1 long 100 << 26 + 1          'NCO/PWM APIN=0
ctraval2 long 100 << 26 + 2          'NCO/PWM APIN=0
ctrbval2 long 100 << 26 + 3          'NCO/PWM APIN=0
period  long 255                       '800kHz (1.25?S period) (_clkfreq / period)
mask    long $FFFF0000
time    res 1
value   res 1
a       res 1
b       res 1
deg long 0,5,11,17,22,28,34,40,45,51,57,62,68,74,80,85,91,97,102,108,114,120,125,131,137,142,148,154,160,165,171,177,182,188,194,200,205,211,217,222,228,234,240,245,251,257,262,268,274,280,285,291,297,302,308,314,320,325,331,337,342,348,354,360


  • kbashkbash Posts: 99
    edited 2011-11-21 - 08:42:07
    I'm not sure if this is the same issue or not, but could be related so I'll mention it.

    I had to build a drive to generate a high current sine wave for a linear (Voice-coil) driver. The PWM output from the prop comes out nice, but due to the on/off times of my power transistors, I got almost no drive current until the very peak of the cycle, somewhere around 80-85 % duty cycle. I finally got a usable wave by doing a custom output, watching my wave-shape and fine-tuning the duty cycle for about 15 steps of up and down, but never got a nice, clean high current sine wave. The attached photo shows about the best I got. The sine wave on the left was the pressure output and the sine wave on the right a corresponding expansion of a mock artery.

    This was "Close enough" for my prototype, but I'd love to know if anyone else out there has a reasonably easy circuit for generating a smooth, high current ( 2-6 amp ) sine wave with the Prop. The filtered PWM into a linear driver works fine, but the expense of the drive itself cancels out a lot of the cost benefit of using the propeller for inexpensive motion controllers.

    I thought about experimenting with the HB-25, but needed a bit more than 12 volts for this. A high power stepper motor drive would be too expensive doing it this way anyway. Anyone got a cheap circuit for high voltage, high current output?


    800 x 532 - 396K
Sign In or Register to comment.