From 7547c8d8d6800dd661dcda9f62ba988eefadd38e Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 12 Jul 2021 02:45:42 +0200 Subject: [PATCH] rp/timer: add --- embassy-macros/src/chip/rp.rs | 6 ++ embassy-rp/Cargo.toml | 4 +- embassy-rp/src/clocks.rs | 1 + embassy-rp/src/lib.rs | 8 ++ embassy-rp/src/timer.rs | 187 ++++++++++++++++++++++++++++++++++ examples/rp/src/bin/blinky.rs | 5 +- 6 files changed, 207 insertions(+), 4 deletions(-) create mode 100644 embassy-rp/src/timer.rs diff --git a/embassy-macros/src/chip/rp.rs b/embassy-macros/src/chip/rp.rs index 04211f8f..d40a4496 100644 --- a/embassy-macros/src/chip/rp.rs +++ b/embassy-macros/src/chip/rp.rs @@ -3,10 +3,16 @@ use proc_macro2::TokenStream; use quote::quote; pub fn generate(embassy_prefix: &ModulePrefix, config: syn::Expr) -> TokenStream { + let embassy_path = embassy_prefix.append("embassy").path(); let embassy_rp_path = embassy_prefix.append("embassy_rp").path(); quote!( use #embassy_rp_path::{interrupt, peripherals}; let p = #embassy_rp_path::init(#config); + + let alarm = unsafe { <#embassy_rp_path::peripherals::TIMER_ALARM0 as #embassy_path::util::Steal>::steal() }; + let mut alarm = #embassy_rp_path::timer::Alarm::new(alarm); + let alarm = unsafe { make_static(&mut alarm) }; + executor.set_alarm(alarm); ) } diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml index 503d2c70..ba9984a8 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml @@ -22,6 +22,6 @@ cortex-m-rt = "0.6.13" cortex-m = "0.7.1" critical-section = "0.2.1" -rp2040-pac2 = { git = "https://github.com/embassy-rs/rp2040-pac2", rev="91fa122b4923fdc02462a39ec109b161aedb29b4", features = ["rt"] } -#rp2040-pac2 = { path = "../../rp/rp2040-pac2" } +rp2040-pac2 = { git = "https://github.com/embassy-rs/rp2040-pac2", rev="2ce29ba58ad904d3995ce65bb46807e853f1fbf9", features = ["rt"] } +#rp2040-pac2 = { path = "../../rp/rp2040-pac2", features = ["rt"] } embedded-hal = { version = "0.2.4", features = [ "unproven" ] } diff --git a/embassy-rp/src/clocks.rs b/embassy-rp/src/clocks.rs index 6835ecad..3082cd0d 100644 --- a/embassy-rp/src/clocks.rs +++ b/embassy-rp/src/clocks.rs @@ -4,6 +4,7 @@ use crate::{pac, reset}; const XOSC_MHZ: u32 = 12; +/// safety: must be called exactly once at bootup pub unsafe fn init() { // Reset everything except: // - QSPI (we're using it to run this code!) diff --git a/embassy-rp/src/lib.rs b/embassy-rp/src/lib.rs index 1e6417c9..aefc86c0 100644 --- a/embassy-rp/src/lib.rs +++ b/embassy-rp/src/lib.rs @@ -11,10 +11,12 @@ pub use rp2040_pac2 as pac; pub(crate) mod fmt; pub mod interrupt; +pub use embassy_macros::interrupt; pub mod dma; pub mod gpio; pub mod spi; +pub mod timer; pub mod uart; mod clocks; @@ -64,6 +66,11 @@ embassy_extras::peripherals! { SPI0, SPI1, + TIMER_ALARM0, + TIMER_ALARM1, + TIMER_ALARM2, + TIMER_ALARM3, + DMA_CH0, DMA_CH1, DMA_CH2, @@ -100,6 +107,7 @@ pub fn init(_config: config::Config) -> Peripherals { unsafe { clocks::init(); + timer::init(); } peripherals diff --git a/embassy-rp/src/timer.rs b/embassy-rp/src/timer.rs new file mode 100644 index 00000000..5baa0260 --- /dev/null +++ b/embassy-rp/src/timer.rs @@ -0,0 +1,187 @@ +use core::cell::Cell; +use critical_section::CriticalSection; +use embassy::interrupt::{Interrupt, InterruptExt}; +use embassy::util::CriticalSectionMutex as Mutex; + +use crate::{interrupt, pac}; + +struct AlarmState { + timestamp: Cell, + callback: Cell>, +} +unsafe impl Send for AlarmState {} + +const ALARM_COUNT: usize = 4; +const DUMMY_ALARM: AlarmState = AlarmState { + timestamp: Cell::new(0), + callback: Cell::new(None), +}; + +static ALARMS: Mutex<[AlarmState; ALARM_COUNT]> = Mutex::new([DUMMY_ALARM; ALARM_COUNT]); + +fn now() -> u64 { + loop { + unsafe { + let hi = pac::TIMER.timerawh().read(); + let lo = pac::TIMER.timerawl().read(); + let hi2 = pac::TIMER.timerawh().read(); + if hi == hi2 { + return (hi as u64) << 32 | (lo as u64); + } + } + } +} + +struct Timer; +impl embassy::time::Clock for Timer { + fn now(&self) -> u64 { + now() + } +} + +pub trait AlarmInstance { + fn alarm_num(&self) -> usize; +} + +impl AlarmInstance for crate::peripherals::TIMER_ALARM0 { + fn alarm_num(&self) -> usize { + 0 + } +} +impl AlarmInstance for crate::peripherals::TIMER_ALARM1 { + fn alarm_num(&self) -> usize { + 1 + } +} +impl AlarmInstance for crate::peripherals::TIMER_ALARM2 { + fn alarm_num(&self) -> usize { + 2 + } +} +impl AlarmInstance for crate::peripherals::TIMER_ALARM3 { + fn alarm_num(&self) -> usize { + 3 + } +} + +pub struct Alarm { + inner: T, +} + +impl Alarm { + pub fn new(inner: T) -> Self { + Self { inner } + } +} + +impl embassy::time::Alarm for Alarm { + fn set_callback(&self, callback: fn(*mut ()), ctx: *mut ()) { + let n = self.inner.alarm_num(); + critical_section::with(|cs| { + let alarm = &ALARMS.borrow(cs)[n]; + alarm.callback.set(Some((callback, ctx))); + }) + } + + fn set(&self, timestamp: u64) { + let n = self.inner.alarm_num(); + + critical_section::with(|cs| { + let alarm = &ALARMS.borrow(cs)[n]; + alarm.timestamp.set(timestamp); + + // Arm it. + // Note that we're not checking the high bits at all. This means the irq may fire early + // if the alarm is more than 72 minutes (2^32 us) in the future. This is OK, since on irq fire + // it is checked if the alarm time has passed. + unsafe { pac::TIMER.alarm(n).write_value(timestamp as u32) }; + + let now = now(); + + // If alarm timestamp has passed, trigger it instantly. + // This disarms it. + if timestamp <= now { + trigger_alarm(n, cs); + } + }) + } + + fn clear(&self) { + self.set(u64::MAX); + } +} + +fn check_alarm(n: usize) { + critical_section::with(|cs| { + let alarm = &ALARMS.borrow(cs)[n]; + let timestamp = alarm.timestamp.get(); + if timestamp <= now() { + trigger_alarm(n, cs) + } else { + // Not elapsed, arm it again. + // This can happen if it was set more than 2^32 us in the future. + unsafe { pac::TIMER.alarm(n).write_value(timestamp as u32) }; + } + }); + + // clear the irq + unsafe { pac::TIMER.intr().write(|w| w.set_alarm(n, true)) } +} + +fn trigger_alarm(n: usize, cs: CriticalSection) { + // disarm + unsafe { pac::TIMER.armed().write(|w| w.set_armed(1 << n)) } + + let alarm = &ALARMS.borrow(cs)[n]; + alarm.timestamp.set(u64::MAX); + + // Call after clearing alarm, so the callback can set another alarm. + if let Some((f, ctx)) = alarm.callback.get() { + f(ctx); + } +} + +/// safety: must be called exactly once at bootup +pub unsafe fn init() { + // init alarms + critical_section::with(|cs| { + let alarms = ALARMS.borrow(cs); + for a in alarms { + a.timestamp.set(u64::MAX); + } + }); + + // enable all irqs + pac::TIMER.inte().write(|w| { + w.set_alarm(0, true); + w.set_alarm(1, true); + w.set_alarm(2, true); + w.set_alarm(3, true); + }); + interrupt::TIMER_IRQ_0::steal().enable(); + interrupt::TIMER_IRQ_1::steal().enable(); + interrupt::TIMER_IRQ_2::steal().enable(); + interrupt::TIMER_IRQ_3::steal().enable(); + + embassy::time::set_clock(&Timer); +} + +#[interrupt] +unsafe fn TIMER_IRQ_0() { + check_alarm(0) +} + +#[interrupt] +unsafe fn TIMER_IRQ_1() { + check_alarm(1) +} + +#[interrupt] +unsafe fn TIMER_IRQ_2() { + check_alarm(2) +} + +#[interrupt] +unsafe fn TIMER_IRQ_3() { + check_alarm(3) +} diff --git a/examples/rp/src/bin/blinky.rs b/examples/rp/src/bin/blinky.rs index a162ed2f..9c476745 100644 --- a/examples/rp/src/bin/blinky.rs +++ b/examples/rp/src/bin/blinky.rs @@ -11,6 +11,7 @@ mod example_common; use defmt::*; use embassy::executor::Spawner; +use embassy::time::{Duration, Timer}; use embassy_rp::{gpio, Peripherals}; use gpio::{Level, Output}; @@ -21,10 +22,10 @@ async fn main(_spawner: Spawner, p: Peripherals) { loop { info!("led on!"); led.set_high(); - cortex_m::asm::delay(1_000_000); + Timer::after(Duration::from_secs(1)).await; info!("led off!"); led.set_low(); - cortex_m::asm::delay(1_000_000); + Timer::after(Duration::from_secs(1)).await; } }