freertos_next/
timers.rs

1use crate::InterruptContext;
2use crate::base::*;
3use crate::base_type::v1::*;
4use crate::shim::*;
5use crate::units::*;
6
7unsafe impl Send for Timer {}
8unsafe impl Sync for Timer {}
9
10/// A FreeRTOS software timer.
11///
12/// Note that all operations on a timer are processed by a FreeRTOS internal task
13/// that receives messages in a queue. Every operation has an associated waiting time
14/// for that queue to get unblocked.
15pub struct Timer {
16    handle: FreeRtosTimerHandle,
17}
18
19/// Helper builder for a new software timer.
20pub struct TimerBuilder<D: DurationTicks> {
21    name: String,
22    period: D,
23    auto_reload: bool,
24}
25
26impl<D: DurationTicks> TimerBuilder<D> {
27    /// Set the name of the timer.
28    pub fn set_name(&mut self, name: &str) -> &mut Self {
29        self.name = name.into();
30        self
31    }
32
33    /// Set the period of the timer.
34    pub fn set_period(&mut self, period: D) -> &mut Self {
35        self.period = period;
36        self
37    }
38
39    /// Should the timer be automatically reloaded?
40    pub fn set_auto_reload(&mut self, auto_reload: bool) -> &mut Self {
41        self.auto_reload = auto_reload;
42        self
43    }
44
45    /// Try to create the new timer.
46    ///
47    /// Note that the newly created timer must be started.
48    pub fn create<F>(&self, callback: F) -> Result<Timer, FreeRtosError>
49    where
50        F: Fn(&Timer) -> (),
51        F: Send + 'static,
52    {
53        Timer::spawn(
54            self.name.as_str(),
55            self.period.to_ticks(),
56            self.auto_reload,
57            callback,
58        )
59    }
60}
61
62impl Timer {
63    /// Create a new timer builder.
64    pub fn new<D: DurationTicks>(period: D) -> TimerBuilder<D> {
65        TimerBuilder {
66            name: "timer".into(),
67            period: period,
68            auto_reload: true,
69        }
70    }
71
72    /// Create a timer from a raw handle.
73    ///
74    /// # Safety
75    ///
76    /// `handle` must be a valid FreeRTOS timer handle.
77    #[inline]
78    pub unsafe fn from_raw_handle(handle: FreeRtosTimerHandle) -> Self {
79        Self { handle }
80    }
81    #[inline]
82    pub fn raw_handle(&self) -> FreeRtosTimerHandle {
83        self.handle
84    }
85
86    unsafe fn spawn_inner<'a>(
87        name: &str,
88        period_ticks: FreeRtosTickType,
89        auto_reload: bool,
90        callback: Box<dyn Fn(&Timer) + Send + 'a>,
91    ) -> Result<Timer, FreeRtosError> {
92        let f = Box::new(callback);
93        let param_ptr = &*f as *const _ as *mut _;
94
95        let (success, timer_handle) = {
96            let name = name.as_bytes();
97            let name_len = name.len();
98
99            let ret = unsafe {
100                freertos_rs_timer_create(
101                    name.as_ptr(),
102                    name_len as u8,
103                    period_ticks,
104                    if auto_reload { 1 } else { 0 },
105                    param_ptr,
106                    timer_callback,
107                )
108            };
109
110            ((ret as usize) != 0, ret)
111        };
112
113        if success {
114            mem::forget(f);
115        } else {
116            return Err(FreeRtosError::OutOfMemory);
117        }
118
119        extern "C" fn timer_callback(handle: FreeRtosTimerHandle) -> () {
120            unsafe {
121                {
122                    let timer = Timer { handle };
123                    if let Ok(callback_ptr) = timer.get_id() {
124                        let b = Box::from_raw(callback_ptr as *mut Box<dyn Fn(&Timer)>);
125                        b(&timer);
126                        let _ = Box::into_raw(b);
127                    }
128                    mem::forget(timer);
129                }
130            }
131        }
132
133        Ok(Timer {
134            handle: timer_handle as *const _,
135        })
136    }
137
138    fn spawn<F>(
139        name: &str,
140        period_tick: FreeRtosTickType,
141        auto_reload: bool,
142        callback: F,
143    ) -> Result<Timer, FreeRtosError>
144    where
145        F: Fn(&Timer) -> (),
146        F: Send + 'static,
147    {
148        unsafe { Timer::spawn_inner(name, period_tick, auto_reload, Box::new(callback)) }
149    }
150
151    /// Start the timer.
152    pub fn start<D: DurationTicks>(&self, block_time: D) -> Result<(), FreeRtosError> {
153        unsafe {
154            if freertos_rs_timer_start(self.handle, block_time.to_ticks()) == 0 {
155                Ok(())
156            } else {
157                Err(FreeRtosError::Timeout)
158            }
159        }
160    }
161
162    /// Start the timer from an interrupt.
163    pub fn start_from_isr(&self, context: &mut InterruptContext) -> Result<(), FreeRtosError> {
164        unsafe {
165            if freertos_rs_timer_start_from_isr(self.handle, context.get_task_field_mut()) == 0 {
166                Ok(())
167            } else {
168                Err(FreeRtosError::QueueSendTimeout)
169            }
170        }
171    }
172
173    /// Stop the timer.
174    pub fn stop<D: DurationTicks>(&self, block_time: D) -> Result<(), FreeRtosError> {
175        unsafe {
176            if freertos_rs_timer_stop(self.handle, block_time.to_ticks()) == 0 {
177                Ok(())
178            } else {
179                Err(FreeRtosError::Timeout)
180            }
181        }
182    }
183
184    /// Change the period of the timer.
185    pub fn change_period<D: DurationTicks>(
186        &self,
187        block_time: D,
188        new_period: D,
189    ) -> Result<(), FreeRtosError> {
190        unsafe {
191            if freertos_rs_timer_change_period(
192                self.handle,
193                block_time.to_ticks(),
194                new_period.to_ticks(),
195            ) == 0
196            {
197                Ok(())
198            } else {
199                Err(FreeRtosError::Timeout)
200            }
201        }
202    }
203
204    /// Detach this timer from Rust's memory management. The timer will still be active and
205    /// will consume the memory.
206    ///
207    /// Can be used for timers that will never be changed and don't need to stay in scope.
208    ///
209    /// This method is safe because resource leak is safe in Rust.
210    pub fn detach(self) {
211        mem::forget(self);
212    }
213
214    fn get_id(&self) -> Result<FreeRtosVoidPtr, FreeRtosError> {
215        unsafe { Ok(freertos_rs_timer_get_id(self.handle)) }
216    }
217}
218
219impl Drop for Timer {
220    #[allow(unused_must_use)]
221    fn drop(&mut self) {
222        unsafe {
223            if let Ok(callback_ptr) = self.get_id() {
224                // free the memory
225                Box::from_raw(callback_ptr as *mut Box<dyn Fn(Timer)>);
226            }
227
228            // todo: configurable timeout?
229            freertos_rs_timer_delete(self.handle, Duration::ms(1000).to_ticks());
230        }
231    }
232}