element14 Community
element14 Community
    Register Log In
  • Site
  • Search
  • Log In Register
  • Community Hub
    Community Hub
    • What's New on element14
    • Feedback and Support
    • Benefits of Membership
    • Personal Blogs
    • Members Area
    • Achievement Levels
  • Learn
    Learn
    • Ask an Expert
    • eBooks
    • element14 presents
    • Learning Center
    • Tech Spotlight
    • STEM Academy
    • Webinars, Training and Events
    • Learning Groups
  • Technologies
    Technologies
    • 3D Printing
    • FPGA
    • Industrial Automation
    • Internet of Things
    • Power & Energy
    • Sensors
    • Technology Groups
  • Challenges & Projects
    Challenges & Projects
    • Design Challenges
    • element14 presents Projects
    • Project14
    • Arduino Projects
    • Raspberry Pi Projects
    • Project Groups
  • Products
    Products
    • Arduino
    • Avnet Boards Community
    • Dev Tools
    • Manufacturers
    • Multicomp Pro
    • Product Groups
    • Raspberry Pi
    • RoadTests & Reviews
  • Store
    Store
    • Visit Your Store
    • Choose another store...
      • Europe
      •  Austria (German)
      •  Belgium (Dutch, French)
      •  Bulgaria (Bulgarian)
      •  Czech Republic (Czech)
      •  Denmark (Danish)
      •  Estonia (Estonian)
      •  Finland (Finnish)
      •  France (French)
      •  Germany (German)
      •  Hungary (Hungarian)
      •  Ireland
      •  Israel
      •  Italy (Italian)
      •  Latvia (Latvian)
      •  
      •  Lithuania (Lithuanian)
      •  Netherlands (Dutch)
      •  Norway (Norwegian)
      •  Poland (Polish)
      •  Portugal (Portuguese)
      •  Romania (Romanian)
      •  Russia (Russian)
      •  Slovakia (Slovak)
      •  Slovenia (Slovenian)
      •  Spain (Spanish)
      •  Sweden (Swedish)
      •  Switzerland(German, French)
      •  Turkey (Turkish)
      •  United Kingdom
      • Asia Pacific
      •  Australia
      •  China
      •  Hong Kong
      •  India
      •  Korea (Korean)
      •  Malaysia
      •  New Zealand
      •  Philippines
      •  Singapore
      •  Taiwan
      •  Thailand (Thai)
      • Americas
      •  Brazil (Portuguese)
      •  Canada
      •  Mexico (Spanish)
      •  United States
      Can't find the country/region you're looking for? Visit our export site or find a local distributor.
  • Translate
  • Profile
  • Settings
Raspberry Pi
  • Products
  • More
Raspberry Pi
Blog Raspberry PIO stepper library documentation - 1: intro, set up project and simple example
  • Blog
  • Forum
  • Documents
  • Quiz
  • Events
  • Polls
  • Files
  • Members
  • Mentions
  • Sub-Groups
  • Tags
  • More
  • Cancel
  • New
Raspberry Pi requires membership for participation - click to join
Featured Articles
Announcing Pi
Technical Specifications
Raspberry Pi FAQs
Win a Pi
GPIO Pinout
Raspberry Pi Wishlist
Comparison Chart
Quiz
  • Share
  • More
  • Cancel
Group Actions
  • Group RSS
  • More
  • Cancel
Engagement
  • Author Author: Jan Cumps
  • Date Created: 18 Apr 2025 1:01 PM Date Created
  • Views 633 views
  • Likes 4 likes
  • Comments 10 comments
Related
Recommended
  • raspberry
  • pico_pio_stepper_lib
  • pico
  • PIO
  • stepper-motor
  • c++

Raspberry PIO stepper library documentation - 1: intro, set up project and simple example

Jan Cumps
Jan Cumps
18 Apr 2025

Raspberry PIO stepper library (pio_stepper_lib) is a C++ library that runs stepper motors in PIO state machines. It's intended to be easy to integrate and use in Pico projects.

  • 4 motors can be controlled per PIO
  • can be used with any stepper motor driver that supports STEP and DIR operation
  • can handle as many commands as PIO FIFO accepts without waiting (default 8).
  • each command can autonomously handle 2147483647 steps, and the spin direction
  • can notify the calling program when a command has finished
  • can report how many commands it processed

