element14 Community
element14 Community
    Register Log In
  • Site
  • Search
  • Log In Register
  • About Us
  • 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 OO Library to handle Pico PIO relative interrupts: library design
  • Blog
  • Forum
  • Documents
  • Quiz
  • Events
  • Polls
  • Files
  • Members
  • Mentions
  • Sub-Groups
  • Tags
  • More
  • Cancel
  • New
Join Raspberry Pi to participate - click to join for free!
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: 27 Aug 2025 9:10 PM Date Created
  • Views 311 views
  • Likes 5 likes
  • Comments 4 comments
Related
Recommended
  • interrupt
  • raspberry
  • pico
  • PIO
  • c++

OO Library to handle Pico PIO relative interrupts: library design

Jan Cumps
Jan Cumps
27 Aug 2025
OO Library to handle Pico PIO relative interrupts: library design

This post explains the internal design of  OO Library to handle Pico PIO relative interrupts: usage and example .

I made an Object Oriented library that supports handling PIO interrupts with C++ objects.
Goal: let a PIO state machine call the very C++ object that can manage (or watch) that particular event.

Make PIO interrupts OO savvy

The core of this library is a single class, called pio_irq. it is specialised in configuring PIO interrupt handling, and routing any interrupts to the object that can handle it (handler objects).

  • It knows how to inform a PIO state machine that we're interested in a particular PIO interrupt
    A Pico will only handle a PIO generated interface, if interest is registered. Else it 'll be ignored.
  • It knows what object in your firmware has the duty to react on that interrupt on that PIO state machine. When an interrupt occurs, it will call that object.
    The Pico SDK supports registering a function to handle a PIO interrupt. It's not possible to call a class method though. This library does that translation.

image

How does it do that?

pio_irq will register itself as handler of the interrupts that you want to handle. And it keeps a register of your handler objects.
When an interrupt occurs, it retrieves PIOand state machine from that info. Then looks in the registry if there is a handling object. If yes, it 'll call that object.
It also marks that interrupt as handled.

As developer, you have to do 3 things:

  1. Design a handler class that can be executed. The handler class. The only requirement is that It needs to have operator () overloaded.
  2. tell pio_irq what interrupt number you want to handle, and what class type will handle it. (this is done with a template. see later)
  3. tell the pio_irq what  state machine to watch, and which of the objects in your design needs to be called when that state machine generates the interrupt.

When I use "a state machine" in this post, it means 1 particular state machine. Pico PIOs have 4 state machines. A Pico2, with 2 PIOs, has 8 state machines. The Pico2 has 3 PIOs, hence 12 state machines. pio_irq is able to manage all of them.

template <std::invocable H, uint32_t interrupt_number> class pio_irq {
    static void register_interrupt(uint irq_channel, PIO pio, uint sm, bool enable) // ...
    static bool register_handler(PIO pio, uint sm, H *handler, bool set) // ...

private:
    // keep pointer of objects that serve the state machines
    // 2-D array with slot for all possible state machines: PIO0[0..3], PIO1[0..3], ...
    static std::array<H *, NUM_PIOS * 4> handlers_;
};

The H in the template is the type of your handler class. The interrupt_number template parameter is the interrupt number the class should handle.

The template is generic. To have a working class in your firmware, you just need to bind the template to the parameters you want:

using pio_irq_manager_t = pio_irq::pio_irq<handler_t, 0>;

In that code, 

  • pio_irq_manager_t is the name of your manager
  • handler_t is the type of your handler object
  • 0 is the interrupt number that should be handled

You 'll end up with a static class. One that you don't need to create an object for. You can directly use its methods:

pio_irq_manager_t::register_interrupt(IRQ_CHAN, h.pio, h.sm, true);
pio_irq_manager_t::register_handler(h.pio, h.sm, &h, true);

When you bind the class, it automatically creates an array. That array has the same length as the number of state machines on your Pico (8 or 12). And each slot can hold a pointer to one of your handlers.

