gin66 / FastAccelStepper

A high speed stepper library for Atmega 168/328p (nano), Atmega32u4, Atmega 2560, ESP32, ESP32S2, ESP32S3, ESP32C3, ESP32C6 and Atmel SAM Due
MIT License
316 stars 73 forks source link
a4988 acceleration arduino atmega328 avr delay driver-ic esp32 esp32-arduino highspeed motor nano platformio sam stepper stepper-motor tested

BE AWARE: ARDUINO LIBRARY MANAGER IS BROKEN AND 0.29.x/0.30.x with x>0 do not show.

No issue with platformio. Check the related issue for the arduino library manager

arduino-library-badge

FastAccelStepper

GitHub tag PlatformIO Registry arduino-library-badge

Run tests Simvar tests

Matrix build for arduino using platformio (esp32, esp32c3, atmega328,...)

Build examples

Matrix build for espidf using platformio

Build espidf

Build for esp32 with tasmota

`V2_0_15`

Arduino and esp32

Arduino core v3.0.x are using esp-idf v5.0 up to v5.1 and FastAccelStepper will fail to compile.

Arduino core 3.1.0 will support ESP-IDF V5.3.0 (based on RC1)

Overview

This is a high speed alternative for the AccelStepper library. Supported are avr (ATmega 168/328/P, ATmega2560, ATmega32u4), esp32, esp32s2, esp32s3, esp32c3, esp32c6 and atmelsam due.

The stepper motors should be connected via a driver IC (like A4988) with a 1, 2 or 3-wire connection:

FastAccelStepper offers the following features:

Star History

Star History Chart

General behaviour of Moves

AVR ATMega 168/168P/328/328P

AVR ATMega 32u4

AVR ATMega 2560

ESP32

ESP-IDF version 4.x.y:

ESP-IDF version >=5.3.0:

ESP32S2

ESP32S3

ESP-IDF version 4.x.y:

ESP-IDF version >=5.3.0:

ESP32C3

ESP32C6

Atmel SAM Due

Tested with max two stepper motors with 50 kHz step rate by clazarowitz

Usage

The library is in use with A4988, but other driver ICs should work, too.

For the API definition please consult the header file FastAccelStepper.h. Or a generated markdown file

Please check the examples for application and how to use the low level interface. Some info is Issue #86.

The module defines the global variable fas_queue. Do not use or redefine this variable.

Using the high level interface with ramp up/down as in UsageExample.ino.

#include "FastAccelStepper.h"
#include "AVRStepperPins.h" // Only required for AVR controllers

#define dirPinStepper    5
#define enablePinStepper 6
#define stepPinStepper   9

// If using an AVR device use the definitons provided in AVRStepperPins
//    stepPinStepper1A
//
// or even shorter (for 2560 the correct pin on the chosen timer is selected):
//    stepPinStepperA

FastAccelStepperEngine engine = FastAccelStepperEngine();
FastAccelStepper *stepper = NULL;

void setup() {
   engine.init();
   stepper = engine.stepperConnectToPin(stepPinStepper);
   if (stepper) {
      stepper->setDirectionPin(dirPinStepper);
      stepper->setEnablePin(enablePinStepper);
      stepper->setAutoEnable(true);

      stepper->setSpeedInHz(500);       // 500 steps/s
      stepper->setAcceleration(100);    // 100 steps/s²
      stepper->move(1000);
   }
}

void loop() {
}

Few comments to auto enable/disable:

Behind the curtains

AVR ATmega168/328 and Atmega32u4

The timer 1 is used with prescaler 1. With the arduino nano running at 16 MHz, timer overflow interrupts are generated every ~4 ms. This timer overflow interrupt is used for adjusting the speed.

The timer compare unit toggles the step pin from Low to High precisely. The transition High to Low is done in the timer compare interrupt routine, thus the High state is only few us.

After stepper movement is completed, the timer compare unit is disconnected from the step pin. Thus the application could change the state freely, while the stepper is not controlled by this library.

Measurement of the acceleration/deacceleration aka timer overflow interrupt yields: one calculation round needs around 300us. Thus it can keep up with the chosen 10 ms planning ahead time.

AVR ATmega2560

Similar to ATmega328, but instead of timer 1, timer 4 is used.

For users of platformio, the used timer can be changed to either 1, 3, 4 or 5. For e.g. timer module 3 add to platformio.ini under build_flags:

build_flags = -DFAS_TIMER_MODULE=3

or better:

build_flags = -Werror -Wall -DFAS_TIMER_MODULE=3

For arduino users, the same can be done by defining the flag before including the FastAccelStepperEngine.h header (as per info ixil), but apparently to issue #50, this approach does not work for everyone: e.g.

sketch.ino
----------
#include <Arduino.h>
#define FAS_TIMER_MODULE 3
#include <FastAccelStepper.h>
/* ... */

ESP32

ESP-IDF version 4.x.y:

This stepper driver uses mcpwm modules of the esp32: for the first three stepper motors mcpwm0, and mcpwm1 for the steppers four to six. In addition, the pulse counter module is used starting from unit_0 to unit_5. This driver uses the pcnt_isr_service, so unallocated modules can still be used by the application. The mcpwm modules' outputs are fed into the pulse counter by direct gpio-matrix-modification.

For the other stepper motors, the rmt module comes into use.

ESP-IDF version >=5.3.0:

Only rmt module is supported.

All

A note to MIN_CMD_TICKS using mcpwm/pcnt: The current implementation uses one interrupt per command in the command queue. This is much less interrupt rate than for avr. Nevertheless at 200kSteps/s the switch from one command to the next one should be ideally serviced before the next step. This means within 5us. As this cannot be guaranteed, the driver remedies an overrun (at least by design) to deduct the overrun pulses from the next command. The overrun pulses will then be run at the former command's tick rate. For real life stepper application, this should be ok. To be considered for raw access: Do not run many steps at high rate e.g. 200kSteps/s followed by a pause.

