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 alarms

Now let’s see an example of using the Pico to interrupt on a timer alarm:

#![no_std]
#![no_main]

mod aux;

use aux::{blink_led, setup_xosc_on_clks};
use cortex_m::{asm, interrupt::InterruptNumber, peripheral::NVIC};
pub use cortex_m_rt::entry;
use defmt::info;

use crate::aux::start_second_cpu;

mod interrupt {
    pub const TIMER0_IRQ_0: isize = 0;
    pub const TIMER0_IRQ_1: isize = 1;
}

#[derive(Clone, Copy)]
struct Interrupt0;
unsafe impl InterruptNumber for Interrupt0 {
    fn number(self) -> u16 {
        0
    }
}
#[derive(Clone, Copy)]
struct Interrupt1;
unsafe impl InterruptNumber for Interrupt1 {
    fn number(self) -> u16 {
        1
    }
}

#[cortex_m_rt_macros::interrupt]
fn TIMER0_IRQ_0() {
    unsafe {
        info!(
            "Hello from interrupt TIMER0_IRQ_0 at CPU {}!",
            // SIO: CPUID Register
            core::ptr::read_volatile(0xd0000000 as *const u32)
        );

        // TIMER0: INTR Register
        // Clear interrupt
        core::ptr::write_volatile((0x400b003c + 0x3000) as *mut u32, 1);

        // Will fire the interrupt for ALARM1
        // (which is unmasked on CPU0)
        alarmer(3_000_000, 1);
    }
}

#[cortex_m_rt_macros::interrupt]
fn TIMER0_IRQ_1() {
    unsafe {
        info!(
            "Hello from interrupt TIMER0_IRQ_1 at CPU {}!",
            // SIO: CPUID Register
            core::ptr::read_volatile(0xd0000000 as *const u32)
        );

        // TIMER0: INTR Register
        // Clear interrupt
        core::ptr::write_volatile((0x400b003c + 0x3000) as *mut u32, 2);

        // Will fire the interrupt for ALARM0
        // (which is unmasked on CPU1)
        alarmer(1_000_000, 0);
    }
}

fn second_cpu_entry_point() -> ! {
    unsafe {
        NVIC::unmask(Interrupt0);
    }
    loop {
        asm::wfi();
    }
}

#[entry]
fn main() -> ! {
    unsafe {
        // GPIO 16 to visually signal board startup to us
        blink_led();

        setup_xosc_on_clks();

        start_second_cpu(second_cpu_entry_point);

        NVIC::unmask(Interrupt1);

        // TICKS: TIMER0_CTRL Register
        core::ptr::write_volatile(0x40108018 as *mut u32, 1);

        // TICKS: TIMER0_CYCLES Register
        // A divider that must result in a 1MHz clock.
        // If using the XOSC of reference board which 12MHz
        // this needs to be 12.
        core::ptr::write_volatile(0x4010801c as *mut u32, 12);

        // TIMER0: INTE Register
        // Enable interrupts ALARM0 and ALARM1
        // for TIMER0
        core::ptr::write_volatile(0x400b0040 as *mut u32, 0b11);

        // Will fire the interrupt for ALARM0
        // (which is unmasked on CPU1)
        alarmer(1_000_000, 0);
    };
    loop {
        asm::wfi();
    }
}

unsafe fn alarmer(to_add: u32, alarm_id: u32) {
    if alarm_id > 3 {
        panic!()
    }
    unsafe {
        // TIMER: PAUSE Register
        // Pause timer
        core::ptr::write_volatile(0x400b0030 as *mut u32, 1);

        let firing_when = core::ptr::read_volatile(0x400b000c as *const u32);
        let firing_when = firing_when.wrapping_add(to_add);

        // TIMER0: ALARMx Register
        core::ptr::write_volatile(
            (0x400b0010 + alarm_id * 4) as *mut u32,
            firing_when,
        );

        // TIMER: PAUSE Register
        // Unpause timer
        core::ptr::write_volatile(0x400b0030 as *mut u32, 0);
    }
}