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