Skip to content

Add isr example with the pio #259

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 6 commits into
base: develop
Choose a base branch
from

Conversation

dangarbri
Copy link

Someone on the pico forum asked how interrupts work with only 2 interrupt lines and 4 state machines.

I put together this example, which only runs with one isr line, but shows how you could identify which state machine the irq is coming from.

The pio program simply stalls waiting for input to the tx fifo from firmware, and then fires an interrupt when it gets anything.

From main.c, I load the program into each pio and fire the interrupts one by one. The code shows how you can identify the state machine the interrupt came from and then dispatch some action.

pio/isr/main.c Outdated
void simply_isr_handler() {
// Read the IRQ register to get the IRQ flags from the state machine
// This tells me which state machine sent the IRQ
irq_flags = read_register(PIO0_BASE, PIO_IRQ_OFFSET);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like you can also get this from https://github.com/raspberrypi/pico-sdk/blob/master/src/rp2040/hardware_structs/include/hardware/structs/pio.h#L127-L130 which might be "less lowlevel" than defining your own read_register and write_register functions? 🤷

Or there's also https://github.com/raspberrypi/pico-sdk/blob/master/src/rp2_common/hardware_pio/include/hardware/pio.h#L799-L822 which is probably even better?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh I didn't notice the pio_interrupt_get. I think that will work better for this example.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated to use pio_interrupt_get.
I think personally I would prefer the struct version to read the IRQ, just because it's 1 read vs. 4 separate reads for pio_interrupt_get. But as an sdk example I think this is good.

@dangarbri dangarbri requested a review from lurch August 25, 2022 20:51
@lurch
Copy link
Contributor

lurch commented Aug 25, 2022

Nice, thanks for addressing my comments 👍
Would you mind also adding this new example to https://github.com/raspberrypi/pico-examples/blob/master/README.md#pio please?

@dangarbri
Copy link
Author

Nice, thanks for addressing my comments +1 Would you mind also adding this new example to https://github.com/raspberrypi/pico-examples/blob/master/README.md#pio please?

done

README.md Outdated
@@ -156,6 +156,7 @@ App|Description
[uart_tx](pio/uart_tx)| Implement the transmit component of a UART serial port, and print hello world.
[ws2812](pio/ws2812)| Examples of driving WS2812 addressable RGB LEDs.
[addition](pio/addition)| Add two integers together using PIO. Only around 8 billion times slower than Cortex-M0+.
[pio_isr](pio/isr)| Respond to interrupts generated by a PIO state machine.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For consistency with the other examples listed here, should this be [isr](pio/isr) ? Sorry for being picky!

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No problem, I understand! I used pio_isr because it's the name of the program that gets compiled, but I can change it to isr if you'd like.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated, but also renamed.

pio/isr/main.c Outdated
*
* Of course, you can really do whatever you want in the ISR, it's up to you.
*/
volatile uint32_t irq_flags = 0;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably makes no difference, but I wonder if a uint8_t would be more appropriate here, given that there are only 8 PIO SMs in total?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

u8 would be more efficient here. I had a u32 because originally I had that register read function which returned a u32 even though I only cared about the lower 8 bits.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated to use u8

@dangarbri
Copy link
Author

@lurch I made your requested review changes, and also a couple more.

I renamed it to IRQ since that's a more standard name. I'm used to calling them ISR (interrupt service routine) which gets confusing because in pico world, ISR means Input Shift Register. So I renamed everything to IRQ.

Also I updated my IRQ handler to use the lower level read/clear method. In this case I think it really does make it simpler for this use case than pio_interrupt_get. It becomes one read to check the IRQ flags instead of iterating over each bit checking if its set.

@dangarbri dangarbri requested a review from lurch August 28, 2022 14:42
* This will make the simple_irq program send an ISR to us!
*/
void trigger_irq(int sm) {
printf("Triggering ISR from state machine %d\n", sm);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ISR -> IRQ here? There's also a few comments that still say "ISR", where perhaps "IRQ" would be more appropriate? (although there are some comments where it might be appropriate to stick to "ISR"?)

In my understanding (which may still be wrong, as I've not done much low-level coding), IRQ = Interrupt ReQuest, i.e. the actual hardware signal, and ISR = Interrupt Service Routine i.e. the callback function you've registered to handle the IRQ.

printf("Expected IRQ flags: 0x%08X\n", (1 << sm));
printf("Actual IRQ Flags: 0x%08X\n", irq_flags);

// Here you could dispatch work for the irq depending one
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"depending one" ? Looks like you might have missed some words out?

}

// clear irq flags now.
irq_flags = 0;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rather than setting all irq_flags to 0 here at the end, I wonder if it might be more idiomatic to clear the flags individually e.g. by doing something like irq_flags &= !PIO_SM_1_IRQ inside the if (irq_flags & PIO_SM_1_IRQ) { block? Or perhaps that's unnecessary complication? 🤷


% c-sdk {
/**
* Initializer for the fake irq program program
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"program program" ?

// ISR would execute only once when simultaneous interrupts happen instead
// of being executed for each interrupt individually.
// For other applications, make sure to check the correct pio (pio0/pio1)
irq_flags = pio0_hw->irq;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps some compilers will need an explicit cast here, to avoid warnings? 🤷 (alternatively, I guess you could make irq_flags a uint32_t again)

*/
void enable_pio_irqs() {
// Set the function that will be called when the PIO IRQ comes in.
irq_set_exclusive_handler(PIO0_IRQ_0, simple_irq_handler);
Copy link
Contributor

@henrygab henrygab Oct 16, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this should be:

irq_add_shared_handler(PIO0_IRQ_0, simple_irq_handler, PICO_SHARED_IRQ_HANDLER_DEFAULT_ORDER_PRIORITY);

After all, each state machine may have its own handler attached, as they could each run entirely distinct programs.

@kilograham kilograham self-assigned this Feb 13, 2025
@kilograham kilograham added this to the 2.2.0 milestone Feb 13, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants