diff options
| -rw-r--r-- | embassy-macros/src/chip/rp.rs | 6 | ||||
| -rw-r--r-- | embassy-rp/Cargo.toml | 4 | ||||
| -rw-r--r-- | embassy-rp/src/clocks.rs | 1 | ||||
| -rw-r--r-- | embassy-rp/src/lib.rs | 8 | ||||
| -rw-r--r-- | embassy-rp/src/timer.rs | 187 | ||||
| -rw-r--r-- | examples/rp/src/bin/blinky.rs | 5 |
6 files changed, 207 insertions, 4 deletions
diff --git a/embassy-macros/src/chip/rp.rs b/embassy-macros/src/chip/rp.rs index 04211f8f3..d40a44963 100644 --- a/embassy-macros/src/chip/rp.rs +++ b/embassy-macros/src/chip/rp.rs | |||
| @@ -3,10 +3,16 @@ use proc_macro2::TokenStream; | |||
| 3 | use quote::quote; | 3 | use quote::quote; |
| 4 | 4 | ||
| 5 | pub fn generate(embassy_prefix: &ModulePrefix, config: syn::Expr) -> TokenStream { | 5 | pub fn generate(embassy_prefix: &ModulePrefix, config: syn::Expr) -> TokenStream { |
| 6 | let embassy_path = embassy_prefix.append("embassy").path(); | ||
| 6 | let embassy_rp_path = embassy_prefix.append("embassy_rp").path(); | 7 | let embassy_rp_path = embassy_prefix.append("embassy_rp").path(); |
| 7 | quote!( | 8 | quote!( |
| 8 | use #embassy_rp_path::{interrupt, peripherals}; | 9 | use #embassy_rp_path::{interrupt, peripherals}; |
| 9 | 10 | ||
| 10 | let p = #embassy_rp_path::init(#config); | 11 | let p = #embassy_rp_path::init(#config); |
| 12 | |||
| 13 | let alarm = unsafe { <#embassy_rp_path::peripherals::TIMER_ALARM0 as #embassy_path::util::Steal>::steal() }; | ||
| 14 | let mut alarm = #embassy_rp_path::timer::Alarm::new(alarm); | ||
| 15 | let alarm = unsafe { make_static(&mut alarm) }; | ||
| 16 | executor.set_alarm(alarm); | ||
| 11 | ) | 17 | ) |
| 12 | } | 18 | } |
diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml index 503d2c706..ba9984a80 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml | |||
| @@ -22,6 +22,6 @@ cortex-m-rt = "0.6.13" | |||
| 22 | cortex-m = "0.7.1" | 22 | cortex-m = "0.7.1" |
| 23 | critical-section = "0.2.1" | 23 | critical-section = "0.2.1" |
| 24 | 24 | ||
| 25 | rp2040-pac2 = { git = "https://github.com/embassy-rs/rp2040-pac2", rev="91fa122b4923fdc02462a39ec109b161aedb29b4", features = ["rt"] } | 25 | rp2040-pac2 = { git = "https://github.com/embassy-rs/rp2040-pac2", rev="2ce29ba58ad904d3995ce65bb46807e853f1fbf9", features = ["rt"] } |
| 26 | #rp2040-pac2 = { path = "../../rp/rp2040-pac2" } | 26 | #rp2040-pac2 = { path = "../../rp/rp2040-pac2", features = ["rt"] } |
| 27 | embedded-hal = { version = "0.2.4", features = [ "unproven" ] } | 27 | embedded-hal = { version = "0.2.4", features = [ "unproven" ] } |
diff --git a/embassy-rp/src/clocks.rs b/embassy-rp/src/clocks.rs index 6835ecad3..3082cd0d7 100644 --- a/embassy-rp/src/clocks.rs +++ b/embassy-rp/src/clocks.rs | |||
| @@ -4,6 +4,7 @@ use crate::{pac, reset}; | |||
| 4 | 4 | ||
| 5 | const XOSC_MHZ: u32 = 12; | 5 | const XOSC_MHZ: u32 = 12; |
| 6 | 6 | ||
| 7 | /// safety: must be called exactly once at bootup | ||
| 7 | pub unsafe fn init() { | 8 | pub unsafe fn init() { |
| 8 | // Reset everything except: | 9 | // Reset everything except: |
| 9 | // - QSPI (we're using it to run this code!) | 10 | // - QSPI (we're using it to run this code!) |
diff --git a/embassy-rp/src/lib.rs b/embassy-rp/src/lib.rs index 1e6417c98..aefc86c00 100644 --- a/embassy-rp/src/lib.rs +++ b/embassy-rp/src/lib.rs | |||
| @@ -11,10 +11,12 @@ pub use rp2040_pac2 as pac; | |||
| 11 | pub(crate) mod fmt; | 11 | pub(crate) mod fmt; |
| 12 | 12 | ||
| 13 | pub mod interrupt; | 13 | pub mod interrupt; |
| 14 | pub use embassy_macros::interrupt; | ||
| 14 | 15 | ||
| 15 | pub mod dma; | 16 | pub mod dma; |
| 16 | pub mod gpio; | 17 | pub mod gpio; |
| 17 | pub mod spi; | 18 | pub mod spi; |
| 19 | pub mod timer; | ||
| 18 | pub mod uart; | 20 | pub mod uart; |
| 19 | 21 | ||
| 20 | mod clocks; | 22 | mod clocks; |
| @@ -64,6 +66,11 @@ embassy_extras::peripherals! { | |||
| 64 | SPI0, | 66 | SPI0, |
| 65 | SPI1, | 67 | SPI1, |
| 66 | 68 | ||
| 69 | TIMER_ALARM0, | ||
| 70 | TIMER_ALARM1, | ||
| 71 | TIMER_ALARM2, | ||
| 72 | TIMER_ALARM3, | ||
| 73 | |||
| 67 | DMA_CH0, | 74 | DMA_CH0, |
| 68 | DMA_CH1, | 75 | DMA_CH1, |
| 69 | DMA_CH2, | 76 | DMA_CH2, |
| @@ -100,6 +107,7 @@ pub fn init(_config: config::Config) -> Peripherals { | |||
| 100 | 107 | ||
| 101 | unsafe { | 108 | unsafe { |
| 102 | clocks::init(); | 109 | clocks::init(); |
| 110 | timer::init(); | ||
| 103 | } | 111 | } |
| 104 | 112 | ||
| 105 | peripherals | 113 | peripherals |
diff --git a/embassy-rp/src/timer.rs b/embassy-rp/src/timer.rs new file mode 100644 index 000000000..5baa02607 --- /dev/null +++ b/embassy-rp/src/timer.rs | |||
| @@ -0,0 +1,187 @@ | |||
| 1 | use core::cell::Cell; | ||
| 2 | use critical_section::CriticalSection; | ||
| 3 | use embassy::interrupt::{Interrupt, InterruptExt}; | ||
| 4 | use embassy::util::CriticalSectionMutex as Mutex; | ||
| 5 | |||
| 6 | use crate::{interrupt, pac}; | ||
| 7 | |||
| 8 | struct AlarmState { | ||
| 9 | timestamp: Cell<u64>, | ||
| 10 | callback: Cell<Option<(fn(*mut ()), *mut ())>>, | ||
| 11 | } | ||
| 12 | unsafe impl Send for AlarmState {} | ||
| 13 | |||
| 14 | const ALARM_COUNT: usize = 4; | ||
| 15 | const DUMMY_ALARM: AlarmState = AlarmState { | ||
| 16 | timestamp: Cell::new(0), | ||
| 17 | callback: Cell::new(None), | ||
| 18 | }; | ||
| 19 | |||
| 20 | static ALARMS: Mutex<[AlarmState; ALARM_COUNT]> = Mutex::new([DUMMY_ALARM; ALARM_COUNT]); | ||
| 21 | |||
| 22 | fn now() -> u64 { | ||
| 23 | loop { | ||
| 24 | unsafe { | ||
| 25 | let hi = pac::TIMER.timerawh().read(); | ||
| 26 | let lo = pac::TIMER.timerawl().read(); | ||
| 27 | let hi2 = pac::TIMER.timerawh().read(); | ||
| 28 | if hi == hi2 { | ||
| 29 | return (hi as u64) << 32 | (lo as u64); | ||
| 30 | } | ||
| 31 | } | ||
| 32 | } | ||
| 33 | } | ||
| 34 | |||
| 35 | struct Timer; | ||
| 36 | impl embassy::time::Clock for Timer { | ||
| 37 | fn now(&self) -> u64 { | ||
| 38 | now() | ||
| 39 | } | ||
| 40 | } | ||
| 41 | |||
| 42 | pub trait AlarmInstance { | ||
| 43 | fn alarm_num(&self) -> usize; | ||
| 44 | } | ||
| 45 | |||
| 46 | impl AlarmInstance for crate::peripherals::TIMER_ALARM0 { | ||
| 47 | fn alarm_num(&self) -> usize { | ||
| 48 | 0 | ||
| 49 | } | ||
| 50 | } | ||
| 51 | impl AlarmInstance for crate::peripherals::TIMER_ALARM1 { | ||
| 52 | fn alarm_num(&self) -> usize { | ||
| 53 | 1 | ||
| 54 | } | ||
| 55 | } | ||
| 56 | impl AlarmInstance for crate::peripherals::TIMER_ALARM2 { | ||
| 57 | fn alarm_num(&self) -> usize { | ||
| 58 | 2 | ||
| 59 | } | ||
| 60 | } | ||
| 61 | impl AlarmInstance for crate::peripherals::TIMER_ALARM3 { | ||
| 62 | fn alarm_num(&self) -> usize { | ||
| 63 | 3 | ||
| 64 | } | ||
| 65 | } | ||
| 66 | |||
| 67 | pub struct Alarm<T: AlarmInstance> { | ||
| 68 | inner: T, | ||
| 69 | } | ||
| 70 | |||
| 71 | impl<T: AlarmInstance> Alarm<T> { | ||
| 72 | pub fn new(inner: T) -> Self { | ||
| 73 | Self { inner } | ||
| 74 | } | ||
| 75 | } | ||
| 76 | |||
| 77 | impl<T: AlarmInstance> embassy::time::Alarm for Alarm<T> { | ||
| 78 | fn set_callback(&self, callback: fn(*mut ()), ctx: *mut ()) { | ||
| 79 | let n = self.inner.alarm_num(); | ||
| 80 | critical_section::with(|cs| { | ||
| 81 | let alarm = &ALARMS.borrow(cs)[n]; | ||
| 82 | alarm.callback.set(Some((callback, ctx))); | ||
| 83 | }) | ||
| 84 | } | ||
| 85 | |||
| 86 | fn set(&self, timestamp: u64) { | ||
| 87 | let n = self.inner.alarm_num(); | ||
| 88 | |||
| 89 | critical_section::with(|cs| { | ||
| 90 | let alarm = &ALARMS.borrow(cs)[n]; | ||
| 91 | alarm.timestamp.set(timestamp); | ||
| 92 | |||
| 93 | // Arm it. | ||
| 94 | // Note that we're not checking the high bits at all. This means the irq may fire early | ||
| 95 | // if the alarm is more than 72 minutes (2^32 us) in the future. This is OK, since on irq fire | ||
| 96 | // it is checked if the alarm time has passed. | ||
| 97 | unsafe { pac::TIMER.alarm(n).write_value(timestamp as u32) }; | ||
| 98 | |||
| 99 | let now = now(); | ||
| 100 | |||
| 101 | // If alarm timestamp has passed, trigger it instantly. | ||
| 102 | // This disarms it. | ||
| 103 | if timestamp <= now { | ||
| 104 | trigger_alarm(n, cs); | ||
| 105 | } | ||
| 106 | }) | ||
| 107 | } | ||
| 108 | |||
| 109 | fn clear(&self) { | ||
| 110 | self.set(u64::MAX); | ||
| 111 | } | ||
| 112 | } | ||
| 113 | |||
| 114 | fn check_alarm(n: usize) { | ||
| 115 | critical_section::with(|cs| { | ||
| 116 | let alarm = &ALARMS.borrow(cs)[n]; | ||
| 117 | let timestamp = alarm.timestamp.get(); | ||
| 118 | if timestamp <= now() { | ||
| 119 | trigger_alarm(n, cs) | ||
| 120 | } else { | ||
| 121 | // Not elapsed, arm it again. | ||
| 122 | // This can happen if it was set more than 2^32 us in the future. | ||
| 123 | unsafe { pac::TIMER.alarm(n).write_value(timestamp as u32) }; | ||
| 124 | } | ||
| 125 | }); | ||
| 126 | |||
| 127 | // clear the irq | ||
| 128 | unsafe { pac::TIMER.intr().write(|w| w.set_alarm(n, true)) } | ||
| 129 | } | ||
| 130 | |||
| 131 | fn trigger_alarm(n: usize, cs: CriticalSection) { | ||
| 132 | // disarm | ||
| 133 | unsafe { pac::TIMER.armed().write(|w| w.set_armed(1 << n)) } | ||
| 134 | |||
| 135 | let alarm = &ALARMS.borrow(cs)[n]; | ||
| 136 | alarm.timestamp.set(u64::MAX); | ||
| 137 | |||
| 138 | // Call after clearing alarm, so the callback can set another alarm. | ||
| 139 | if let Some((f, ctx)) = alarm.callback.get() { | ||
| 140 | f(ctx); | ||
| 141 | } | ||
| 142 | } | ||
| 143 | |||
| 144 | /// safety: must be called exactly once at bootup | ||
| 145 | pub unsafe fn init() { | ||
| 146 | // init alarms | ||
| 147 | critical_section::with(|cs| { | ||
| 148 | let alarms = ALARMS.borrow(cs); | ||
| 149 | for a in alarms { | ||
| 150 | a.timestamp.set(u64::MAX); | ||
| 151 | } | ||
| 152 | }); | ||
| 153 | |||
| 154 | // enable all irqs | ||
| 155 | pac::TIMER.inte().write(|w| { | ||
| 156 | w.set_alarm(0, true); | ||
| 157 | w.set_alarm(1, true); | ||
| 158 | w.set_alarm(2, true); | ||
| 159 | w.set_alarm(3, true); | ||
| 160 | }); | ||
| 161 | interrupt::TIMER_IRQ_0::steal().enable(); | ||
| 162 | interrupt::TIMER_IRQ_1::steal().enable(); | ||
| 163 | interrupt::TIMER_IRQ_2::steal().enable(); | ||
| 164 | interrupt::TIMER_IRQ_3::steal().enable(); | ||
| 165 | |||
| 166 | embassy::time::set_clock(&Timer); | ||
| 167 | } | ||
| 168 | |||
| 169 | #[interrupt] | ||
| 170 | unsafe fn TIMER_IRQ_0() { | ||
| 171 | check_alarm(0) | ||
| 172 | } | ||
| 173 | |||
| 174 | #[interrupt] | ||
| 175 | unsafe fn TIMER_IRQ_1() { | ||
| 176 | check_alarm(1) | ||
| 177 | } | ||
| 178 | |||
| 179 | #[interrupt] | ||
| 180 | unsafe fn TIMER_IRQ_2() { | ||
| 181 | check_alarm(2) | ||
| 182 | } | ||
| 183 | |||
| 184 | #[interrupt] | ||
| 185 | unsafe fn TIMER_IRQ_3() { | ||
| 186 | check_alarm(3) | ||
| 187 | } | ||
diff --git a/examples/rp/src/bin/blinky.rs b/examples/rp/src/bin/blinky.rs index a162ed2f7..9c4767458 100644 --- a/examples/rp/src/bin/blinky.rs +++ b/examples/rp/src/bin/blinky.rs | |||
| @@ -11,6 +11,7 @@ mod example_common; | |||
| 11 | 11 | ||
| 12 | use defmt::*; | 12 | use defmt::*; |
| 13 | use embassy::executor::Spawner; | 13 | use embassy::executor::Spawner; |
| 14 | use embassy::time::{Duration, Timer}; | ||
| 14 | use embassy_rp::{gpio, Peripherals}; | 15 | use embassy_rp::{gpio, Peripherals}; |
| 15 | use gpio::{Level, Output}; | 16 | use gpio::{Level, Output}; |
| 16 | 17 | ||
| @@ -21,10 +22,10 @@ async fn main(_spawner: Spawner, p: Peripherals) { | |||
| 21 | loop { | 22 | loop { |
| 22 | info!("led on!"); | 23 | info!("led on!"); |
| 23 | led.set_high(); | 24 | led.set_high(); |
| 24 | cortex_m::asm::delay(1_000_000); | 25 | Timer::after(Duration::from_secs(1)).await; |
| 25 | 26 | ||
| 26 | info!("led off!"); | 27 | info!("led off!"); |
| 27 | led.set_low(); | 28 | led.set_low(); |
| 28 | cortex_m::asm::delay(1_000_000); | 29 | Timer::after(Duration::from_secs(1)).await; |
| 29 | } | 30 | } |
| 30 | } | 31 | } |
