2024-08-11

PIO and Interrupts

Introduction

I want to be able to raise system level interrupts from PIO programs and there aren’t too many examples of this, so here is my simple contribution.

PIO program

First, the PIO program, which simply waits for a pin to change and then immediately raises PIO IRQ 0.

.program irq_example
.wrap_target

wait 0 pin 0	; wait for a 0 on IN pin 0
irq nowait 0	; raise PIO IRQ 0

.wrap

Compile this with pioasm then #include the .h in your C program.

C Program

Interrupt Setup

There are four system level interrupts related to PIO: PIO0_IRQ_0, PIO0_IRQ_1, PIO1_IRQ_0, PIO1_IRQ_1. We shall use PIO0_IRQ_0.

First we attach an interrupt handler to PIO0_IRQ_0:

void isr(void) {
	// clear PIO IRQ 0 that was raised by irq nowait 0
	pio_interrupt_clear(pio0, 0);
}

irq_add_shared_handler(PIO0_IRQ_0,
                       isr,
                       PICO_SHARED_IRQ_HANDLER_DEFAULT_ORDER_PRIORITY);

It is important that pio_interrupt_clear() is called otherwise the CPU will lock up constantly executing the interrupt handler (aka ISR - interrupt service routine)

We then enable the interrupt:

irq_set_enabled(PIO0_IRQ_0, true);

Finally we configure PIO0_IRQ_0 to be triggered when PIO IRQ 0 is raised:

pio_set_irq0_source_enabled(pio0, pis_interrupt0, true);

The functions used above are described in the Pico C SDK documentation.

PIO Setup

The PIO program can now by installed the usual way by first setting up the state machine:

uint sm = pio_claim_unused_sm(pio0, true);
uint offset = pio_add_program(pio0, &irq_example_program);
pio_sm_config cfg = irq_example_program_get_default_config(offset);

Configure and connect GPIO to the state machine:

// connect GP 2 to IN of state machine
sm_config_set_in_pins(&cfg, 2);

// it is an input pin
pio_sm_set_consecutive_pindirs(pio0, sm, 2, 1, false);

// we want pull up on the pin
gpio_pull_up(2);

Now we are ready to start the state machine:

pio_sm_init(pio0, sm, offset, &cfg);
pio_sm_set_enabled(pio0, sm, true);