freertos_rs/
timers.rs

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