freertos_rust/
timers.rs

1use crate::InterruptContext;
2use crate::base::*;
3use crate::prelude::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 = freertos_rs_timer_create(
100                name.as_ptr(),
101                name_len as u8,
102                period_ticks,
103                if auto_reload { 1 } else { 0 },
104                param_ptr,
105                timer_callback,
106            );
107
108            ((ret as usize) != 0, ret)
109        };
110
111        if success {
112            mem::forget(f);
113        } else {
114            return Err(FreeRtosError::OutOfMemory);
115        }
116
117        extern "C" fn timer_callback(handle: FreeRtosTimerHandle) -> () {
118            unsafe {
119                {
120                    let timer = Timer { handle };
121                    if let Ok(callback_ptr) = timer.get_id() {
122                        let b = Box::from_raw(callback_ptr as *mut Box<dyn Fn(&Timer)>);
123                        b(&timer);
124                        let _ = Box::into_raw(b);
125                    }
126                    mem::forget(timer);
127                }
128            }
129        }
130
131        Ok(Timer {
132            handle: timer_handle as *const _,
133        })
134    }
135
136    fn spawn<F>(
137        name: &str,
138        period_tick: FreeRtosTickType,
139        auto_reload: bool,
140        callback: F,
141    ) -> Result<Timer, FreeRtosError>
142    where
143        F: Fn(&Timer) -> (),
144        F: Send + 'static,
145    {
146        unsafe { Timer::spawn_inner(name, period_tick, auto_reload, Box::new(callback)) }
147    }
148
149    /// Start the timer.
150    pub fn start<D: DurationTicks>(&self, block_time: D) -> Result<(), FreeRtosError> {
151        unsafe {
152            if freertos_rs_timer_start(self.handle, block_time.to_ticks()) == 0 {
153                Ok(())
154            } else {
155                Err(FreeRtosError::Timeout)
156            }
157        }
158    }
159
160    /// Start the timer from an interrupt.
161    pub fn start_from_isr(&self, context: &mut InterruptContext) -> Result<(), FreeRtosError> {
162        unsafe {
163            if freertos_rs_timer_start_from_isr(self.handle, context.get_task_field_mut()) == 0 {
164                Ok(())
165            } else {
166                Err(FreeRtosError::QueueSendTimeout)
167            }
168        }
169    }
170
171    /// Stop the timer.
172    pub fn stop<D: DurationTicks>(&self, block_time: D) -> Result<(), FreeRtosError> {
173        unsafe {
174            if freertos_rs_timer_stop(self.handle, block_time.to_ticks()) == 0 {
175                Ok(())
176            } else {
177                Err(FreeRtosError::Timeout)
178            }
179        }
180    }
181
182    /// Change the period of the timer.
183    pub fn change_period<D: DurationTicks>(
184        &self,
185        block_time: D,
186        new_period: D,
187    ) -> Result<(), FreeRtosError> {
188        unsafe {
189            if freertos_rs_timer_change_period(
190                self.handle,
191                block_time.to_ticks(),
192                new_period.to_ticks(),
193            ) == 0
194            {
195                Ok(())
196            } else {
197                Err(FreeRtosError::Timeout)
198            }
199        }
200    }
201
202    /// Detach this timer from Rust's memory management. The timer will still be active and
203    /// will consume the memory.
204    ///
205    /// Can be used for timers that will never be changed and don't need to stay in scope.
206    ///
207    /// This method is safe because resource leak is safe in Rust.
208    pub fn detach(self) {
209        mem::forget(self);
210    }
211
212    fn get_id(&self) -> Result<FreeRtosVoidPtr, FreeRtosError> {
213        unsafe { Ok(freertos_rs_timer_get_id(self.handle)) }
214    }
215}
216
217impl Drop for Timer {
218    #[allow(unused_must_use)]
219    fn drop(&mut self) {
220        unsafe {
221            if let Ok(callback_ptr) = self.get_id() {
222                // free the memory
223                Box::from_raw(callback_ptr as *mut Box<dyn Fn(Timer)>);
224            }
225
226            // todo: configurable timeout?
227            freertos_rs_timer_delete(self.handle, Duration::ms(1000).to_ticks());
228        }
229    }
230}