Skip to content

System time based on static systick isr counter #149

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 2 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -134,3 +134,7 @@ required-features = ["rt", "stm32f407"]
[[example]]
name = "qei"
required-features = ["rt", "stm32f411"]

[[example]]
name = "systick-blinky"
required-features = ["rt", "stm32f429"]
50 changes: 50 additions & 0 deletions examples/systick-blinky.rs
Original file line number Diff line number Diff line change
@@ -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 {}
}
2 changes: 2 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
114 changes: 114 additions & 0 deletions src/systick.rs
Original file line number Diff line number Diff line change
@@ -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<T>(self, timeout: T, clocks: Clocks) -> SysTime
where
T: Into<Hertz>;
}
impl SysTickTime for SYST {
fn to_systemtime<T>(self, timeout_hz: T, clocks: Clocks) -> SysTime
where
T: Into<Hertz>,
{
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<SYST>,
systick: u64,
tick_to_ns: u32,
}

// there can only be one!
static mut SYSTIME: Option<SysTickTimeStatic> = 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;
}
});
}