Pico C++ library for Allegro A4988 stepper driver IC.
I created a generic stepper driver library, and made a first example: Raspberry Pico stepper driver IC library, with example: Texas Instruments DRV8711. In this post, I create a plug-in replaceable driver for that Allegro IC.
The abstract base class that I created, supports 3 functions:
- init()
- microsteps()
- enable()
Those will also be the functions that the new A4900 supports. While the TI IC in my first post used SPI for all configurations, this IC uses input pins:
image source: allegro a4988 datasheet
The pins that I want to control are the 3 microstep pins, and enable.I don't program sleep and reset. Both are pulled to VDD to disable them.
This is how the class will be used in the code:
import stepper_driver; // wakeup class import a4988_pico; // driver classes and registers // ... const uint n_enable = 6U; const uint ms1 = 7U; const uint ms2 = 8U; const uint ms3 = 9U; // object to manage the a4988 IC used for motor1 using driver_t = a4988_pico::a4988_pico; driver_t driver1(n_enable, ms1, ms2, ms3);
In the firmware, it will now work exactly the same as the TI DRV8711 of the previous post. No code changes needed.
Pico A4988 driver class
This is the class you'd instantiate, if you want to control a A4988 on a Pico:
class a4988_pico: public stepper_driver::stepper_driver { public: a4988_pico( uint n_enable, uint ms1, uint ms2, uint ms3) : n_enable_(n_enable), ms1_(ms1), ms2_(ms2), ms3_(ms3) {} virtual inline bool init() override { init_gpio(); return true; } virtual bool microsteps(unsigned int microsteps) override { unsigned int mode = true; switch (microsteps) { case 1: mode = 0x0000; break; case 2: mode = 0x0001; break; case 4: mode = 0x0002; break; case 8: mode = 0x0003; break; case 16: mode = 0x0004; break; default: assert(false); // develop check unsupported microstep mode = 0x0000; } gpio_put(ms1_, mode & 0b001); gpio_put(ms2_, mode & 0b010); gpio_put(ms1_, mode & 0b100); return true; } virtual void enable(bool enable) override { gpio_put(n_enable_, enable? 0 : 1); } private: uint n_enable_; uint ms1_; uint ms2_; uint ms3_; virtual void init_gpio() { gpio_init(n_enable_); gpio_put(n_enable_, 1); gpio_set_dir(n_enable_, GPIO_OUT); gpio_init(ms1_); gpio_put(ms1_, 0); gpio_set_dir(ms1_, GPIO_OUT); gpio_init(ms2_); gpio_put(ms2_, 0); gpio_set_dir(ms2_, GPIO_OUT); gpio_init(ms3_); gpio_put(ms3_, 0); gpio_set_dir(ms3_, GPIO_OUT); } };
In action
Check the previous (DRV8711) post. This class makes the A4988 behave in exactly the same way.
The protocol is different: GPIO instead of SPI, but the API is identical.
Here are the CMake sections:
include(FetchContent) FetchContent_Declare(stepper GIT_REPOSITORY "https://github.com/jancumps/pio_stepper_lib.git" GIT_TAG "origin/main" ) FetchContent_MakeAvailable(stepper) # start DRIVER IC specific FetchContent_Declare(stepper_driver GIT_REPOSITORY "https://github.com/jancumps/stepper_driver_lib.git" GIT_TAG "origin/main" ) FetchContent_MakeAvailable(stepper_driver) FetchContent_Declare(pico_a4988 GIT_REPOSITORY "https://github.com/jancumps/pico_a4988_lib.git" GIT_TAG "origin/main" # prevent makefile execution (if any) SOURCE_SUBDIR = ) FetchContent_MakeAvailable(pico_a4988) add_library(driver) target_sources(driver PUBLIC FILE_SET cxx_modules TYPE CXX_MODULES FILES ${stepper_driver_SOURCE_DIR}/source/stepper_driver.cpp ${pico_a4988_SOURCE_DIR}/source/a4988_pico.cpp ) target_link_libraries(driver $<COMPILE_ONLY:hardware_gpio>) # end DRIVER IC specific # ... target_link_libraries( your_firmware pico_stdlib hardware_gpio # start DRIVER IC specific driver # end DRIVER IC specific stepper )
project: https://github.com/jancumps/pio_a4988_stepper
driver code (automatically fetched by the project): https://github.com/jancumps/pio_a4988_stepper