diff options
Diffstat (limited to 'cortex-m/src/peripheral/syst.rs')
-rw-r--r-- | cortex-m/src/peripheral/syst.rs | 185 |
1 files changed, 185 insertions, 0 deletions
diff --git a/cortex-m/src/peripheral/syst.rs b/cortex-m/src/peripheral/syst.rs new file mode 100644 index 0000000..345acc2 --- /dev/null +++ b/cortex-m/src/peripheral/syst.rs @@ -0,0 +1,185 @@ +//! SysTick: System Timer + +use volatile_register::{RO, RW}; + +use crate::peripheral::SYST; + +/// Register block +#[repr(C)] +pub struct RegisterBlock { + /// Control and Status + pub csr: RW<u32>, + /// Reload Value + pub rvr: RW<u32>, + /// Current Value + pub cvr: RW<u32>, + /// Calibration Value + pub calib: RO<u32>, +} + +/// SysTick clock source +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum SystClkSource { + /// Core-provided clock + Core, + /// External reference clock + External, +} + +const SYST_COUNTER_MASK: u32 = 0x00ff_ffff; + +const SYST_CSR_ENABLE: u32 = 1 << 0; +const SYST_CSR_TICKINT: u32 = 1 << 1; +const SYST_CSR_CLKSOURCE: u32 = 1 << 2; +const SYST_CSR_COUNTFLAG: u32 = 1 << 16; + +const SYST_CALIB_SKEW: u32 = 1 << 30; +const SYST_CALIB_NOREF: u32 = 1 << 31; + +impl SYST { + /// Clears current value to 0 + /// + /// After calling `clear_current()`, the next call to `has_wrapped()` will return `false`. + #[inline] + pub fn clear_current(&mut self) { + unsafe { self.cvr.write(0) } + } + + /// Disables counter + #[inline] + pub fn disable_counter(&mut self) { + unsafe { self.csr.modify(|v| v & !SYST_CSR_ENABLE) } + } + + /// Disables SysTick interrupt + #[inline] + pub fn disable_interrupt(&mut self) { + unsafe { self.csr.modify(|v| v & !SYST_CSR_TICKINT) } + } + + /// Enables counter + /// + /// *NOTE* The reference manual indicates that: + /// + /// "The SysTick counter reload and current value are undefined at reset, the correct + /// initialization sequence for the SysTick counter is: + /// + /// - Program reload value + /// - Clear current value + /// - Program Control and Status register" + /// + /// The sequence translates to `self.set_reload(x); self.clear_current(); self.enable_counter()` + #[inline] + pub fn enable_counter(&mut self) { + unsafe { self.csr.modify(|v| v | SYST_CSR_ENABLE) } + } + + /// Enables SysTick interrupt + #[inline] + pub fn enable_interrupt(&mut self) { + unsafe { self.csr.modify(|v| v | SYST_CSR_TICKINT) } + } + + /// Gets clock source + /// + /// *NOTE* This takes `&mut self` because the read operation is side effectful and can clear the + /// bit that indicates that the timer has wrapped (cf. `SYST.has_wrapped`) + #[inline] + pub fn get_clock_source(&mut self) -> SystClkSource { + // NOTE(unsafe) atomic read with no side effects + if self.csr.read() & SYST_CSR_CLKSOURCE != 0 { + SystClkSource::Core + } else { + SystClkSource::External + } + } + + /// Gets current value + #[inline] + pub fn get_current() -> u32 { + // NOTE(unsafe) atomic read with no side effects + unsafe { (*Self::PTR).cvr.read() } + } + + /// Gets reload value + #[inline] + pub fn get_reload() -> u32 { + // NOTE(unsafe) atomic read with no side effects + unsafe { (*Self::PTR).rvr.read() } + } + + /// Returns the reload value with which the counter would wrap once per 10 + /// ms + /// + /// Returns `0` if the value is not known (e.g. because the clock can + /// change dynamically). + #[inline] + pub fn get_ticks_per_10ms() -> u32 { + // NOTE(unsafe) atomic read with no side effects + unsafe { (*Self::PTR).calib.read() & SYST_COUNTER_MASK } + } + + /// Checks if an external reference clock is available + #[inline] + pub fn has_reference_clock() -> bool { + // NOTE(unsafe) atomic read with no side effects + unsafe { (*Self::PTR).calib.read() & SYST_CALIB_NOREF == 0 } + } + + /// Checks if the counter wrapped (underflowed) since the last check + /// + /// *NOTE* This takes `&mut self` because the read operation is side effectful and will clear + /// the bit of the read register. + #[inline] + pub fn has_wrapped(&mut self) -> bool { + self.csr.read() & SYST_CSR_COUNTFLAG != 0 + } + + /// Checks if counter is enabled + /// + /// *NOTE* This takes `&mut self` because the read operation is side effectful and can clear the + /// bit that indicates that the timer has wrapped (cf. `SYST.has_wrapped`) + #[inline] + pub fn is_counter_enabled(&mut self) -> bool { + self.csr.read() & SYST_CSR_ENABLE != 0 + } + + /// Checks if SysTick interrupt is enabled + /// + /// *NOTE* This takes `&mut self` because the read operation is side effectful and can clear the + /// bit that indicates that the timer has wrapped (cf. `SYST.has_wrapped`) + #[inline] + pub fn is_interrupt_enabled(&mut self) -> bool { + self.csr.read() & SYST_CSR_TICKINT != 0 + } + + /// Checks if the calibration value is precise + /// + /// Returns `false` if using the reload value returned by + /// `get_ticks_per_10ms()` may result in a period significantly deviating + /// from 10 ms. + #[inline] + pub fn is_precise() -> bool { + // NOTE(unsafe) atomic read with no side effects + unsafe { (*Self::PTR).calib.read() & SYST_CALIB_SKEW == 0 } + } + + /// Sets clock source + #[inline] + pub fn set_clock_source(&mut self, clk_source: SystClkSource) { + match clk_source { + SystClkSource::External => unsafe { self.csr.modify(|v| v & !SYST_CSR_CLKSOURCE) }, + SystClkSource::Core => unsafe { self.csr.modify(|v| v | SYST_CSR_CLKSOURCE) }, + } + } + + /// Sets reload value + /// + /// Valid values are between `1` and `0x00ffffff`. + /// + /// *NOTE* To make the timer wrap every `N` ticks set the reload value to `N - 1` + #[inline] + pub fn set_reload(&mut self, value: u32) { + unsafe { self.rvr.write(value) } + } +} |