mirror of
https://github.com/italicsjenga/rp-hal-boards.git
synced 2024-12-24 05:01:31 +11:00
2bbc52ffce
* Add struct VectorTable to represent an interrupt vector table * Add member function to VectorTable to initialise based on the current Interrupt Vector Table from VTOR * Add member function to VectorTable to register an extern "C" function to call on interrupt * Add example using VectorTable to demonstrate initialisation and interrupt function registration
191 lines
6.6 KiB
Rust
191 lines
6.6 KiB
Rust
//! # RAM Vector Table example
|
|
//!
|
|
//! This application demonstrates how to create a new Interrupt Vector Table in RAM.
|
|
//! To demonstrate the extra utility of this, we also replace an entry in the Vector Table
|
|
//! with a new one.
|
|
//!
|
|
//! See the `Cargo.toml` file for Copyright and license details.
|
|
|
|
#![no_std]
|
|
#![no_main]
|
|
|
|
// The macro for our start-up function
|
|
use cortex_m_rt::entry;
|
|
|
|
// Ensure we halt the program on panic
|
|
use panic_halt as _;
|
|
|
|
// Alias for our HAL crate
|
|
use rp2040_hal as hal;
|
|
|
|
// A shorter alias for the Peripheral Access Crate
|
|
use hal::pac;
|
|
|
|
// Some traits we need
|
|
use core::cell::RefCell;
|
|
use cortex_m::interrupt::Mutex;
|
|
use embedded_hal::digital::v2::ToggleableOutputPin;
|
|
use embedded_time::duration::Microseconds;
|
|
use embedded_time::fixed_point::FixedPoint;
|
|
use pac::interrupt;
|
|
use rp2040_hal::clocks::Clock;
|
|
use rp2040_hal::timer::Alarm;
|
|
use rp2040_hal::vector_table::VectorTable;
|
|
|
|
// Memory that will hold our vector table in RAM
|
|
static mut RAM_VTABLE: VectorTable = VectorTable::new();
|
|
|
|
// Give our LED and Alarm a type alias to make it easier to refer to them
|
|
type LedAndAlarm = (
|
|
hal::gpio::Pin<hal::gpio::bank0::Gpio25, hal::gpio::PushPullOutput>,
|
|
hal::timer::Alarm0,
|
|
);
|
|
|
|
// Place our LED and Alarm type in a static variable, so we can access it from interrupts
|
|
static mut LED_AND_ALARM: Mutex<RefCell<Option<LedAndAlarm>>> = Mutex::new(RefCell::new(None));
|
|
|
|
// Period that each of the alarms will be set for - 1 second and 300ms respectively
|
|
const SLOW_BLINK_INTERVAL_US: u32 = 1_000_000;
|
|
const FAST_BLINK_INTERVAL_US: u32 = 300_000;
|
|
|
|
/// The linker will place this boot block at the start of our program image. We
|
|
/// need this to help the ROM bootloader get our code up and running.
|
|
#[link_section = ".boot2"]
|
|
#[used]
|
|
pub static BOOT2: [u8; 256] = rp2040_boot2::BOOT_LOADER_W25Q080;
|
|
|
|
/// External high-speed crystal on the Raspberry Pi Pico board is 12 MHz. Adjust
|
|
/// if your board has a different frequency
|
|
const XTAL_FREQ_HZ: u32 = 12_000_000u32;
|
|
|
|
/// Entry point to our bare-metal application.
|
|
///
|
|
/// The `#[entry]` macro ensures the Cortex-M start-up code calls this function
|
|
/// as soon as all global variables are initialised.
|
|
///
|
|
/// The function configures the RP2040 peripherals, then toggles a GPIO pin in
|
|
/// an infinite loop. If there is an LED connected to that pin, it will blink.
|
|
#[entry]
|
|
fn main() -> ! {
|
|
// Grab our singleton objects
|
|
let mut pac = pac::Peripherals::take().unwrap();
|
|
let core = pac::CorePeripherals::take().unwrap();
|
|
|
|
// Set up the watchdog driver - needed by the clock setup code
|
|
let mut watchdog = hal::Watchdog::new(pac.WATCHDOG);
|
|
// The single-cycle I/O block controls our GPIO pins
|
|
let sio = hal::Sio::new(pac.SIO);
|
|
|
|
// Need to make a reference to the Peripheral Base at this scope to avoid confusing the borrow checker
|
|
let ppb = &mut pac.PPB;
|
|
unsafe {
|
|
// Copy the vector table that cortex_m_rt produced into the RAM vector table
|
|
RAM_VTABLE.init(ppb);
|
|
// Replace the function that is called on Alarm0 interrupts with a new one
|
|
RAM_VTABLE.register_handler(pac::Interrupt::TIMER_IRQ_0 as usize, timer_irq0_replacement);
|
|
}
|
|
|
|
// Configure the clocks
|
|
let clocks = hal::clocks::init_clocks_and_plls(
|
|
XTAL_FREQ_HZ,
|
|
pac.XOSC,
|
|
pac.CLOCKS,
|
|
pac.PLL_SYS,
|
|
pac.PLL_USB,
|
|
&mut pac.RESETS,
|
|
&mut watchdog,
|
|
)
|
|
.ok()
|
|
.unwrap();
|
|
|
|
// Create simple delay
|
|
let mut delay = cortex_m::delay::Delay::new(core.SYST, clocks.system_clock.freq().integer());
|
|
|
|
// Set the pins to their default state
|
|
let pins = hal::gpio::Pins::new(
|
|
pac.IO_BANK0,
|
|
pac.PADS_BANK0,
|
|
sio.gpio_bank0,
|
|
&mut pac.RESETS,
|
|
);
|
|
|
|
// Configure GPIO25 as an output
|
|
let led_pin = pins.gpio25.into_push_pull_output();
|
|
|
|
let mut timer = hal::Timer::new(pac.TIMER, &mut pac.RESETS);
|
|
cortex_m::interrupt::free(|cs| {
|
|
let mut alarm = timer.alarm_0().unwrap();
|
|
// Schedule an alarm in 1 second
|
|
let _ = alarm.schedule(Microseconds(SLOW_BLINK_INTERVAL_US));
|
|
// Enable generating an interrupt on alarm
|
|
alarm.enable_interrupt();
|
|
// Move alarm into ALARM, so that it can be accessed from interrupts
|
|
unsafe {
|
|
LED_AND_ALARM.borrow(cs).replace(Some((led_pin, alarm)));
|
|
}
|
|
});
|
|
// Unmask the timer0 IRQ so that it will generate an interrupt
|
|
unsafe {
|
|
pac::NVIC::unmask(pac::Interrupt::TIMER_IRQ_0);
|
|
}
|
|
|
|
// After 5 seconds, switch to our modified vector rable
|
|
delay.delay_ms(5000);
|
|
unsafe {
|
|
cortex_m::interrupt::free(|_| {
|
|
RAM_VTABLE.activate(ppb);
|
|
});
|
|
}
|
|
|
|
loop {
|
|
// Wait for an interrupt to fire before doing any more work
|
|
cortex_m::asm::wfi();
|
|
}
|
|
}
|
|
|
|
// Regular interrupt handler for Alarm0. The `interrupt` macro will perform some transformations to ensure
|
|
// that this interrupt entry ends up in the vector table.
|
|
#[interrupt]
|
|
fn TIMER_IRQ_0() {
|
|
cortex_m::interrupt::free(|cs| {
|
|
// Temporarily take our LED_AND_ALARM
|
|
let ledalarm = unsafe { LED_AND_ALARM.borrow(cs).take() };
|
|
if let Some((mut led, mut alarm)) = ledalarm {
|
|
// Clear the alarm interrupt or this interrupt service routine will keep firing
|
|
alarm.clear_interrupt();
|
|
// Schedule a new alarm after SLOW_BLINK_INTERVAL_US have passed (1 second)
|
|
let _ = alarm.schedule(Microseconds(SLOW_BLINK_INTERVAL_US));
|
|
// Blink the LED so we know we hit this interrupt
|
|
led.toggle().unwrap();
|
|
// Return LED_AND_ALARM into our static variable
|
|
unsafe {
|
|
LED_AND_ALARM
|
|
.borrow(cs)
|
|
.replace_with(|_| Some((led, alarm)));
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
// This is the function we will use to replace TIMER_IRQ_0 in our RAM Vector Table
|
|
extern "C" fn timer_irq0_replacement() {
|
|
cortex_m::interrupt::free(|cs| {
|
|
let ledalarm = unsafe { LED_AND_ALARM.borrow(cs).take() };
|
|
if let Some((mut led, mut alarm)) = ledalarm {
|
|
// Clear the alarm interrupt or this interrupt service routine will keep firing
|
|
alarm.clear_interrupt();
|
|
// Schedule a new alarm after FAST_BLINK_INTERVAL_US have passed (300 milliseconds)
|
|
let _ = alarm.schedule(Microseconds(FAST_BLINK_INTERVAL_US));
|
|
led.toggle().unwrap();
|
|
// Return LED_AND_ALARM into our static variable
|
|
unsafe {
|
|
LED_AND_ALARM
|
|
.borrow(cs)
|
|
.replace_with(|_| Some((led, alarm)));
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
// End of file
|