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 Handle Raspberry Pico PIO "relative interrupts" in C
  • 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: 13 Apr 2025 5:44 PM Date Created
  • Views 1170 views
  • Likes 7 likes
  • Comments 4 comments
Related
Recommended
  • interrupt
  • raspberry
  • pico
  • PIO
  • pico c sdk

Handle Raspberry Pico PIO "relative interrupts" in C

Jan Cumps
Jan Cumps
13 Apr 2025

There are decent examples available on how to handle PIO interrupts in C. But not so much on dealing with relative IRQs.

With a normal IRQ, PIO fires an interrupt with a fixed number. The number that you give it. 
A relative IRQ - in the context of this post - is an IRQ raised by a PIO, that has state machine ID encoded. It's not just "this interrupt from that state machine". The interrupt number and state machine ID are combined. You have to use that combination, in different ways, in your C code.

A relative IRQ adds the state machine ID and the two lowest bits of  the IRQ number, and does a MOD 4 on that result. So it only affects those last two bits. I ran all combinations (state machine 0 - 3 and interrupt 0 -3) and collected two pieces of info:

  • what interrupt do I have to register / enable / clear in C
  • how do I know what state machine trew the interrupt, based on its state

Each PIO has 4 state machines. In this post I consequently call them 0 to 3. And C can capture the first 4 interrupts (on the Pico 2: all 8). I call those 0 to 3 too.

This is the table that reflects each scenario. Blue is the value I retrieved via the debugger. Green is me calculating those same attributes (and hopefully getting the same result). Black are the settings I used (state machine and interrupt

    irq value in PIO irq received interrupt to manage irq value to validate
sm irq pio irq ARM irq MOD(sm+irq,4) bitshift left
0 0 1 0 0 1
0 1 2 1 1 2
0 2 4 2 2 4
0 3 8 3 3 8
1 0 2 1 1 2
1 1 4 2 2 4
1 2 8 3 3 8
1 3 1 0 0 1
2 0 4 2 2 4
2 1 8 3 3 8
2 2 1 0 0 1
2 3 2 1 1 2
3 0 8 3 3 8
3 1 1 0 0 1
3 2 2 1 1 2
3 3 4 2 2 4

This is the binary representation - easier to understand what's happening with those 2 last bits:

bin
sm irq pio irq int mod shift left
0000 0000 0001 0000 0000 0001
0000 0001 0010 0001 0001 0010
0000 0010 0100 0010 0010 0100
0000 0011 1000 0011 0011 1000
0001 0000 0010 0001 0001 0010
0001 0001 0100 0010 0010 0100
0001 0010 1000 0011 0011 1000
0001 0011 0001 0000 0000 0001
0010 0000 0100 0010 0010 0100
0010 0001 1000 0011 0011 1000
0010 0010 0001 0000 0000 0001
0010 0011 0010 0001 0001 0010
0011 0000 1000 0011 0011 1000
0011 0001 0001 0000 0000 0001
0011 0010 0010 0001 0001 0010
0011 0011 0100 0010 0010 0100

What I learned from this:

  • to tell in C what interrupt to enable, catch and clear, I have to calculate the sum of the two LSB of the IRQ, + the state machine ID, mod 4: Let's call that the relative_irq

uint relative_irq = IRQ_NUMBER & 0x03; // two last bits
relative_irq += sm; // add state machine ID
relative_irq = relative_irq % 4; // modulo 4, retaining only the last 2 bits again

I created a function to calculate that value:

// 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
    return retval;
}

this function hasn't been tested for interrupts 4 .. 7, that can be propagated on a Pico 2. Send me one...

  • to see which of the 4 state machine actually threw the interrupt, we have to interpret the value we get in the pio_hw_t.irq. structure. This variable has always 1 bit set when we get the interrupt in C. The position of that bit reflects the state machine ID.
    We can check if the interrupt is coming from the right state machine:
    assert(piostep->irq == 1 << relative_interrupt(stepper_PIO_IRQ_DONE, sm));

    or we can retrieve what state machine threw the interrupt by using this code:

uint sm_from_interrupt(const uint32_t irq_val, const uint32_t ir) {
    uint i;
    for (i = 0; i < 4; i++) {
        if (irq_val == 1 << relative_interrupt(ir, i)) {
            break;
        }
    }
    return i;
}

