dmercer

Program Analog Discovery to Drive A Stepper Motor

Blog Post created by dmercer Employee on Nov 30, 2013

Program Analog Discovery to Drive A Stepper Motor

The recently released software development kit (SDK) for Analog Discovery provides a way for users to write specific control routines for external hardware like stepper motors. This blog installment will cover one example using Python to control a Mitsumi M35SP-7T stepper motor salvaged from a flatbed scanner.

The example driver circuit, shown in figure 1, is somewhat incidental to this programing discussion and the code examples can in general be used to control any similar driver circuit / motor combination. This particular stepper motor is of the bipolar ( 4 wire differential ) drive variety and it thus needs to be driven by an H-Bridge configuration.

image001.png

Figure 1, Differential stepper motor driver schematic

The M35SP-7T stepper motor has 9Ω windings and is specified to be driven nominally with 5 volts which would give a coil current of around 555 mA. Do not attempt to use the +5V supply from Analog Discovery for this. Use a supply that is capable of supplying at least 1A of current. To make the driver circuit simple for this demonstration, an octal CMOS buffer such as the 74HC541 can serve as an H-bridge. To supply sufficient current to the motor windings we will need to connect multiple buffers in parallel. The schematic example shows two buffers in parallel. Because of the on resistance of the CMOS buffer output transistors in the 74HC541, the coil voltage will not be the full 5 V power supply. Two buffers are strong enough to make the motor spin but not with full torque. Stacking two 74HC541s will provide 4 buffers in parallel, this is easy to accomplish by piggy backing the two DIP packages one on top of the other and soldering their leads together. Using 4 buffers results in about 250 mA of drive current which is about one half of the rated current so the maximum pulse rate will also be about one half of the specified rate or 50 Hz. Two H-bridge circuits using four NMOS and four PMOS power transistors such as the ZVN2110A and ZVP2110A from the Analog Parts Kit could be constructed to provide higher coil currents.

The pulse waveform sequence for “forward” rotation is shown in figure 2. A pulse on D0 will energize coil A with a positive polarity and a pulse on D2 will energize coil A with an opposite negative polarity. Similarly, a pulse on D1 will energize coil B with a positive polarity and a pulse on D3 will energize coil B with an opposite negative polarity.

image002.png

Figure 2, Pulse sequence for forward rotation

As we can see from the timing diagram the coils are energized in a +A, +B, -A, -B sequence. One cycle is four steps.

For “reverse” rotation we need to swap the pulse sequence as shown in figure 3. As we can see this is essentially the sequence in reverse order.

image003.png

Figure 3, Pulse sequence for reverse rotation

The SDK contains a number of functions to configure and control the digital input / output pins on Analog Discovery. The digital outputs can be configured as either static outputs or as pulses with defined frequency, phase and duty cycle. The following Python code examples make use of both of these methods.

Code example 1:

In this first code example we will first configure four digital pins, D0 – 3, as outputs and use the basic digital output set function to turn them on and off one at a time in the desired sequence. While this is a fairly simple approach it is sort of brute force and the width and frequency of the pulses is highly dependent on the speed and activity level of the computer being used.

 

 

# enable output/mask on 4 LSB IO pins, from DIO 0 to 3

dwf.FDwfDigitalIOOutputEnableSet(hdwf, c_int(0x000F))

# set value on enabled IO pins

dwf.FDwfDigitalIOOutputSet(hdwf, c_int(0x00))


 

Next we define two functions, one for forward rotation and one for reverse rotation. The time.sleep(steptime) function adds a delay which determines the time a bit is on before the next in the sequence is turned on. The routine does four steps to illustrate the required sequence. You would need to alter the function for one step pulse per loop. The StepCount variable is used to determine how many times the four bit sequence is sent.


 

# forward rotation routine

def BForward():

    global hdwf

 

    StepCount = eval(e1.get())

    print "Number of forward steps requested " + str(eval(e1.get()))

    i = 0

    while i < StepCount:

        dwf.FDwfDigitalIOOutputSet(hdwf, c_int(0x01)) # D0 high

        time.sleep(steptime)

        dwf.FDwfDigitalIOOutputSet(hdwf, c_int(0x02)) # D1 high

        time.sleep(steptime)

        dwf.FDwfDigitalIOOutputSet(hdwf, c_int(0x04)) # D2 high

        time.sleep(steptime)

        dwf.FDwfDigitalIOOutputSet(hdwf, c_int(0x08)) # D3 high

        time.sleep(steptime)

       

        i = i + 1

    # turns off all bits while idle

    dwf.FDwfDigitalIOOutputSet(hdwf, c_int(0x00))

 

# reverse rotation routine

def BReverse():

    global hdwf

   

    StepCount = eval(e1.get())

    print "Number of reverse steps requested " + str(eval(e1.get()))

    i = 0

    while i < StepCount:

        dwf.FDwfDigitalIOOutputSet(hdwf, c_int(0x08)) # D3 high

        time.sleep(steptime)

        dwf.FDwfDigitalIOOutputSet(hdwf, c_int(0x04)) # D2 high

        time.sleep(steptime)

        dwf.FDwfDigitalIOOutputSet(hdwf, c_int(0x02)) # D1 high

        time.sleep(steptime)

        dwf.FDwfDigitalIOOutputSet(hdwf, c_int(0x01)) # D0 high

        time.sleep(steptime)

      

        i = i + 1

    # turns off all bits while idle

    dwf.FDwfDigitalIOOutputSet(hdwf, c_int(0x00))

 

