1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
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) }
    }
}