freertos_next/
task.rs

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