Code example 2:

In this code example we will use the built-in pulse generation capabilities of the hardware to generate the pulse sequences. First we need to configure four of the digital pins, D0 – 3, for output. The first function call enables a bit for output. The second function sets the clock divider. The master clock is 100 MHz and we need to divide that down to 50 Hz. The third function sets the counter for 3 intervals low and 1 high or 25% duty cycle. This configuration set-up needs to be done only once when the program starts.

# generate on D0 - D3 50 Hz pulse (100MHz/500000/(3+1)), 25% duty (3low 1high)

dwf.FDwfDigitalOutEnableSet(hdwf, c_int(0), c_int(1))

dwf.FDwfDigitalOutDividerSet(hdwf, c_int(0), c_int(500000))

dwf.FDwfDigitalOutCounterSet(hdwf, c_int(0), c_int(3), c_int(1))

#

dwf.FDwfDigitalOutEnableSet(hdwf, c_int(1), c_int(1))

dwf.FDwfDigitalOutDividerSet(hdwf, c_int(1), c_int(500000))

dwf.FDwfDigitalOutCounterSet(hdwf, c_int(1), c_int(3), c_int(1))

#

dwf.FDwfDigitalOutEnableSet(hdwf, c_int(2), c_int(1))

dwf.FDwfDigitalOutDividerSet(hdwf, c_int(2), c_int(500000))

dwf.FDwfDigitalOutCounterSet(hdwf, c_int(2), c_int(3), c_int(1))

#

dwf.FDwfDigitalOutEnableSet(hdwf, c_int(3), c_int(1))

dwf.FDwfDigitalOutDividerSet(hdwf, c_int(3), c_int(500000))

dwf.FDwfDigitalOutCounterSet(hdwf, c_int(3), c_int(3), c_int(1))

#

 

 

Next we define two functions, one for forward rotation and one for reverse rotation. The StepCount variable along with the 50 Hz frequency is used to set the run time of the pulses which should result in the requested number of steps. Because this method uses repeating patterns of pulses it is not generally useful for single steps. This method produces faster and smoother rotation than the method used in example 1.


 

# forward rotation routine

def BForward():

    global hdwf

 

    dwf.FDwfDigitalOutConfigure(hdwf, c_int(0))

    time.sleep(0.1)

    StepCount = eval(e1.get())

    print "Number of forward steps requested " + str(StepCount)

    # initialize counters for four phases

    dwf.FDwfDigitalOutCounterInitSet(hdwf, c_int(0), c_int(1), c_int(1))

    dwf.FDwfDigitalOutCounterInitSet(hdwf, c_int(1), c_int(1), c_int(2))

    dwf.FDwfDigitalOutCounterInitSet(hdwf, c_int(2), c_int(1), c_int(3))

    dwf.FDwfDigitalOutCounterInitSet(hdwf, c_int(3), c_int(1), c_int(4))

    #

    RunTime = float(StepCount/50.0)

    print "Run time for forward steps " + str(RunTime)

    dwf.FDwfDigitalOutRunSet(hdwf, c_double(RunTime))

    dwf.FDwfDigitalOutRepeatSet(hdwf, c_uint(1))

    dwf.FDwfDigitalOutConfigure(hdwf, c_int(1))

 

# reverse rotation routine

def BReverse():

    global hdwf

   

    dwf.FDwfDigitalOutConfigure(hdwf, c_int(0))

    time.sleep(0.1)

    StepCount = eval(e1.get())

    print "Number of reverse steps requested " + str(StepCount)

    # initialize counters for four phases

    dwf.FDwfDigitalOutCounterInitSet(hdwf, c_int(0), c_int(1), c_int(1))

    dwf.FDwfDigitalOutCounterInitSet(hdwf, c_int(1), c_int(1), c_int(4))

    dwf.FDwfDigitalOutCounterInitSet(hdwf, c_int(2), c_int(1), c_int(3))

    dwf.FDwfDigitalOutCounterInitSet(hdwf, c_int(3), c_int(1), c_int(2))

    #

    RunTime = float(StepCount/50.0)

    print "Run time for reverse steps " + str(RunTime)

    dwf.FDwfDigitalOutRunSet(hdwf, c_double(RunTime))

    dwf.FDwfDigitalOutRepeatSet(hdwf, c_uint(1))

    dwf.FDwfDigitalOutConfigure(hdwf, c_int(1))


 

Program files ( Stepper_test.zip ) for this demonstration are attached to this blog. Stepper_test_1.py uses the first method. Stepper_test_2.py uses the second method. Stepper_test_3.py uses the static bit on/off mode but demonstrates a half step sequence. Two additional versions with single full and half stepping are in Single_step.zip.

In conclusion, controlling stepper motors is a perfect candidate for writing a custom Python control program using the new Analog Discovery SDK.

As always I welcome comments and suggestions from the user community out there.

Doug

Attachments

Outcomes