freertos_rust/
task.rs

1use crate::base::*;
2use crate::isr::*;
3use crate::prelude::v1::*;
4use crate::shim::*;
5use crate::units::*;
6use crate::utils::*;
7
8unsafe impl Send for Task {}
9
10/// Handle for a FreeRTOS task
11#[derive(Debug, Clone)]
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
86        F: FnOnce(Task) -> (),
87        F: Send + 'static,
88    {
89        Task::spawn(
90            &self.task_name,
91            self.task_stack_size,
92            self.task_priority,
93            func,
94        )
95    }
96}
97
98impl Task {
99    /// Prepare a builder object for the new task.
100    pub fn new() -> TaskBuilder {
101        TaskBuilder {
102            task_name: "rust_task".into(),
103            task_stack_size: 1024,
104            task_priority: TaskPriority(1),
105        }
106    }
107
108    /// # Safety
109    ///
110    /// `handle` must be a valid FreeRTOS task handle.
111    #[inline]
112    pub unsafe fn from_raw_handle(handle: FreeRtosTaskHandle) -> Self {
113        Self { task_handle: handle }
114    }
115    #[inline]
116    pub fn raw_handle(&self) -> FreeRtosTaskHandle {
117        self.task_handle
118    }
119
120    pub fn suspend_all() {
121      unsafe {
122          freertos_rs_vTaskSuspendAll();
123      }
124    }
125
126    /// # Safety
127    ///
128    /// For every call to this method there must be a matching previous call to
129    /// `Task::suspend_all`.
130    pub unsafe fn resume_all() {
131        unsafe {
132            freertos_rs_xTaskResumeAll();
133        }
134    }
135
136    unsafe fn spawn_inner<'a>(
137        f: Box<dyn FnOnce(Task)>,
138        name: &str,
139        stack_size: u16,
140        priority: TaskPriority,
141    ) -> Result<Task, FreeRtosError> {
142        let f = Box::new(f);
143        let param_ptr = &*f as *const _ as *mut _;
144
145        let (success, task_handle) = {
146            let name = name.as_bytes();
147            let name_len = name.len();
148            let mut task_handle = core::ptr::null();
149
150            let ret = freertos_rs_spawn_task(
151                thread_start,
152                param_ptr,
153                name.as_ptr(),
154                name_len as u8,
155                stack_size,
156                priority.to_freertos(),
157                &mut task_handle,
158            );
159
160            (ret == 0, task_handle)
161        };
162
163        if success {
164            mem::forget(f);
165        } else {
166            return Err(FreeRtosError::OutOfMemory);
167        }
168
169        use core::ffi::c_void;
170        extern "C" fn thread_start(main: *mut c_void) -> *mut c_void {
171            unsafe {
172                {
173                    let b = Box::from_raw(main as *mut Box<dyn FnOnce(Task)>);
174                    b(Task {
175                        task_handle: freertos_rs_get_current_task(),
176                    });
177                }
178
179                #[cfg(feature = "delete_task")]
180                freertos_rs_delete_task(0 as *const _);
181            }
182
183            #[cfg(feature = "delete_task")]
184            return 0 as *mut _;
185            #[cfg(not(feature = "delete_task"))]
186            panic!("Not allowed to quit the task!");
187        }
188
189        Ok(Task { task_handle })
190    }
191
192    fn spawn<F>(
193        name: &str,
194        stack_size: u16,
195        priority: TaskPriority,
196        f: F,
197    ) -> Result<Task, FreeRtosError>
198    where
199        F: FnOnce(Task) -> (),
200        F: Send + 'static,
201    {
202        unsafe {
203            return Task::spawn_inner(Box::new(f), name, stack_size, priority);
204        }
205    }
206
207    /// Get the name of the current task.
208    pub fn get_name(&self) -> Result<String, ()> {
209        unsafe {
210            let name_ptr = freertos_rs_task_get_name(self.task_handle);
211            let name = str_from_c_string(name_ptr);
212            if let Ok(name) = name {
213                return Ok(name.to_string());
214            }
215
216            Err(())
217        }
218    }
219
220    /// Try to find the task of the current execution context.
221    pub fn current() -> Result<Task, FreeRtosError> {
222        unsafe {
223            let t = freertos_rs_get_current_task();
224            if t != 0 as *const _ {
225                Ok(Task { task_handle: t })
226            } else {
227                Err(FreeRtosError::TaskNotFound)
228            }
229        }
230    }
231
232    /// Forcibly set the notification value for this task.
233    pub fn set_notification_value(&self, val: u32) {
234        self.notify(TaskNotification::OverwriteValue(val))
235    }
236
237    /// Notify this task.
238    pub fn notify(&self, notification: TaskNotification) {
239        unsafe {
240            let n = notification.to_freertos();
241            freertos_rs_task_notify(self.task_handle, n.0, n.1);
242        }
243    }
244
245    /// Notify this task from an interrupt.
246    pub fn notify_from_isr(
247        &self,
248        context: &mut InterruptContext,
249        notification: TaskNotification,
250    ) -> Result<(), FreeRtosError> {
251        unsafe {
252            let n = notification.to_freertos();
253            let t = freertos_rs_task_notify_isr(
254                self.task_handle,
255                n.0,
256                n.1,
257                context.get_task_field_mut(),
258            );
259            if t != 0 {
260                Err(FreeRtosError::QueueFull)
261            } else {
262                Ok(())
263            }
264        }
265    }
266
267    /// Wait for a notification to be posted.
268    pub fn wait_for_notification<D: DurationTicks>(
269        &self,
270        clear_bits_enter: u32,
271        clear_bits_exit: u32,
272        wait_for: D,
273    ) -> Result<u32, FreeRtosError> {
274        let mut val = 0;
275        let r = unsafe {
276            freertos_rs_task_notify_wait(
277                clear_bits_enter,
278                clear_bits_exit,
279                &mut val as *mut _,
280                wait_for.to_ticks(),
281            )
282        };
283
284        if r == 0 {
285            Ok(val)
286        } else {
287            Err(FreeRtosError::Timeout)
288        }
289    }
290
291    /// Get the minimum amount of stack that was ever left on this task.
292    pub fn get_stack_high_water_mark(&self) -> u32 {
293        unsafe { freertos_rs_get_stack_high_water_mark(self.task_handle) as u32 }
294    }
295
296    pub fn get_id(&self) -> Result<FreeRtosBaseType, FreeRtosError> {
297        let task_id = unsafe { freertos_rs_uxTaskGetTaskNumber(self.task_handle) };
298        if task_id == 0 {
299            Err(FreeRtosError::TaskNotFound)
300        } else {
301            Ok(task_id)
302        }
303    }
304
305    pub fn set_id(&mut self, value: FreeRtosUBaseType) {
306        unsafe { freertos_rs_vTaskSetTaskNumber(self.task_handle, value) };
307    }
308}
309
310/// Helper methods to be performed on the task that is currently executing.
311pub struct CurrentTask;
312
313impl CurrentTask {
314    /// Delay the execution of the current task.
315    pub fn delay<D: DurationTicks>(delay: D) {
316        unsafe {
317            freertos_rs_vTaskDelay(delay.to_ticks());
318        }
319    }
320
321    pub fn suspend() {
322        unsafe {
323            freertos_rs_suspend_task(0 as FreeRtosTaskHandle)
324        }
325    }
326
327    /// Take the notification and either clear the notification value or decrement it by one.
328    pub fn take_notification<D: DurationTicks>(clear: bool, wait_for: D) -> u32 {
329        unsafe { freertos_rs_task_notify_take(if clear { 1 } else { 0 }, wait_for.to_ticks()) }
330    }
331
332    /// Get the minimum amount of stack that was ever left on the current task.
333    pub fn get_stack_high_water_mark() -> u32 {
334        unsafe { freertos_rs_get_stack_high_water_mark(0 as FreeRtosTaskHandle) as u32 }
335    }
336}
337
338#[derive(Debug)]
339pub struct FreeRtosSystemState {
340    pub tasks: Vec<FreeRtosTaskStatus>,
341    pub total_run_time: u32,
342}
343
344impl fmt::Display for FreeRtosSystemState {
345    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
346        fmt.write_str("FreeRTOS tasks\r\n")?;
347
348        write!(fmt, "{id: <6} | {name: <16} | {state: <9} | {priority: <8} | {stack: >10} | {cpu_abs: >10} | {cpu_rel: >4}\r\n",
349               id = "ID",
350               name = "Name",
351               state = "State",
352               priority = "Priority",
353               stack = "Stack left",
354               cpu_abs = "CPU",
355               cpu_rel = "%"
356        )?;
357
358        for task in &self.tasks {
359            write!(fmt, "{id: <6} | {name: <16} | {state: <9} | {priority: <8} | {stack: >10} | {cpu_abs: >10} | {cpu_rel: >4}\r\n",
360                   id = task.task_number,
361                   name = task.name,
362                   state = format!("{:?}", task.task_state),
363                   priority = task.current_priority.0,
364                   stack = task.stack_high_water_mark,
365                   cpu_abs = task.run_time_counter,
366                   cpu_rel = if self.total_run_time > 0 && task.run_time_counter <= self.total_run_time {
367                       let p = (((task.run_time_counter as u64) * 100) / self.total_run_time as u64) as u32;
368                       let ps = if p == 0 && task.run_time_counter > 0 {
369                           "<1".to_string()
370                       } else {
371                           p.to_string()
372                       };
373                       format!("{: >3}%", ps)
374                   } else {
375                       "-".to_string()
376                   }
377            )?;
378        }
379
380        if self.total_run_time > 0 {
381            write!(fmt, "Total run time: {}\r\n", self.total_run_time)?;
382        }
383
384        Ok(())
385    }
386}
387
388#[derive(Debug)]
389pub struct FreeRtosTaskStatus {
390    pub task: Task,
391    pub name: String,
392    pub task_number: FreeRtosUBaseType,
393    pub task_state: FreeRtosTaskState,
394    pub current_priority: TaskPriority,
395    pub base_priority: TaskPriority,
396    pub run_time_counter: FreeRtosUnsignedLong,
397    pub stack_high_water_mark: FreeRtosUnsignedShort,
398}
399
400pub struct FreeRtosUtils;
401
402#[derive(Debug, Clone, Copy, PartialEq)]
403pub enum FreeRtosSchedulerState {
404  Suspended,
405  NotStarted,
406  Running
407}
408
409impl FreeRtosUtils {
410    // Should only be used for testing purpose!
411    pub fn invoke_assert() {
412        unsafe {
413            freertos_rs_invoke_configASSERT();
414        }
415    }
416    pub fn start_scheduler() -> ! {
417        unsafe {
418            freertos_rs_vTaskStartScheduler();
419        }
420    }
421
422    pub fn scheduler_state() -> FreeRtosSchedulerState {
423      unsafe {
424        match freertos_rt_xTaskGetSchedulerState() {
425          0 => FreeRtosSchedulerState::Suspended,
426          1 => FreeRtosSchedulerState::NotStarted,
427          2 => FreeRtosSchedulerState::Running,
428          _ => unreachable!(),
429        }
430      }
431    }
432
433    pub fn get_tick_count() -> FreeRtosTickType {
434        unsafe { freertos_rs_xTaskGetTickCount() }
435    }
436
437    pub fn get_tick_count_duration() -> Duration {
438        Duration::ticks(Self::get_tick_count())
439    }
440
441    pub fn get_number_of_tasks() -> usize {
442        unsafe { freertos_rs_get_number_of_tasks() as usize }
443    }
444
445    pub fn get_all_tasks(tasks_len: Option<usize>) -> FreeRtosSystemState {
446        let tasks_len = tasks_len.unwrap_or(Self::get_number_of_tasks());
447        let mut tasks = Vec::with_capacity(tasks_len as usize);
448        let mut total_run_time = 0;
449
450        unsafe {
451            let filled = freertos_rs_get_system_state(
452                tasks.as_mut_ptr(),
453                tasks_len as FreeRtosUBaseType,
454                &mut total_run_time,
455            );
456            tasks.set_len(filled as usize);
457        }
458
459        let tasks = tasks
460            .into_iter()
461            .map(|t| FreeRtosTaskStatus {
462                task: Task {
463                    task_handle: t.handle,
464                },
465                name: unsafe { str_from_c_string(t.task_name) }
466                    .unwrap_or_else(|_| "?").to_string(),
467                task_number: t.task_number,
468                task_state: t.task_state,
469                current_priority: TaskPriority(t.current_priority as u8),
470                base_priority: TaskPriority(t.base_priority as u8),
471                run_time_counter: t.run_time_counter,
472                stack_high_water_mark: t.stack_high_water_mark,
473            })
474            .collect();
475
476        FreeRtosSystemState {
477            tasks: tasks,
478            total_run_time: total_run_time,
479        }
480    }
481}