diff --git a/Cargo.toml b/Cargo.toml index e90fd66c..462d246b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -134,3 +134,7 @@ required-features = ["rt", "stm32f407"] [[example]] name = "qei" required-features = ["rt", "stm32f411"] + +[[example]] +name = "systick-blinky" +required-features = ["rt", "stm32f429"] diff --git a/examples/systick-blinky.rs b/examples/systick-blinky.rs new file mode 100644 index 00000000..68bf5093 --- /dev/null +++ b/examples/systick-blinky.rs @@ -0,0 +1,50 @@ +#![deny(unsafe_code)] +#![no_main] +#![no_std] + +// Halt on panic +#[allow(unused_extern_crates)] // NOTE(allow) bug rust-lang/rust#53964 +extern crate panic_halt; // panic handler + +use cortex_m; +use cortex_m_rt::entry; +use stm32f4xx_hal as hal; + +use crate::hal::{prelude::*, stm32, systick::SysTickTime}; + +#[entry] +fn main() -> ! { + if let (Some(dp), Some(cp)) = ( + stm32::Peripherals::take(), + cortex_m::peripheral::Peripherals::take(), + ) { + // Set up the LEDs. On the STM32F429I-DISC[O1] they are connected to pin PG13/14. + let gpiog = dp.GPIOG.split(); + let mut led1 = gpiog.pg13.into_push_pull_output(); + let mut led2 = gpiog.pg14.into_push_pull_output(); + + // Set up the system clock. We want to run at 48MHz for this one. + let rcc = dp.RCC.constrain(); + let clocks = rcc.cfgr.sysclk(48.mhz()).freeze(); + + // Create a delay abstraction based on SysTick + let mut systime = cp.SYST.to_systemtime(1000.hz(), clocks); + + loop { + // On for 1s, off for 1s. + led1.set_high().unwrap(); + led2.set_low().unwrap(); + systime.delay_ms(1000_u32); + led1.set_low().unwrap(); + led2.set_high().unwrap(); + systime.delay_ms(1000_u32); + // Also you can get the current time + let _t = systime.as_secs_f64(); // in seconds as f64 + let _t = systime.as_millis(); // in milliseconds as u64 + let _t = systime.as_micros(); // in microseconds as u64 + let _t = systime.as_nanos(); // in nanoseconds as u64 + } + } + + loop {} +} diff --git a/src/lib.rs b/src/lib.rs index be09750d..9f085b3f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -176,6 +176,8 @@ pub mod signature; #[cfg(feature = "device-selected")] pub mod spi; #[cfg(feature = "device-selected")] +pub mod systick; +#[cfg(feature = "device-selected")] pub mod time; #[cfg(feature = "device-selected")] pub mod timer; diff --git a/src/systick.rs b/src/systick.rs new file mode 100644 index 00000000..3b7626a6 --- /dev/null +++ b/src/systick.rs @@ -0,0 +1,114 @@ +//! SYSTEMTIME based on systick + +use cortex_m::interrupt::free; +use cortex_m::peripheral::SYST; +use cortex_m_rt::exception; + +use crate::rcc::Clocks; +use crate::time::Hertz; +use crate::timer::{Event, Timer}; + +use embedded_hal::blocking::delay::{DelayMs, DelayUs}; + +pub trait SysTickTime { + fn to_systemtime(self, timeout: T, clocks: Clocks) -> SysTime + where + T: Into; +} +impl SysTickTime for SYST { + fn to_systemtime(self, timeout_hz: T, clocks: Clocks) -> SysTime + where + T: Into, + { + let timeout_hz = timeout_hz.into(); + + let mut systime = SysTickTimeStatic { + countdown: Timer::syst(self, timeout_hz, clocks), + systick: 0, + tick_to_ns: 1_000_000_000 / timeout_hz.0, + }; + + systime.countdown.listen(Event::TimeOut); + + free(|_| unsafe { + SYSTIME = Some(systime); + }); + + SysTime {} + } +} + +struct SysTickTimeStatic { + countdown: Timer, + systick: u64, + tick_to_ns: u32, +} + +// there can only be one! +static mut SYSTIME: Option = None; + +pub struct SysTime {} +impl SysTime { + /// return time in ns + pub fn as_nanos(&self) -> u64 { + let mut tick_to_ns = 0u32; + free(|_| unsafe { + if let Some(systime) = &SYSTIME { + tick_to_ns = systime.tick_to_ns; + (&systime.systick as *const u64).read_volatile() + } else { + 0 + } + }) * tick_to_ns as u64 + } + /// return time in us + pub fn as_micros(&self) -> u64 { + self.as_nanos() / 1_000_000 + } + /// return time in ms + pub fn as_millis(&self) -> u64 { + self.as_nanos() / 1_000_000 + } + /// return time in seconds, as f64 + pub fn as_secs_f64(&self) -> f64 { + self.as_nanos() as f64 / 1_000_000_000f64 + } + /// return time in seconds, as f32 + pub fn as_secs_f32(&self) -> f32 { + self.as_nanos() as f32 / 1_000_000_000f32 + } + + /// delay n ns + /// note: this function depends on the systick interrupt, + /// so do not use it from other interrupts (with higher priority). + fn delay_ns_(&self, ns: u64) { + let timeout = self.as_nanos() + ns; + while timeout >= self.as_nanos() {} + } +} +// Implement DelayUs/DelayMs for various integer types +macro_rules! impl_DelayIntT { + (for $($t:ty),+) => {$( + impl DelayMs<$t> for SysTime { + fn delay_ms(&mut self, ms: $t) { + self.delay_ns_(ms as u64 * 1_000_000); + } + } + impl DelayUs<$t> for SysTime { + fn delay_us(&mut self, us: $t) { + self.delay_ns_(us as u64 * 1_000); + } + } + )*} +} +impl_DelayIntT!(for usize,u64,u32,u16,u8,i64,i32,i16,i8); + +// there can only be one! +#[exception] +fn SysTick() { + free(|_| unsafe { + if let Some(systime) = &mut SYSTIME { + systime.systick += 1; + } + }); +}