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: usage and example
  • 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: 26 Aug 2025 7:34 PM Date Created
  • Views 346 views
  • Likes 3 likes
  • Comments 3 comments
Related
Recommended
  • Modern C++
  • pico
  • PIO
  • object oriented
  • c++

OO Library to handle Pico PIO relative interrupts: usage and example

Jan Cumps
Jan Cumps
26 Aug 2025
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.

For who is this library?

It's most useful in

  • object oriented designs, where
  • PIO state machines are managed by objects. 
  • When you have an object that needs to know when a state machine has completed an activity.
  • When you have multiple state machines, all  independently performing their task, and your program need to know what's happening.

A typical design is to have your own little object (a handler) for each state machine + interrupt combination.
You register that object in the library. It will get called when that state machine fires that interrupt.

There is only a single requirement for the handler class(es): The () operator has to be available (overloaded).

image

Let's imagine that you have 7 stepper motors. They are kept running by PIO state machines, and those state machines are set up to send an interrupt when a particular motor has done its steps.
This lib will call your one of those 7 handler object. The one that manages the motor that has finished stepping.

PIO will raise an interrupt, with info about what state machine and PIO has raised it. This library analyses that info, will determine what object has to handle the event, and will call that object when this happens.

PIO relative interrupts aren't that straightforward. And aren't OO savvy. This lib solves the riddle of understanding what state machine threw what interrupt on what PIO. And it will call the very object that you registered for that event.

Reusable and tested. With low run time costs.

Write a handler class

You have to write a class that will react on the PIO interrupt. In the stepper motor scenario, this could be the same class that controls all aspects of a stepper motor. In my example here, it is a very small class that will count how many times it got invoked, and prints that:

struct dummy_handler {

    dummy_handler(PIO pio, uint sm) : pio(pio), sm(sm), counter(0) {}
    
    PIO pio;
    uint sm;
    
    inline void operator()() {
        counter += 1;
        printf("PIO %d SM %d: %d\n", PIO_NUM(pio), sm, counter);
    }    
    
private:
    volatile uint counter;
};

This is obviously a demo design. I have given it attributes to hold state machine and PIO. That's not a requirement, just used to print meaningful info.

The only mandatory part is the void operator()().

image

Define the interrupt manager for your design

Easy :). We just need to create a type that knows what handler type and what interrupt number we're servicing.

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

That's really all there is to it. You don't have to (in fact: can't) create an object of this class. You can directly use it.

Example:

// ...

pio_irq_manager_t::register_interrupt(0, h.pio, h.sm, true);

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

// ...

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

Real Example

image

I wrote a simple PIO program, that just fires an interrupt 0 every second. It's Raspberry's Pico PIO blink example. Instead of blinking, it shoots the interrupt after it completes a loop (line 11).

.program run
    pull block
    out y, 32
.wrap_target
    mov x, y [1]
lp1:
    jmp x-- lp1   ; Delay for (x + 1) cycles, x is a 32 bit number
    mov x, y [1]
lp2:
    jmp x-- lp2   ; Delay for the same number of cycles again
    irq 0 rel     ; 20250824 jc throw a relative interrupt
.wrap             ; run forever!

I use a Pico2 with 3 PIOs. Because this is a demo, I use each available state machine: 3 PIOs x 4 state machines is 12 state machines. So I instantiate 12 handler objects. I use an array to store them, making the code easier. But that's not needed. You could just create 12 individual object if that suits your design better:

const uint32_t IRQ_NUM = 0U;

using handler_t = dummy_handler::dummy_handler;

std::array<handler_t, 4 * NUM_PIOS> handlers {{
    {pio0, 0}, {pio0, 1}, {pio0, 2}, {pio0, 3},
    {pio1, 0}, {pio1, 1}, {pio1, 2}, {pio1, 3},
    {pio2, 0}, {pio2, 1}, {pio2, 2}, {pio2, 3}
}};

We use the library to enable the interrupt for each of the handlers. In your design; you do that after programming the PIO(s). I left that out of the snippet below, but the attached project has that code.

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


int main()
{
    // ... program the PIO first
    // ...
    
    for (auto &h: handlers) {
        pio_irq_manager_t::register_interrupt(0, h.pio, h.sm, true);
    }

You can see the advantage of placing all handlers in a container here. They are stored in an organised way, and we can register all interrupts in a simple loop.

Then we register the handlers to the library. Once all is enabled, the operator()() will be called when a PIO state machine arrives at line 11 of its little program.

    for (auto &h: handlers) {
        pio_irq_manager_t::register_handler(h.pio, h.sm, &h, true);
    }
    
    // ... enable each state machine
    // ...

As soon as the SMs are enabled and the handlers registered, the mechanism is active. You can see a snippet of the output here.

image

Each registered object gets called when its state machine fired interrupt 0. And executes the code of operator()().In this little, that is: increase counter, print message:

    inline void operator()() {
        counter += 1;
        printf("PIO %d SM %d: %d\n", PIO_NUM(pio), sm, counter);
    }   

The attached archive has the code of the library and the example. It's a VSCode Pico Extension project, configured for Pico2 / RISCV. You can alter the settings with the Pico extension.

If you select a Pico1, it 'll use the 8 state machines of PIO 0 and 1 only (because the Pico1 has 1 PIO less than the Pico2. Try it!

pio_relative_interrupt_20250826.zip

In the next post, I'll review the library's design and code:  OO Library to handle Pico PIO relative interrupts: library design 

  • Sign in to reply
  • Jan Cumps
    Jan Cumps 25 days ago in reply to DAB

    10:

    image

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

    Great post Jan.

    I am curious, do you have an implementation that actually uses seven stepper motors?

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

     OO Library to handle Pico PIO relative interrupts: library design 

    • 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