-
Notifications
You must be signed in to change notification settings - Fork 283
Expand file tree
/
Copy pathvector_table.rs
More file actions
193 lines (169 loc) · 6.72 KB
/
vector_table.rs
File metadata and controls
193 lines (169 loc) · 6.72 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
//! # 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]
// 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 critical_section::Mutex;
use embedded_hal::digital::v2::ToggleableOutputPin;
use fugit::MicrosDurationU32;
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::FunctionSioOutput, hal::gpio::PullDown>,
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: MicrosDurationU32 = MicrosDurationU32::secs(1);
const FAST_BLINK_INTERVAL_US: MicrosDurationU32 = MicrosDurationU32::millis(300);
/// 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.
/// Note: This boot block is not necessary when using a rp-hal based BSP
/// as the BSPs already perform this step.
#[link_section = ".boot2"]
#[used]
pub static BOOT2: [u8; 256] = rp2040_boot2::BOOT_LOADER_GENERIC_03H;
/// 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 `#[rp2040_hal::entry]` macro ensures the Cortex-M start-up code calls this function
/// as soon as all global variables and the spinlock 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.
#[rp2040_hal::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().to_Hz());
// 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,
&watchdog,
&clocks.reference_clock,
);
critical_section::with(|cs| {
let mut alarm = timer.alarm_0().unwrap();
// Schedule an alarm in 1 second
let _ = alarm.schedule(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 {
critical_section::with(|_| {
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() {
critical_section::with(|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(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() {
critical_section::with(|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(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