Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Interrupts on single-core

Let’s try with a single-core example. See what happens when you make GPIO go from high to low (e.g. use a button) or alternatively change the code to put the GPIO in output mode and make it go from high to low yourself.

#![no_std]
#![no_main]
use cortex_m::{
    asm, interrupt::InterruptNumber,
    peripheral::NVIC,
};
pub use cortex_m_rt::entry;
use defmt::info;
use rp235x_hal as _;
use {defmt_rtt as _, panic_probe as _};

#[unsafe(link_section = ".start_block")]
#[unsafe(no_mangle)]
static BOOT_ROM_INFO: [u8; 44] = [
    0xd3, 0xde, 0xff, 0xff, 0x42, 0x01, 0x21,
    0x10, 0xff, 0x01, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x79, 0x35, 0x12, 0xab, 0xf2,
    0xeb, 0x88, 0x71, 0x6c, 0x93, 0x02, 0x10,
    0x7c, 0x93, 0x02, 0x10, 0x70, 0xf6, 0x02,
    0x10, 0x90, 0xa3, 0x1a, 0xe7, 0x1f, 0x01,
    0x02, 0x03,
];

mod interrupt {
    pub const IO_IRQ_BANK0: isize = 21;
}

#[derive(Clone, Copy)]
struct Interrupt;
unsafe impl InterruptNumber for Interrupt {
    fn number(self) -> u16 {
        21
    }
}

#[cortex_m_rt_macros::interrupt]
fn IO_IRQ_BANK0() {
    info!("Hello from interrupt!");
    unsafe {
        // IO_BANK0: INTR2 Register
        // 3 GPIO16_EDGE_HIGH WC 0x0
        // 2 GPIO16_EDGE_LOW WC 0x0
        // 1 GPIO16_LEVEL_HIGH RO 0x0
        // 0 GPIO16_LEVEL_LOW RO 0x0
        core::ptr::write_volatile(
            0x40028238 as *mut u32,
            1 << 2 | 1 << 3,
        );
    }
    for _ in 1..10000 {
        asm::nop();
    }
}

#[entry]
fn main() -> ! {
    unsafe {
        for _ in 1..10000 {
            asm::nop();
        }

        // this would call the interrupt handler
        NVIC::pend(Interrupt);

        // if comment out this line, the interrupt handler will be
        // called immediately after unmasking
        NVIC::unpend(Interrupt);

        info!("unmasking");
        NVIC::unmask(Interrupt);

        // now this will call the interrupt once
        info!("pending");
        NVIC::pend(Interrupt);

        // IO_BANK0: GPIO16_CTRL Register
        // Set to use SIO
        core::ptr::write_volatile(
            0x40028084 as *mut u32,
            0x5,
        );

        // GPIO_OE_CLEAR
        // 0x10000: enable output for bit 16 (pin 16)
        core::ptr::write_volatile(
            0xd0000040 as *mut u32,
            1 << 16,
        );

        // PADS_BANK0: GPIO16 Register
        // 0x48: set bit 6 (IE: Input enable) and bit 3 (PUE: Pull up enable)
        core::ptr::write_volatile(
            0x40038044 as *mut u32,
            1 << 6 | 1 << 3,
        );
        info!("in input mode");

        // IO_BANK0: PROC0_INTE2 Register
        // Interrupt Enable for processor 0
        // Bit 3: GPIO16_EDGE_HIGH
        // Bit 2: GPIO16_EDGE_LOW
        // Bit 1: GPIO16_LEVEL_HIGH
        // Bit 0: GPIO16_LEVEL_LOW
        info!("will enable interrupt now");
        core::ptr::write_volatile(
            0x40028250 as *mut u32,
            1 << 2,
        );
    };
    status();
    loop {
        unsafe {
            info!(
                "{:#x} GPIO_IN",
                core::ptr::read_volatile(
                    0xd0000004 as *const u32
                )
            );
        }
        for _ in 1..100000 {
            asm::nop();
        }
    }
}

fn status() {
    unsafe {
        info!(
            "{:#x} IO_BANK0: GPIO16_STATUS Register",
            core::ptr::read_volatile(
                0x40028080 as *const u32
            )
        );
        info!(
            "{:#x} IO_BANK0: GPIO16_CTRL Register",
            core::ptr::read_volatile(
                0x40028084 as *const u32
            )
        );
        info!(
            "{:#x} GPIO_OUT",
            core::ptr::read_volatile(
                0xd0000010 as *const u32
            )
        );
        info!(
            "{:#x} GPIO_OE",
            core::ptr::read_volatile(
                0xd0000030 as *const u32
            )
        );
        info!(
            "{:#x} PADS_BANK0: GPIO16 Register",
            core::ptr::read_volatile(
                0x40038044 as *const u32
            )
        );
    }
}

Cargo.toml changes

To make this work, we had again to make some changes in our Cargo.toml:

[package]
name = "pico-simple"
version = "0.1.0"
edition = "2024"

[dependencies]
cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] }
cortex-m-rt = { version = "0.7.0", features = ["device"] }
cortex-m-rt-macros = "0.7.5"
panic-probe = { version = "1.0.0", features = ["print-defmt"] }
defmt = "1.0.1"
defmt-rtt = "1.0.0"
rp235x-hal = "0.4.0"

We don’t need the multi-core critical section anymore as again running on single-core, but on the other hand we had to enable the device feature of cortex-m-rt and again needed to use the rp235x-hal crate. The reason for that is that the pointer to the function that is our interrupt handle must be stored at cortex-m-rt

If you try to use the Cargo.toml like it was in the previous sections, then the code won’t go our desired interrupt handler, but rather to a default one, that doesn’t clear the GPIO16_EDGE_LOW, so it will be stuck calling the interrupt handler again and again.