freertos_rust/
timers.rs

1use crate::prelude::v1::*;
2use crate::base::*;
3use crate::shim::*;
4use crate::units::*;
5
6unsafe impl Send for Timer {}
7unsafe impl Sync for Timer {}
8
9/// A FreeRTOS software timer.
10///
11/// Note that all operations on a timer are processed by a FreeRTOS internal task
12/// that receives messages in a queue. Every operation has an associated waiting time
13/// for that queue to get unblocked.
14pub struct Timer {
15    handle: FreeRtosTimerHandle,
16    detached: bool
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 F: Fn(Timer) -> (),
50              F: Send + 'static
51    {
52        Timer::spawn(self.name.as_str(), self.period.to_ticks(), self.auto_reload, callback)
53    }
54}
55
56
57
58impl Timer {
59    /// Create a new timer builder.
60    pub fn new<D: DurationTicks>(period: D) -> TimerBuilder<D> {
61        TimerBuilder {
62            name: "timer".into(),
63            period: period,
64            auto_reload: true
65        }
66    }
67
68    unsafe fn spawn_inner<'a>(name: &str,
69                              period_ticks: FreeRtosTickType,
70                              auto_reload: bool,
71                              callback: Box<dyn Fn(Timer) + Send + 'a>,)
72                              -> Result<Timer, FreeRtosError> {
73        let f = Box::new(callback);
74        let param_ptr = &*f as *const _ as *mut _;
75
76        let (success, timer_handle) = {
77            let name = name.as_bytes();
78            let name_len = name.len();
79            let mut _timer_handle = mem::zeroed::<CVoid>();
80
81            let ret = freertos_rs_timer_create(name.as_ptr(),
82                                               name_len as u8,
83                                               period_ticks,
84                                               if auto_reload { 1 } else { 0 },
85                                               param_ptr,
86                                               timer_callback);
87
88            ((ret as usize) != 0, ret)
89        };
90
91        if success {
92            mem::forget(f);
93        } else {
94            return Err(FreeRtosError::OutOfMemory);
95        }
96
97        extern "C" fn timer_callback(handle: FreeRtosTimerHandle) -> () {
98            unsafe {                
99                {
100                    let timer = Timer {
101                        handle: handle,
102                        detached: true
103                    };
104                    if let Ok(callback_ptr) = timer.get_id() {
105                        let b = Box::from_raw(callback_ptr as *mut Box<dyn Fn(Timer)>);
106                        b(timer);
107                        Box::into_raw(b);
108                    }
109                }
110            }
111        }
112
113        Ok(Timer { 
114            handle: timer_handle as *const _,
115            detached: false
116        })
117    }
118
119
120    fn spawn<F>(name: &str,
121                period_tick: FreeRtosTickType,
122                auto_reload: bool,
123                callback: F)
124                -> Result<Timer, FreeRtosError>
125        where F: Fn(Timer) -> (),
126              F: Send + 'static
127    {
128        unsafe {
129            Timer::spawn_inner(name, period_tick, auto_reload, Box::new(callback))
130        }
131    }
132
133    /// Start the timer.
134    pub fn start<D: DurationTicks>(&self, block_time: D) -> Result<(), FreeRtosError> {
135        unsafe {
136            if freertos_rs_timer_start(self.handle, block_time.to_ticks()) == 0 {
137                Ok(())
138            } else {
139                Err(FreeRtosError::Timeout)
140            }
141        }
142    }
143
144    /// Stop the timer.
145    pub fn stop<D: DurationTicks>(&self, block_time: D) -> Result<(), FreeRtosError> {
146        unsafe {
147            if freertos_rs_timer_stop(self.handle, block_time.to_ticks()) == 0 {
148                Ok(())
149            } else {
150                Err(FreeRtosError::Timeout)
151            }
152        }
153    }
154
155    /// Change the period of the timer.
156    pub fn change_period<D: DurationTicks>(&self, block_time: D, new_period: D) -> Result<(), FreeRtosError> {
157        unsafe {
158            if freertos_rs_timer_change_period(self.handle, block_time.to_ticks(), new_period.to_ticks()) == 0 {
159                Ok(())
160            } else {
161                Err(FreeRtosError::Timeout)
162            }
163        }
164    }
165
166    /// Detach this timer from Rust's memory management. The timer will still be active and
167    /// will consume the memory.
168    ///
169    /// Can be used for timers that will never be changed and don't need to stay in scope.
170    pub unsafe fn detach(mut self) {
171        self.detached = true;
172    }
173
174    fn get_id(&self) -> Result<FreeRtosVoidPtr, FreeRtosError> {
175        unsafe {
176            Ok(freertos_rs_timer_get_id(self.handle))
177        }
178    }
179}
180
181
182impl Drop for Timer {
183    #[allow(unused_must_use)]
184    fn drop(&mut self) {
185        if self.detached == true { return; }
186
187        unsafe {
188            if let Ok(callback_ptr) = self.get_id() {
189                // free the memory
190                Box::from_raw(callback_ptr as *mut Box<dyn Fn(Timer)>);
191            }
192            
193            // todo: configurable timeout?
194            freertos_rs_timer_delete(self.handle, Duration::ms(1000).to_ticks());
195        }
196    }
197}