Add the library to your Pico project

The library has its own CMake script. You add it to your own project by fetching it in your CMakeFiles.txt. It is then available to your code as a library.

Fetch and make available

# your CMakeFiles.txt
# ...

include(FetchContent)
FetchContent_Declare(stepper
  GIT_REPOSITORY "https://github.com/jancumps/pio_stepper_lib.git"
  GIT_TAG "origin/main"
)
FetchContent_MakeAvailable(stepper)

# ...

Link with your firmware executable

# add stepper as a library to your executable
add_executable(your_stepper_project)
# ...
target_link_libraries(your_stepper_project
  pico_stdlib
  hardware_gpio
  stepper
)

That's it. When building your project, CMake will fetch the stepper code from GitHub, and build the stepper library.
The look and feel is identical to adding, say, hardware_spi. Something you will be familiar with.
No includes need to be defined, or sources individually added. It's all handled in the few lines in the make script above.
This will work if you use and IDE such as VSCode, CLion, Eclipse. And if you build from the Linux or Windows command line.

First project: simple stepper

This Pico firmware example will define a stepper motor, and will make it take 400 steps clockwise.

Resources used

pins

dir: gpio4
step: gpio5

PIO

pio 1
state machine 2
divide pio clock by 16

stepper lib 1 instance of class stepper_controller
other your stepper motor driver IC may require other resources, such as gpio for  enable, reset, ...

Use the library  and create the motor

#include "pico/stdlib.h"

// the PIOs are declared here
// and we select one (or more) of those
// to run the motor state machine(s) on
#include "hardware/pio.h"

import stepper; // PIO stepper lib

// pins
const uint dir = 4U; // implies that step is gpio 5

// config what PIO, SM to use
const auto piostep = pio1;
const uint sm = 2U;

const float clock_divider = 16.0; // works well with full steps

using motor_t = stepper::stepper_controller;
motor_t motor1(piostep, sm);

We now have motor1, that we 'll use in our firmware.

PIO and stepper setup

All PIO activities are handled by the library. You invoke them by taking these steps:

We 'll first tell the motor class to load its firmware into the selected PIO. This is an activity that needs to be done one time for each PIO used.
In this example, we only use PIO1. It can host 4 motors. If you want more motors, you can also use PIO0.
And if you have a Pico 2, there's an additional PIO that enables you to control maximum 12 motors.

Then, we configure each motor. That takes care that the class sets up its PIO state machine and enables it.

void init_pio() {
    // program the pio used for the motors
    // do this only once per used pio
    motor_t::pio_program(piostep);

    // initialise and enable the motor state machine
    motor1.pio_init(dir, clock_divider);
    motor1.enable(true);
}

You call this function at the start of your program. From then on, all is ready to use.

the main function

int main() {
    init_pio();
    motor1.set_delay(4500); // this is fast
    
    motor1.take_steps({400, false}); // 400 steps clockwise
    sleep_ms(2000); // wait for the motor to complete
   
    return 0;
}

motor speed: The delay takes care that we slow down the steps that are sent to the motor driver.
At the start of the program we already slowed down the PIO clock. I set the clock to a frequency that's not crazy, but fast enough to give us good speed finetune resolution.
In main(), I set a particular delay for the next commands. The higher the delay, the slower the motor will run.
With my motor, when I use full steps, I set the divider to 16. When using 8 microsteps per step, I set it to 3.
In both cases, a delay of 4500 is then about the fastest the motor can step, with little load.

classes used

image

where to find the code

The pio_stepper_lib is hosted on GitHub. You don't need to clone or build it, to use the library in your project. CMake does that for you.

A working project, using the Texas Instruments DRV8711 as stepper driver, is also in GitHub.
I took care to clearly define where driver specific code is used, in case your hardware uses another one.
If you like to read up on the example's history, and for wiring information, start here: Stepper Motor Control with Raspberry Pico PIO and DRV8711 driver- Part 1: Hardware Provisioning..
If you get a build error, check the toolchain requirements.

next chapter: get notification when the motor has executed a command

