├── .devcontainer
├── Dockerfile
└── devcontainer.json
├── .github
├── ISSUE_TEMPLATE
│ ├── bug_report.md
│ └── feature_request.md
├── stale.yml
└── workflows
│ ├── arduino.yml
│ └── platformio.yml
├── .gitignore
├── CMakeLists.txt
├── LICENSE
├── Makefile
├── README.md
├── _config.yml
├── arduino-cli.yaml
├── examples
├── AccelTest
│ └── AccelTest.ino
├── BasicStepperDriver
│ └── BasicStepperDriver.ino
├── ClockStepper
│ └── ClockStepper.ino
├── MicroStepping
│ └── MicroStepping.ino
├── MultiAxis
│ └── MultiAxis.ino
├── NonBlocking
│ └── NonBlocking.ino
├── SpeedProfile
│ └── SpeedProfile.ino
└── UnitTest
│ ├── UnitTest.ino
│ ├── adafruit_feather_m0.txt
│ └── esp8266_nodemcu.txt
├── keywords.txt
├── library.properties
├── platformio.ini
├── src
├── A4988.cpp
├── A4988.h
├── BasicStepperDriver.cpp
├── BasicStepperDriver.h
├── DRV8825.cpp
├── DRV8825.h
├── DRV8834.cpp
├── DRV8834.h
├── DRV8880.cpp
├── DRV8880.h
├── MultiDriver.cpp
├── MultiDriver.h
├── SyncDriver.cpp
└── SyncDriver.h
└── test
└── README
/.devcontainer/Dockerfile:
--------------------------------------------------------------------------------
1 | #-------------------------------------------------------------------------------------------------------------
2 | # Copyright (c) Microsoft Corporation. All rights reserved.
3 | # Licensed under the MIT License. See https://go.microsoft.com/fwlink/?linkid=2090316 for license information.
4 | #-------------------------------------------------------------------------------------------------------------
5 |
6 | # To fully customize the contents of this image, use the following Dockerfile instead:
7 | # https://github.com/microsoft/vscode-dev-containers/tree/v0.112.0/containers/ubuntu-18.04-git/.devcontainer/Dockerfile
8 | FROM mcr.microsoft.com/vscode/devcontainers/base:0-ubuntu-18.04
9 |
10 | # ** [Optional] Uncomment this section to install additional packages. **
11 | #
12 | ENV DEBIAN_FRONTEND=noninteractive
13 | RUN apt-get update \
14 | && apt-get -y install --no-install-recommends make curl clang clang-tools \
15 | #
16 | # Clean up
17 | && apt-get autoremove -y \
18 | && apt-get clean -y \
19 | && rm -rf /var/lib/apt/lists/*
20 | ENV DEBIAN_FRONTEND=dialog
21 |
--------------------------------------------------------------------------------
/.devcontainer/devcontainer.json:
--------------------------------------------------------------------------------
1 | // For format details, see https://aka.ms/vscode-remote/devcontainer.json or this file's README at:
2 | // https://github.com/microsoft/vscode-dev-containers/tree/v0.112.0/containers/ubuntu-18.04-git
3 | {
4 | "name": "Ubuntu 18.04 & Git",
5 | "dockerFile": "Dockerfile",
6 | "workspaceFolder": "/workspaces/StepperDriver",
7 | "mounts": [
8 | "source=arduino-cli,target=/workspaces/StepperDriver/.arduino,type=volume"
9 | ],
10 |
11 | // Set *default* container specific settings.json values on container create.
12 | "settings": {
13 | "terminal.integrated.shell.linux": "/bin/bash"
14 | },
15 |
16 | // Add the IDs of extensions you want installed when the container is created.
17 | "extensions": [
18 | "vsciot-vscode.vscode-arduino",
19 | "ms-vscode.cpptools",
20 | "vector-of-bool.gitflow",
21 | "eamodio.gitlens"
22 | ],
23 |
24 | // Use 'forwardPorts' to make a list of ports inside the container available locally.
25 | // "forwardPorts": [],
26 |
27 | // Use 'postCreateCommand' to run commands after the container is created.
28 | "postCreateCommand": "sudo chown vscode /workspaces/StepperDriver/.arduino; make setup",
29 |
30 | // Uncomment to use the Docker CLI from inside the container. See https://aka.ms/vscode-remote/samples/docker-in-docker.
31 | // "mounts": [ "source=/var/run/docker.sock,target=/var/run/docker.sock,type=bind" ],
32 |
33 | // Uncomment when using a ptrace-based debugger like C++, Go, and Rust
34 | // "runArgs": [ "--cap-add=SYS_PTRACE", "--security-opt", "seccomp=unconfined" ],
35 |
36 | // Uncomment to connect as a non-root user. See https://aka.ms/vscode-remote/containers/non-root.
37 | "remoteUser": "vscode"
38 | }
39 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Bug report
4 | title: ''
5 | labels: bug
6 | assignees: laurb9
7 |
8 | ---
9 |
10 | **Describe the bug**
11 | A clear and concise description of what the bug is.
12 |
13 | **To Reproduce**
14 | Steps to reproduce the behavior:
15 | 1. Example sketch code (smallest code that reproduces the problem)
16 |
17 | **Expected behavior**
18 | A clear and concise description of what you expected to happen.
19 |
20 | **Platform Setup (please complete the following information):**
21 | - Arduino IDE version (try with latest version)
22 | - Board type (Uno, Feather M0 etc)
23 | - Stepper driver type (A4988, DRV8834, etc)
24 | - Wiring (if needed) - please note that hardware issues are outside of the scope of this project
25 |
26 | **Additional context**
27 | Add any other context about the problem here.
28 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an improvement idea for this project
4 | title: ''
5 | labels: improvement
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Is your feature request related to a problem? Please describe.**
11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
12 |
13 | **Describe the solution you'd like**
14 | A clear and concise description of what you want to happen.
15 |
16 | **Describe alternatives you've considered**
17 | A clear and concise description of any alternative solutions or features you've considered.
18 |
19 | **Additional context**
20 | Add any other context or screenshots about the feature request here.
21 |
--------------------------------------------------------------------------------
/.github/stale.yml:
--------------------------------------------------------------------------------
1 | # Number of days of inactivity before an issue becomes stale
2 | daysUntilStale: 90
3 | # Number of days of inactivity before a stale issue is closed
4 | daysUntilClose: 90
5 | # Issues with these labels will never be considered stale
6 | exemptLabels:
7 | - improvement
8 | - help wanted
9 | # Label to use when marking an issue as stale
10 | staleLabel: wontfix
11 | # Comment to post when marking an issue as stale. Set to `false` to disable
12 | markComment: >
13 | This issue has been automatically marked as stale because it has not had
14 | recent activity. It will be closed if no further activity occurs. Thank you
15 | for your contributions.
16 | # Comment to post when closing a stale issue. Set to `false` to disable
17 | closeComment: false
18 |
--------------------------------------------------------------------------------
/.github/workflows/arduino.yml:
--------------------------------------------------------------------------------
1 | name: Arduino
2 |
3 | on:
4 | push:
5 | branches:
6 | - master
7 | - develop
8 | pull_request:
9 | release:
10 | types:
11 | - created
12 |
13 | jobs:
14 | Boards:
15 | timeout-minutes: 10
16 | strategy:
17 | fail-fast: false
18 | matrix:
19 | board:
20 | - arduino:avr:uno
21 | - arduino:avr:mega
22 | - adafruit:samd:adafruit_feather_m0
23 | - esp8266:esp8266:nodemcu
24 |
25 | runs-on: ubuntu-latest
26 | steps:
27 | - uses: actions/checkout@v2
28 | - name: Set up Python
29 | uses: actions/setup-python@v1
30 | - name: Install arduino-cli
31 | run: |
32 | make setup
33 | - name: Build
34 | run: |
35 | make setup
36 | make all TARGET=${{ matrix.board }}
37 |
--------------------------------------------------------------------------------
/.github/workflows/platformio.yml:
--------------------------------------------------------------------------------
1 | name: PlatformIO
2 |
3 | on:
4 | push:
5 | branches:
6 | - master
7 | - develop
8 | pull_request:
9 | release:
10 | types:
11 | - created
12 |
13 | jobs:
14 | Boards:
15 | needs: Examples
16 | timeout-minutes: 10
17 | strategy:
18 | fail-fast: false
19 | matrix:
20 | board:
21 | - esp32dev
22 | - nodemcuv2
23 | - teensylc
24 | - adafruit_feather_m0
25 | - uno
26 |
27 | runs-on: ubuntu-latest
28 | steps:
29 | - uses: actions/checkout@v2
30 | - name: Set up Python
31 | uses: actions/setup-python@v1
32 | with:
33 | python-version: 3
34 | - name: Install PlatformIO
35 | run: |
36 | python -m pip install --upgrade pip
37 | pip install platformio
38 | - name: Build
39 | run: |
40 | platformio run -e ${{ matrix.board }}
41 |
42 | Examples:
43 | timeout-minutes: 5
44 | strategy:
45 | fail-fast: true
46 | matrix:
47 | board:
48 | - adafruit_feather_m0
49 | runs-on: ubuntu-latest
50 | steps:
51 | - uses: actions/checkout@v2
52 | - name: Set up Python 3
53 | uses: actions/setup-python@v1
54 | with:
55 | python-version: 3
56 | - name: Install PlatformIO
57 | run: |
58 | python -m pip install --upgrade pip
59 | pip install platformio
60 | - name: Build
61 | run: |
62 | for sketch in examples/*; do
63 | platformio ci --lib src --keep-build-dir --board ${{ matrix.board }} ${sketch} || exit 1;
64 | done
65 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Compiled Object files
2 | *.slo
3 | *.lo
4 | *.o
5 | *.obj
6 |
7 | # Precompiled Headers
8 | *.gch
9 | *.pch
10 |
11 | # Compiled Dynamic libraries
12 | *.so
13 | *.dylib
14 | *.dll
15 |
16 | # Fortran module files
17 | *.mod
18 |
19 | # Compiled Static libraries
20 | *.lai
21 | *.la
22 | *.a
23 | *.lib
24 |
25 | # Executables
26 | *.exe
27 | *.out
28 | *.app
29 |
30 | # Eclipse
31 | .*project
32 |
33 | # VScode
34 | .vscode
35 |
36 | # PyCharm/Jetbrains editors
37 | .idea
38 |
39 | # Mac
40 | .DS_Store
41 |
42 | # Arduino
43 | *.hex
44 | *.bin
45 | *.elf
46 | .arduino
47 |
48 | # PlatformIO
49 | .pio
--------------------------------------------------------------------------------
/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.24)
2 | idf_component_register(
3 | SRC_DIRS "src" # Add all source files here
4 | INCLUDE_DIRS "src"
5 | REQUIRES "arduino" # Add include directories here
6 | )
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 Laurentiu Badea
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
23 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | # Default build architecture and board
2 | TARGET ?= arduino:avr:uno
3 | CORE = $(shell echo $(TARGET) | cut -d: -f1,2)
4 |
5 | # Where to save the Arduino support files, this should match what is in arduino-cli.yaml
6 | ARDUINO_DIR ?= .arduino
7 |
8 | default:
9 | #################################################################################################
10 | # Initial setup: make .arduino/arduino-cli setup
11 | #
12 | # Build all the examples: make all TARGET=adafruit:samd:adafruit_feather_m0
13 | #
14 | # Install more cores: make core TARGET=adafruit:samd:adafruit_feather_m0
15 | # (edit arduino-cli.yaml and add repository if needed)
16 | #################################################################################################
17 |
18 | # See https://arduino.github.io/arduino-cli/installation/
19 | ARDUINO_CLI_URL = https://downloads.arduino.cc/arduino-cli/arduino-cli_latest_Linux_64bit.tar.gz
20 | ARDUINO_CLI ?= $(ARDUINO_DIR)/arduino-cli --config-file arduino-cli.yaml
21 | EXAMPLES := $(shell ls examples)
22 |
23 | COMPILE = $(ARDUINO_CLI) compile --warnings all --fqbn $(TARGET)
24 |
25 | all: # Build all example sketches
26 | all: $(EXAMPLES:%=%.hex)
27 | ls -l build
28 |
29 | %.hex: # Generic rule for compiling sketch to uploadable hex file
30 | %.hex: examples/% core
31 | $(ARDUINO_CLI) compile --warnings all --fqbn $(TARGET) --output-dir build $<
32 |
33 | # Remove built objects
34 | clean:
35 | rm -rfv build
36 |
37 | core: $(ARDUINO_DIR)/arduino-cli
38 | $(ARDUINO_CLI) core install $(CORE)
39 |
40 | $(ARDUINO_DIR)/arduino-cli: # Download and install arduino-cli
41 | $(ARDUINO_DIR)/arduino-cli:
42 | mkdir -p $(ARDUINO_DIR)
43 | cd $(ARDUINO_DIR)
44 | curl -L -s $(ARDUINO_CLI_URL) \
45 | | tar xfz - -C $(ARDUINO_DIR) arduino-cli
46 | chmod 755 $@
47 | $(ARDUINO_CLI) version
48 |
49 | setup: # Configure cores and libraries for arduino-cli (which it will download if missing)
50 | setup: $(ARDUINO_DIR)/arduino-cli
51 | mkdir -p $(ARDUINO_DIR)/libraries
52 | ln -sf $(CURDIR) $(ARDUINO_DIR)/libraries/
53 | $(ARDUINO_CLI) config dump
54 | $(ARDUINO_CLI) core update-index
55 | $(ARDUINO_CLI) core list
56 |
57 | .PHONY: clean %.hex all setup
58 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://www.ardu-badge.com/StepperDriver)
2 | [](https://github.com/laurb9/StepperDriver/actions)
3 | [](https://github.com/laurb9/StepperDriver/actions)
4 |
5 | StepperDriver
6 | =============
7 |
8 | A4988, DRV8825, DRV8834, DRV8880 and generic two-pin stepper motor driver library.
9 | Features:
10 | - Constant speed mode (low rpms)
11 | - Linear (accelerated) speed mode, with separate acceleration and deceleration settings.
12 | - Non-blocking mode (yields back to caller after each pulse)
13 | - Early brake / increase runtime in non-blocking mode
14 |
15 | Hardware currently supported:
16 | - DRV8834 Low-Voltage Stepper Motor Driver
17 | up to 1:32
18 | - A4988 Stepper Motor Driver up to 1:16
19 | - DRV8825 up to 1:32
20 | - DRV8880 up to 1:16, with current/torque control
21 | - any other 2-pin stepper via DIR and STEP pins, microstepping up to 1:128 externally set
22 |
23 | Microstepping
24 | =============
25 |
26 | The library can set microstepping and generate the signals for each of the support driver boards.
27 |
28 | High RPM plus high microstep combinations may not work correctly on slower MCUs, there is a maximum speed
29 | achieveable for each board, especially with acceleration on multiple motors at the same time.
30 |
31 | Motors
32 | ======
33 |
34 | - 4-wire bipolar stepper motor or
35 | - some 6-wire unipolar in 4-wire configuration (leaving centers out) or
36 | - 28BYJ-48 (commonly available) with a small modification (search for "convert 28byj-48 to 4-wire").
37 |
38 | Connections
39 | ===========
40 |
41 | Minimal configuration from Pololu DRV8834 page:
42 |
43 |
44 |
45 | Wiring
46 | ======
47 |
48 | This is suggested wiring for running the examples unmodified. All the pins below can be changed.
49 |
50 | - Arduino to driver board:
51 | - DIR - D8
52 | - STEP - D9
53 | - GND - Arduino GND
54 | - GND - Motor power GND
55 | - VMOT - Motor power (check driver-specific voltage range)
56 | - A4988/DRV8825 microstep control
57 | - MS1/MODE0 - D10
58 | - MS2/MODE1 - D11
59 | - MS3/MODE2 - D12
60 | - DRV8834/DRV8880 microstep control
61 | - M0 - D10
62 | - M1 - D11
63 | - ~SLEEP (optional) D13
64 |
65 | - driver board to motor (this varies from motor to motor, check motor coils schematic).
66 | - 100uF capacitor between GND - VMOT
67 | - Make sure to set the max current on the driver board to the motor limit (see below).
68 | - Have a motor power supply that can deliver that current.
69 | - Make sure the motor power supply voltage is within the range supported by the driver board.
70 |
71 | Set Max Current
72 | ===============
73 |
74 | The max current is set via the potentiometer on board.
75 | Turn it while measuring voltage at the passthrough next to it.
76 | The formula is V = I*5*R where I=max current, R=current sense resistor installed onboard
77 |
78 | - DRV8834 or DRV8825 Pololu boards, R=0.1 and V = 0.5 * max current(A).
79 | For example, for 1A you will set it to 0.5V.
80 |
81 | For latest info, see the Pololu board information pages.
82 |
83 | Code
84 | ====
85 |
86 | See the BasicStepperDriver example for a generic driver that should work with any board
87 | supporting the DIR/STEP indexing mode.
88 |
89 | The Microstepping example works with a DRV8834 board.
90 |
91 | For example, to show what is possible, here is the ClockStepper example that moves a
92 | stepper motor like the seconds hand of a watch:
93 |
94 | ```C++
95 | #include
96 | #include "A4988.h"
97 |
98 | // using a 200-step motor (most common)
99 | #define MOTOR_STEPS 200
100 | // configure the pins connected
101 | #define DIR 8
102 | #define STEP 9
103 | #define MS1 10
104 | #define MS2 11
105 | #define MS3 12
106 | A4988 stepper(MOTOR_STEPS, DIR, STEP, MS1, MS2, MS3);
107 |
108 | void setup() {
109 | // Set target motor RPM to 1RPM and microstepping to 1 (full step mode)
110 | stepper.begin(1, 1);
111 | }
112 |
113 | void loop() {
114 | // Tell motor to rotate 360 degrees. That's it.
115 | stepper.rotate(360);
116 | }
117 | ```
118 |
119 | Hardware
120 | ========
121 | - Arduino-compatible board
122 | - A stepper motor driver, for example DRV8834, DRV8825, DRV8824, A4988.
123 | - A Stepper Motor.
124 | - 1 x 100uF capacitor
125 |
--------------------------------------------------------------------------------
/_config.yml:
--------------------------------------------------------------------------------
1 | theme: jekyll-theme-modernist
--------------------------------------------------------------------------------
/arduino-cli.yaml:
--------------------------------------------------------------------------------
1 | directories:
2 | data: .arduino
3 | downloads: .arduino/staging
4 | user: .arduino
5 | board_manager:
6 | additional_urls:
7 | - http://arduino.esp8266.com/stable/package_esp8266com_index.json
8 | - https://dl.espressif.com/dl/package_esp32_index.json
9 | - https://adafruit.github.io/arduino-board-index/package_adafruit_index.json
10 | telemetry:
11 | enabled: false
12 |
--------------------------------------------------------------------------------
/examples/AccelTest/AccelTest.ino:
--------------------------------------------------------------------------------
1 | /*
2 | * Using accelerated motion ("linear speed") in nonblocking mode
3 | *
4 | * Copyright (C)2015-2017 Laurentiu Badea
5 | *
6 | * This file may be redistributed under the terms of the MIT license.
7 | * A copy of this license has been included with this distribution in the file LICENSE.
8 | */
9 | #include
10 |
11 | // Motor steps per revolution. Most steppers are 200 steps or 1.8 degrees/step
12 | #define MOTOR_STEPS 200
13 | // Target RPM for cruise speed
14 | #define RPM 120
15 | // Acceleration and deceleration values are always in FULL steps / s^2
16 | #define MOTOR_ACCEL 2000
17 | #define MOTOR_DECEL 1000
18 |
19 | // Microstepping mode. If you hardwired it to save pins, set to the same value here.
20 | #define MICROSTEPS 16
21 |
22 | #define DIR 8
23 | #define STEP 9
24 | #define SLEEP 13 // optional (just delete SLEEP from everywhere if not used)
25 |
26 | /*
27 | * Choose one of the sections below that match your board
28 | */
29 |
30 | #include "DRV8834.h"
31 | #define M0 10
32 | #define M1 11
33 | DRV8834 stepper(MOTOR_STEPS, DIR, STEP, SLEEP, M0, M1);
34 |
35 | // #include "A4988.h"
36 | // #define MS1 10
37 | // #define MS2 11
38 | // #define MS3 12
39 | // A4988 stepper(MOTOR_STEPS, DIR, STEP, SLEEP, MS1, MS2, MS3);
40 |
41 | // #include "DRV8825.h"
42 | // #define MODE0 10
43 | // #define MODE1 11
44 | // #define MODE2 12
45 | // DRV8825 stepper(MOTOR_STEPS, DIR, STEP, SLEEP, MODE0, MODE1, MODE2);
46 |
47 | // #include "DRV8880.h"
48 | // #define M0 10
49 | // #define M1 11
50 | // #define TRQ0 6
51 | // #define TRQ1 7
52 | // DRV8880 stepper(MOTOR_STEPS, DIR, STEP, SLEEP, M0, M1, TRQ0, TRQ1);
53 |
54 | // #include "BasicStepperDriver.h" // generic
55 | // BasicStepperDriver stepper(MOTOR_STEPS, DIR, STEP);
56 |
57 | void setup() {
58 | Serial.begin(115200);
59 |
60 | stepper.begin(RPM, MICROSTEPS);
61 | // if using enable/disable on ENABLE pin (active LOW) instead of SLEEP uncomment next line
62 | // stepper.setEnableActiveState(LOW);
63 | stepper.enable();
64 | // set current level (for DRV8880 only). Valid percent values are 25, 50, 75 or 100.
65 | // stepper.setCurrent(100);
66 |
67 | /*
68 | * Set LINEAR_SPEED (accelerated) profile.
69 | */
70 | stepper.setSpeedProfile(stepper.LINEAR_SPEED, MOTOR_ACCEL, MOTOR_DECEL);
71 |
72 | Serial.println("START");
73 | /*
74 | * Using non-blocking mode to print out the step intervals.
75 | * We could have just as easily replace everything below this line with
76 | * stepper.rotate(360);
77 | */
78 | stepper.startRotate(360);
79 | }
80 |
81 | void loop() {
82 | static int step = 0;
83 | unsigned wait_time = stepper.nextAction();
84 | if (wait_time){
85 | Serial.print(" step="); Serial.print(step++);
86 | Serial.print(" dt="); Serial.print(wait_time);
87 | Serial.print(" rpm="); Serial.print(stepper.getCurrentRPM());
88 | Serial.println();
89 | } else {
90 | stepper.disable();
91 | Serial.println("END");
92 | delay(3600000);
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/examples/BasicStepperDriver/BasicStepperDriver.ino:
--------------------------------------------------------------------------------
1 | /*
2 | * Simple demo, should work with any driver board
3 | *
4 | * Connect STEP, DIR as indicated
5 | *
6 | * Copyright (C)2015-2017 Laurentiu Badea
7 | *
8 | * This file may be redistributed under the terms of the MIT license.
9 | * A copy of this license has been included with this distribution in the file LICENSE.
10 | */
11 | #include
12 | #include "BasicStepperDriver.h"
13 |
14 | // Motor steps per revolution. Most steppers are 200 steps or 1.8 degrees/step
15 | #define MOTOR_STEPS 200
16 | #define RPM 120
17 |
18 | // Since microstepping is set externally, make sure this matches the selected mode
19 | // If it doesn't, the motor will move at a different RPM than chosen
20 | // 1=full step, 2=half step etc.
21 | #define MICROSTEPS 1
22 |
23 | // All the wires needed for full functionality
24 | #define DIR 8
25 | #define STEP 9
26 | //Uncomment line to use enable/disable functionality
27 | //#define SLEEP 13
28 |
29 | // 2-wire basic config, microstepping is hardwired on the driver
30 | BasicStepperDriver stepper(MOTOR_STEPS, DIR, STEP);
31 |
32 | //Uncomment line to use enable/disable functionality
33 | //BasicStepperDriver stepper(MOTOR_STEPS, DIR, STEP, SLEEP);
34 |
35 | void setup() {
36 | stepper.begin(RPM, MICROSTEPS);
37 | // if using enable/disable on ENABLE pin (active LOW) instead of SLEEP uncomment next line
38 | // stepper.setEnableActiveState(LOW);
39 | }
40 |
41 | void loop() {
42 |
43 | // energize coils - the motor will hold position
44 | // stepper.enable();
45 |
46 | /*
47 | * Moving motor one full revolution using the degree notation
48 | */
49 | stepper.rotate(360);
50 |
51 | /*
52 | * Moving motor to original position using steps
53 | */
54 | stepper.move(-MOTOR_STEPS*MICROSTEPS);
55 |
56 | // pause and allow the motor to be moved by hand
57 | // stepper.disable();
58 |
59 | delay(5000);
60 | }
61 |
--------------------------------------------------------------------------------
/examples/ClockStepper/ClockStepper.ino:
--------------------------------------------------------------------------------
1 | /*
2 | * Clock Microstepping demo
3 | *
4 | * Moves the stepper motor like the seconds hand of a watch.
5 | *
6 | * Copyright (C)2015-2017 Laurentiu Badea
7 | *
8 | * This file may be redistributed under the terms of the MIT license.
9 | * A copy of this license has been included with this distribution in the file LICENSE.
10 | */
11 | #include
12 |
13 | // Motor steps per revolution. Most steppers are 200 steps or 1.8 degrees/step
14 | #define MOTOR_STEPS 200
15 |
16 | // Microstepping mode. If you hardwired it to save pins, set to the same value here.
17 | #define MICROSTEPS 1
18 |
19 | #define DIR 8
20 | #define STEP 9
21 | #define SLEEP 13 // optional (just delete SLEEP from everywhere if not used)
22 |
23 | /*
24 | * Choose one of the sections below that match your board
25 | */
26 |
27 | #include "DRV8834.h"
28 | #define M0 10
29 | #define M1 11
30 | DRV8834 stepper(MOTOR_STEPS, DIR, STEP, SLEEP, M0, M1);
31 |
32 | // #include "A4988.h"
33 | // #define MS1 10
34 | // #define MS2 11
35 | // #define MS3 12
36 | // A4988 stepper(MOTOR_STEPS, DIR, STEP, SLEEP, MS1, MS2, MS3);
37 |
38 | // #include "DRV8825.h"
39 | // #define MODE0 10
40 | // #define MODE1 11
41 | // #define MODE2 12
42 | // DRV8825 stepper(MOTOR_STEPS, DIR, STEP, SLEEP, MODE0, MODE1, MODE2);
43 |
44 | // #include "DRV8880.h"
45 | // #define M0 10
46 | // #define M1 11
47 | // #define TRQ0 6
48 | // #define TRQ1 7
49 | // DRV8880 stepper(MOTOR_STEPS, DIR, STEP, SLEEP, M0, M1, TRQ0, TRQ1);
50 |
51 | // #include "BasicStepperDriver.h" // generic
52 | // BasicStepperDriver stepper(MOTOR_STEPS, DIR, STEP);
53 |
54 | void setup() {
55 | /*
56 | * Set target motor RPM=1
57 | */
58 | stepper.begin(1, MICROSTEPS);
59 |
60 | // if using enable/disable on ENABLE pin (active LOW) instead of SLEEP uncomment next line
61 | // stepper.setEnableActiveState(LOW);
62 | stepper.enable();
63 | }
64 |
65 | void loop() {
66 | /*
67 | * The easy way is just tell the motor to rotate 360 degrees at 1rpm
68 | */
69 | stepper.rotate(360);
70 | }
71 |
--------------------------------------------------------------------------------
/examples/MicroStepping/MicroStepping.ino:
--------------------------------------------------------------------------------
1 | /*
2 | * Microstepping demo
3 | *
4 | * This requires that microstep control pins be connected in addition to STEP,DIR
5 | *
6 | * Copyright (C)2015 Laurentiu Badea
7 | *
8 | * This file may be redistributed under the terms of the MIT license.
9 | * A copy of this license has been included with this distribution in the file LICENSE.
10 | */
11 | #include
12 |
13 | // Motor steps per revolution. Most steppers are 200 steps or 1.8 degrees/step
14 | #define MOTOR_STEPS 200
15 | #define RPM 120
16 |
17 | #define DIR 8
18 | #define STEP 9
19 | #define SLEEP 13 // optional (just delete SLEEP from everywhere if not used)
20 |
21 | /*
22 | * Choose one of the sections below that match your board
23 | */
24 |
25 | #include "DRV8834.h"
26 | #define M0 10
27 | #define M1 11
28 | DRV8834 stepper(MOTOR_STEPS, DIR, STEP, SLEEP, M0, M1);
29 |
30 | // #include "A4988.h"
31 | // #define MS1 10
32 | // #define MS2 11
33 | // #define MS3 12
34 | // A4988 stepper(MOTOR_STEPS, DIR, STEP, SLEEP, MS1, MS2, MS3);
35 |
36 | // #include "DRV8825.h"
37 | // #define MODE0 10
38 | // #define MODE1 11
39 | // #define MODE2 12
40 | // DRV8825 stepper(MOTOR_STEPS, DIR, STEP, SLEEP, MODE0, MODE1, MODE2);
41 |
42 | // #include "DRV8880.h"
43 | // #define M0 10
44 | // #define M1 11
45 | // #define TRQ0 6
46 | // #define TRQ1 7
47 | // DRV8880 stepper(MOTOR_STEPS, DIR, STEP, SLEEP, M0, M1, TRQ0, TRQ1);
48 |
49 | // #include "BasicStepperDriver.h" // generic
50 | // BasicStepperDriver stepper(MOTOR_STEPS, DIR, STEP);
51 |
52 | void setup() {
53 | /*
54 | * Set target motor RPM.
55 | */
56 | stepper.begin(RPM);
57 | // if using enable/disable on ENABLE pin (active LOW) instead of SLEEP uncomment next line
58 | // stepper.setEnableActiveState(LOW);
59 | stepper.enable();
60 |
61 | // set current level (for DRV8880 only).
62 | // Valid percent values are 25, 50, 75 or 100.
63 | // stepper.setCurrent(100);
64 | }
65 |
66 | void loop() {
67 | delay(1000);
68 |
69 | /*
70 | * Moving motor in full step mode is simple:
71 | */
72 | stepper.setMicrostep(1); // Set microstep mode to 1:1
73 |
74 | // One complete revolution is 360°
75 | stepper.rotate(360); // forward revolution
76 | stepper.rotate(-360); // reverse revolution
77 |
78 | // One complete revolution is also MOTOR_STEPS steps in full step mode
79 | stepper.move(MOTOR_STEPS); // forward revolution
80 | stepper.move(-MOTOR_STEPS); // reverse revolution
81 |
82 | /*
83 | * Microstepping mode: 1, 2, 4, 8, 16 or 32 (where supported by driver)
84 | * Mode 1 is full speed.
85 | * Mode 32 is 32 microsteps per step.
86 | * The motor should rotate just as fast (at the set RPM),
87 | * but movement precision is increased, which may become visually apparent at lower RPMs.
88 | */
89 | stepper.setMicrostep(8); // Set microstep mode to 1:8
90 |
91 | // In 1:8 microstepping mode, one revolution takes 8 times as many microsteps
92 | stepper.move(8 * MOTOR_STEPS); // forward revolution
93 | stepper.move(-8 * MOTOR_STEPS); // reverse revolution
94 |
95 | // One complete revolution is still 360° regardless of microstepping mode
96 | // rotate() is easier to use than move() when no need to land on precise microstep position
97 | stepper.rotate(360);
98 | stepper.rotate(-360);
99 |
100 | delay(5000);
101 | }
102 |
--------------------------------------------------------------------------------
/examples/MultiAxis/MultiAxis.ino:
--------------------------------------------------------------------------------
1 | /*
2 | * Multi-motor control (experimental)
3 | *
4 | * Move two or three motors at the same time.
5 | * This module is still work in progress and may not work well or at all.
6 | *
7 | * Copyright (C)2017 Laurentiu Badea
8 | *
9 | * This file may be redistributed under the terms of the MIT license.
10 | * A copy of this license has been included with this distribution in the file LICENSE.
11 | */
12 | #include
13 | #include "BasicStepperDriver.h"
14 | #include "MultiDriver.h"
15 | #include "SyncDriver.h"
16 |
17 | // Motor steps per revolution. Most steppers are 200 steps or 1.8 degrees/step
18 | #define MOTOR_STEPS 200
19 | // Target RPM for X axis motor
20 | #define MOTOR_X_RPM 30
21 | // Target RPM for Y axis motor
22 | #define MOTOR_Y_RPM 90
23 |
24 | // X motor
25 | #define DIR_X 8
26 | #define STEP_X 9
27 |
28 | // Y motor
29 | #define DIR_Y 6
30 | #define STEP_Y 7
31 |
32 | // If microstepping is set externally, make sure this matches the selected mode
33 | // 1=full step, 2=half step etc.
34 | #define MICROSTEPS 32
35 |
36 | // 2-wire basic config, microstepping is hardwired on the driver
37 | // Other drivers can be mixed and matched but must be configured individually
38 | BasicStepperDriver stepperX(MOTOR_STEPS, DIR_X, STEP_X);
39 | BasicStepperDriver stepperY(MOTOR_STEPS, DIR_Y, STEP_Y);
40 |
41 | // Pick one of the two controllers below
42 | // each motor moves independently, trajectory is a hockey stick
43 | // MultiDriver controller(stepperX, stepperY);
44 | // OR
45 | // synchronized move, trajectory is a straight line
46 | SyncDriver controller(stepperX, stepperY);
47 |
48 | void setup() {
49 | /*
50 | * Set target motors RPM.
51 | */
52 | stepperX.begin(MOTOR_X_RPM, MICROSTEPS);
53 | stepperY.begin(MOTOR_Y_RPM, MICROSTEPS);
54 | // if using enable/disable on ENABLE pin (active LOW) instead of SLEEP uncomment next two lines
55 | // stepperX.setEnableActiveState(LOW);
56 | // stepperY.setEnableActiveState(LOW);
57 | }
58 |
59 | void loop() {
60 |
61 | controller.rotate(90*5, 60*15);
62 | delay(1000);
63 | controller.rotate(-90*5, -30*15);
64 | delay(1000);
65 | controller.rotate(0, -30*15);
66 | delay(30000);
67 | }
68 |
--------------------------------------------------------------------------------
/examples/NonBlocking/NonBlocking.ino:
--------------------------------------------------------------------------------
1 | /*
2 | * Example using non-blocking mode to move until a switch is triggered.
3 | *
4 | * Copyright (C)2015-2017 Laurentiu Badea
5 | *
6 | * This file may be redistributed under the terms of the MIT license.
7 | * A copy of this license has been included with this distribution in the file LICENSE.
8 | */
9 | #include
10 |
11 | // this pin should connect to Ground when want to stop the motor
12 | #define STOPPER_PIN 4
13 |
14 | // Motor steps per revolution. Most steppers are 200 steps or 1.8 degrees/step
15 | #define MOTOR_STEPS 200
16 | #define RPM 120
17 | // Microstepping mode. If you hardwired it to save pins, set to the same value here.
18 | #define MICROSTEPS 16
19 |
20 | #define DIR 8
21 | #define STEP 9
22 | #define SLEEP 13 // optional (just delete SLEEP from everywhere if not used)
23 |
24 | /*
25 | * Choose one of the sections below that match your board
26 | */
27 |
28 | #include "DRV8834.h"
29 | #define M0 10
30 | #define M1 11
31 | DRV8834 stepper(MOTOR_STEPS, DIR, STEP, SLEEP, M0, M1);
32 |
33 | // #include "A4988.h"
34 | // #define MS1 10
35 | // #define MS2 11
36 | // #define MS3 12
37 | // A4988 stepper(MOTOR_STEPS, DIR, STEP, SLEEP, MS1, MS2, MS3);
38 |
39 | // #include "DRV8825.h"
40 | // #define MODE0 10
41 | // #define MODE1 11
42 | // #define MODE2 12
43 | // DRV8825 stepper(MOTOR_STEPS, DIR, STEP, SLEEP, MODE0, MODE1, MODE2);
44 |
45 | // #include "DRV8880.h"
46 | // #define M0 10
47 | // #define M1 11
48 | // #define TRQ0 6
49 | // #define TRQ1 7
50 | // DRV8880 stepper(MOTORS_STEPS, DIR, STEP, SLEEP, M0, M1, TRQ0, TRQ1);
51 |
52 | // #include "BasicStepperDriver.h" // generic
53 | // BasicStepperDriver stepper(MOTOR_STEPS, DIR, STEP);
54 |
55 | void setup() {
56 | Serial.begin(115200);
57 |
58 | // Configure stopper pin to read HIGH unless grounded
59 | pinMode(STOPPER_PIN, INPUT_PULLUP);
60 |
61 | stepper.begin(RPM, MICROSTEPS);
62 | // if using enable/disable on ENABLE pin (active LOW) instead of SLEEP uncomment next line
63 | // stepper.setEnableActiveState(LOW);
64 | stepper.enable();
65 |
66 | // set current level (for DRV8880 only). Valid percent values are 25, 50, 75 or 100.
67 | // stepper.setCurrent(100);
68 |
69 | Serial.println("START");
70 |
71 | // set the motor to move continuously for a reasonable time to hit the stopper
72 | // let's say 100 complete revolutions (arbitrary number)
73 | stepper.startMove(100 * MOTOR_STEPS * MICROSTEPS); // in microsteps
74 | // stepper.startRotate(100 * 360); // or in degrees
75 | }
76 |
77 | void loop() {
78 | // first, check if stopper was hit
79 | if (digitalRead(STOPPER_PIN) == LOW){
80 | Serial.println("STOPPER REACHED");
81 |
82 | /*
83 | * Choosing stop() vs startBrake():
84 | *
85 | * constant speed mode, they are the same (stop immediately)
86 | * linear (accelerated) mode with brake, the motor will go past the stopper a bit
87 | */
88 |
89 | stepper.stop();
90 | // stepper.startBrake();
91 | }
92 |
93 | // motor control loop - send pulse and return how long to wait until next pulse
94 | unsigned wait_time_micros = stepper.nextAction();
95 |
96 | // 0 wait time indicates the motor has stopped
97 | if (wait_time_micros <= 0) {
98 | stepper.disable(); // comment out to keep motor powered
99 | delay(3600000);
100 | }
101 |
102 | // (optional) execute other code if we have enough time
103 | if (wait_time_micros > 100){
104 | // other code here
105 | }
106 | }
107 |
--------------------------------------------------------------------------------
/examples/SpeedProfile/SpeedProfile.ino:
--------------------------------------------------------------------------------
1 | /*
2 | * This is not an example sketch, it is used to visualize the motor speed.
3 | *
4 | * Usage: upload and start Tool -> Serial Plotter
5 | *
6 | * All driver tests are done with microstep 1. Increasing microstep halves max rpm with each level.
7 | * The maximum usable RPM can be determined from the output.
8 | * The max RPM at a different microstep can be calculated with formula "max rpm / microstep"
9 | *
10 | * Copyright (C)2020 Laurentiu Badea
11 | *
12 | * This file may be redistributed under the terms of the MIT license.
13 | * A copy of this license has been included with this distribution in the file LICENSE.
14 | */
15 | #include
16 |
17 | #include "BasicStepperDriver.h"
18 | #include "MultiDriver.h"
19 | #include "SyncDriver.h"
20 |
21 | #define RPM 150
22 | #define MICROSTEP 1
23 |
24 | #define MOTOR_STEPS 200
25 |
26 | // Do not use with a real motor, the step timing is very delayed due to serial printing.
27 | BasicStepperDriver s1(MOTOR_STEPS, 12, 13);
28 | BasicStepperDriver s2(MOTOR_STEPS, 12, 13);
29 | BasicStepperDriver s3(MOTOR_STEPS, 12, 13);
30 |
31 | void setup() {
32 | Serial.begin(115200);
33 | delay(4000);
34 |
35 | s1.setSpeedProfile(BasicStepperDriver::LINEAR_SPEED, 2000, 2000);
36 | s2.setSpeedProfile(BasicStepperDriver::LINEAR_SPEED, 500, 500);
37 | s3.setSpeedProfile(BasicStepperDriver::CONSTANT_SPEED);
38 |
39 | s1.begin(RPM, MICROSTEP);
40 | s2.begin(RPM, MICROSTEP);
41 | s3.begin(RPM, MICROSTEP);
42 |
43 | s1.startMove(500);
44 | s2.startMove(500);
45 | s3.startMove(500);
46 | }
47 |
48 | void loop() {
49 | unsigned w1, w2, w3;
50 |
51 | w1 = s1.nextAction();
52 | w2 = s2.nextAction();
53 | w3 = s3.nextAction();
54 |
55 | if (w1 > 0 || w2 > 0 || w3 > 0){
56 | // uncomment to see step delays instead of speed
57 | /*
58 | Serial.print(w1);
59 | Serial.print("\t");
60 | Serial.print(w2);
61 | Serial.print("\t");
62 | Serial.print(w2);
63 | Serial.println();
64 | */
65 |
66 | // graph current rpm
67 | Serial.print(s1.getCurrentRPM());
68 | Serial.print("\t");
69 | Serial.print(s2.getCurrentRPM());
70 | Serial.print("\t");
71 | Serial.print(s3.getCurrentRPM());
72 | Serial.println();
73 | } else {
74 | delay(100000);
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/examples/UnitTest/UnitTest.ino:
--------------------------------------------------------------------------------
1 | /*
2 | * This is not an example sketch, it is used to validate code changes
3 | * and determine maximum workable RPM/microsteps parameters for a given board.
4 | *
5 | * Usage: run with serial terminal open
6 | *
7 | * All driver tests are done with microstep 1. Increasing microstep halves max rpm with each level.
8 | * The maximum usable RPM can be determined from the output.
9 | * The max RPM at a different microstep can be calculated with formula "max rpm / microstep"
10 | *
11 | * Copyright (C)2020 Laurentiu Badea
12 | *
13 | * This file may be redistributed under the terms of the MIT license.
14 | * A copy of this license has been included with this distribution in the file LICENSE.
15 | */
16 | #include
17 |
18 | #include "BasicStepperDriver.h"
19 | #include "MultiDriver.h"
20 | #include "SyncDriver.h"
21 |
22 | // RPMS contains the list of RPMS to test at, assuming microstep=1
23 | const float RPMS[] = {6000, 600, 60, 6};
24 | const int RPMS_COUNT = sizeof(RPMS)/sizeof(*RPMS);
25 | const long DURATION_CONSTANT[] = {10000, 100000, 1000000, 10000000};
26 | const long DURATION_LINEAR[] = {365148, 365148, 1033246, 10000000};
27 | // STEPS is how many steps for each test. More has better accuracy but slower
28 | #define STEPS 200
29 | // ALLOWED_DEVIATION is the error tolerance. 0.10 considers 90% - 110% range aceptable
30 | #define ALLOWED_DEVIATION 0.10
31 |
32 | /*
33 | * Verify that the expected time calculation is correct at different rpms and two microstep levels
34 | */
35 | bool test_calculations(BasicStepperDriver stepper, const long duration[]){
36 | bool pass = true;
37 | char t[128];
38 | for (int i = 0; i < RPMS_COUNT; i++){
39 | float rpm = RPMS[i];
40 | for (int microstep = 1; microstep <= 16; microstep <<= 4){
41 | long expected_micros = duration[i];
42 | stepper.begin(rpm, microstep);
43 | long estimated_micros = stepper.getTimeForMove(STEPS*microstep);
44 | sprintf(t, " rpm=%-4d microstep=%-2d expected=%10luµs estimated %10luµs",
45 | int(rpm), microstep, expected_micros, estimated_micros);
46 | Serial.print(t);
47 | float ratio = float(estimated_micros) / float(expected_micros);
48 | if (ratio > 1.01 or ratio < 0.99) {
49 | Serial.print(" FAIL");
50 | pass = false;
51 | }
52 | Serial.println();
53 | }
54 | }
55 | return pass;
56 | }
57 |
58 | /*
59 | * Pass/fail the result and print it out in a one-line format
60 | */
61 | bool result(float rpm, int microstep, int steps, long elapsed_micros, long expected_micros){
62 | bool pass = true;
63 | char t[128];
64 | float error = float(elapsed_micros) / float(expected_micros);
65 | unsigned step_micros = expected_micros / steps;
66 | unsigned error_micros = labs(elapsed_micros - expected_micros) / steps;
67 | sprintf(t, " rpm=%-4d expected=%10luµs elapsed=%10luµs step_err=%6uµs avgstep=%6uµs",
68 | int(rpm), expected_micros, elapsed_micros, error_micros, step_micros);
69 | Serial.print(t);
70 | if (error >= 1.0f + ALLOWED_DEVIATION || error <= 1.0f - ALLOWED_DEVIATION) {
71 | pass = false;
72 | Serial.print(" FAIL");
73 | }
74 | Serial.println();
75 | return pass;
76 | }
77 |
78 | /*
79 | * Run the tests for BasicStepperDriver
80 | */
81 | bool test_basic(BasicStepperDriver stepper){
82 | bool pass = true;
83 | for (int i = 0; i < RPMS_COUNT; i++){
84 | float rpm = RPMS[i];
85 | stepper.begin(rpm, 1);
86 | unsigned long start_time_micros = micros();
87 | stepper.move(STEPS);
88 | long elapsed_micros = micros() - start_time_micros;
89 | pass &= result(rpm, 1, STEPS, elapsed_micros, stepper.getTimeForMove(STEPS));
90 | }
91 | return pass;
92 | }
93 |
94 | /*
95 | * Run the tests for MultiDriver with 3 motors
96 | */
97 | bool test_multi(BasicStepperDriver s1, BasicStepperDriver s2, BasicStepperDriver s3){
98 | MultiDriver controller(s1, s2, s3);
99 | bool pass = true;
100 | for (int i = 0; i < RPMS_COUNT; i++){
101 | float rpm = RPMS[i];
102 | s1.begin(rpm, 1);
103 | s2.begin(rpm, 1);
104 | s3.begin(rpm, 1);
105 | unsigned long start_time_micros = micros();
106 | controller.move(STEPS, 2*STEPS/3, -STEPS/2);
107 | long elapsed_micros = micros() - start_time_micros;
108 | pass &= result(rpm, 1, STEPS, elapsed_micros, s1.getTimeForMove(STEPS));
109 | }
110 | return pass;
111 | }
112 |
113 | /*
114 | * Run the tests for SyncDriver with 3 motors
115 | */
116 | bool test_sync(BasicStepperDriver s1, BasicStepperDriver s2, BasicStepperDriver s3){
117 | SyncDriver controller(s1, s2, s3);
118 | bool pass = true;
119 | for (int i = 0; i < RPMS_COUNT; i++){
120 | float rpm = RPMS[i];
121 | s1.begin(rpm, 1);
122 | s2.begin(rpm, 1);
123 | s3.begin(rpm, 1);
124 | unsigned long start_time_micros = micros();
125 | controller.move(STEPS, 2*STEPS/3, -STEPS/2);
126 | long elapsed_micros = micros() - start_time_micros;
127 | pass &= result(rpm, 1, STEPS, elapsed_micros, s1.getTimeForMove(STEPS));
128 | }
129 | return pass;
130 | }
131 |
132 | #define TEST_RESULT(result, func, ...) #func "(" #__VA_ARGS__ "): " result
133 | #define RUN_TEST(desc, func, ...) Serial.println(desc); Serial.println(func(__VA_ARGS__) ? TEST_RESULT("OK", func, __VA_ARGS__) : TEST_RESULT("FAIL", func, __VA_ARGS__))
134 |
135 | void setup() {
136 |
137 | BasicStepperDriver s1(200, 12, 13);
138 | BasicStepperDriver s2(200, 12, 13);
139 | BasicStepperDriver s3(200, 12, 13);
140 |
141 | Serial.begin(115200);
142 | delay(2000);
143 | #ifdef ARDUINO_BOARD
144 | Serial.println(ARDUINO_BOARD);
145 | #endif
146 | RUN_TEST("Timing Calculation test, constant speed", test_calculations, s1, DURATION_CONSTANT);
147 | RUN_TEST("BasicStepperDriver test, constant speed", test_basic, s1);
148 | RUN_TEST("MultiDriver test, constant speed", test_multi, s1, s2, s3);
149 | RUN_TEST("SyncDriver test, constant speed", test_sync, s1, s2, s3);
150 |
151 | s1.setSpeedProfile(s1.LINEAR_SPEED, 6000, 6000);
152 | s2.setSpeedProfile(s2.LINEAR_SPEED, 6000, 6000);
153 | s3.setSpeedProfile(s3.LINEAR_SPEED, 6000, 6000);
154 |
155 | RUN_TEST("Timing Calculation test, linear speed", test_calculations, s1, DURATION_LINEAR);
156 | RUN_TEST("BasicStepperDriver test, linear speed", test_basic, s1);
157 | RUN_TEST("MultiDriver test, linear speed", test_multi, s1, s2, s3);
158 | RUN_TEST("SyncDriver test, linear speed", test_sync, s1, s2, s3);
159 | }
160 |
161 | void loop() {
162 | delay(1);
163 | }
164 |
--------------------------------------------------------------------------------
/examples/UnitTest/adafruit_feather_m0.txt:
--------------------------------------------------------------------------------
1 | Timing Calculation test, constant speed
2 | rpm=6000 microstep=1 expected= 10000µs estimated 10000µs
3 | rpm=6000 microstep=16 expected= 10000µs estimated 10000µs
4 | rpm=600 microstep=1 expected= 100000µs estimated 100000µs
5 | rpm=600 microstep=16 expected= 100000µs estimated 100000µs
6 | rpm=60 microstep=1 expected= 1000000µs estimated 1000000µs
7 | rpm=60 microstep=16 expected= 1000000µs estimated 1000000µs
8 | rpm=6 microstep=1 expected= 10000000µs estimated 10000000µs
9 | rpm=6 microstep=16 expected= 10000000µs estimated 10000000µs
10 | test_calculations(s1, DURATION_CONSTANT): OK
11 | BasicStepperDriver test, constant speed
12 | rpm=6000 expected= 10000µs elapsed= 11336µs step_err= 6µs avgstep= 50µs FAIL
13 | rpm=600 expected= 100000µs elapsed= 100728µs step_err= 3µs avgstep= 500µs
14 | rpm=60 expected= 1000000µs elapsed= 996284µs step_err= 18µs avgstep= 5000µs
15 | rpm=6 expected= 10000000µs elapsed= 9951257µs step_err= 243µs avgstep= 50000µs
16 | test_basic(s1): FAIL
17 | MultiDriver test, constant speed
18 | rpm=6000 expected= 10000µs elapsed= 18210µs step_err= 41µs avgstep= 50µs FAIL
19 | rpm=600 expected= 100000µs elapsed= 108452µs step_err= 42µs avgstep= 500µs
20 | rpm=60 expected= 1000000µs elapsed= 1008295µs step_err= 41µs avgstep= 5000µs
21 | rpm=6 expected= 10000000µs elapsed= 10008300µs step_err= 41µs avgstep= 50000µs
22 | test_multi(s1, s2, s3): FAIL
23 | SyncDriver test, constant speed
24 | rpm=6000 expected= 10000µs elapsed= 19284µs step_err= 46µs avgstep= 50µs FAIL
25 | rpm=600 expected= 100000µs elapsed= 109244µs step_err= 46µs avgstep= 500µs
26 | rpm=60 expected= 1000000µs elapsed= 1009259µs step_err= 46µs avgstep= 5000µs
27 | rpm=6 expected= 10000000µs elapsed= 10009274µs step_err= 46µs avgstep= 50000µs
28 | test_sync(s1, s2, s3): FAIL
29 | Timing Calculation test, linear speed
30 | rpm=6000 microstep=1 expected= 365148µs estimated 365148µs
31 | rpm=6000 microstep=16 expected= 365148µs estimated 365148µs
32 | rpm=600 microstep=1 expected= 365148µs estimated 365148µs
33 | rpm=600 microstep=16 expected= 365148µs estimated 365148µs
34 | rpm=60 microstep=1 expected= 1033246µs estimated 1033246µs
35 | rpm=60 microstep=16 expected= 1033246µs estimated 1033333µs
36 | rpm=6 microstep=1 expected= 10000000µs estimated 10000000µs
37 | rpm=6 microstep=16 expected= 10000000µs estimated 10000000µs
38 | test_calculations(s1, DURATION_LINEAR): OK
39 | BasicStepperDriver test, linear speed
40 | rpm=6000 expected= 365148µs elapsed= 343357µs step_err= 108µs avgstep= 1825µs
41 | rpm=600 expected= 365148µs elapsed= 343361µs step_err= 108µs avgstep= 1825µs
42 | rpm=60 expected= 1033246µs elapsed= 1010367µs step_err= 114µs avgstep= 5166µs
43 | rpm=6 expected= 10000000µs elapsed= 2457426µs step_err= 37712µs avgstep= 50000µs FAIL
44 | test_basic(s1): FAIL
45 | MultiDriver test, linear speed
46 | rpm=6000 expected= 365148µs elapsed= 364538µs step_err= 3µs avgstep= 1825µs
47 | rpm=600 expected= 365148µs elapsed= 364552µs step_err= 2µs avgstep= 1825µs
48 | rpm=60 expected= 1033246µs elapsed= 1030380µs step_err= 14µs avgstep= 5166µs
49 | rpm=6 expected= 10000000µs elapsed= 2477104µs step_err= 37614µs avgstep= 50000µs FAIL
50 | test_multi(s1, s2, s3): FAIL
51 | SyncDriver test, linear speed
52 | rpm=6000 expected= 365148µs elapsed= 365655µs step_err= 2µs avgstep= 1825µs
53 | rpm=600 expected= 365148µs elapsed= 365591µs step_err= 2µs avgstep= 1825µs
54 | rpm=60 expected= 1033246µs elapsed= 1245906µs step_err= 1063µs avgstep= 5166µs FAIL
55 | rpm=6 expected= 10000000µs elapsed= 2478551µs step_err= 37607µs avgstep= 50000µs FAIL
56 | test_sync(s1, s2, s3): FAIL
57 |
--------------------------------------------------------------------------------
/examples/UnitTest/esp8266_nodemcu.txt:
--------------------------------------------------------------------------------
1 |
2 | ESP8266_NODEMCU
3 | Timing Calculation test, constant speed
4 | rpm=6000 microstep=1 expected= 10000µs estimated 10000µs
5 | rpm=6000 microstep=16 expected= 10000µs estimated 10000µs
6 | rpm=600 microstep=1 expected= 100000µs estimated 100000µs
7 | rpm=600 microstep=16 expected= 100000µs estimated 100000µs
8 | rpm=60 microstep=1 expected= 1000000µs estimated 1000000µs
9 | rpm=60 microstep=16 expected= 1000000µs estimated 1000000µs
10 | rpm=6 microstep=1 expected= 10000000µs estimated 10000000µs
11 | rpm=6 microstep=16 expected= 10000000µs estimated 10000000µs
12 | test_calculations(s1, DURATION_CONSTANT): OK
13 | BasicStepperDriver test, constant speed
14 | rpm=6000 expected= 10000µs elapsed= 10346µs step_err= 1µs avgstep= 50µs
15 | rpm=600 expected= 100000µs elapsed= 99760µs step_err= 1µs avgstep= 500µs
16 | rpm=60 expected= 1000000µs elapsed= 995291µs step_err= 23µs avgstep= 5000µs
17 | rpm=6 expected= 10000000µs elapsed= 9950300µs step_err= 248µs avgstep= 50000µs
18 | test_basic(s1): OK
19 | MultiDriver test, constant speed
20 | rpm=6000 expected= 10000µs elapsed= 12420µs step_err= 12µs avgstep= 50µs FAIL
21 | rpm=600 expected= 100000µs elapsed= 105190µs step_err= 25µs avgstep= 500µs
22 | rpm=60 expected= 1000000µs elapsed= 1006547µs step_err= 32µs avgstep= 5000µs
23 | rpm=6 expected= 10000000µs elapsed= 10017375µs step_err= 86µs avgstep= 50000µs
24 | test_multi(s1, s2, s3): FAIL
25 | SyncDriver test, constant speed
26 | rpm=6000 expected= 10000µs elapsed= 14511µs step_err= 22µs avgstep= 50µs FAIL
27 | rpm=600 expected= 100000µs elapsed= 105689µs step_err= 28µs avgstep= 500µs
28 | rpm=60 expected= 1000000µs elapsed= 1006867µs step_err= 34µs avgstep= 5000µs
29 | rpm=6 expected= 10000000µs elapsed= 10018707µs step_err= 93µs avgstep= 50000µs
30 | test_sync(s1, s2, s3): FAIL
31 | Timing Calculation test, linear speed
32 | rpm=6000 microstep=1 expected= 365148µs estimated 365148µs
33 | rpm=6000 microstep=16 expected= 365148µs estimated 365148µs
34 | rpm=600 microstep=1 expected= 365148µs estimated 365148µs
35 | rpm=600 microstep=16 expected= 365148µs estimated 365148µs
36 | rpm=60 microstep=1 expected= 1033246µs estimated 1033246µs
37 | rpm=60 microstep=16 expected= 1033246µs estimated 1033333µs
38 | rpm=6 microstep=1 expected= 10000000µs estimated 10000000µs
39 | rpm=6 microstep=16 expected= 10000000µs estimated 10000000µs
40 | test_calculations(s1, DURATION_LINEAR): OK
41 | BasicStepperDriver test, linear speed
42 | rpm=6000 expected= 365148µs elapsed= 342337µs step_err= 114µs avgstep= 1825µs
43 | rpm=600 expected= 365148µs elapsed= 342328µs step_err= 114µs avgstep= 1825µs
44 | rpm=60 expected= 1033246µs elapsed= 1009343µs step_err= 119µs avgstep= 5166µs
45 | rpm=6 expected= 10000000µs elapsed= 2456407µs step_err= 37717µs avgstep= 50000µs FAIL
46 | test_basic(s1): FAIL
47 | MultiDriver test, linear speed
48 | rpm=6000 expected= 365148µs elapsed= 360765µs step_err= 21µs avgstep= 1825µs
49 | rpm=600 expected= 365148µs elapsed= 360705µs step_err= 22µs avgstep= 1825µs
50 | rpm=60 expected= 1033246µs elapsed= 1028486µs step_err= 23µs avgstep= 5166µs
51 | rpm=6 expected= 10000000µs elapsed= 2477224µs step_err= 37613µs avgstep= 50000µs FAIL
52 | test_multi(s1, s2, s3): FAIL
53 | SyncDriver test, linear speed
54 | rpm=6000 expected= 365148µs elapsed= 361367µs step_err= 18µs avgstep= 1825µs
55 | rpm=600 expected= 365148µs elapsed= 361408µs step_err= 18µs avgstep= 1825µs
56 | rpm=60 expected= 1033246µs elapsed= 1242580µs step_err= 1046µs avgstep= 5166µs FAIL
57 | rpm=6 expected= 10000000µs elapsed= 2477912µs step_err= 37610µs avgstep= 50000µs FAIL
58 | test_sync(s1, s2, s3): FAIL
59 |
--------------------------------------------------------------------------------
/keywords.txt:
--------------------------------------------------------------------------------
1 | StepperDriver KEYWORD1
2 |
3 | BasicStepperDriver KEYWORD1
4 | DRV8880 KEYWORD1
5 | DRV8834 KEYWORD1
6 | DRV8824 KEYWORD1
7 | DRV8825 KEYWORD1
8 | A4988 KEYWORD1
9 | MultiDriver KEYWORD1
10 | SyncDriver KEYWORD1
11 |
12 | setMicrostep KEYWORD2
13 | setSpeedProfile KEYWORD2
14 | move KEYWORD2
15 | rotate KEYWORD2
16 | setRPM KEYWORD2
17 | getRPM KEYWORD2
18 | setCurrent KEYWORD2
19 | enable KEYWORD2
20 | disable KEYWORD2
21 | startMove KEYWORD2
22 | startRotate KEYWORD2
23 | nextAction KEYWORD2
24 | stop KEYWORD2
25 | startBrake KEYWORD2
26 |
27 | CONSTANT_SPEED LITERAL1
28 | LINEAR_SPEED LITERAL1
29 |
--------------------------------------------------------------------------------
/library.properties:
--------------------------------------------------------------------------------
1 | name=StepperDriver
2 | version=1.4.1
3 | author=Laurentiu Badea
4 | maintainer=Laurentiu Badea
5 | sentence=A4988, DRV8825 and generic two-pin stepper motor driver library.
6 | paragraph=Control steppers via a driver board providing STEP+DIR like the ones from Pololu. Microstepping is supported. Acceleration is supported. Supported drivers are A4988, DRV8824, DRV8825, DRV8834, DRV8880.
7 | category=Device Control
8 | url=https://github.com/laurb9/StepperDriver
9 | architectures=*
10 |
--------------------------------------------------------------------------------
/platformio.ini:
--------------------------------------------------------------------------------
1 | ; PlatformIO Project Configuration File
2 | ;
3 | ; Build options: build flags, source filter
4 | ; Upload options: custom upload port, speed and extra flags
5 | ; Library options: dependencies, extra library storages
6 | ; Advanced options: extra scripting
7 | ;
8 | ; Please visit documentation for the other options and examples
9 | ; https://docs.platformio.org/page/projectconf.html
10 |
11 | [platformio]
12 | src_dir = examples/UnitTest
13 | lib_dir = .
14 | default_envs =
15 | nodemcuv2
16 | adafruit_feather_m0
17 | esp32dev
18 | teensylc
19 |
20 | [env]
21 | framework = arduino
22 | monitor_filters =
23 | colorize
24 | send_on_enter
25 | monitor_speed = 115200
26 |
27 | [env:nodemcuv2]
28 | platform = espressif8266
29 | board = nodemcuv2
30 | upload_speed = 1000000
31 |
32 | [env:adafruit_feather_m0]
33 | platform = atmelsam
34 | board = adafruit_feather_m0
35 |
36 | [env:esp32dev]
37 | board = esp32dev
38 | platform = espressif32
39 |
40 | [env:teensylc]
41 | platform = teensy
42 | board = teensylc
43 |
44 | [env:uno]
45 | board = uno
46 | platform = atmelavr
47 |
48 | [env:native]
49 | platform = native
50 |
--------------------------------------------------------------------------------
/src/A4988.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | * A4988 - Stepper Motor Driver Driver
3 | * Indexer mode only.
4 |
5 | * Copyright (C)2015 Laurentiu Badea
6 | *
7 | * This file may be redistributed under the terms of the MIT license.
8 | * A copy of this license has been included with this distribution in the file LICENSE.
9 | */
10 | #include "A4988.h"
11 |
12 | /*
13 | * Microstepping resolution truth table (Page 6 of A4988 pdf)
14 | * 0bMS3,MS2,MS1 for 1,2,4,8,16 microsteps
15 | */
16 | const uint8_t A4988::MS_TABLE[] = {0b000, 0b001, 0b010, 0b011, 0b111};
17 |
18 | /*
19 | * Basic connection: only DIR, STEP are connected.
20 | * Microstepping controls should be hardwired.
21 | */
22 | A4988::A4988(short steps, short dir_pin, short step_pin)
23 | :BasicStepperDriver(steps, dir_pin, step_pin)
24 | {}
25 |
26 | A4988::A4988(short steps, short dir_pin, short step_pin, short enable_pin)
27 | :BasicStepperDriver(steps, dir_pin, step_pin, enable_pin)
28 | {}
29 |
30 | /*
31 | * Fully wired.
32 | * All the necessary control pins for A4988 are connected.
33 | */
34 | A4988::A4988(short steps, short dir_pin, short step_pin, short ms1_pin, short ms2_pin, short ms3_pin)
35 | :BasicStepperDriver(steps, dir_pin, step_pin),
36 | ms1_pin(ms1_pin), ms2_pin(ms2_pin), ms3_pin(ms3_pin)
37 | {}
38 |
39 | A4988::A4988(short steps, short dir_pin, short step_pin, short enable_pin, short ms1_pin, short ms2_pin, short ms3_pin)
40 | :BasicStepperDriver(steps, dir_pin, step_pin, enable_pin),
41 | ms1_pin(ms1_pin), ms2_pin(ms2_pin), ms3_pin(ms3_pin)
42 | {}
43 |
44 | void A4988::begin(float rpm, short microsteps){
45 | BasicStepperDriver::begin(rpm, microsteps);
46 |
47 | if (!IS_CONNECTED(ms1_pin) || !IS_CONNECTED(ms2_pin) || !IS_CONNECTED(ms3_pin)){
48 | return;
49 | }
50 |
51 | pinMode(ms1_pin, OUTPUT);
52 | pinMode(ms2_pin, OUTPUT);
53 | pinMode(ms3_pin, OUTPUT);
54 | }
55 |
56 | /*
57 | * Set microstepping mode (1:divisor)
58 | * Allowed ranges for A4988 are 1:1 to 1:16
59 | * If the control pins are not connected, we recalculate the timing only
60 | */
61 | short A4988::setMicrostep(short microsteps){
62 | BasicStepperDriver::setMicrostep(microsteps);
63 |
64 | if (!IS_CONNECTED(ms1_pin) || !IS_CONNECTED(ms2_pin) || !IS_CONNECTED(ms3_pin)){
65 | return this->microsteps;
66 | }
67 |
68 | const uint8_t* ms_table = getMicrostepTable();
69 | size_t ms_table_size = getMicrostepTableSize();
70 |
71 | unsigned short i = 0;
72 | while (i < ms_table_size){
73 | if (this->microsteps & (1<microsteps;
83 | }
84 |
85 | const uint8_t* A4988::getMicrostepTable(){
86 | return A4988::MS_TABLE;
87 | }
88 |
89 | size_t A4988::getMicrostepTableSize(){
90 | return sizeof(A4988::MS_TABLE);
91 | }
92 |
93 | short A4988::getMaxMicrostep(){
94 | return A4988::MAX_MICROSTEP;
95 | }
96 |
--------------------------------------------------------------------------------
/src/A4988.h:
--------------------------------------------------------------------------------
1 | /*
2 | * A4988 - Stepper Motor Driver Driver
3 | * Indexer mode only.
4 | *
5 | * Copyright (C)2015 Laurentiu Badea
6 | *
7 | * This file may be redistributed under the terms of the MIT license.
8 | * A copy of this license has been included with this distribution in the file LICENSE.
9 | */
10 | #ifndef A4988_H
11 | #define A4988_H
12 | #include
13 | #include "BasicStepperDriver.h"
14 |
15 | class A4988 : public BasicStepperDriver {
16 | protected:
17 | static const uint8_t MS_TABLE[];
18 | short ms1_pin = PIN_UNCONNECTED;
19 | short ms2_pin = PIN_UNCONNECTED;
20 | short ms3_pin = PIN_UNCONNECTED;
21 | // tA STEP minimum, HIGH pulse width (1us)
22 | static const int step_high_min = 1;
23 | // tB STEP minimum, LOW pulse width (1us)
24 | static const int step_low_min = 1;
25 | // wakeup time, nSLEEP inactive to STEP (1000us)
26 | static const int wakeup_time = 1000;
27 | // also 200ns between ENBL/DIR/MSx changes and STEP HIGH
28 |
29 | // Get the microstep table
30 | virtual const uint8_t* getMicrostepTable();
31 | virtual size_t getMicrostepTableSize();
32 |
33 | // Get max microsteps supported by the device
34 | short getMaxMicrostep() override;
35 |
36 | private:
37 | // microstep range (1, 16, 32 etc)
38 | static const short MAX_MICROSTEP = 16;
39 |
40 | public:
41 | /*
42 | * Basic connection: only DIR, STEP are connected.
43 | * Microstepping controls should be hardwired.
44 | */
45 | A4988(short steps, short dir_pin, short step_pin);
46 | A4988(short steps, short dir_pin, short step_pin, short enable_pin);
47 |
48 | void begin(float rpm=60, short microsteps=1);
49 | /*
50 | * Fully wired. All the necessary control pins for A4988 are connected.
51 | */
52 | A4988(short steps, short dir_pin, short step_pin, short ms1_pin, short ms2_pin, short ms3_pin);
53 | A4988(short steps, short dir_pin, short step_pin, short enable_pin, short ms1_pin, short ms2_pin, short ms3_pin);
54 | short setMicrostep(short microsteps) override;
55 | };
56 | #endif // A4988_H
57 |
--------------------------------------------------------------------------------
/src/BasicStepperDriver.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | * Generic Stepper Motor Driver Driver
3 | * Indexer mode only.
4 |
5 | * Copyright (C)2015-2019 Laurentiu Badea
6 | *
7 | * This file may be redistributed under the terms of the MIT license.
8 | * A copy of this license has been included with this distribution in the file LICENSE.
9 | *
10 | * Linear speed profile calculations based on
11 | * - Generating stepper-motor speed profiles in real time - David Austin, 2004
12 | * - Atmel AVR446: Linear speed control of stepper motor, 2006
13 | */
14 | #include "BasicStepperDriver.h"
15 |
16 |
17 | /*
18 | * Min/Max functions which avoid evaluating the arguments multiple times.
19 | * See also https://github.com/arduino/Arduino/issues/2069
20 | */
21 | template
22 | constexpr const T& stepperMin(const T& a, const T& b)
23 | {
24 | return b < a ? b : a;
25 | }
26 |
27 | template
28 | constexpr const T& stepperMax(const T& a, const T& b)
29 | {
30 | return a < b ? b : a;
31 | }
32 |
33 | /*
34 | * Basic connection: only DIR, STEP are connected.
35 | * Microstepping controls should be hardwired.
36 | */
37 | BasicStepperDriver::BasicStepperDriver(short steps, short dir_pin, short step_pin)
38 | :BasicStepperDriver(steps, dir_pin, step_pin, PIN_UNCONNECTED)
39 | {
40 | }
41 |
42 | BasicStepperDriver::BasicStepperDriver(short steps, short dir_pin, short step_pin, short enable_pin)
43 | :motor_steps(steps), dir_pin(dir_pin), step_pin(step_pin), enable_pin(enable_pin)
44 | {
45 | steps_to_cruise = 0;
46 | steps_remaining = 0;
47 | dir_state = 0;
48 | steps_to_brake = 0;
49 | step_pulse = 0;
50 | cruise_step_pulse = 0;
51 | rest = 0;
52 | step_count = 0;
53 | }
54 |
55 | /*
56 | * Initialize pins, calculate timings etc
57 | */
58 | void BasicStepperDriver::begin(float rpm, short microsteps){
59 | pinMode(dir_pin, OUTPUT);
60 | digitalWrite(dir_pin, HIGH);
61 |
62 | pinMode(step_pin, OUTPUT);
63 | digitalWrite(step_pin, LOW);
64 |
65 | if IS_CONNECTED(enable_pin){
66 | pinMode(enable_pin, OUTPUT);
67 | disable();
68 | }
69 |
70 | this->rpm = rpm;
71 | setMicrostep(microsteps);
72 |
73 | enable();
74 | }
75 |
76 | /*
77 | * Set target motor RPM (1-200 is a reasonable range)
78 | */
79 | void BasicStepperDriver::setRPM(float rpm){
80 | if (this->rpm == 0){ // begin() has not been called (old 1.0 code)
81 | begin(rpm, microsteps);
82 | }
83 | this->rpm = rpm;
84 | }
85 |
86 | /*
87 | * Set stepping mode (1:microsteps)
88 | * Allowed ranges for BasicStepperDriver are 1:1 to 1:128
89 | */
90 | short BasicStepperDriver::setMicrostep(short microsteps){
91 | for (short ms=1; ms <= getMaxMicrostep(); ms<<=1){
92 | if (microsteps == ms){
93 | this->microsteps = microsteps;
94 | break;
95 | }
96 | }
97 | return this->microsteps;
98 | }
99 |
100 | /*
101 | * Set speed profile - CONSTANT_SPEED, LINEAR_SPEED (accelerated)
102 | * accel and decel are given in [full steps/s^2]
103 | */
104 | void BasicStepperDriver::setSpeedProfile(Mode mode, short accel, short decel){
105 | profile.mode = mode;
106 | profile.accel = accel;
107 | profile.decel = decel;
108 | }
109 | void BasicStepperDriver::setSpeedProfile(struct Profile profile){
110 | this->profile = profile;
111 | }
112 |
113 | /*
114 | * Move the motor a given number of steps.
115 | * positive to move forward, negative to reverse
116 | */
117 | void BasicStepperDriver::move(long steps){
118 | startMove(steps);
119 | while (nextAction());
120 | }
121 | /*
122 | * Move the motor a given number of degrees (1-360)
123 | */
124 | void BasicStepperDriver::rotate(long deg){
125 | move(calcStepsForRotation(deg));
126 | }
127 | /*
128 | * Move the motor with sub-degree precision.
129 | * Note that using this function even once will add 1K to your program size
130 | * due to inclusion of float support.
131 | */
132 | void BasicStepperDriver::rotate(double deg){
133 | move(calcStepsForRotation(deg));
134 | }
135 |
136 | /*
137 | * Set up a new move (calculate and save the parameters)
138 | */
139 | void BasicStepperDriver::startMove(long steps, long time){
140 | float speed;
141 | // set up new move
142 | dir_state = (steps >= 0) ? HIGH : LOW;
143 | last_action_end = 0;
144 | steps_remaining = labs(steps);
145 | step_count = 0;
146 | rest = 0;
147 | switch (profile.mode){
148 | case LINEAR_SPEED:
149 | // speed is in [steps/s]
150 | speed = rpm * motor_steps / 60;
151 | if (time > 0){
152 | // Calculate a new speed to finish in the time requested
153 | float t = time / (1e+6); // convert to seconds
154 | float d = steps_remaining / microsteps; // convert to full steps
155 | float a2 = 1.0 / profile.accel + 1.0 / profile.decel;
156 | float sqrt_candidate = t*t - 2 * a2 * d; // in √b^2-4ac
157 | if (sqrt_candidate >= 0){
158 | speed = stepperMin(speed, (t - (float)sqrt(sqrt_candidate)) / a2);
159 | };
160 | }
161 | // how many microsteps from 0 to target speed
162 | steps_to_cruise = microsteps * (speed * speed / (2 * profile.accel));
163 | // how many microsteps are needed from cruise speed to a full stop
164 | steps_to_brake = steps_to_cruise * profile.accel / profile.decel;
165 | if (steps_remaining < steps_to_cruise + steps_to_brake){
166 | // cannot reach max speed, will need to brake early
167 | steps_to_cruise = steps_remaining * profile.decel / (profile.accel + profile.decel);
168 | steps_to_brake = steps_remaining - steps_to_cruise;
169 | }
170 | // Initial pulse (c0) including error correction factor 0.676 [us]
171 | step_pulse = (1e+6)*0.676*sqrt(2.0f/profile.accel/microsteps);
172 | // Save cruise timing since we will no longer have the calculated target speed later
173 | cruise_step_pulse = 1e+6 / speed / microsteps;
174 | break;
175 |
176 | case CONSTANT_SPEED:
177 | default:
178 | steps_to_cruise = 0;
179 | steps_to_brake = 0;
180 | step_pulse = cruise_step_pulse = STEP_PULSE(motor_steps, microsteps, rpm);
181 | if (time > steps_remaining * step_pulse){
182 | step_pulse = (float)time / steps_remaining;
183 | }
184 | }
185 | }
186 | /*
187 | * Alter a running move by adding/removing steps
188 | * FIXME: This is a naive implementation and it only works well in CRUISING state
189 | */
190 | void BasicStepperDriver::alterMove(long steps){
191 | switch (getCurrentState()){
192 | case ACCELERATING: // this also works but will keep the original speed target
193 | case CRUISING:
194 | if (steps >= 0){
195 | steps_remaining += steps;
196 | } else {
197 | steps_remaining = stepperMax(steps_to_brake, steps_remaining+steps);
198 | };
199 | break;
200 | case DECELERATING:
201 | // would need to start accelerating again -- NOT IMPLEMENTED
202 | break;
203 | case STOPPED:
204 | startMove(steps);
205 | break;
206 | }
207 | }
208 | /*
209 | * Brake early.
210 | */
211 | void BasicStepperDriver::startBrake(void){
212 | switch (getCurrentState()){
213 | case CRUISING: // this applies to both CONSTANT_SPEED and LINEAR_SPEED modes
214 | steps_remaining = steps_to_brake;
215 | break;
216 |
217 | case ACCELERATING:
218 | steps_remaining = step_count * profile.accel / profile.decel;
219 | break;
220 |
221 | default:
222 | break; // nothing to do if already stopped or braking
223 | }
224 | }
225 | /*
226 | * Stop movement immediately and return remaining steps.
227 | */
228 | long BasicStepperDriver::stop(void){
229 | long retval = steps_remaining;
230 | steps_remaining = 0;
231 | return retval;
232 | }
233 | /*
234 | * Return calculated time to complete the given move
235 | */
236 | long BasicStepperDriver::getTimeForMove(long steps){
237 | float t;
238 | long cruise_steps;
239 | float speed;
240 | if (steps == 0){
241 | return 0;
242 | }
243 | switch (profile.mode){
244 | case LINEAR_SPEED:
245 | startMove(steps);
246 | cruise_steps = steps_remaining - steps_to_cruise - steps_to_brake;
247 | speed = rpm * motor_steps / 60; // full steps/s
248 | t = (cruise_steps / (microsteps * speed)) +
249 | sqrt(2.0 * steps_to_cruise / profile.accel / microsteps) +
250 | sqrt(2.0 * steps_to_brake / profile.decel / microsteps);
251 | t *= (1e+6); // seconds -> micros
252 | break;
253 | case CONSTANT_SPEED:
254 | default:
255 | t = steps * STEP_PULSE(motor_steps, microsteps, rpm);
256 | }
257 | return round(t);
258 | }
259 | /*
260 | * Move the motor an integer number of degrees (360 = full rotation)
261 | * This has poor precision for small amounts, since step is usually 1.8deg
262 | */
263 | void BasicStepperDriver::startRotate(long deg){
264 | startMove(calcStepsForRotation(deg));
265 | }
266 | /*
267 | * Move the motor with sub-degree precision.
268 | * Note that calling this function will increase program size substantially
269 | * due to inclusion of float support.
270 | */
271 | void BasicStepperDriver::startRotate(double deg){
272 | startMove(calcStepsForRotation(deg));
273 | }
274 |
275 | /*
276 | * calculate the interval til the next pulse
277 | */
278 | void BasicStepperDriver::calcStepPulse(void){
279 | if (steps_remaining <= 0){ // this should not happen, but avoids strange calculations
280 | return;
281 | }
282 | steps_remaining--;
283 | step_count++;
284 |
285 | if (profile.mode == LINEAR_SPEED){
286 | switch (getCurrentState()){
287 | case ACCELERATING:
288 | if (step_count < steps_to_cruise){
289 | step_pulse = step_pulse - (2*step_pulse+rest)/(4*step_count+1);
290 | rest = (2*step_pulse+rest) % (4*step_count+1);
291 | } else {
292 | // The series approximates target, set the final value to what it should be instead
293 | step_pulse = cruise_step_pulse;
294 | rest = 0;
295 | }
296 | break;
297 |
298 | case DECELERATING:
299 | step_pulse = step_pulse - (2*step_pulse+rest)/(-4*steps_remaining+1);
300 | rest = (2*step_pulse+rest) % (-4*steps_remaining+1);
301 | break;
302 |
303 | default:
304 | break; // no speed changes
305 | }
306 | }
307 | }
308 | /*
309 | * Yield to step control
310 | * Toggle step and return time until next change is needed (micros)
311 | */
312 | long BasicStepperDriver::nextAction(void){
313 | if (steps_remaining > 0){
314 | delayMicros(next_action_interval, last_action_end);
315 | /*
316 | * DIR pin is sampled on rising STEP edge, so it is set first
317 | */
318 | digitalWrite(dir_pin, dir_state);
319 | digitalWrite(step_pin, HIGH);
320 | unsigned m = micros();
321 | unsigned long pulse = step_pulse; // save value because calcStepPulse() will overwrite it
322 | calcStepPulse();
323 | // We should pull HIGH for at least 1-2us (step_high_min)
324 | delayMicros(step_high_min);
325 | digitalWrite(step_pin, LOW);
326 | // account for calcStepPulse() execution time; sets ceiling for max rpm on slower MCUs
327 | last_action_end = micros();
328 | m = last_action_end - m;
329 | next_action_interval = (pulse > m) ? pulse - m : 1;
330 | } else {
331 | // end of move
332 | last_action_end = 0;
333 | next_action_interval = 0;
334 | }
335 | return next_action_interval;
336 | }
337 |
338 | enum BasicStepperDriver::State BasicStepperDriver::getCurrentState(void){
339 | enum State state;
340 | if (steps_remaining <= 0){
341 | state = STOPPED;
342 | } else {
343 | if (steps_remaining <= steps_to_brake){
344 | state = DECELERATING;
345 | } else if (step_count <= steps_to_cruise){
346 | state = ACCELERATING;
347 | } else {
348 | state = CRUISING;
349 | }
350 | }
351 | return state;
352 | }
353 | /*
354 | * Configure which logic state on ENABLE pin means active
355 | * when using SLEEP (default) this is active HIGH
356 | */
357 | void BasicStepperDriver::setEnableActiveState(short state){
358 | enable_active_state = state;
359 | }
360 | /*
361 | * Enable/Disable the motor by setting a digital flag
362 | */
363 | void BasicStepperDriver::enable(void){
364 | if IS_CONNECTED(enable_pin){
365 | digitalWrite(enable_pin, enable_active_state);
366 | };
367 | delayMicros(2);
368 | }
369 |
370 | void BasicStepperDriver::disable(void){
371 | if IS_CONNECTED(enable_pin){
372 | digitalWrite(enable_pin, (enable_active_state == HIGH) ? LOW : HIGH);
373 | }
374 | }
375 |
376 | short BasicStepperDriver::getMaxMicrostep(){
377 | return BasicStepperDriver::MAX_MICROSTEP;
378 | }
379 |
--------------------------------------------------------------------------------
/src/BasicStepperDriver.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Generic Stepper Motor Driver Driver
3 | * Indexer mode only.
4 | *
5 | * Copyright (C)2015-2018 Laurentiu Badea
6 | *
7 | * This file may be redistributed under the terms of the MIT license.
8 | * A copy of this license has been included with this distribution in the file LICENSE.
9 | */
10 | #ifndef STEPPER_DRIVER_BASE_H
11 | #define STEPPER_DRIVER_BASE_H
12 | #include
13 |
14 | // used internally by the library to mark unconnected pins
15 | #define PIN_UNCONNECTED -1
16 | #define IS_CONNECTED(pin) (pin != PIN_UNCONNECTED)
17 |
18 | /*
19 | * calculate the step pulse in microseconds for a given rpm value.
20 | * 60[s/min] * 1000000[us/s] / microsteps / steps / rpm
21 | */
22 | #define STEP_PULSE(steps, microsteps, rpm) (60.0*1000000L/steps/microsteps/rpm)
23 |
24 | // don't call yield if we have a wait shorter than this
25 | #define MIN_YIELD_MICROS 50
26 |
27 | /*
28 | * Basic Stepper Driver class.
29 | * Microstepping level should be externally controlled or hardwired.
30 | */
31 | class BasicStepperDriver {
32 | public:
33 | enum Mode {CONSTANT_SPEED, LINEAR_SPEED};
34 | enum State {STOPPED, ACCELERATING, CRUISING, DECELERATING};
35 | struct Profile {
36 | Mode mode = CONSTANT_SPEED;
37 | short accel = 1000; // acceleration [steps/s^2]
38 | short decel = 1000; // deceleration [steps/s^2]
39 | };
40 | static inline void delayMicros(unsigned long delay_us, unsigned long start_us = 0){
41 | if (delay_us){
42 | if (!start_us){
43 | start_us = micros();
44 | }
45 | if (delay_us > MIN_YIELD_MICROS){
46 | yield();
47 | }
48 | // See https://www.gammon.com.au/millis
49 | while (micros() - start_us < delay_us);
50 | }
51 | }
52 |
53 | private:
54 | // calculation remainder to be fed into successive steps to increase accuracy (Atmel DOC8017)
55 | long rest;
56 | unsigned long last_action_end = 0;
57 | unsigned long next_action_interval = 0;
58 |
59 | protected:
60 | /*
61 | * Motor Configuration
62 | */
63 | short motor_steps; // motor steps per revolution (usually 200)
64 |
65 | /*
66 | * Driver Configuration
67 | */
68 | short dir_pin;
69 | short step_pin;
70 | short enable_pin = PIN_UNCONNECTED;
71 | short enable_active_state = HIGH;
72 | // Get max microsteps supported by the device
73 | virtual short getMaxMicrostep();
74 | // current microstep level (1,2,4,8,...), must be < getMaxMicrostep()
75 | short microsteps = 1;
76 | // tWH(STEP) pulse duration, STEP high, min value (us)
77 | static const int step_high_min = 1;
78 | // tWL(STEP) pulse duration, STEP low, min value (us)
79 | static const int step_low_min = 1;
80 | // tWAKE wakeup time, nSLEEP inactive to STEP (us)
81 | static const int wakeup_time = 0;
82 |
83 | float rpm = 0;
84 |
85 | /*
86 | * Movement state
87 | */
88 | struct Profile profile;
89 |
90 | long step_count; // current position
91 | long steps_remaining; // to complete the current move (absolute value)
92 | long steps_to_cruise; // steps to reach cruising (max) rpm
93 | long steps_to_brake; // steps needed to come to a full stop
94 | long step_pulse; // step pulse duration (microseconds)
95 | long cruise_step_pulse; // step pulse duration for constant speed section (max rpm)
96 |
97 | // DIR pin state
98 | short dir_state;
99 |
100 | void calcStepPulse(void);
101 |
102 | // this is internal because one can call the start methods while CRUISING to get here
103 | void alterMove(long steps);
104 |
105 | private:
106 | // microstep range (1, 16, 32 etc)
107 | static const short MAX_MICROSTEP = 128;
108 |
109 | public:
110 | /*
111 | * Basic connection: DIR, STEP are connected.
112 | */
113 | BasicStepperDriver(short steps, short dir_pin, short step_pin);
114 | BasicStepperDriver(short steps, short dir_pin, short step_pin, short enable_pin);
115 | /*
116 | * Initialize pins, calculate timings etc
117 | */
118 | void begin(float rpm=60, short microsteps=1);
119 | /*
120 | * Set current microstep level, 1=full speed, 32=fine microstepping
121 | * Returns new level or previous level if value out of range
122 | */
123 | virtual short setMicrostep(short microsteps);
124 | short getMicrostep(void){
125 | return microsteps;
126 | }
127 | short getSteps(void){
128 | return motor_steps;
129 | }
130 | /*
131 | * Set target motor RPM (1-200 is a reasonable range)
132 | */
133 | void setRPM(float rpm);
134 | float getRPM(void){
135 | return rpm;
136 | };
137 | float getCurrentRPM(void){
138 | return (60.0*1000000L / step_pulse / microsteps / motor_steps);
139 | }
140 | /*
141 | * Set speed profile - CONSTANT_SPEED, LINEAR_SPEED (accelerated)
142 | * accel and decel are given in [full steps/s^2]
143 | */
144 | void setSpeedProfile(Mode mode, short accel=1000, short decel=1000);
145 | void setSpeedProfile(struct Profile profile);
146 | struct Profile getSpeedProfile(void){
147 | return profile;
148 | }
149 | short getAcceleration(void){
150 | return profile.accel;
151 | }
152 | short getDeceleration(void){
153 | return profile.decel;
154 | }
155 | /*
156 | * Move the motor a given number of steps.
157 | * positive to move forward, negative to reverse
158 | */
159 | void move(long steps);
160 | /*
161 | * Rotate the motor a given number of degrees (1-360)
162 | */
163 | void rotate(long deg);
164 | inline void rotate(int deg){
165 | rotate((long)deg);
166 | };
167 | /*
168 | * Rotate using a float or double for increased movement precision.
169 | */
170 | void rotate(double deg);
171 | /*
172 | * Configure which logic state on ENABLE pin means active
173 | * when using SLEEP (default) this is active HIGH
174 | */
175 | void setEnableActiveState(short state);
176 | /*
177 | * Turn off/on motor to allow the motor to be moved by hand/hold the position in place
178 | */
179 | virtual void enable(void);
180 | virtual void disable(void);
181 | /*
182 | * Methods for non-blocking mode.
183 | * They use more code but allow doing other operations between impulses.
184 | * The flow has two parts - start/initiate followed by looping with nextAction.
185 | * See NonBlocking example.
186 | */
187 | /*
188 | * Initiate a move over known distance (calculate and save the parameters)
189 | * Pick just one based on move type and distance type.
190 | * If time (microseconds) is given, the driver will attempt to execute the move in exactly that time
191 | * by altering rpm for this move only (up to preset rpm).
192 | */
193 | void startMove(long steps, long time=0);
194 | inline void startRotate(int deg){
195 | startRotate((long)deg);
196 | };
197 | void startRotate(long deg);
198 | void startRotate(double deg);
199 | /*
200 | * Toggle step at the right time and return time until next change is needed (micros)
201 | */
202 | long nextAction(void);
203 | /*
204 | * Optionally, call this to begin braking (and then stop) early
205 | * For constant speed, this is the same as stop()
206 | */
207 | void startBrake(void);
208 | /*
209 | * Immediate stop
210 | * Returns the number of steps remaining.
211 | */
212 | long stop(void);
213 | /*
214 | * State querying
215 | */
216 | enum State getCurrentState(void);
217 | /*
218 | * Get the number of completed steps so far.
219 | * This is always a positive number
220 | */
221 | long getStepsCompleted(void){
222 | return step_count;
223 | }
224 | /*
225 | * Get the number of steps remaining to complete the move
226 | * This is always a positive number
227 | */
228 | long getStepsRemaining(void){
229 | return steps_remaining;
230 | }
231 | /*
232 | * Get movement direction: forward +1, back -1
233 | */
234 | int getDirection(void){
235 | return (dir_state == HIGH) ? 1 : -1;
236 | }
237 | /*
238 | * Return calculated time to complete the given move
239 | */
240 | long getTimeForMove(long steps);
241 | /*
242 | * Calculate steps needed to rotate requested angle, given in degrees
243 | */
244 | long calcStepsForRotation(long deg){
245 | return deg * motor_steps * (long)microsteps / 360;
246 | }
247 | long calcStepsForRotation(double deg){
248 | return deg * motor_steps * microsteps / 360;
249 | }
250 | };
251 | #endif // STEPPER_DRIVER_BASE_H
252 |
--------------------------------------------------------------------------------
/src/DRV8825.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | * DRV8825 - Stepper Motor Driver Driver
3 | * Indexer mode only.
4 |
5 | * Copyright (C)2015 Laurentiu Badea
6 | *
7 | * This file may be redistributed under the terms of the MIT license.
8 | * A copy of this license has been included with this distribution in the file LICENSE.
9 | */
10 | #include "DRV8825.h"
11 |
12 | /*
13 | * Microstepping resolution truth table (Page 13 of DRV8825 pdf)
14 | * 0bMODE2,MODE1,MODE0 for 1,2,4,8,16,32 microsteps
15 | */
16 | const uint8_t DRV8825::MS_TABLE[] = {0b000, 0b001, 0b010, 0b011, 0b100, 0b111};
17 |
18 | DRV8825::DRV8825(short steps, short dir_pin, short step_pin)
19 | :A4988(steps, dir_pin, step_pin)
20 | {}
21 |
22 | DRV8825::DRV8825(short steps, short dir_pin, short step_pin, short enable_pin)
23 | :A4988(steps, dir_pin, step_pin, enable_pin)
24 | {}
25 |
26 | /*
27 | * A4988-DRV8825 Compatibility map: MS1-MODE0, MS2-MODE1, MS3-MODE2
28 | */
29 | DRV8825::DRV8825(short steps, short dir_pin, short step_pin, short mode0_pin, short mode1_pin, short mode2_pin)
30 | :A4988(steps, dir_pin, step_pin, mode0_pin, mode1_pin, mode2_pin)
31 | {}
32 |
33 | DRV8825::DRV8825(short steps, short dir_pin, short step_pin, short enable_pin, short mode0_pin, short mode1_pin, short mode2_pin)
34 | :A4988(steps, dir_pin, step_pin, enable_pin, mode0_pin, mode1_pin, mode2_pin)
35 | {}
36 |
37 | const uint8_t* DRV8825::getMicrostepTable()
38 | {
39 | return (uint8_t*)DRV8825::MS_TABLE;
40 | }
41 |
42 | size_t DRV8825::getMicrostepTableSize()
43 | {
44 | return sizeof(DRV8825::MS_TABLE);
45 | }
46 |
47 | short DRV8825::getMaxMicrostep(){
48 | return DRV8825::MAX_MICROSTEP;
49 | }
50 |
--------------------------------------------------------------------------------
/src/DRV8825.h:
--------------------------------------------------------------------------------
1 | /*
2 | * DRV8825 - Stepper Motor Driver Driver (A4988-compatible)
3 | * Indexer mode only.
4 | *
5 | * Copyright (C)2015 Laurentiu Badea
6 | *
7 | * This file may be redistributed under the terms of the MIT license.
8 | * A copy of this license has been included with this distribution in the file LICENSE.
9 | */
10 | #ifndef DRV8825_H
11 | #define DRV8825_H
12 | #include
13 | #include "A4988.h"
14 |
15 | class DRV8825 : public A4988 {
16 | protected:
17 | static const uint8_t MS_TABLE[];
18 | // tWH(STEP) pulse duration, STEP high, min value (1.9us)
19 | static const int step_high_min = 2;
20 | // tWL(STEP) pulse duration, STEP low, min value (1.9us)
21 | static const int step_low_min = 2;
22 | // tWAKE wakeup time, nSLEEP inactive to STEP (1000us)
23 | static const int wakeup_time = 1700;
24 | // also 650ns between ENBL/DIR/MODEx changes and STEP HIGH
25 |
26 | // Get the microstep table
27 | const uint8_t* getMicrostepTable() override;
28 | size_t getMicrostepTableSize() override;
29 |
30 | // Get max microsteps supported by the device
31 | short getMaxMicrostep() override;
32 |
33 | private:
34 | // microstep range (1, 16, 32 etc)
35 | static const short MAX_MICROSTEP = 32;
36 |
37 | public:
38 | DRV8825(short steps, short dir_pin, short step_pin);
39 | DRV8825(short steps, short dir_pin, short step_pin, short enable_pin);
40 | DRV8825(short steps, short dir_pin, short step_pin, short mode0_pin, short mode1_pin, short mode2_pin);
41 | DRV8825(short steps, short dir_pin, short step_pin, short enable_pin, short mode0_pin, short mode1_pin, short mode2_pin);
42 | };
43 | #endif // DRV8825_H
44 |
--------------------------------------------------------------------------------
/src/DRV8834.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | * DRV8834 - LV Stepper Motor Driver Driver (A4988-compatible - mostly)
3 | * Indexer mode only.
4 |
5 | * Copyright (C)2015 Laurentiu Badea
6 | *
7 | * This file may be redistributed under the terms of the MIT license.
8 | * A copy of this license has been included with this distribution in the file LICENSE.
9 | */
10 | #include "DRV8834.h"
11 |
12 | /*
13 | * Basic connection: only DIR, STEP are connected.
14 | * Microstepping controls should be hardwired.
15 | */
16 | DRV8834::DRV8834(short steps, short dir_pin, short step_pin)
17 | :BasicStepperDriver(steps, dir_pin, step_pin)
18 | {}
19 |
20 | DRV8834::DRV8834(short steps, short dir_pin, short step_pin, short enable_pin)
21 | :BasicStepperDriver(steps, dir_pin, step_pin, enable_pin)
22 | {}
23 |
24 | /*
25 | * Fully wired. All the necessary control pins for DRV8834 are connected.
26 | */
27 | DRV8834::DRV8834(short steps, short dir_pin, short step_pin, short m0_pin, short m1_pin)
28 | :BasicStepperDriver(steps, dir_pin, step_pin), m0_pin(m0_pin), m1_pin(m1_pin)
29 | {}
30 |
31 | DRV8834::DRV8834(short steps, short dir_pin, short step_pin, short enable_pin, short m0_pin, short m1_pin)
32 | :BasicStepperDriver(steps, dir_pin, step_pin, enable_pin), m0_pin(m0_pin), m1_pin(m1_pin)
33 | {}
34 |
35 | /*
36 | * Set microstepping mode (1:divisor)
37 | * Allowed ranges for DRV8834 are 1:1 to 1:32
38 | * If the control pins are not connected, we recalculate the timing only
39 | *
40 | */
41 | short DRV8834::setMicrostep(short microsteps){
42 | BasicStepperDriver::setMicrostep(microsteps);
43 |
44 | if (!IS_CONNECTED(m0_pin) || !IS_CONNECTED(m1_pin)){
45 | return this->microsteps;
46 | }
47 |
48 | /*
49 | * Step mode truth table
50 | * M1 M0 step mode
51 | * 0 0 1
52 | * 0 1 2
53 | * 0 Z 4
54 | * 1 0 8
55 | * 1 1 16
56 | * 1 Z 32
57 | *
58 | * Z = high impedance mode (M0 is tri-state)
59 | */
60 |
61 | pinMode(m1_pin, OUTPUT);
62 | digitalWrite(m1_pin, (this->microsteps < 8) ? LOW : HIGH);
63 |
64 | switch(this->microsteps){
65 | case 1:
66 | case 8:
67 | pinMode(m0_pin, OUTPUT);
68 | digitalWrite(m0_pin, LOW);
69 | break;
70 | case 2:
71 | case 16:
72 | pinMode(m0_pin, OUTPUT);
73 | digitalWrite(m0_pin, HIGH);
74 | break;
75 | case 4:
76 | case 32:
77 | pinMode(m0_pin, INPUT); // Z - high impedance
78 | break;
79 | }
80 | return this->microsteps;
81 | }
82 |
83 | short DRV8834::getMaxMicrostep(){
84 | return DRV8834::MAX_MICROSTEP;
85 | }
86 |
--------------------------------------------------------------------------------
/src/DRV8834.h:
--------------------------------------------------------------------------------
1 | /*
2 | * DRV8834 - LV Stepper Motor Driver Driver (A4988-compatible - mostly)
3 | * Indexer mode only.
4 | *
5 | * Copyright (C)2015 Laurentiu Badea
6 | *
7 | * This file may be redistributed under the terms of the MIT license.
8 | * A copy of this license has been included with this distribution in the file LICENSE.
9 | */
10 | #ifndef DRV8834_H
11 | #define DRV8834_H
12 | #include
13 | #include "BasicStepperDriver.h"
14 |
15 | class DRV8834 : public BasicStepperDriver {
16 | protected:
17 | short m0_pin = PIN_UNCONNECTED;
18 | short m1_pin = PIN_UNCONNECTED;
19 | // tWH(STEP) pulse duration, STEP high, min value (1.9us)
20 | static const int step_high_min = 2;
21 | // tWL(STEP) pulse duration, STEP low, min value (1.9us)
22 | static const int step_low_min = 2;
23 | // tWAKE wakeup time, nSLEEP inactive to STEP (1000us)
24 | static const int wakeup_time = 1000;
25 | // also 200ns between ENBL/DIR/Mx changes and STEP HIGH
26 |
27 | // Get max microsteps supported by the device
28 | short getMaxMicrostep() override;
29 |
30 | private:
31 | // microstep range (1, 16, 32 etc)
32 | static const short MAX_MICROSTEP = 32;
33 |
34 | public:
35 | /*
36 | * Basic connection: only DIR, STEP are connected.
37 | * Microstepping controls should be hardwired.
38 | */
39 | DRV8834(short steps, short dir_pin, short step_pin);
40 | DRV8834(short steps, short dir_pin, short step_pin, short enable_pin);
41 | /*
42 | * Fully wired. All the necessary control pins for DRV8834 are connected.
43 | */
44 | DRV8834(short steps, short dir_pin, short step_pin, short m0_pin, short m1_pin);
45 | DRV8834(short steps, short dir_pin, short step_pin, short enable_pin, short m0_pin, short m1_pin);
46 | short setMicrostep(short microsteps) override;
47 | };
48 | #endif // DRV8834_H
49 |
--------------------------------------------------------------------------------
/src/DRV8880.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | * DRV8880 - 2A Stepper Motor Driver with AutoTune and Torque Control
3 | *
4 | * Copyright (C)2017 Laurentiu Badea
5 | *
6 | * This file may be redistributed under the terms of the MIT license.
7 | * A copy of this license has been included with this distribution in the file LICENSE.
8 | */
9 | #include "DRV8880.h"
10 |
11 | /*
12 | * Basic connection: only DIR, STEP are connected.
13 | * Microstepping controls should be hardwired.
14 | */
15 | DRV8880::DRV8880(short steps, short dir_pin, short step_pin)
16 | :BasicStepperDriver(steps, dir_pin, step_pin)
17 | {}
18 |
19 | DRV8880::DRV8880(short steps, short dir_pin, short step_pin, short enable_pin)
20 | :BasicStepperDriver(steps, dir_pin, step_pin, enable_pin)
21 | {}
22 |
23 | /*
24 | * Fully wired. All the necessary control pins for DRV8880 are connected.
25 | */
26 | DRV8880::DRV8880(short steps, short dir_pin, short step_pin, short m0, short m1)
27 | :BasicStepperDriver(steps, dir_pin, step_pin), m0(m0), m1(m1)
28 | {}
29 |
30 | DRV8880::DRV8880(short steps, short dir_pin, short step_pin, short enable_pin, short m0, short m1)
31 | :BasicStepperDriver(steps, dir_pin, step_pin, enable_pin), m0(m0), m1(m1)
32 | {}
33 |
34 | DRV8880::DRV8880(short steps, short dir_pin, short step_pin, short m0, short m1, short trq0, short trq1)
35 | :BasicStepperDriver(steps, dir_pin, step_pin), m0(m0), m1(m1), trq0(trq0), trq1(trq1)
36 | {}
37 |
38 | DRV8880::DRV8880(short steps, short dir_pin, short step_pin, short enable_pin, short m0, short m1, short trq0, short trq1)
39 | :BasicStepperDriver(steps, dir_pin, step_pin, enable_pin), m0(m0), m1(m1), trq0(trq0), trq1(trq1)
40 | {}
41 |
42 | void DRV8880::begin(float rpm, short microsteps){
43 | BasicStepperDriver::begin(rpm, microsteps);
44 | setCurrent(100);
45 | }
46 |
47 | short DRV8880::getMaxMicrostep(){
48 | return DRV8880::MAX_MICROSTEP;
49 | }
50 |
51 | /*
52 | * Set microstepping mode (1:divisor)
53 | * Allowed ranges for DRV8880 are 1:1 to 1:16
54 | * If the control pins are not connected, we recalculate the timing only
55 | */
56 | short DRV8880::setMicrostep(short microsteps){
57 | BasicStepperDriver::setMicrostep(microsteps);
58 |
59 | if (!IS_CONNECTED(m0) || !IS_CONNECTED(m1)){
60 | return this->microsteps;
61 | }
62 |
63 | /*
64 | * Step mode truth table
65 | * M1 M0 step mode
66 | * 0 0 1
67 | * 1 0 2
68 | * 1 1 4
69 | * 0 Z 8
70 | * 1 Z 16
71 | *
72 | * 0 1 2 (non-circular, not implemented)
73 | * Z = high impedance mode (M0 is tri-state)
74 | */
75 |
76 | pinMode(m1, OUTPUT);
77 | pinMode(m0, OUTPUT);
78 | switch(this->microsteps){
79 | case 1:
80 | digitalWrite(m1, LOW);
81 | digitalWrite(m0, LOW);
82 | break;
83 | case 2:
84 | digitalWrite(m1, HIGH);
85 | digitalWrite(m0, LOW);
86 | break;
87 | case 4:
88 | digitalWrite(m1, HIGH);
89 | digitalWrite(m0, HIGH);
90 | break;
91 | case 8:
92 | digitalWrite(m1, LOW);
93 | pinMode(m0, INPUT); // Z - high impedance
94 | break;
95 | case 16:
96 | digitalWrite(m1, HIGH);
97 | pinMode(m0, INPUT); // Z - high impedance
98 | break;
99 | }
100 | return this->microsteps;
101 | }
102 |
103 | void DRV8880::setCurrent(short percent){
104 | /*
105 | * Torque DAC Settings table
106 | * TRQ1 TRQ0 Current scalar
107 | * 1 1 25%
108 | * 1 0 50%
109 | * 0 1 75%
110 | * 0 0 100%
111 | */
112 | if (!IS_CONNECTED(trq1) || !IS_CONNECTED(trq0)){
113 | return;
114 | }
115 | pinMode(trq1, OUTPUT);
116 | pinMode(trq0, OUTPUT);
117 | percent = (100-percent)/25;
118 | digitalWrite(trq1, percent & 2);
119 | digitalWrite(trq0, percent & 1);
120 | }
121 |
122 |
--------------------------------------------------------------------------------
/src/DRV8880.h:
--------------------------------------------------------------------------------
1 | /*
2 | * DRV8880 - 2A Stepper Motor Driver with AutoTune and Torque Control
3 | *
4 | * Copyright (C)2017 Laurentiu Badea
5 | *
6 | * This file may be redistributed under the terms of the MIT license.
7 | * A copy of this license has been included with this distribution in the file LICENSE.
8 | */
9 | #ifndef DRV8880_H
10 | #define DRV8880_H
11 | #include
12 | #include "BasicStepperDriver.h"
13 |
14 | class DRV8880 : public BasicStepperDriver {
15 | protected:
16 | short m0 = PIN_UNCONNECTED;
17 | short m1 = PIN_UNCONNECTED;
18 | short trq0 = PIN_UNCONNECTED;
19 | short trq1 = PIN_UNCONNECTED;
20 | // tWH(STEP) pulse duration, STEP high, min value
21 | static const int step_high_min = 0; // 0.47us
22 | // tWL(STEP) pulse duration, STEP low, min value
23 | static const int step_low_min = 0; // 0.47us
24 | // tWAKE wakeup time, nSLEEP inactive to STEP
25 | static const int wakeup_time = 1500;
26 | // also 200ns between ENBL/DIR/Mx changes and STEP HIGH
27 |
28 | // Get max microsteps supported by the device
29 | short getMaxMicrostep() override;
30 |
31 | private:
32 | // microstep range (1, 16, 32 etc)
33 | static const short MAX_MICROSTEP = 16;
34 |
35 | public:
36 | /*
37 | * Basic connection: only DIR, STEP are connected.
38 | * Microstepping controls should be hardwired.
39 | */
40 | DRV8880(short steps, short dir_pin, short step_pin);
41 | DRV8880(short steps, short dir_pin, short step_pin, short enable_pin);
42 | /*
43 | * DIR, STEP and microstep control M0, M1
44 | */
45 | DRV8880(short steps, short dir_pin, short step_pin, short m0, short m1);
46 | DRV8880(short steps, short dir_pin, short step_pin, short enable_pin, short m0, short m1);
47 | /*
48 | * Fully Wired - DIR, STEP, microstep and current control
49 | */
50 | DRV8880(short steps, short dir_pin, short step_pin, short m0, short m1, short trq0, short trq1);
51 | DRV8880(short steps, short dir_pin, short step_pin, short enable_pin, short m0, short m1, short trq0, short trq1);
52 |
53 | void begin(float rpm=60, short microsteps=1);
54 |
55 | short setMicrostep(short microsteps) override;
56 |
57 | /*
58 | * Torque DAC Control
59 | * current percent value must be 25, 50, 75 or 100.
60 | */
61 | void setCurrent(short percent=100);
62 | };
63 | #endif // DRV8880_H
64 |
--------------------------------------------------------------------------------
/src/MultiDriver.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | * Multi-motor group driver
3 | *
4 | * Copyright (C)2017-2019 Laurentiu Badea
5 | *
6 | * This file may be redistributed under the terms of the MIT license.
7 | * A copy of this license has been included with this distribution in the file LICENSE.
8 | */
9 | #include "MultiDriver.h"
10 |
11 | #define FOREACH_MOTOR(action) for (short i=count-1; i >= 0; i--){action;}
12 |
13 | /*
14 | * Initialize motor parameters
15 | */
16 | void MultiDriver::startMove(long steps1, long steps2, long steps3){
17 | long steps[3] = {steps1, steps2, steps3};
18 | /*
19 | * Initialize state for all active motors
20 | */
21 | FOREACH_MOTOR(
22 | if (steps[i]){
23 | motors[i]->startMove(steps[i]);
24 | event_timers[i] = 1;
25 | } else {
26 | event_timers[i] = 0;
27 | }
28 | );
29 | ready = false;
30 | last_action_end = 0;
31 | next_action_interval = 1;
32 | }
33 | /*
34 | * Trigger next step action
35 | */
36 | long MultiDriver::nextAction(void){
37 | Motor::delayMicros(next_action_interval, last_action_end);
38 |
39 | // TODO: unroll these loops
40 | // Trigger all the motors that need it
41 | FOREACH_MOTOR(
42 | if (event_timers[i] <= next_action_interval){
43 | event_timers[i] = motors[i]->nextAction();
44 | } else {
45 | event_timers[i] -= next_action_interval;
46 | }
47 | );
48 | last_action_end = micros();
49 |
50 | next_action_interval = 0;
51 | // Find the time when the next pulse needs to fire
52 | // this is the smallest non-zero timer value from all active motors
53 | FOREACH_MOTOR(
54 | if (event_timers[i] > 0 && (event_timers[i] < next_action_interval || next_action_interval == 0)){
55 | next_action_interval = event_timers[i];
56 | }
57 | );
58 | ready = (next_action_interval == 0);
59 |
60 | return next_action_interval;
61 | }
62 | /*
63 | * Optionally, call this to begin braking to stop early
64 | */
65 | void MultiDriver::startBrake(void){
66 | FOREACH_MOTOR(
67 | if (event_timers[i] > 0){
68 | motors[i]->startBrake();
69 | }
70 | )
71 | }
72 | /*
73 | * Immediate stop
74 | * Returns the number of steps remaining.
75 | */
76 | MultiDriver::Steps MultiDriver::stop(void){
77 | Steps retval = Steps();
78 | FOREACH_MOTOR(
79 | if (event_timers[i] > 0){
80 | retval.steps[i] = motors[i]->stop();
81 | }
82 | )
83 | return retval;
84 | }
85 | /*
86 | * State querying
87 | */
88 | bool MultiDriver::isRunning(void){
89 | bool running = false;
90 | FOREACH_MOTOR(
91 | if (motors[i]->getCurrentState() != Motor::STOPPED){
92 | running = true;
93 | break;
94 | }
95 | )
96 | return running;
97 | }
98 |
99 | /*
100 | * Initialize pins, calculate timings etc
101 | */
102 | void MultiDriver::begin(float rpm, short microsteps){
103 | FOREACH_MOTOR(
104 | motors[i]->begin(rpm, microsteps);
105 | )
106 | }
107 |
108 | /*
109 | * Move each motor the requested number of steps, in parallel
110 | * positive to move forward, negative to reverse, 0 to remain still
111 | */
112 | void MultiDriver::move(long steps1, long steps2, long steps3){
113 | startMove(steps1, steps2, steps3);
114 | while (!ready){
115 | nextAction();
116 | }
117 | }
118 |
119 | #define CALC_STEPS(i, deg) ((motors[i] && deg) ? motors[i]->calcStepsForRotation(deg) : 0)
120 | void MultiDriver::rotate(long deg1, long deg2, long deg3){
121 | move(CALC_STEPS(0, deg1), CALC_STEPS(1, deg2), CALC_STEPS(2, deg3));
122 | }
123 |
124 | void MultiDriver::rotate(double deg1, double deg2, double deg3){
125 | move(CALC_STEPS(0, deg1), CALC_STEPS(1, deg2), CALC_STEPS(2, deg3));
126 | }
127 |
128 | void MultiDriver::startRotate(long deg1, long deg2, long deg3){
129 | startMove(CALC_STEPS(0, deg1), CALC_STEPS(1, deg2), CALC_STEPS(2, deg3));
130 | }
131 |
132 | void MultiDriver::startRotate(double deg1, double deg2, double deg3){
133 | startMove(CALC_STEPS(0, deg1), CALC_STEPS(1, deg2), CALC_STEPS(2, deg3));
134 | }
135 |
136 | void MultiDriver::setMicrostep(unsigned microsteps){
137 | FOREACH_MOTOR(motors[i]->setMicrostep(microsteps));
138 | }
139 |
140 | void MultiDriver::setRPM(float rpm){
141 | FOREACH_MOTOR(motors[i]->setRPM(rpm));
142 | }
143 |
144 | void MultiDriver::enable(void){
145 | FOREACH_MOTOR(motors[i]->enable());
146 | }
147 | void MultiDriver::disable(void){
148 | FOREACH_MOTOR(motors[i]->disable());
149 | }
150 |
--------------------------------------------------------------------------------
/src/MultiDriver.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Multi-motor group driver
3 | *
4 | * Copyright (C)2017 Laurentiu Badea
5 | *
6 | * This file may be redistributed under the terms of the MIT license.
7 | * A copy of this license has been included with this distribution in the file LICENSE.
8 | */
9 | #ifndef MULTI_DRIVER_H
10 | #define MULTI_DRIVER_H
11 | #include
12 | #include "BasicStepperDriver.h"
13 |
14 | #define MAX_MOTORS 3 // a reasonable but arbitrary limit
15 | #define Motor BasicStepperDriver
16 | /*
17 | * Multi-motor group driver class.
18 | */
19 | class MultiDriver {
20 | protected:
21 | /*
22 | * Configuration
23 | */
24 | unsigned short count;
25 | Motor* const *motors;
26 | /*
27 | * Generic initializer, will be called by the others
28 | */
29 | MultiDriver(const unsigned short count, Motor* const *motors)
30 | :count(count), motors(motors)
31 | {};
32 |
33 | /*
34 | * Movement state
35 | */
36 | // ready to start a new move
37 | bool ready = true;
38 | // when next state change is due for each motor
39 | unsigned long event_timers[MAX_MOTORS];
40 | unsigned long next_action_interval = 0;
41 | unsigned long last_action_end = 0;
42 |
43 | public:
44 | struct Steps {
45 | long steps[3];
46 | };
47 | /*
48 | * Two-motor setup
49 | */
50 | MultiDriver(Motor& motor1, Motor& motor2)
51 | :MultiDriver(2, new Motor* const[2]{&motor1, &motor2})
52 | {};
53 | /*
54 | * Three-motor setup (X, Y, Z for example)
55 | */
56 | MultiDriver(Motor& motor1, Motor& motor2, Motor& motor3)
57 | :MultiDriver(3, new Motor* const[3]{&motor1, &motor2, &motor3})
58 | {};
59 | unsigned short getCount(void){
60 | return count;
61 | }
62 | Motor& getMotor(short index){
63 | return *motors[index];
64 | }
65 | /*
66 | * Initialize pins, calculate timings etc
67 | */
68 | void begin(float rpm=60, short microsteps=1);
69 | /*
70 | * Move the motors a given number of steps.
71 | * positive to move forward, negative to reverse
72 | */
73 | void move(long steps1, long steps2, long steps3=0);
74 | void rotate(int deg1, int deg2, int deg3=0){
75 | rotate((long)deg1, (long)deg2, (long)deg3);
76 | };
77 | void rotate(long deg1, long deg2, long deg3=0);
78 | void rotate(double deg1, double deg2, double deg3=0);
79 |
80 | /*
81 | * Motor movement with external control of timing
82 | */
83 | virtual void startMove(long steps1, long steps2, long steps3=0);
84 | void startRotate(int deg1, int deg2, int deg3=0){
85 | startRotate((long)deg1, (long)deg2, (long)deg3);
86 | };
87 | void startRotate(long deg1, long deg2, long deg3=0);
88 | void startRotate(double deg1, double deg2, double deg3=0);
89 | /*
90 | * Toggle step and return time until next change is needed (micros)
91 | */
92 | virtual long nextAction(void);
93 | /*
94 | * Optionally, call this to begin braking to stop early
95 | */
96 | void startBrake(void);
97 | /*
98 | * Immediate stop
99 | * Returns the number of steps remaining.
100 | */
101 | Steps stop(void);
102 | /*
103 | * State querying
104 | */
105 | bool isRunning(void);
106 |
107 | /*
108 | * Set the same microstepping level on all motors
109 | */
110 | void setMicrostep(unsigned microsteps);
111 | /*
112 | * Set all motors RPM (1-200 is a reasonable range)
113 | */
114 | void setRPM(float rpm);
115 | /*
116 | * Turn all motors on or off
117 | */
118 | void enable(void);
119 | void disable(void);
120 | };
121 | #endif // MULTI_DRIVER_H
122 |
--------------------------------------------------------------------------------
/src/SyncDriver.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | * Synchronous Multi-motor group driver
3 | * All motors reach their target at the same time.
4 | *
5 | * Copyright (C)2017-2019 Laurentiu Badea
6 | *
7 | * This file may be redistributed under the terms of the MIT license.
8 | * A copy of this license has been included with this distribution in the file LICENSE.
9 | */
10 | #include "SyncDriver.h"
11 |
12 | #define FOREACH_MOTOR(action) for (short i=count-1; i >= 0; i--){action;}
13 |
14 | /*
15 | * Initialize motor parameters
16 | */
17 | void SyncDriver::startMove(long steps1, long steps2, long steps3){
18 | long steps[3] = {steps1, steps2, steps3};
19 | /*
20 | * find which motor would take the longest to finish,
21 | */
22 | long move_time = 0;
23 | FOREACH_MOTOR(
24 | long m = motors[i]->getTimeForMove(labs(steps[i]));
25 | if (m > move_time){
26 | move_time = m;
27 | }
28 | );
29 | /*
30 | * Initialize state for all active motors to complete with micros
31 | */
32 | FOREACH_MOTOR(
33 | if (steps[i]){
34 | motors[i]->startMove(steps[i], move_time);
35 | event_timers[i] = 1;
36 | } else {
37 | event_timers[i] = 0;
38 | }
39 | );
40 | ready = false;
41 | last_action_end = 0;
42 | next_action_interval = 1;
43 | }
44 |
--------------------------------------------------------------------------------
/src/SyncDriver.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Synchronous Multi-motor group driver
3 | * All motors reach their target at the same time.
4 | *
5 | * Copyright (C)2017-2019 Laurentiu Badea
6 | *
7 | * This file may be redistributed under the terms of the MIT license.
8 | * A copy of this license has been included with this distribution in the file LICENSE.
9 | */
10 | #ifndef SYNC_DRIVER_H
11 | #define SYNC_DRIVER_H
12 | #include
13 | #include "MultiDriver.h"
14 |
15 | /*
16 | * Synchronous Multi-motor group driver class.
17 | * This driver sets up timing so all motors reach their target at the same time.
18 | */
19 | class SyncDriver : public MultiDriver {
20 | using MultiDriver::MultiDriver;
21 |
22 | public:
23 |
24 | void startMove(long steps1, long steps2, long steps3=0) override;
25 | };
26 | #endif // SYNC_DRIVER_H
27 |
--------------------------------------------------------------------------------
/test/README:
--------------------------------------------------------------------------------
1 |
2 | This directory is intended for PlatformIO Unit Testing and project tests.
3 |
4 | Unit Testing is a software testing method by which individual units of
5 | source code, sets of one or more MCU program modules together with associated
6 | control data, usage procedures, and operating procedures, are tested to
7 | determine whether they are fit for use. Unit testing finds problems early
8 | in the development cycle.
9 |
10 | More information about PlatformIO Unit Testing:
11 | - https://docs.platformio.org/page/plus/unit-testing.html
12 |
--------------------------------------------------------------------------------