Stepper Motor Examples - Raspberry Pi Pico

The following short Python programs will demonstrate essential operation of the Raspberry Pi Pico board. These assume one or more binary input or output circuits are externally attached. Each can be run by copying the program into code.py on the CIRCUITPY drive offered by the board. The text can be pasted directly from this page, or each file can be downloaded from the CircuitPython sample code folder on this site.

Related Pages

Sample A4988 Stepper Driver Circuit

../_images/Pico-A4988-example.png

Sample stepper driver circuit using an A4988 module. Note that any two GPIO pins may be used to control the driver. Please be careful with the power wiring, motor voltages can destroy the Pico.

A4988 Stepper Driver Demo

Direct download: a4988_demo.py.

  1# a4988.py
  2#
  3# Raspberry Pi Pico - stepper motor driver support
  4#
  5# This module provides a class for controlling an Allegro 4988 stepper motor
  6# driver.  This device can drive one bipolar stepper motor up to 2A per coil
  7# using microstepping current control.
  8
  9# A typical usage requires two digital outputs.  The defaults assumes a Pololu
 10# A4988 stepper driver has been wired up to the Pico as follows:
 11#
 12#   Pico pin 21, GPIO16   -> DIR
 13#   Pico pin 22, GPIO17   -> STEP
 14#   any Pico GND          -> GND
 15
 16# A4988 carrier board: https://www.pololu.com/product/1182
 17
 18# This implementation bit-bangs the step line and so is limited to about 1000
 19# steps/sec as a result of CircuitPython execution speed.  This solution is is
 20# only suitable for low-speed stepper motion.
 21
 22# Likely a better long-term solution will be to use the RP2040 programmable IO
 23# peripheral (PIO) to cycle the step output.
 24
 25################################################################
 26# CircuitPython module documentation:
 27# time       https://circuitpython.readthedocs.io/en/latest/shared-bindings/time/index.html
 28# board      https://circuitpython.readthedocs.io/en/latest/shared-bindings/board/index.html
 29# digitalio  https://circuitpython.readthedocs.io/en/latest/shared-bindings/digitalio/index.html
 30#
 31# Driver lifecycle documentation:
 32# https://circuitpython.readthedocs.io/en/latest/docs/design_guide.html#lifetime-and-contextmanagers
 33#
 34################################################################################
 35# load standard Python modules
 36import time
 37
 38# load the CircuitPython hardware definition module for pin definitions
 39import board
 40
 41# load the CircuitPython GPIO support
 42import digitalio
 43
 44#--------------------------------------------------------------------------------
 45class A4988:
 46    def __init__(self, DIR=board.GP16, STEP=board.GP17):
 47        """This class represents an A4988 stepper motor driver.  It uses two output pins
 48        for direction and step control signals."""
 49
 50        self._dir  = digitalio.DigitalInOut(DIR)
 51        self._step = digitalio.DigitalInOut(STEP)
 52
 53        self._dir.direction  = digitalio.Direction.OUTPUT
 54        self._step.direction = digitalio.Direction.OUTPUT
 55
 56        self._dir.value = False
 57        self._step.value = False
 58
 59    def step(self, forward=True):
 60        """Emit one step pulse, with an optional direction flag."""
 61        self._dir.value = forward
 62
 63        # Create a short pulse on the step pin.  Note that CircuitPython is slow
 64        # enough that normal execution delay is sufficient without actually
 65        # sleeping.
 66        self._step.value = True
 67        # time.sleep(1e-6)
 68        self._step.value = False
 69
 70    def move_sync(self, steps, speed=1000.0):
 71        """Move the stepper motor the signed number of steps forward or backward at the
 72        speed specified in steps per second.  N.B. this function will not return
 73        until the move is done, so it is not compatible with asynchronous event
 74        loops.
 75        """
 76
 77        self._dir.value = (steps >= 0)
 78        time_per_step = 1.0 / speed
 79        for count in range(abs(steps)):
 80            self._step.value = True
 81            # time.sleep(1e-6)
 82            self._step.value = False
 83            time.sleep(time_per_step)
 84
 85    def deinit(self):
 86        """Manage resource release as part of object lifecycle."""
 87        self._dir.deinit()
 88        self._step.deinit()
 89        self._dir  = None
 90        self._step = None
 91
 92    def __enter__(self):
 93        return self
 94
 95    def __exit__(self):
 96        # Automatically deinitializes the hardware when exiting a context.
 97        self.deinit()
 98
 99#--------------------------------------------------------------------------------
100# Stepper motor demonstration.
101
102stepper = A4988()
103print("Starting stepper motor test.")
104
105speed = 200
106
107while True:
108    print(f"Speed: {speed} steps/sec.")
109    stepper.move_sync(800, speed)
110    time.sleep(1.0)
111
112    stepper.move_sync(-800, speed)
113    time.sleep(1.0)
114
115    speed *= 1.2
116    if speed > 2000:
117        speed = 100