What are the differences between mcpwm/pcnt and rmt ?

mcpwm/pcnt rmt
Interrupt rate/stepper one interrupt per command min: one interrupt per command, max: one interrupt per 31 steps at high speed
Required interrupt response at high speed: time between two steps at high speed: time between 31 steps
Module usage 1 or 2 mcpcms, up to 6 channels of pcnt rmt
esp32 notes availabe pcnt modules can be connected no pcnt module used, so can be attached to rmt output as realtime position

If the interrupt load is not an issue, then rmt is the better choice. With rmt the below (multi-axis application) mentioned loss of synchonicity at high speeds can be avoided. The rmt driver is - besides some rmt modules perks - less complex and way more straightforward.

As of now, allocation of steppers on esp32 are: first all 6 mcpwm/pcnt drivers and then the 8 rmt drivers. In future this may be under application control. Starting with 0.29.2, the module can be directly selected on call of stepperConnectToPin(). So the allocation gets more flexible.

One specific note for the rmt: If a direction pin toggle is needed directly after a command with steps, then the driver will add before that direction pin toggle another pause of MIN_CMD_TICKS ticks.

ESP32S2

This stepper driver uses rmt module.

ESP32S3

The ESP32S3's rmt module is similar to esp32c3 with 4 instead of 2 channels and with different register names.

ESP-IDF version 4.x.y:

This stepper driver uses mcpwm/pcnt + rmt modules. Can drive up to 8 motors. Tested with 6 motors (not by me).

ESP-IDF version >=5.3.0:

This stepper driver uses rmt modules. Can drive up to 4 motors.

ESP32C3

This stepper driver uses rmt module and can drive up to 2 motors. Not thoroughly tested, so only experimental support.

ESP32-MINI-1

Compatibility with ESP32-MINI-1: At least mcpwm and pulse counter modules are listed in the datasheet. So there are chances, that this lib works.

Atmel SAM Due

This is supported by clazarowitz

ALL

The used formula is just s = 1/2 a t² = v² / (2 a) with s = steps, a = acceleration, v = speed and t = time. In order to determine the speed for a given step, the calculation is v = sqrt(2 a s). The performed square root is an 8 bit table lookup using log2/pow2. Sufficient exact for this purpose.

For the linear acceleration from/to standstill the used formula is s = 1/2 j t³. The variable j is calculated from acceleration and steps of linear acceleration, which is set by setLinearAcceleration().

The compare interrupt routines use 16bit tick counters, which translates to approx. 4ms. For longer time between pulses, pauses without step output can be added. With this approach the ramp generation supports up to one step per 268s.

The low level command queue for each stepper allows direct speed control - when high level ramp generation is not operating. This allows precise control of the stepper, if the code, generating the commands, can cope with the stepper speed (beware of any Serial.print in your hot path).

The chosen approach has few limitations for esp32. With acceleration = 1 step/s², the maximum speed is approx. 92 kStep/s. The max. supported speed for esp32 will be reachable only with acceleration >= 5 step/s².

Usage for multi-axis applications

For coordinated movement of two or more axis, the current ramp generation will not provide good results. The planning of steps needs to take into consideration max.speed/acceleration of all steppers and eventually the net speed/acceleration of the resulting movement together with its restrictions. Nice example of multi-axis forward planning can be found within the marlin-project. If this kind of multi-dimensional planning is used, then FastAccelStepper is a good solution to execute the raw commands (without ramp generation) with near-synchronous start of involved steppers. With the tick-exact execution of commands, the synchronization will not be lost as long as the command queues are not running out of commands. And for esp32, second requirement is, that the interrupts can be serviced on time (no pulses issued with previous command's pulse period time)

To keep up the synchronization of two steppers please keep in mind:

Note for esp32 rmt driver:

TODO

See project

Arduino

Arduino library manager log

PLATFORMIO

Library on platformio

If you prefer platformio and you are running Linux, then platformio version of the examples are created by executing

ci/build-platformio.sh

This will create a directory pio_dirs, which contains all examples. Can be executed by e.g.

cd pio_dirs/StepperDemo
pio run -e avr --target upload --upload-port /dev/ttyUSB0

ESP-IDF

A CMakeLists.txt is provided to use FastAccelStepper as an ESP-IDF component. Clone it into the components/ directory in the root of your project and build as usual. You must have Arduino available as a component. See this for instructions on how to set that up. Tested as ESP-IDF component on PlatformIO Espressif32 Platform v3.3.2.

For any questions/support please contact gagank1, as I do not use esp-idf

TEST STRATEGY

The library is tested with different kind of tests:

Test sequences from StepperDemo

Short info, what the test sequences, embedded in StepperDemo in the test mode, do:

All those tests have no internal test passed/failed criteria. Those are used for automated tests with ./tests/simavr_based and ./tests/esp32_hw_based. The test pass criteria are: They should run smoothly without hiccups and return to start position.

CHANGELOG

See changelog

ISSUES

Error investigation

In case the stepper does not run smoothly, then StepperDemo contains commands to simulate two type of error causes. For avr the commands are r and e. For esp32 only e can be used.

For avr: cause of long interrupt being blocked can be e.g.:

Especially in interrupt service routines, the digitalRead()/digitalWrite() must be avoided. Alternative solution is described e.g. here: blog, or digitalWriteFast, or fast versions.

This feature of StepperDemo allows to compare non-smooth running stepper in an application with these error types.

Lessons Learned

3rd party videos in action

Found on youtube:

As mentioned by kthod861 in Issue #110:

Contribution