freertos_rs/
task.rs

1use crate::prelude::v1::*;
2use crate::base::*;
3use crate::shim::*;
4use crate::units::*;
5use crate::utils::*;
6use crate::isr::*;
7
8unsafe impl Send for Task {}
9
10/// Handle for a FreeRTOS task
11#[derive(Debug)]
12pub struct Task {
13    task_handle: FreeRtosTaskHandle,
14}
15
16/// Task's execution priority. Low priority numbers denote low priority tasks.
17#[derive(Debug, Copy, Clone)]
18pub struct TaskPriority(pub u8);
19
20/// Notification to be sent to a task.
21#[derive(Debug, Copy, Clone)]
22pub enum TaskNotification {
23    /// Send the event, unblock the task, the task's notification value isn't changed.
24    NoAction,
25    /// Perform a logical or with the task's notification value.
26    SetBits(u32),
27    /// Increment the task's notification value by one.
28    Increment,
29    /// Set the task's notification value to this value.
30    OverwriteValue(u32),
31    /// Try to set the task's notification value to this value. Succeeds
32    /// only if the task has no pending notifications. Otherwise, the
33    /// notification call will fail.
34    SetValue(u32),
35}
36
37impl TaskNotification {
38    fn to_freertos(&self) -> (u32, u8) {
39        match *self {
40            TaskNotification::NoAction => (0, 0),
41            TaskNotification::SetBits(v) => (v, 1),
42            TaskNotification::Increment => (0, 2),
43            TaskNotification::OverwriteValue(v) => (v, 3),
44            TaskNotification::SetValue(v) => (v, 4),
45        }
46    }
47}
48
49impl TaskPriority {
50    fn to_freertos(&self) -> FreeRtosUBaseType {
51        self.0 as FreeRtosUBaseType
52    }
53}
54
55/// Helper for spawning a new task. Instantiate with [`Task::new()`].
56///
57/// [`Task::new()`]: struct.Task.html#method.new
58pub struct TaskBuilder {
59    task_name: String,
60    task_stack_size: u16,
61    task_priority: TaskPriority,
62}
63
64impl TaskBuilder {
65    /// Set the task's name.
66    pub fn name(&mut self, name: &str) -> &mut Self {
67        self.task_name = name.into();
68        self
69    }
70
71    /// Set the stack size, in words.
72    pub fn stack_size(&mut self, stack_size: u16) -> &mut Self {
73        self.task_stack_size = stack_size;
74        self
75    }
76
77    /// Set the task's priority.
78    pub fn priority(&mut self, priority: TaskPriority) -> &mut Self {
79        self.task_priority = priority;
80        self
81    }
82
83    /// Start a new task that can't return a value.
84    pub fn start<F>(&self, func: F) -> Result<Task, FreeRtosError>
85        where F: FnOnce() -> (),
86              F: Send + 'static
87    {
88
89        Task::spawn(&self.task_name,
90                    self.task_stack_size,
91                    self.task_priority,
92                    func)
93
94    }
95}
96
97
98
99impl Task {
100    /// Prepare a builder object for the new task.
101    pub fn new() -> TaskBuilder {
102        TaskBuilder {
103            task_name: "rust_task".into(),
104            task_stack_size: 1024,
105            task_priority: TaskPriority(1),
106        }
107    }
108
109    unsafe fn spawn_inner<'a>(f: Box<dyn FnOnce()>,
110                              name: &str,
111                              stack_size: u16,
112                              priority: TaskPriority)
113                              -> Result<Task, FreeRtosError> {
114        let f = Box::new(f);
115        let param_ptr = &*f as *const _ as *mut _;
116
117        let (success, task_handle) = {
118            let name = name.as_bytes();
119            let name_len = name.len();
120            let mut task_handle = mem::zeroed::<CVoid>();
121
122            let ret = freertos_rs_spawn_task(thread_start,
123                                             param_ptr,
124                                             name.as_ptr(),
125                                             name_len as u8,
126                                             stack_size,
127                                             priority.to_freertos(),
128                                             &mut task_handle);
129
130            (ret == 0, task_handle)
131        };
132
133        if success {
134            mem::forget(f);
135        } else {
136            return Err(FreeRtosError::OutOfMemory);
137        }
138
139        extern "C" fn thread_start(main: *mut CVoid) -> *mut CVoid {
140            unsafe {
141                {
142                    let b = Box::from_raw(main as *mut Box<dyn FnOnce()>);
143                    b();
144                }
145
146                freertos_rs_delete_task(0 as *const _);
147            }
148
149            0 as *mut _
150        }
151
152        Ok(Task { task_handle: task_handle as usize as *const _ })
153    }
154
155    fn spawn<F>(name: &str,
156                stack_size: u16,
157                priority: TaskPriority,
158                f: F)
159                -> Result<Task, FreeRtosError>
160        where F: FnOnce() -> (),
161              F: Send + 'static
162    {
163        unsafe {
164            return Task::spawn_inner(Box::new(f), name, stack_size, priority);
165        }        
166    }
167
168
169    /// Get the name of the current task.
170    pub fn get_name(&self) -> Result<String, ()> {
171        unsafe {
172            let name_ptr = freertos_rs_task_get_name(self.task_handle);
173            let name = str_from_c_string(name_ptr);
174            if let Ok(name) = name {
175                return Ok(name);
176            }
177
178            Err(())
179        }
180    }
181
182    /// Try to find the task of the current execution context.
183    pub fn current() -> Result<Task, FreeRtosError> {
184        unsafe {
185            let t = freertos_rs_get_current_task();
186            if t != 0 as *const _ {
187                Ok(Task { task_handle: t })
188            } else {
189                Err(FreeRtosError::TaskNotFound)
190            }
191        }
192    }
193
194    /// Forcibly set the notification value for this task.
195    pub fn set_notification_value(&self, val: u32) {
196        self.notify(TaskNotification::OverwriteValue(val))
197    }
198
199    /// Notify this task.
200    pub fn notify(&self, notification: TaskNotification) {
201        unsafe {
202            let n = notification.to_freertos();
203            freertos_rs_task_notify(self.task_handle, n.0, n.1);
204        }
205    }
206
207    /// Notify this task from an interrupt.
208    pub fn notify_from_isr(&self,
209                           context: &InterruptContext,
210                           notification: TaskNotification)
211                           -> Result<(), FreeRtosError> {
212        unsafe {
213            let n = notification.to_freertos();
214            let t = freertos_rs_task_notify_isr(self.task_handle,
215                                                n.0,
216                                                n.1,
217                                                context.get_task_field_mut());
218            if t != 0 {
219                Err(FreeRtosError::QueueFull)
220            } else {
221                Ok(())
222            }
223        }
224    }
225
226    /// Take the notification and either clear the notification value or decrement it by one.
227    pub fn take_notification<D: DurationTicks>(&self, clear: bool, wait_for: D) -> u32 {
228        unsafe { freertos_rs_task_notify_take(if clear { 1 } else { 0 }, wait_for.to_ticks()) }
229    }
230
231    /// Wait for a notification to be posted.
232    pub fn wait_for_notification<D: DurationTicks>(&self,
233                                 clear_bits_enter: u32,
234                                 clear_bits_exit: u32,
235                                 wait_for: D)
236                                 -> Result<u32, FreeRtosError>
237    {        
238        let mut val = 0;
239        let r = unsafe { 
240            freertos_rs_task_notify_wait(clear_bits_enter,
241                                         clear_bits_exit,
242                                         &mut val as *mut _,
243                                         wait_for.to_ticks())
244        };
245
246        if r == 0 {
247            Ok(val)
248        } else {
249            Err(FreeRtosError::Timeout)
250        }
251    }
252
253    /// Get the minimum amount of stack that was ever left on this task.
254    pub fn get_stack_high_water_mark(&self) -> u32 {
255        unsafe {
256            freertos_rs_get_stack_high_water_mark(self.task_handle) as u32
257        }
258    }
259}
260
261/// Helper methods to be performed on the task that is currently executing.
262pub struct CurrentTask;
263impl CurrentTask {
264    /// Delay the execution of the current task.
265    pub fn delay<D: DurationTicks>(delay: D) {
266        unsafe {
267            freertos_rs_vTaskDelay(delay.to_ticks());
268        }
269    }
270
271    /// Get the minimum amount of stack that was ever left on the current task.
272    pub fn get_stack_high_water_mark() -> u32 {
273        unsafe {
274            freertos_rs_get_stack_high_water_mark(0 as FreeRtosTaskHandle) as u32
275        }
276    }
277}
278
279#[derive(Debug)]
280pub struct FreeRtosSchedulerState {
281    pub tasks: Vec<FreeRtosTaskStatus>,
282    pub total_run_time: u32
283}
284
285impl fmt::Display for FreeRtosSchedulerState {
286    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {        
287        fmt.write_str("FreeRTOS tasks\r\n")?;
288
289        write!(fmt, "{id: <6} | {name: <16} | {state: <9} | {priority: <8} | {stack: >10} | {cpu_abs: >10} | {cpu_rel: >4}\r\n",
290            id = "ID",
291            name = "Name",
292            state = "State",
293            priority = "Priority",
294            stack = "Stack left",
295            cpu_abs = "CPU",
296            cpu_rel = "%"
297            )?;
298
299        for task in &self.tasks {
300            write!(fmt, "{id: <6} | {name: <16} | {state: <9} | {priority: <8} | {stack: >10} | {cpu_abs: >10} | {cpu_rel: >4}\r\n",
301            id = task.task_number,
302            name = task.name,
303            state = format!("{:?}", task.task_state),
304            priority = task.current_priority.0,
305            stack = task.stack_high_water_mark,
306            cpu_abs = task.run_time_counter,
307            cpu_rel = if self.total_run_time > 0 && task.run_time_counter <= self.total_run_time {
308                let p = (((task.run_time_counter as u64) * 100) / self.total_run_time as u64) as u32;
309                let ps = if p == 0 && task.run_time_counter > 0 {
310                    "<1".to_string()
311                } else {
312                    p.to_string()
313                };
314                format!("{: >3}%", ps)
315            } else {
316                "-".to_string()
317            }
318            )?;
319        }
320        
321        if self.total_run_time > 0 {
322            write!(fmt, "Total run time: {}\r\n", self.total_run_time)?;
323        }
324
325        Ok(())
326    }
327}
328
329#[derive(Debug)]
330pub struct FreeRtosTaskStatus {
331    pub task: Task,
332    pub name: String,
333    pub task_number: FreeRtosUBaseType,
334    pub task_state: FreeRtosTaskState,
335    pub current_priority: TaskPriority,
336    pub base_priority: TaskPriority,
337    pub run_time_counter: FreeRtosUnsignedLong,
338    pub stack_high_water_mark: FreeRtosUnsignedShort
339}
340
341
342pub struct FreeRtosUtils;
343impl FreeRtosUtils {
344    pub fn get_tick_count() -> FreeRtosTickType {
345        unsafe { freertos_rs_xTaskGetTickCount() }
346    }
347
348    pub fn get_tick_count_duration() -> Duration {
349        Duration::ticks(Self::get_tick_count())
350    }
351
352    pub fn get_number_of_tasks() -> usize {
353        unsafe { freertos_rs_get_number_of_tasks() as usize }
354    }
355
356    pub fn get_all_tasks(tasks_len: Option<usize>) -> FreeRtosSchedulerState {        
357        let tasks_len = tasks_len.unwrap_or(Self::get_number_of_tasks());
358        let mut tasks = Vec::with_capacity(tasks_len as usize);
359        let mut total_run_time = 0;
360        
361        unsafe {            
362            let filled = freertos_rs_get_system_state(tasks.as_mut_ptr(), tasks_len as FreeRtosUBaseType, &mut total_run_time);
363            tasks.set_len(filled as usize);
364        }
365
366        let tasks = tasks.into_iter().map(|t| {
367            FreeRtosTaskStatus {
368                task: Task { task_handle: t.handle },
369                name: unsafe { str_from_c_string(t.task_name) }.unwrap_or_else(|_| String::from("?")),
370                task_number: t.task_number,
371                task_state: t.task_state,
372                current_priority: TaskPriority(t.current_priority as u8),
373                base_priority: TaskPriority(t.base_priority as u8),
374                run_time_counter: t.run_time_counter,
375                stack_high_water_mark: t.stack_high_water_mark
376            }
377        }).collect();
378
379        FreeRtosSchedulerState {
380            tasks: tasks,
381            total_run_time: total_run_time
382        }
383    }
384}