freertos_rust/
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        Task::spawn(&self.task_name,
89                    self.task_stack_size,
90                    self.task_priority,
91                    func)
92    }
93}
94
95
96impl Task {
97    /// Prepare a builder object for the new task.
98    pub fn new() -> TaskBuilder {
99        TaskBuilder {
100            task_name: "rust_task".into(),
101            task_stack_size: 1024,
102            task_priority: TaskPriority(1),
103        }
104    }
105
106    unsafe fn spawn_inner<'a>(f: Box<dyn FnOnce()>,
107                              name: &str,
108                              stack_size: u16,
109                              priority: TaskPriority)
110                              -> Result<Task, FreeRtosError> {
111        let f = Box::new(f);
112        let param_ptr = &*f as *const _ as *mut _;
113
114        let (success, task_handle) = {
115            let name = name.as_bytes();
116            let name_len = name.len();
117            let mut task_handle = mem::zeroed::<CVoid>();
118
119            let ret = freertos_rs_spawn_task(thread_start,
120                                             param_ptr,
121                                             name.as_ptr(),
122                                             name_len as u8,
123                                             stack_size,
124                                             priority.to_freertos(),
125                                             &mut task_handle);
126
127            (ret == 0, task_handle)
128        };
129
130        if success {
131            mem::forget(f);
132        } else {
133            return Err(FreeRtosError::OutOfMemory);
134        }
135
136        extern "C" fn thread_start(main: *mut CVoid) -> *mut CVoid {
137            unsafe {
138                {
139                    let b = Box::from_raw(main as *mut Box<dyn FnOnce()>);
140                    b();
141                }
142
143                freertos_rs_delete_task(0 as *const _);
144            }
145
146            0 as *mut _
147        }
148
149        Ok(Task { task_handle: task_handle as usize as *const _ })
150    }
151
152    fn spawn<F>(name: &str,
153                stack_size: u16,
154                priority: TaskPriority,
155                f: F)
156                -> Result<Task, FreeRtosError>
157        where F: FnOnce() -> (),
158              F: Send + 'static
159    {
160        unsafe {
161            return Task::spawn_inner(Box::new(f), name, stack_size, priority);
162        }
163    }
164
165
166    /// Get the name of the current task.
167    pub fn get_name(&self) -> Result<String, ()> {
168        unsafe {
169            let name_ptr = freertos_rs_task_get_name(self.task_handle);
170            let name = str_from_c_string(name_ptr);
171            if let Ok(name) = name {
172                return Ok(name);
173            }
174
175            Err(())
176        }
177    }
178
179    /// Try to find the task of the current execution context.
180    pub fn current() -> Result<Task, FreeRtosError> {
181        unsafe {
182            let t = freertos_rs_get_current_task();
183            if t != 0 as *const _ {
184                Ok(Task { task_handle: t })
185            } else {
186                Err(FreeRtosError::TaskNotFound)
187            }
188        }
189    }
190
191    /// Forcibly set the notification value for this task.
192    pub fn set_notification_value(&self, val: u32) {
193        self.notify(TaskNotification::OverwriteValue(val))
194    }
195
196    /// Notify this task.
197    pub fn notify(&self, notification: TaskNotification) {
198        unsafe {
199            let n = notification.to_freertos();
200            freertos_rs_task_notify(self.task_handle, n.0, n.1);
201        }
202    }
203
204    /// Notify this task from an interrupt.
205    pub fn notify_from_isr(&self,
206                           context: &InterruptContext,
207                           notification: TaskNotification)
208                           -> Result<(), FreeRtosError> {
209        unsafe {
210            let n = notification.to_freertos();
211            let t = freertos_rs_task_notify_isr(self.task_handle,
212                                                n.0,
213                                                n.1,
214                                                context.get_task_field_mut());
215            if t != 0 {
216                Err(FreeRtosError::QueueFull)
217            } else {
218                Ok(())
219            }
220        }
221    }
222
223    /// Take the notification and either clear the notification value or decrement it by one.
224    pub fn take_notification<D: DurationTicks>(&self, clear: bool, wait_for: D) -> u32 {
225        unsafe { freertos_rs_task_notify_take(if clear { 1 } else { 0 }, wait_for.to_ticks()) }
226    }
227
228    /// Wait for a notification to be posted.
229    pub fn wait_for_notification<D: DurationTicks>(&self,
230                                                   clear_bits_enter: u32,
231                                                   clear_bits_exit: u32,
232                                                   wait_for: D)
233                                                   -> Result<u32, FreeRtosError>
234    {
235        let mut val = 0;
236        let r = unsafe {
237            freertos_rs_task_notify_wait(clear_bits_enter,
238                                         clear_bits_exit,
239                                         &mut val as *mut _,
240                                         wait_for.to_ticks())
241        };
242
243        if r == 0 {
244            Ok(val)
245        } else {
246            Err(FreeRtosError::Timeout)
247        }
248    }
249
250    /// Get the minimum amount of stack that was ever left on this task.
251    pub fn get_stack_high_water_mark(&self) -> u32 {
252        unsafe {
253            freertos_rs_get_stack_high_water_mark(self.task_handle) as u32
254        }
255    }
256}
257
258/// Helper methods to be performed on the task that is currently executing.
259pub struct CurrentTask;
260
261impl CurrentTask {
262    /// Delay the execution of the current task.
263    pub fn delay<D: DurationTicks>(delay: D) {
264        unsafe {
265            freertos_rs_vTaskDelay(delay.to_ticks());
266        }
267    }
268
269    /// Get the minimum amount of stack that was ever left on the current task.
270    pub fn get_stack_high_water_mark() -> u32 {
271        unsafe {
272            freertos_rs_get_stack_high_water_mark(0 as FreeRtosTaskHandle) as u32
273        }
274    }
275}
276
277#[derive(Debug)]
278pub struct FreeRtosSchedulerState {
279    pub tasks: Vec<FreeRtosTaskStatus>,
280    pub total_run_time: u32,
281}
282
283impl fmt::Display for FreeRtosSchedulerState {
284    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
285        fmt.write_str("FreeRTOS tasks\r\n")?;
286
287        write!(fmt, "{id: <6} | {name: <16} | {state: <9} | {priority: <8} | {stack: >10} | {cpu_abs: >10} | {cpu_rel: >4}\r\n",
288               id = "ID",
289               name = "Name",
290               state = "State",
291               priority = "Priority",
292               stack = "Stack left",
293               cpu_abs = "CPU",
294               cpu_rel = "%"
295        )?;
296
297        for task in &self.tasks {
298            write!(fmt, "{id: <6} | {name: <16} | {state: <9} | {priority: <8} | {stack: >10} | {cpu_abs: >10} | {cpu_rel: >4}\r\n",
299                   id = task.task_number,
300                   name = task.name,
301                   state = format!("{:?}", task.task_state),
302                   priority = task.current_priority.0,
303                   stack = task.stack_high_water_mark,
304                   cpu_abs = task.run_time_counter,
305                   cpu_rel = if self.total_run_time > 0 && task.run_time_counter <= self.total_run_time {
306                       let p = (((task.run_time_counter as u64) * 100) / self.total_run_time as u64) as u32;
307                       let ps = if p == 0 && task.run_time_counter > 0 {
308                           "<1".to_string()
309                       } else {
310                           p.to_string()
311                       };
312                       format!("{: >3}%", ps)
313                   } else {
314                       "-".to_string()
315                   }
316            )?;
317        }
318
319        if self.total_run_time > 0 {
320            write!(fmt, "Total run time: {}\r\n", self.total_run_time)?;
321        }
322
323        Ok(())
324    }
325}
326
327#[derive(Debug)]
328pub struct FreeRtosTaskStatus {
329    pub task: Task,
330    pub name: String,
331    pub task_number: FreeRtosUBaseType,
332    pub task_state: FreeRtosTaskState,
333    pub current_priority: TaskPriority,
334    pub base_priority: TaskPriority,
335    pub run_time_counter: FreeRtosUnsignedLong,
336    pub stack_high_water_mark: FreeRtosUnsignedShort,
337}
338
339
340pub struct FreeRtosUtils;
341
342impl FreeRtosUtils {
343    // Should only be used for testing purpose!
344    pub fn invoke_assert() { unsafe { freertos_rs_invoke_configASSERT(); } }
345    pub fn start_scheduler() -> ! { unsafe { freertos_rs_vTaskStartScheduler(); } }
346
347    pub fn get_tick_count() -> FreeRtosTickType {
348        unsafe { freertos_rs_xTaskGetTickCount() }
349    }
350
351    pub fn get_tick_count_duration() -> Duration {
352        Duration::ticks(Self::get_tick_count())
353    }
354
355    pub fn get_number_of_tasks() -> usize {
356        unsafe { freertos_rs_get_number_of_tasks() as usize }
357    }
358
359    pub fn get_all_tasks(tasks_len: Option<usize>) -> FreeRtosSchedulerState {
360        let tasks_len = tasks_len.unwrap_or(Self::get_number_of_tasks());
361        let mut tasks = Vec::with_capacity(tasks_len as usize);
362        let mut total_run_time = 0;
363
364        unsafe {
365            let filled = freertos_rs_get_system_state(tasks.as_mut_ptr(), tasks_len as FreeRtosUBaseType, &mut total_run_time);
366            tasks.set_len(filled as usize);
367        }
368
369        let tasks = tasks.into_iter().map(|t| {
370            FreeRtosTaskStatus {
371                task: Task { task_handle: t.handle },
372                name: unsafe { str_from_c_string(t.task_name) }.unwrap_or_else(|_| String::from("?")),
373                task_number: t.task_number,
374                task_state: t.task_state,
375                current_priority: TaskPriority(t.current_priority as u8),
376                base_priority: TaskPriority(t.base_priority as u8),
377                run_time_counter: t.run_time_counter,
378                stack_high_water_mark: t.stack_high_water_mark,
379            }
380        }).collect();
381
382        FreeRtosSchedulerState {
383            tasks: tasks,
384            total_run_time: total_run_time,
385        }
386    }
387}