© The Author(s), under exclusive license to APress Media, LLC, part of Springer Nature 2021
G. FlurryJava on the Raspberry Pihttps://doi.org/10.1007/978-1-4842-7264-0_13

13. A Stepper Motor Driver

Greg Flurry1  
(1)
Austin, TX, USA
 

In this chapter, we’ll build a device library for a stepper motor driver. Stepper motors get used primarily in robotics projects, but it is not inconceivable they could be used in IOT projects.

There are many ways of driving a stepper motor, including simple drivers such as discrete transistors and H-bridges, which force you to do most of the work, and sophisticated drivers that do most of the work for you. In this book, we’ll look at the Watterott SilentStepStick (https://learn.watterott.com/silentstepstick/). It is a driver that I’ve used in some projects; its primary attraction is silent operation. I’d consider it around the middle of the “sophistication spectrum,” but it is still very easy to use.

In this chapter, I’ll discuss
  • Using multiple diozero base I/O devices, specifically GPIO output devices, to construct a single logical device

  • Finding, and ignoring, existing device libraries

  • Exploring the options and limitations of diozero

Understand the Device

The SilentStepStick breakout board (https://github.com/watterott/SilentStepStick/blob/master/hardware/SilentStepStick-TMC2100_v10.pdf) leverages the Trinamic TMC2100 chip (www.trinamic.com/fileadmin/assets/Products/ICs_Documents/TMC2100_datasheet_Rev1.11.pdf). That means you get to read and understand two datasheets. Lucky you (and me, of course). I suggest browsing the TMC2100 datasheet, then closely reading the SilentStepStick datasheet, then closely reading the TMC2100 datasheet. The following are the most salient TMC2100 features:
  • Drives bipolar motors at up to 2A per coil, with voltages from 4.75V to 46V.

  • Can interpolate steps up to 256 microsteps per step.

  • StealthChop mode enables “extremely quiet” operation.

  • Enable, direction, and step signals control movement.

  • Seven configuration pins (CFG0–CFG6) control operation; one of them, CFG6, is the enable signal.

  • Maximum motor current can be controlled internally or externally.

  • Logic voltage can be 3.3V or 5V.

The following are the salient features of the SilentStepStick:
  • CFG0, CFG4, and CFG5 control “chopper” operation. All three default to the “recommended, most universal choice.” CFG4 and CFG5 have jumpers that allow change from the default.

  • CFG1 and CFG2 control the mode and microstep resolution of the driver. See the table on page 9 of TMC2100 datasheet or the table in Section 3 of the SilentStepStick datasheet for details.

  • CFG3 configures the means of setting the maximum motor current. It defaults to “float” for external control. It too has a jumper to allow change from the default.

  • A potentiometer on the breakout adjusts maximum motor current; both datasheets provide instructions on how to adjust the current.

In effect, the SilentStepStick establishes a reasonable default configuration that can be changed if you really need to do so. As a result, under the majority of circumstances, you only need to worry about CFG1 and CFG2.

Of course, the SilentStepStick is only interesting if you have a stepper motor attached. Stepper motors are fascinating beasts. See https://learn.adafruit.com/all-about-stepper-motors/what-is-a-stepper-motor for a useful introduction. Stepper motors come in many different sizes, require different voltages and currents, exhibit different step sizes, different torque, and so on. And of course, they are used for many different purposes.

That means it is impossible to determine a truly universal set of requirements for the device library or a universal configuration. Thus, I’ll simply identify a set of library and configuration requirements based on my past stepper projects.

I have a bipolar stepper motor for which the specifications are 12V, 0.4A, and 200 full steps/revolution (most stepper motors you’ll encounter are 200 steps/revolution). Further, I’ll require silent operation, but as fast as possible.

I am also going to make some simplifying, but rational, assumptions. First, the default values for CFG0, CFG3, CFG4, and CFG5 are acceptable. Second, the library does not set the configuration of CFG1 and CFG2; instead, it must be told the number of microsteps per step that result from the configuration. These assumptions save GPIO pins but might not be right for all projects.

Find a Device Library

To find a device library to use or port, I’ll follow the procedure outlined in Chapter 6. A look at the diozero device libraries shows no stepper motor drivers.

Searching for Java libraries produced nothing for the TMC2100. I did find hints of libraries for its more sophisticated cousins.

Search for Non-Java Libraries

Searching for Python libraries produced nothing for the TMC2100. Again, I found hints of libraries for its more sophisticated cousins.

The SilentStepStick product page has links to an “Arduino Library and Examples,” a “General Software Library,” and “Arduino Examples.” The first two do not offer support for the TMC2100 and so are no help. The last contains a very trivial example that is also not much help. Quite surprising, actually.

I found an Arduino sketch at https://electropeak.com/learn/interfacing-tmc2100-stepper-motor-driver-with-arduino/. The page contains an interesting summary of the SilentStepStick and TMC2100 datasheets as well as helpful hints. I expected to identify far more Arduino-based candidates.

You may have noticed the SilentStepStick product page says it is compatible with two other stepper motor controllers, the Watterott StepStick and the Pololu A4988 (www.pololu.com/product/1182). I’d claim the A4988 is partly compatible. It has only three configuration pins, which control the microsteps per step. Fortunately, the available resolutions match those of the SilentStepStick. Also, fortunately, Pololu provides an Arduino library for the A4988 (https://github.com/laurb9/StepperDriver/blob/master/src/A4988.cpp). The design is actually quite sophisticated in that it allows for “speed profiles,” so that the motor is accelerated from stopped up to nominal speed, run at nominal speed, then decelerated down to stopped.

And the Answer Is …

For better or worse, I will treat this as a “start from scratch” situation because of the anticipated simplicity. I will use the A4988 library for guidance, but ignore its sophisticated aspects, for two reasons. First, my expectations are low-speed and low-torque requirements. Second, I must leave something for you to do! More on the subject near the end of the chapter.

Device Library Design

Once again, I’ll use a top-down approach. We must start with requirements. In very general terms, stepper motors are used in situations that require accurate position control, accurate speed control, or both. For example, a 3D printer requires both. My past stepper motor projects required only speed control, however, and I’ll use them as a model to drive requirements. Based on my past projects, I’ll summarize the requirements:
  • Want to control the direction and speed of rotation.

  • Want to start and stop the rotation.

  • Anticipate only low speeds.

  • Want to enable and disable the driver. It is important to note that when enabled, the driver powers the motor, so the motor produces torque even when stopped. When disabled, the driver does not power the motor, so it does not produce torque; thus, the shaft and anything attached to it can move freely.

  • Do not want to control the microstepping configuration. Instead, will be told the configuration.

As hinted earlier, you must use the diozero GPIO digital output devices to control the SilentStepStick. Enable and direction controls are static and should use DigitalOutputDevice . The speed is determined by the step control; it could use a DigitalOutputDevice or a PwmOutputDevice. See Chapter 7 for more information on those diozero devices.

In theory, there could be multiple SilentStepSticks in a project, so there can be multiple instances of the library; in my last stepper project, I in fact used three SilentStepSticks. That means we must allow for multiple instances.

Caution

Raspberry Pi OS is not a real-time operating system and Java is not a real-time language. As a result, you cannot expect to produce truly accurate stepper motor speed control with the SilentStepStick since the Pi generates a step signal that is subject to the vagaries of the OS and Java. That said, you can produce truly accurate stepper motor position control because position depends only on the number of steps, which the Pi can accurately control.

Interface Design

Based on the earlier requirements , and examination of the A4988 library, the interface needs methods that offer the ability to
  • Enable or disable the driver

  • Set the direction, either clockwise or counterclockwise

  • Set the speed of rotation

  • Run or stop

The constructor requires the following parameters:
  • The GPIO pins for the enable, direction, and step pins

  • The microsteps per step, as determined by the SilentStepStick configuration pins CFG1 and CFG2

Device Library Development

As with any new diozero-based project, you must create a new NetBeans project, package, and class; configure the project for remote development on your Raspberry Pi; and configure the project to use diozero. See Chapter 7 for a summary of the steps. I’ll create a project called SSS (because SilentStepStick is too long), a package org.gaf.sss, and a class SilentStepStick. However, before creating the library, you should recognize the SilentStepStick presents a perfect opportunity to play. So that’s what we’ll do.

Play with the Device

Of course, before playing, you must construct the proper circuit for the SilentStepStick. That means connecting the motor, the motor power supply, and the logic power supply (3.3V from the Raspberry Pi). Page 3 of the SilentStepStick datasheet contains a nice circuit diagram you can use as a guide; page 6 contains some pictures I found useful for connecting the motor properly. You must also adjust the maximum motor current (see page 4 of the SilentStepStick datasheet and page 24 of the TMC2100 datasheet).

Since the configuration pins (including the enable pin) default to some reasonable value and direction does not matter, you can drive the motor using only the step pin driven by the Pi. Very nice!

An interesting question is how to drive the step pin. Earlier, I hypothesized using DigitalOutputDevice or PwmOutputDevice. The onOffLoop method of the former supports either a given number of cycles (steps) or an infinite number of cycles at a chosen frequency; good! The latter supports only an infinite number of cycles, though you can change the frequency; also good! Finally, if you read the documentation closely, you’ll find that with PwmOutputDevice, the desired frequency must be an integer; in contrast, with DigitalOutputDevice , you set the on and off periods in floating point, so, effectively, the frequency is in floating point. Thus, while either class works, DigitalOutputDevice offers more flexibility, so I’ll use it.

An important question is what frequency to use for the PWM signal that will drive the motor. You don’t want to go too fast or too slow. The motor has 200 steps per revolution. 1 RPM = 1/60 revolutions/second (RPS), so to produce 1 RPM, you must drive the motor at 200/60 = ~3.333 Hz. If the driver configuration you choose uses microstepping, you have to multiply that result by the number of microsteps per step. For example, if your configuration has 4 microsteps per step, to produce 1 RPM, you must drive the motor at (200/60) * 4 = ~13.333 Hz.

Since I’m after silent operation, I’m going to set CFG1=3.3V and CFG2=open, which turns on StealthChop at 4 microsteps/step. Now, 1 RPM is pretty slow, so let’s say the speed should be 4 RPM. Using the earlier formulas, that means a frequency of 4 * (200/60) * 4 = ~53.333 Hz, which produces a period of 18.75 milliseconds and a half period of 9.375 milliseconds.

Now, we’ll create a simple program to run the stepper motor. Listing 13-1 shows the program Step in package org.gaf.sss.test. The program is truly very simple; it has just three interesting statements. The first creates a DigitalOutputDevice instance that drives GPIO pin 17 which is connected to the SilentStepStick step pin; the second generates a 53.333 Hz step signal on GPIO pin 17; the third stops the step signal after 5 seconds.

Notice Step enables the diozero safety net. It does so because the DigitalOutputDevice uses a different thread to drive the step pin; that thread must be terminated at shutdown. See Chapter 7.
package org.gaf.sss.test;
import com.diozero.api.DigitalOutputDevice;
import com.diozero.util.Diozero;
public class Step {
    public static void main(String[] args)
             throws InterruptedException {
        try (DigitalOutputDevice pwm =
                new DigitalOutputDevice(17, true,
                    false)) {
            pwm.onOffLoop(0.009375f, 0.009375f,
                DigitalOutputDevice.
                    INFINITE_ITERATIONS,
                true, null);
            System.out.println("Waiting ...");
            Thread.sleep(5000);
            pwm.stopOnOffLoop();
            System.out.println("Done");
        } finally {
            Diozero.shutdown();
        }
    }
}
Listing 13-1

Step

When you run Step , if everything is connected properly, the stepper motor shaft rotates at roughly 4 RPM for 5 seconds. You can put a piece of tape on the motor shaft to make the rotation easier to detect.

We’ve confirmed that the initial hardware and software configuration works. You can now begin to play/experiment with different configurations and different PWM frequencies to find a combination that works well for your project. You can also determine the motor direction for different states of the direction pin.

SilentStepStick Implementation

Now, we’ll develop SilentStepStick.1 In earlier chapters, we first developed a core. In the case of SilentStepStick, however, there is little difference between the core and the full library!

Listing 13-2 shows the initial implementation. We know from the interface discussion earlier that we need to set the direction of rotation as either clockwise or counterclockwise; the Direction enum supplies the appropriate constants. We also need to set the configuration in terms of microstep per step; the Resolution enum supplies the appropriate constants. Per Chapter 7, the class implements java.io.AutoCloseable; thus, it also has a close method, which we will complete later.
package org.gaf.sss;
public class SilentStepStick implements
        AutoCloseable {
    @Override
    public void close(){
    }
    public enum Direction {
        CW,
        CCW;
    }
    public enum Resolution {
        Full(1),
        Half(2),
        Quarter(4),
        Eighth(8),
        Sixteenth(16);
        public  final int resolution;
        Resolution(int resolution) {
            this.resolution = resolution;
        }
    }
}
Listing 13-2

SilentStepStick constants and close method

Constructor Implementation

Listing 13-3 shows the SilentStepStick constructor . It implements the requirements discussed earlier. The only parameter not previously mentioned is stepsPerRev; it specifies the number of steps per revolution for the stepper motor being driven, necessary for calculating the frequency of the step signal.

The constructor creates a DigitalOutputDevice to drive the enable pin (initialized disabled), a second to drive the direction pin (initialized clockwise), and a third to drive the step pin; the step pin is configured active high and is initially set low, so no stepping occurs. The constructor also calculates the number of SilentStepStick microsteps per revolution, which is used later to calculate the frequency of the step signal to achieve a desired speed.
import com.diozero.api.DigitalOutputDevice;
import com.diozero.api.RuntimeIOException;
import com.diozero.util.SleepUtil;
import java.io.IOException;
    private DigitalOutputDevice dir;
    private DigitalOutputDevice enable;
    private DigitalOutputDevice step;
    private final float microstepsPerRev;
    private boolean running = false;
    public SilentStepStick(int enablePin,
            int directionPin, int stepPin,
            int stepsPerRev, Resolution resolution)
            throws IOException {
        try {
            // set up GPIO
            enable = new DigitalOutputDevice(
                enablePin, false, false);
            dir = new DigitalOutputDevice(
                directionPin, true, false);
            step = new DigitalOutputDevice(
                stepPin, true, false);
            // set configuration
            microstepsPerRev = (float)
               (stepsPerRev * resolution.resolution);
        } catch (RuntimeIOException ex) {
            throw new IOException(ex.getMessage());
        }
    }
    @Override
    public void close() {
        // disable
        if (enable != null) {
            enable.off();
            enable.close();
            enable = null;
        }
        // stop
        if (step != null) {
            // turn it off
            step.stopOnOffLoop();
            step.close();
            step = null;
        }
        if (dir != null) {
            dir.close();
            dir = null;
        }
    }
Listing 13-3

SilentStepStick constructor and close method

Listing 13-3 also shows the completed close method. It ensures the driver is disabled and the step signal is off, so that the motor stops. The method also closes all the diozero device instances.

Listing 13-4 shows the implementation of the operative methods described earlier. The enable method is self-explanatory, as is the setDirection method .

After further reflection on the interface discussion earlier, it seemed appropriate to provide a single method that sets the direction, sets the speed, and turns on the step signal. Thus, the run method has parameters for direction and rotation speed. It uses the parameters to set the direction and determine the frequency of the step signal. The method turns on the step signal at the desired frequency by starting a DigitalOutputDevice infinite on/off loop. The step signal runs until it is turned off by calling the stop method .

Note that I made the decision to run the infinite on/off loop in the background. Further, I decided to ignore the ability to be notified when the loop stops because the loop must be explicitly stopped. These choices seemed reasonable to me. You might make different choices.
public void enable(boolean enableIt)
        throws RuntimeIOException {
    if (enableIt) {
        enable.on();
    }
    else {
        enable.off();
    }
}
private void setDirection(Direction direction)
        throws RuntimeIOException {
    if (direction == Direction.CW) dir.off();
    else dir.on();
}
public void run(Direction direction, float speedRPM)
        throws RuntimeIOException {
    if (running) step.stopOnOffLoop();
    // let motor rest (see p.9 of datasheet)
    SleepUtil.sleepMillis(100);
    setDirection(direction);
    float halfPeriod = getHalfPeriod(speedRPM);
    step.onOffLoop(halfPeriod, halfPeriod,
            DigitalOutputDevice.INFINITE_ITERATIONS,
            true, null);
    running = true;
}
public void stop() throws RuntimeIOException {
    step.stopOnOffLoop();
    running = false;
}
private float getHalfPeriod(float speedRPM) {
    float speedRPS = speedRPM/60f;
    float frequency = speedRPS * microstepsPerRev;
    float halfPeriod = 0.5f / frequency;
    return halfPeriod;
}
public int getStepCount() {
    return step.getCycleCount();
}
Listing 13-4

SilentStepStick operative methods

The run method calls the getHalfPeriod method . The latter performs the calculations explained earlier to produce a step signal frequency from the speed parameter (in RPM). It then calculates the half period run uses to set up the DigitalOutputDevice on/off loop.

Finally, note the getStepCount method in Listing 13-4. It is not in the requirements or interface mentioned earlier. I realized after playing with Step (Listing 13-1) and thinking about the implications of the run method in Listing 13-4 that something like getStepCount would be quite useful in understanding stepper motor positioning in the context of a “run then stop” scenario. I requested the developer of diozero insert the necessary logic in DigitalOutputDevice.

Test SilentStepStick

Now, we’ll test SilentStepStick . A good first test is to reproduce the effect of the Step program in Listing 13-1. Listing 13-5 shows TestSSS1 that does so.

The “applications” in this chapter of course engage the try-with-resources and diozero shutdown safety nets. They do not engage the Java shutdown safety net since nothing is attached to the stepper motor shaft and thus no damage can result from improper termination.
package org.gaf.sss.test;
import com.diozero.util.Diozero;
import java.io.IOException;
import org.gaf.sss.SilentStepStick;
public class TestSSS1 {
    public static void main(String[] args)
           throws IOException, InterruptedException {
        try (SilentStepStick stepper =
               new SilentStepStick(4, 27, 17, 200,
               SilentStepStick.Resolution.Quarter)) {
            stepper.enable(true);
            System.out.println("Run CW");
            stepper.run(SilentStepStick.Direction.CW, 4f);
            Thread.sleep(5000);
            System.out.println("Stopping");
            stepper.stop();
            System.out.println("Count = " +
                stepper.getStepCount());
            System.out.println("Disabling");
            stepper.enable(false);
            System.out.println("Closing");
        } finally {
            Diozero.shutdown();
        }
    }
}
Listing 13-5

TestSSS1

Run TestSSS1 and you should see motor behavior identical to that when you run Step; you should also see the results shown in Listing 13-6. Note in particular the count of microsteps. With the motor specification, the microstep configuration, and the requested speed, the frequency of the step signal to the SilentStepStick should be 53.333 Hz; thus, a running period of 5 seconds should result in a count of ~267; a count of 275 is a bit disappointing, but not unreasonable. Clearly the loop runs a bit fast.
Run CW
Stopping
Count = 275
Disabling
Closing
Listing 13-6

Results of running TestSSS1

To have a bit more fun, we can now make the motor run clockwise for a while and then counterclockwise. Listing 13-7 shows TestSSS2, which does just that.
package org.gaf.sss.test;
import com.diozero.util.Diozero;
import java.io.IOException;
import org.gaf.sss.SilentStepStick;
public class TestSSS2 {
    public static void main(String[] args)
           throws IOException, InterruptedException {
        try (SilentStepStick stepper =
               new SilentStepStick(4, 27, 17, 200,
               SilentStepStick.Resolution.Quarter)) {
            stepper.enable(true);
            System.out.println("Run CW");
            stepper.run(
                SilentStepStick.Direction.CW, 4f);
            Thread.sleep(5000);
            System.out.println("Stopping");
            stepper.stop();
            System.out.println("Count = " +
                stepper.getStepCount());
            System.out.println("Run CCW");
            stepper.run(
                SilentStepStick.Direction.CCW, 2f);
            Thread.sleep(5000);
            System.out.println("Stopping");
            stepper.stop();
            System.out.println("Count = " +
                stepper.getStepCount());
            stepper.enable(false);
            System.out.println("Closing");
        } finally {
            Diozero.shutdown();
        }
    }
}
Listing 13-7

TestSSS2

Run TestSSS2, and if everything is wired properly, you should see the motor rotate clockwise for 5 seconds at 4 RPM and then counterclockwise for 5 seconds at 2 RPM. Success!

Listing 13-8 shows the console results of running TestSSS2. You can see that again the clockwise count is 275. You can also see that the counterclockwise count is 138, about half of 275, so that count also seems reasonable, if also somewhat higher than expected.
Run CW
Stopping
Count = 275
Run CCW
Stopping
Count = 138
Disabling
Closing
Listing 13-8

Result of running TestSSS2

What Next?

The implementation of SilentStepStick fulfills one benefit of stepper motors – speed control.2 The clever choice of DigitalOutputDevice allows us to also provide accurate position control! The reason is that with a stepping motor, accurate position control translates to moving an accurate number of steps, and DigitalOutputDevice can do that.

Listing 13-9 shows the stepCount method for SilentStepStick that performs position control. It is more complex than the run method (Listing 13-4):
  • It does not allow termination of any stepping currently running. While a somewhat arbitrary decision, it does help maintain accurate positioning.

  • It exposes the ability of the DigitalOutputDevice to run an on/off loop in the foreground or background. The step count could be small enough that running in the foreground makes sense.

  • It exposes the ability of the DigitalOutputDevice to call the caller’s Action at the termination of the on/off loop. In most background situations, this is a good idea.

  • It must intercept the call by the DigitalOutputDevice to an Action in order to maintain internal state.

These design decisions seem reasonable and prudent to me, but you might decide to do something different.
public boolean stepCount(int count,
        Direction direction, float speedRPM,
        boolean background, Action stopAction)
        throws RuntimeIOException {
    if (running) {
        return false;
    } else {
        // let motor rest (see p.9 of datasheet)
        SleepUtil.sleepMillis(100);
        // set up an intercept
        Action intercept = () -> {
            System.out.println("intercept");
            running = false;
        };
        setDirection(direction);
        running = true;
        float halfPeriod = getHalfPeriod(speedRPM);
        if (stopAction != null) {
            step.onOffLoop(halfPeriod, halfPeriod,
                    count, background,
                    intercept.andThen(stopAction));
        } else {
            step.onOffLoop(halfPeriod, halfPeriod,
                    count, background, intercept);
        }
        return true;
    }
}
Listing 13-9

SilentStepStick stepCount method

An explanation of how stepCount works could be helpful. First, I’ll elaborate on the Action mechanism. stepCount always defines an internal “intercept” Action and provides that in the call to the onOffLoop method. Thus, when the on/off loop terminates, the DigitalOutputDevice always calls the intercept so it can do any internal housekeeping. If the caller provides a non-null stopAction, that Action gets called after the internal Action.

Next, I’ll address the foreground/background option. Assume the caller chooses to run in the foreground. The running flag gets set true prior to the call to the onOffLoop method . The onOffLoop method
  • Runs until the count is complete

  • Calls the internal Action which sets the running flag false (and then calls the caller’s Action if it exists)

  • Returns to the stepCount method

The stepCount method then returns to the caller with the running flag false.

Now assume the caller chooses to run in the background. The running flag gets set true prior to the call to the onOffLoop method . The onOffLoop method spawns a background thread to run the on/off loop and returns to the stepCount method, which in turn returns to the caller with the running flag true. The caller can perform other tasks while the background thread runs the on/off loop. The background thread
  • Runs until the count is complete

  • Calls the internal Action which sets the running flag false (and then calls the caller’s Action if it exists)

At this point, the SilentStepStick instance has the running flag false, and another stepper activity can be initiated.

Now we can test the stepCount method. Listing 13-10 shows the program TestSSS3 that does so. The AtomicBoolean is a Java concurrency construct that enables synchronized communication between two threads; TestSSS3 uses it to know when a step count is finished. As you can see from Listing 13-10, TestSSS3 is similar to TestSSS2, except that it requests a fixed number of steps rather than an infinite number. Further, TestSSS3 identifies an Action (the method whenDone) to take upon count completion; it simply indicates, via the AtomicBoolean , that the count is complete.
package org.gaf.sss.test;
import com.diozero.util.Diozero;
import java.io.IOException;
import java.util.concurrent.atomic.AtomicBoolean;
import org.gaf.sss.SilentStepStick;
public class TestSSS3 {
    private static AtomicBoolean done;
    public static void main(String[] args)
           throws IOException, InterruptedException {
        try (SilentStepStick stepper =
               new SilentStepStick(4, 27, 17, 200,
               SilentStepStick.Resolution.Quarter)) {
            done = new AtomicBoolean(false);
            stepper.enable(true);
            System.out.println("Run CW");
            done.set(false);
            boolean status = stepper.stepCount(100,
                    SilentStepStick.Direction.CW, 4f,
                    true, TestSSS3:: whenDone);
            while (!done.get()) {
                Thread.sleep(100);
            }
            System.out.println("DONE");
            System.out.println("Count = " +
                stepper.getStepCount());
            System.out.println("Run CCW");
            done.set(false);
            status = stepper.stepCount(100,
                    SilentStepStick.Direction.CCW,
                    2f, true, TestSSS3:: whenDone);
            while (!done.get()) {
                Thread.sleep(100);
            }
            System.out.println("DONE");
            System.out.println("Count = " +
                stepper.getStepCount());
            System.out.println("Disabling");
            stepper.enable(false);
            System.out.println("Closing");
        } finally {
            Diozero.shutdown();
        }
    }
     private static void whenDone () {
        System.out.println("Device done");
        done.set(true);
    }
}
Listing 13-10

TestSSS3

Run TestSSS3 and you should see results as in Listing 13-11. It is quite reassuring to see that the microstep counts for both directions of rotation agree with the requested counts.
Run CW
intercept
Device done
DONE
Count = 100
Run CCW
intercept
Device done
DONE
Count = 200
Disabling
Closing
Listing 13-11

Results of running TestSSS3

Speed Profiles

I mentioned in the section on libraries the notion of “speed profiles” implemented in the Pololu A4988 library. Speed profiles can be extremely important in some stepper motor applications, especially where high speed or high torque are involved. The paper www.ti.com/lit/an/slyt482/slyt482.pdf?ts=1615587700571&ref_url=https%253A%252F%252Fwww.google.com%252F explains the concepts and issues.

Fundamentally, the goal of a speed profile is to accelerate the motor from stopped up to a target speed, run for some period at the target speed, and then decelerate down to stopped. A really good question is whether or not it is possible to implement a speed profile using the diozero base I/O API. The answer is a bit complex:
  • At the time of writing, the answer is no. I base that on an examination of the implementation of DigitalOutputDevice.

  • That said, with some changes suggested by the Arduino library mentioned earlier, the answer becomes yes, but. The “but” has several facets:
    • One form of the changes would impact the performance in ways that might not be palatable for some projects.

    • A second form of the changes would force a potentially unpleasant change in the interface of DigitalOutputDevice.

    • Using the revised DigitalOutputDevice would require ramping to be done in the foreground or use of Java concurrency constructs or perhaps diozero threading constructs to do it in the background.

I think in reality, the best choice would be to produce a peer or subclass of DigitalOutputDevice specifically targeting stepper motor speed profiles. Sadly, both are beyond the scope of the book. That said, if you really need speed profiles and you don’t want to create a “speed profile” class, you may be able to find a more sophisticated stepper motor driver that implements profiles, much as the RoboClaw controller does for DC motors (see Chapter 8).

Summary

In this chapter, you’ve experienced
  • Finding existing device libraries and mostly ignoring them

  • Creating a device library mostly from scratch

  • Using several diozero digital I/O devices to construct a single logical device

  • Playing with the device prior to implementing the library

  • Realizing that diozero cannot do everything

All good stuff!

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.145.2.184