static std::array<H *, NUM_PIOS * 4> handlers_;
When you register one of your handler objects, a pointer to it will be stored into the slot that matches the correct state machine. When PIO fires an interrupt, pio_irq unpacks the info, calculates the corresponding index in the handler array, and execute the object referenced at that index.
The logic is a bit involved. I created a post earlier on how to correctly register for a particular state machine / interrupt number. And then how to find what state machine fired that interrupt:Handle Raspberry Pico PIO "relative interrupts" in C++ 
.

You don't have permission to edit metadata of this video.
Edit media
x
image
Upload Preview
image

What are the requirements for your handler class and objects?

There is a single requirement. Your class type has to be invocable. It needs to implement (overload) the operator()(). This is the C++ way to have something that can be executed.

Example:

struct stepper_handler {
    inline void operator()() {
        // .. your code to handle the interrupt that signals the end of doing X steps
    }
};

This is not a complex requirement. You don't need a base class, or inherit from a library class. If you have an existing design, it is straightforward to create a wrapper1 or bridge class.

In the example that I made for the library, my handler class has a counter that keeps stock of how many interrupts it has received, When receiving a new interrupt, it prints that counter.
A stepper motor design would use this mechanism to tell the business logic that the motor has moved the requested amount of steps. The logic can then tell another motor (maybe a press drill) to do its job ...
1 My callback manager OO library is a plug-in usable wrapper, in case you can't have an operator()() your handler class design.

What other things does the library do?

Nothing really. It's focused on the one thing it's designed for: register interest in an interrupt, and have an interrupt handled by the relevant object in your firmware.

There are some internal functions, to register the interrupts correctly, and to parse the interrupt payload and retrieve the state machine that fired it. These are not complex manipulations. But it's hard to get them right by reading the datasheet. All the investigating and testing has been done for this lib.
The origin of this library is a stepper motor lib that I designed. In that library, the PIO interrupt handler is an object embedded of the stepper motor class. While writing that, I learned that the interrupt part is of a similar complexity than the stepper logic in that class. 

By extracting that into its own library, I try to simplify my stepper motor class design. And have a reusable library, helpful in other designs. The end user impact in my stepper motor library will ne 0. Because this lib will still call the same object. But instead of an embedded class managing the interrupt, it 'll be this new library.
In the end, both libs will become simpler, and more broadly usable...

bonus: only accept handler objects that can be executed?