view all posts

  • Sign in to reply
  • Jan Cumps
    Jan Cumps 28 days ago in reply to shabaz

    a few posts that will help:

    • the multiple motor firmware design explained:  Raspberry PIO stepper library documentation - 3: control multiple motors with 1 or more PIOs 
    • clock divider, delay, and stepper frequency explained:  Raspberry PIO stepper library documentation - 4: understand the step frequency 
    • Cancel
    • Vote Up 0 Vote Down
    • Sign in to reply
    • More
    • Cancel
  • Jan Cumps
    Jan Cumps 29 days ago in reply to Jan Cumps

    ... and 2 motors running in parallel at different speeds:

    image

    • Cancel
    • Vote Up 0 Vote Down
    • Sign in to reply
    • More
    • Cancel
  • DAB
    DAB 29 days ago

    Nice update Jan.

    • Cancel
    • Vote Up 0 Vote Down
    • Sign in to reply
    • More
    • Cancel
  • Jan Cumps
    Jan Cumps 29 days ago in reply to Jan Cumps

    Here is a capture of 2 motors running this command set:

        std::array<motor_command, 7> cmd{{
            { {200 * microstep_x, true}, 0},
            { {200 * microstep_x, true}, 1},
            { {300 * microstep_x, false}, 1},
            { {200 * microstep_x, false}, 1},
            { {100 * microstep_x, true}, 1},
            { {200 * microstep_x, false}, 1},
            { {200 * microstep_x, true}, 0}
        }
        };

    single run (both motors same speed, but they can run at different speeds when desired):

    image

    continuous run at different stepper speeds (again both motors at same speed each time)

    image

    You will see that the motors don't run these in order. Each runs all the commands that were sent to them, immediately.*
    If you want things to happen in sequence, then just send the batch you want to have done at a single run, and wait for it to finish before sending a next one.

    I ran the two motors on a different PIO. There's a bug in my code, when 2 state machines on the same PIO fire interrupts. I need to read up on why this happens - it's hard to debug because the machines keep running when you put a breakpoint in the event handler ...

    *that's the demo behaviour. You can write a different program that

    • first does all steps for 1 motor, then for the next one
    • sends all commands until it reaches one for a different motor, and wait until the previous has done, then continue, etc ...
    • waits after every command until it is don then send the next command
    • etc .. etc..
    • Cancel
    • Vote Up 0 Vote Down
    • Sign in to reply
    • More
    • Cancel
  • Jan Cumps
    Jan Cumps 29 days ago in reply to shabaz

    I created an example for 2 motors: https://gist.github.com/jancumps/c66e8af42dc30ee6dfbdfc06aea496e1

    It should run these 7 commands (steps, direction, motor#), across the 2 motors, and wait till both finished.

        std::array<motor_command, 7> cmd{{
            { {200 * microstep_x, true}, 0},
            { {200 * microstep_x, true}, 1},
            { {300 * microstep_x, false}, 0},
            { {200 * microstep_x, false}, 1},
            { {100 * microstep_x, true}, 1},
            { {200 * microstep_x, false}, 0},
            { {200 * microstep_x, true}, 1}
        }
        };


    Then it should run those commands again, at a different speed.

    untested - I only have one motor + driver pair. But I'm testing it with an oscilloscope.

    image

    I marked where the IC should be initialised (if needed), and where the IC should be enabled (if you disable it standard), with // TODO comments.

    • Cancel
    • Vote Up 0 Vote Down
    • Sign in to reply
    • More
    • Cancel
>
element14 Community

element14 is the first online community specifically for engineers. Connect with your peers and get expert answers to your questions.

  • Members
  • Learn
  • Technologies
  • Challenges & Projects
  • Products
  • Store
  • About Us
  • Feedback & Support
  • FAQs
  • Terms of Use
  • Privacy Policy
  • Legal and Copyright Notices
  • Sitemap
  • Cookies

An Avnet Company © 2025 Premier Farnell Limited. All Rights Reserved.

Premier Farnell Ltd, registered in England and Wales (no 00876412), registered office: Farnell House, Forge Lane, Leeds LS12 2NE.

ICP 备案号 10220084.

Follow element14

  • X
  • Facebook
  • linkedin
  • YouTube