this function hasn't been tested for interrupts 4 .. 7, that can be propagated on a Pico 2.

Let's take a pause now Slight smile. At this moment, we know all the numbers needed. let's do something easy

How to generate a relative IRQ in PIO

To make PIO generate relative interrupts, you add "rel" to IRQ command:

.define public PIO_IRQ_DONE 0
; ...
    irq PIO_IRQ_DONE rel        side 0 ; flag that the job's done
; ...

How to enable PIO to throw the relative IRQ

We have to calculate the right index in the ARM interrupt vector (NVIC).
Raspberry's C SDK uses an enum for the interrupts. That's a bit unfortunate, because we have to calculate those numbers. enums are actually there to steer you away from doing math. But we can override this:
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);
}
All that to add a number to the first PÏO IRQ. We have to add the relative irq to it. Else PIO won't be able to generate these. Here is the code to enable PIO to throw this and send it to ARM:
pio_set_irq0_source_enabled(
        pio1, interrupt_source(pis_interrupt0,
        relative_interrupt(stepper_PIO_IRQ_DONE, sm)), true);
That's step 1. 

How to register and enable the request handler correctly for the relative IRQ

This is easier. We just have to know which PIO we use, what interrupt channel we use, and what the handler name is

irq_add_shared_handler(PIO1_IRQ_0, pio_irq_handler,PICO_SHARED_IRQ_HANDLER_DEFAULT_ORDER_PRIORITY);  //Set the handler in the NVIC

The first parameter is dependent on what PIO you use, and what interrupt channel we'll use. In my code, I use PIO1 and IRQ_0. We 'll see the content of parameter 2, the handler function, in a bit.

Then enable this interrupt on ARM:

irq_set_enabled(PIO1_IRQ_0, true);

Find (or validate) the state machine in the interrupt handler

Last step: if we want to react on the interrupt, we need a handler. This handler will be called when interrupts arrive from any state machine of the PIO. Inside the handler, we have ability to either (or both):

  • check if the interrupt came from a particular state machine. This works well if you know what SM you want to react to. Let's say you have one stepper motor, and you know it runs on state machine 0 of pio1. And the interrupt ID is 0
    assert(pio1->irq == 1 << relative_interrupt(0, 0));
  • find out what state machine caused the interrupt. Let's say you have a pio that runs 4 stepper motors independently, each controlled by its own state machine.
    sm_from_interrupt(pio1->irq, 0));

You have to clear the interrupt in the handler: 

pio_interrupt_clear(pio1, relative_interrupt(0, 0));

An example function, that increments a  count, each time it receives an interrupt 0 from state machine 0:

volitile uint commands_completed = 0U;

void pio_irq_handler(void){
    uint ir = relative_interrupt(0, 0);
    assert(piostep->irq == 1 << ir); // develop check: interrupt is from the correct state machine
    pio_interrupt_clear(pio1, ir);
    commands_completed = commands_completed + 1;
}

Next post C++:  Handle Raspberry Pico PIO "relative interrupts" in C++ . 

For a project that uses this, check  Stepper Motor Control with Raspberry Pico PIO and DRV8711 driver- Part 7: autonomous PIO with interrupt when done .

Thank you for reading.

  • Sign in to reply
  • DAB
    DAB 5 months ago

    Nice post Jan.

    • Cancel
    • Vote Up 0 Vote Down
    • Sign in to reply
    • More
    • Cancel
  • Jan Cumps
    Jan Cumps 5 months ago

    For the C++ inclined crowd:  Handle Raspberry Pico PIO "relative interrupts" in C++ 

    • Cancel
    • Vote Up 0 Vote Down
    • Sign in to reply
    • More
    • Cancel
  • BigG
    BigG 5 months ago

    Wow! I would never have figured this out from the SDK documentation. Thanks for sharing. This has really helped me.

    • Cancel
    • Vote Up 0 Vote Down
    • Sign in to reply
    • More
    • Cancel
  • Jan Cumps
    Jan Cumps 5 months ago

    If you use this on a Pico 2, you should be able to choose PIO interrupts 4 .. 7 too.

    I don't have one, so it's untested. I tried to imagine what the impact is on the code base, and adapted it.

    available on GitHub, but I didn't adapt the blog. Impacted code:

    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;
    }

    • 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