In the old days, you guaranteed that a class had a particular method, by either writing a base class, or an interface. Both require that you inherit from that. Modern C++ can check whether a class has a particular capability, by using constraints.
Constraints are cheap: they have no runtime cost. Everything is evaluated at build time. Nothing is added to the data or code size. If your design fails to meet the constraint, the compilation will fail. 
in this lib, I've used a constraint, to ensure that when you bind the pio_irq template, the handler class has the operator ()(). C++ has the invokable concept (constraint) for that.
template <std::invocable H, uint32_t interrupt_number> class pio_irq { // ...
When you bind this template to a handler class that has the operator overloaded, build will succeed. If you try to make it work with a class that can't be executed (invoked), the compiler will tell you that you are using a type that's not compatible with the invokable concept.

Code

This is the full library code:
module;

#include <array>
#include "hardware/pio.h"
#include <concepts>
#include <utility>

export module pio_irq;

// find for what state machine a relative interrupt was thrown
uint sm_from_interrupt(const uint32_t irq_val, const uint32_t ir) {
    uint i;
    for (i = 0; i < 4; i++) { // should be sm 0 .. 3
        if (irq_val & 1 << i)
        { // is bit set?
            break;
        }
    }
    assert(i != 4); // develop check there has to be a bit
    return i;
}

// calculate relative IRQ flag for a state machine
// 10 (REL): the state machine ID (0…3) is added to the IRQ flag index, by way of
// modulo-4 addition on the two LSBs
inline uint relative_interrupt(const uint32_t ir, const uint sm) {
    uint32_t retval = ir & 0x03; // last 2 bits
    retval += sm;                // add relative value (is sm)
    retval = retval % 4;         // mod 4
    retval |= ir & 0xfffffffc;
    return retval;
}

// utility calculates the index for the object that serves a state machine in handlers_
size_t index_for(PIO pio, uint sm) { return PIO_NUM(pio) * 4 + sm; }

// utility to do math on pio_interrupt_source enum
inline pio_interrupt_source interrupt_source(const pio_interrupt_source is, const uint32_t ir){
    return static_cast<pio_interrupt_source>(std::to_underlying(is) + ir);
}

// creating non-inline wrapper for some API calls, because GCC 15.1 for RISKV complains about exposing local TU

void _pio_set_irq0_source_enabled(PIO pio, pio_interrupt_source_t source, bool enabled) {
    pio_set_irq0_source_enabled(pio, source, enabled);
} 
void _pio_set_irq1_source_enabled(PIO pio, pio_interrupt_source_t source, bool enabled) {
    pio_set_irq1_source_enabled(pio, source, enabled);
}
void _pio_interrupt_clear(PIO pio, uint ir) {
    pio_interrupt_clear(pio, ir);
}

export namespace pio_irq {

/*
PIO interrupts can only call functions without parameters. They can't call object members.
This static embedded class matches interrupts to the relevant object.
These handler objects have to implement the () operation.
*/
// guard that handler H has to support () operator.
template <std::invocable H, uint32_t interrupt_number> class pio_irq {
private:
    pio_irq() = delete; // static class. prevent instantiating.
public:
    static void register_interrupt(uint irq_channel, PIO pio, uint sm, bool enable) {
        assert (irq_channel < 2); // develop check that we use 0 or 1 only
        uint irq_num = PIO0_IRQ_0 + 2 * PIO_NUM(pio) + irq_channel;
        irq_handler_t handler = nullptr;

        if (irq_channel == 0) {
            _pio_set_irq0_source_enabled(pio, interrupt_source(pis_interrupt0, 
                relative_interrupt(interrupt_number, sm)), true);
        } else {
            _pio_set_irq1_source_enabled(pio, interrupt_source(pis_interrupt0, 
                relative_interrupt(interrupt_number, sm)), true);
        }

        switch (PIO_NUM(pio)) {
        case 0:
            handler = interrupt_handler_PIO0;
            break;
        case 1:
            handler = interrupt_handler_PIO1;
            break;
#if (NUM_PIOS > 2) // pico 2       
        case 2:
            handler = interrupt_handler_PIO2;
            break;
#endif            
        }

        irq_add_shared_handler(irq_num, handler, PICO_SHARED_IRQ_HANDLER_DEFAULT_ORDER_PRIORITY );  //Set the handler in the NVIC
        if (enable) {
            irq_set_enabled(irq_num, true);
        }
    }    

    // if an object is currently handling the pio + sm combination, it will
    // be replaced and will no longer receive interrupts
    // return false as warning if an existing combination is replaced
    static bool register_handler(PIO pio, uint sm, H *handler, bool set)      {
        size_t idx = index_for(pio, sm);
        H *old = handlers_[idx];
        handlers_[idx] = set ? handler : nullptr;
        return set ? old == nullptr : true;
    }

private:
    // forwards the interrupt to the surrounding class
    static void interrupt_handler(PIO pio) {
        if (pio->irq == 0U) {
            return; // we can't handle IRQs that don't have sm info
        }
        assert (pio->irq); // there should always be a sm
        uint sm = sm_from_interrupt(pio->irq, interrupt_number);
        // TODO: do I need to retrieve the ir, or is it this->ir? 
        uint ir = relative_interrupt(interrupt_number, sm);
        _pio_interrupt_clear(pio, ir); // TODO: should I clear if there is no handler?
        H *handler =  handlers_[index_for(pio, sm)];
        if (handler != nullptr) {
            (*handler)();
        }
    }

    static inline void interrupt_handler_PIO0() { interrupt_handler(pio0); }
    static inline void interrupt_handler_PIO1() { interrupt_handler(pio1); }
#if (NUM_PIOS > 2) // pico 2
    static inline void interrupt_handler_PIO2() { interrupt_handler(pio2); }
#endif

    // keep pointer of objects that serve the state machines
    // 2-D array with slot for all possible state machines: PIO0[0..3], PIO1[0..3], ...
    static std::array<H *, NUM_PIOS * 4> handlers_;
};

// static data member must be initialised outside of the class, or the linker will not capture it
template <std::invocable H, uint32_t interrupt_number> std::array<H *, NUM_PIOS * 4>  pio_irq<H, interrupt_number>::handlers_;

// lib currently supports 1 (base) class without considerations.
// Behaviour when more than one handler is registered for the same sm/interrupt number combination is undefined. 
// If you use one handler class and one interrupt number, the library guarantees that there will be no conflict.
// else, take care that there's no overlap

} // namespace pio_irq
  • Sign in to reply
  • Jan Cumps
    Jan Cumps 24 days ago

    The main reasons why I built this library:

    • My PIO stepper motor library had this functionality as part of the motor manager class.
      I think that this functionality is useful in other scenarios too.
      Factoring it out to its own reusable library makes sense

    • The class in my PIO stepper motor driver was complex. Complexity of the interrupt handler logic was higher than the core functionality's complexity.
      Factoring out the interrupt logic vastly simplifies the class design (see below)

    stepper class declaration before factoring out the interrupt handling:
    this is the complex design, where the irq handling is part of the logic

    class stepper_callback_controller : public stepper_controller {
    using notifier_t = void (*)(const stepper_callback_controller&); // callback definition
    private:
    
        /*
        PIO interrupts can only call functions without parameters. They can't call object members.
        This static embedded utility class helps matching interrupts to the relevant object.
        */
        class interrupt_manager {
        private:
            interrupt_manager() = delete;  // static class. prevent instantiating.
        public:        
            // if an object is currently handling a pio + sm combination, it will 
            // be replaced and will no longer receive interrupts
            // return false as warning if an existing combination is replaced
            static bool register_stepper(stepper_callback_controller * stepper, bool set);
    
            // PIO API doesn't accept a callback with parameters, so I can't pass the PIO instance
            // provide a parameter-less method for ezach PIO is a reasonable solution
            // only task is to call interrupt_handler() and passing it the PIO indicated in the name.
            // Without overhead: optimised out inrelease code
            static inline void interrupt_handler_PIO0() { interrupt_handler(pio0); }    
            static inline void interrupt_handler_PIO1() { interrupt_handler(pio1); }
    #if (NUM_PIOS > 2) // pico 2       
            static inline void interrupt_handler_PIO2() { interrupt_handler(pio2); }
    #endif        
        private:
            // forwards the interrupt to the surrounding class
            static void interrupt_handler(PIO pio);
            // utility calculates the index for the object staht serves a state machine in steppers_
            static inline size_t index_for(PIO pio, uint sm) { return PIO_NUM(pio) * 4 + sm; }
            // keep pointer of objects that serve the state machines
            // 2-D array with slot for all possible state machines: PIO0[0..3], PIO1[0..3], ...
            static std::array<stepper_callback_controller *, NUM_PIOS * 4> steppers_;
        };   
    public:
        stepper_callback_controller(PIO pio, uint sm) : stepper_controller(pio,sm), commands_(0U),
            callback_(nullptr) { interrupt_manager::register_stepper(this, true); }
        virtual ~stepper_callback_controller() { interrupt_manager::register_stepper(this, false); }
    
        // return commands completed
        inline uint commands() const { return commands_; }
        // reset commands completed to 0
        inline void reset_commands() { commands_ = 0U; }
    
        // register object as interrupt service for its state machine
        void register_pio_interrupt(uint irq_channel, bool enable);
        // user code class to call when a command is finished.
        // Pass immutable reference to object as user info
        inline void on_complete_callback(notifier_t callback) { callback_ = callback; }
    private:
        void handler();
        volatile uint commands_; // volatile: updated by interrupt handler
        notifier_t callback_;
    };

    stepper class declaration after factoring out the interrupt handling:
    this is the simple design, where the irq handling is farmed out to the pio_irq lib
    The embedded class is gone. So are irq related private methods.

    class stepper_callback_controller : public stepper_controller {
    using notifier_t = void (*)(const stepper_callback_controller&); // callback definition
    
    public:
        stepper_callback_controller(PIO pio, uint sm);
        virtual ~stepper_callback_controller();
    
        // return commands completed
        inline uint commands() const { return commands_; }
        // reset commands completed to 0
        inline void reset_commands() { commands_ = 0U; }
    
        // register object as interrupt service for its state machine
        void register_pio_interrupt(uint irq_channel, bool enable);
        // user code class to call when a command is finished.
        // Pass immutable reference to object as user info
        inline void on_complete_callback(notifier_t callback) { callback_ = callback; }
        inline void operator()() {
            commands_ = commands_ + 1;
            if (callback_ != nullptr) { (callback_)( *this); }
        }
    
    private:
        volatile uint commands_; // volatile: updated by interrupt handler
        notifier_t callback_;
    };

    The changes in the class implementation are even more drastic.

    stepper class implementation before:

    bool stepper_callback_controller::interrupt_manager::register_stepper(stepper_callback_controller * stepper, bool set) {
        size_t idx = index_for(stepper->pio_, stepper->sm_);
        stepper_callback_controller *old = steppers_[idx];
        steppers_[idx] = set ? stepper : nullptr;
        return set ? old == nullptr : true;
    }
    
    void stepper_callback_controller::interrupt_manager::interrupt_handler(PIO pio) {
        if (pio->irq == 0U) {
            return; // we can't handle IRQs that don't have sm info
        }
        assert (pio->irq); // there should always be a sm
        uint sm = pio_irq_util::sm_from_interrupt(pio->irq, stepper_PIO_IRQ_DONE);
        stepper_callback_controller *stepper =  steppers_[index_for(pio, sm)];
        if (stepper != nullptr) {
            stepper -> handler();
        }
    }
    
    
    void stepper_callback_controller::register_pio_interrupt(uint irq_channel, bool enable) {
        assert (irq_channel < 2); // develop check that we use 0 or 1 only
        uint irq_num = PIO0_IRQ_0 + 2 * PIO_NUM(pio_) + irq_channel;
        irq_handler_t handler = nullptr;
    
        if (irq_channel == 0) {
            pio_set_irq0_source_enabled(pio_, pio_irq_util::interrupt_source(pis_interrupt0, 
                pio_irq_util::relative_interrupt(stepper_PIO_IRQ_DONE, sm_)), true);
        } else {
            pio_set_irq1_source_enabled(pio_, pio_irq_util::interrupt_source(pis_interrupt0, 
                pio_irq_util::relative_interrupt(stepper_PIO_IRQ_DONE, sm_)), true);
        }
    
        switch (PIO_NUM(pio_)) {
        case 0:
            handler = interrupt_manager::interrupt_handler_PIO0;
            break;
        case 1:
            handler = interrupt_manager::interrupt_handler_PIO1;
            break;
    #if (NUM_PIOS > 2) // pico 2       
        case 2:
            handler = interrupt_manager::interrupt_handler_PIO2;
            break;
    #endif            
        }
    
        irq_add_shared_handler(irq_num, handler, PICO_SHARED_IRQ_HANDLER_DEFAULT_ORDER_PRIORITY );  //Set the handler in the NVIC
        if (enable) {
            irq_set_enabled(irq_num, true);
        }
    }
    
    void stepper_callback_controller::handler() {
        uint irq = pio_->irq;
        uint ir = pio_irq_util::relative_interrupt(stepper_PIO_IRQ_DONE, sm_);
        assert(irq & 1 << sm_); // develop check: interrupt is from the correct state machine
        commands_ = commands_ + 1;
        pio_interrupt_clear(pio_, ir);
        if (callback_ != nullptr) {
            (callback_)( *this);
        }
    }
    
    // initialise static class data
    // static pio program offset
    uint stepper_controller::pio_offset_[NUM_PIOS];
    // static data member must be initialised outside of the class, or the linker will not capture it
    std::array<stepper_callback_controller *, NUM_PIOS * 4> stepper_callback_controller::interrupt_manager::steppers_;

    stepper class implementation after:

    // relative interrupt handler
    using pio_irq_manager_t = pio_irq::pio_irq<stepper_callback_controller, stepper_PIO_IRQ_DONE>;
    
    stepper_callback_controller::stepper_callback_controller(PIO pio, uint sm) : 
            stepper_controller(pio,sm), commands_(0U), callback_(nullptr) { 
        pio_irq_manager_t::register_handler(pio_, sm_, this, true); 
    }
    
    stepper_callback_controller::~stepper_callback_controller() { 
        pio_irq_manager_t::register_handler(pio_, sm_, this, false); 
    }
    
    void stepper_callback_controller::register_pio_interrupt(uint irq_channel, bool enable) {
        pio_irq_manager_t::register_interrupt(irq_channel, pio_, sm_, enable);
    }
    
    // initialise static class data
    // static pio program offset
    uint stepper_controller::pio_offset_[NUM_PIOS];

    utility file gone:
    all PIO interrupt helper functions that were sitting in the PIO stepper lib, have been factored into the pio_irq lib.
    The file pio_irq_util.cpp could be removed from the PIO stepper lib. Making that lib focus better on it's single job of handling stepper motors.

    / calculate relative IRQ flag for a state mchine
    // 10 (REL): the state machine ID (0…3) is added to the IRQ flag index, by way of
    // modulo-4 addition on the two LSBs
    inline uint relative_interrupt(const uint32_t ir, const uint sm) {
        // TODO validate for Pico 2 interrupts 4 .. 7
        uint32_t retval = ir & 0x03; // last 2 bits
        retval += sm; // add relative value (is sm)
        retval = retval % 4; // mod 4
        // TODO most likely I have to restore bits 31..2 here to work with Pico 2
        // but I don't have one to test it
        retval |= ir & 0xfffffffc;
        return retval;
    }
    
    // utility to do math on pio_interrupt_source enum
    inline pio_interrupt_source interrupt_source(const pio_interrupt_source is, const uint32_t ir) {
        return static_cast<pio_interrupt_source>(std::to_underlying(is) + ir);
    }
    
    // find for what state machine a relative interrupt was thrown
    uint sm_from_interrupt(const uint32_t irq_val, const uint32_t ir) {
        // TODO validate for Pico 2 interrupts 4 .. 7
        uint i;
        for (i = 0; i < 4; i++) { // should be sm 0 .. 3
            if (irq_val & 1 << i) { // is bit set?
                    break;
            }
        }
        assert(i != 4); // develop check there has to be a bit
        return i;
    }
    

    GitHub reports a netto removal of 130 lines of code in the PIO stepper library.

    This does not require changes to the firmware that uses the stepper motor library. The pio_irq library dependency is resolved automatically during the CMake execution, and the public API has not changed.

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

    yes. And in generic: scenarios where you let the PIOs do things autonomously, and you want to get notified when they are done.

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

    Great post Jan.

    I can see where your approach would be very useful, especially in ramping up or ramping down the speed.

    • Cancel
    • Vote Up 0 Vote Down
    • Sign in to reply
    • More
    • Cancel
  • genebren
    genebren 25 days ago

    Another great PICO blog!  I am still planning on trying out these processors and your software.  I just seem to be stuck dealing with my existing projects.  Thanks for sharing!

    • 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