Skip to main content

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    core_affinity: Option<u32>,
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 task's priority.
79    pub fn priority(&mut self, priority: TaskPriority) -> &mut Self {
80        self.task_priority = priority;
81        self
82    }
83
84    /// Set the core affinity bitmask (SMP only).
85    ///
86    /// Each bit corresponds to a core: `0b01` = core 0 only, `0b10` = core 1 only,
87    /// `0b11` = any core (default when not set).
88    pub fn core_affinity(&mut self, mask: u32) -> &mut Self {
89        self.core_affinity = Some(mask);
90        self
91    }
92
93    /// Start a new task that can't return a value.
94    pub fn start<F>(&self, func: F) -> Result<Task, FreeRtosError>
95    where
96        F: FnOnce(Task) -> (),
97        F: Send + 'static,
98    {
99        let task = Task::spawn(
100            &self.task_name,
101            self.task_stack_size,
102            self.task_priority,
103            func,
104        )?;
105        if let Some(mask) = self.core_affinity {
106            task.set_core_affinity(mask);
107        }
108        Ok(task)
109    }
110}
111
112impl Task {
113    /// Prepare a builder object for the new task.
114    pub fn new() -> TaskBuilder {
115        TaskBuilder {
116            task_name: "rust_task".into(),
117            task_stack_size: 1024,
118            task_priority: TaskPriority(1),
119            core_affinity: None,
120        }
121    }
122
123    /// # Safety
124    ///
125    /// `handle` must be a valid FreeRTOS task handle.
126    #[inline]
127    pub unsafe fn from_raw_handle(handle: FreeRtosTaskHandle) -> Self {
128        Self { task_handle: handle }
129    }
130    #[inline]
131    pub fn raw_handle(&self) -> FreeRtosTaskHandle {
132        self.task_handle
133    }
134
135    pub fn suspend_all() {
136      unsafe {
137          freertos_rs_vTaskSuspendAll();
138      }
139    }
140
141    /// # Safety
142    ///
143    /// For every call to this method there must be a matching previous call to
144    /// `Task::suspend_all`.
145    pub unsafe fn resume_all() {
146        unsafe {
147            freertos_rs_xTaskResumeAll();
148        }
149    }
150
151    unsafe fn spawn_inner<'a>(
152        f: Box<dyn FnOnce(Task)>,
153        name: &str,
154        stack_size: u16,
155        priority: TaskPriority,
156    ) -> Result<Task, FreeRtosError> {
157        let f = Box::new(f);
158        let param_ptr = &*f as *const _ as *mut _;
159
160        let (success, task_handle) = {
161            let name = name.as_bytes();
162            let name_len = name.len();
163            let mut task_handle = core::ptr::null();
164
165            let ret = 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            (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    /// Aborts the delay this task is currently blocked on, if any.
253    /// Has no effect if the task is not in the Blocked state.
254    pub fn abort_delay(&self) {
255        unsafe { freertos_rs_task_abort_delay(self.task_handle); }
256    }
257
258    /// Notify this task.
259    pub fn notify(&self, notification: TaskNotification) {
260        unsafe {
261            let n = notification.to_freertos();
262            freertos_rs_task_notify(self.task_handle, n.0, n.1);
263        }
264    }
265
266    /// Notify this task from an interrupt.
267    pub fn notify_from_isr(
268        &self,
269        context: &mut InterruptContext,
270        notification: TaskNotification,
271    ) -> Result<(), FreeRtosError> {
272        unsafe {
273            let n = notification.to_freertos();
274            let t = freertos_rs_task_notify_isr(
275                self.task_handle,
276                n.0,
277                n.1,
278                context.get_task_field_mut(),
279            );
280            if t != 0 {
281                Err(FreeRtosError::QueueFull)
282            } else {
283                Ok(())
284            }
285        }
286    }
287
288    /// Wait for a notification to be posted.
289    pub fn wait_for_notification<D: DurationTicks>(
290        &self,
291        clear_bits_enter: u32,
292        clear_bits_exit: u32,
293        wait_for: D,
294    ) -> Result<u32, FreeRtosError> {
295        let mut val = 0;
296        let r = unsafe {
297            freertos_rs_task_notify_wait(
298                clear_bits_enter,
299                clear_bits_exit,
300                &mut val as *mut _,
301                wait_for.to_ticks(),
302            )
303        };
304
305        if r == 0 {
306            Ok(val)
307        } else {
308            Err(FreeRtosError::Timeout)
309        }
310    }
311
312    /// Get the minimum amount of stack that was ever left on this task.
313    pub fn get_stack_high_water_mark(&self) -> u32 {
314        unsafe { freertos_rs_get_stack_high_water_mark(self.task_handle) as u32 }
315    }
316
317    pub fn get_id(&self) -> Result<FreeRtosBaseType, FreeRtosError> {
318        let task_id = unsafe { freertos_rs_uxTaskGetTaskNumber(self.task_handle) };
319        if task_id == 0 {
320            Err(FreeRtosError::TaskNotFound)
321        } else {
322            Ok(task_id)
323        }
324    }
325
326    pub fn set_id(&mut self, value: FreeRtosUBaseType) {
327        unsafe { freertos_rs_vTaskSetTaskNumber(self.task_handle, value) };
328    }
329
330    /// Set the core affinity bitmask for this task (SMP only).
331    ///
332    /// Bitmask: `0b01` = core 0 only, `0b10` = core 1 only, `0b11` = any core.
333    /// Has no effect when `configUSE_CORE_AFFINITY` is not enabled in FreeRTOSConfig.h.
334    pub fn set_core_affinity(&self, mask: u32) {
335        unsafe { freertos_rs_task_set_core_affinity(self.task_handle, mask as FreeRtosUBaseType) }
336    }
337
338    /// Get the core affinity bitmask for this task (SMP only).
339    pub fn get_core_affinity(&self) -> u32 {
340        unsafe { freertos_rs_task_get_core_affinity(self.task_handle) as u32 }
341    }
342}
343
344/// Returns the ID of the core that the calling task is currently running on (SMP only).
345///
346/// Returns 0 on single-core builds.
347pub fn get_core_id() -> u32 {
348    unsafe { freertos_rs_get_core_id() }
349}
350
351/// Helper methods to be performed on the task that is currently executing.
352pub struct CurrentTask;
353
354impl CurrentTask {
355    /// Delay the execution of the current task.
356    pub fn delay<D: DurationTicks>(delay: D) {
357        unsafe {
358            freertos_rs_vTaskDelay(delay.to_ticks());
359        }
360    }
361
362    pub fn suspend() {
363        unsafe {
364            freertos_rs_suspend_task(0 as FreeRtosTaskHandle)
365        }
366    }
367
368    /// Take the notification and either clear the notification value or decrement it by one.
369    pub fn take_notification<D: DurationTicks>(clear: bool, wait_for: D) -> u32 {
370        unsafe { freertos_rs_task_notify_take(if clear { 1 } else { 0 }, wait_for.to_ticks()) }
371    }
372
373    /// Get the minimum amount of stack that was ever left on the current task.
374    pub fn get_stack_high_water_mark() -> u32 {
375        unsafe { freertos_rs_get_stack_high_water_mark(0 as FreeRtosTaskHandle) as u32 }
376    }
377}
378
379#[derive(Debug)]
380pub struct FreeRtosSystemState {
381    pub tasks: Vec<FreeRtosTaskStatus>,
382    pub total_run_time: u32,
383}
384
385impl fmt::Display for FreeRtosSystemState {
386    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
387        fmt.write_str("FreeRTOS tasks\r\n")?;
388
389        write!(fmt, "{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!(fmt, "{id: <6} | {name: <16} | {state: <9} | {priority: <8} | {stack: >10} | {cpu_abs: >10} | {cpu_rel: >4}\r\n",
401                   id = task.task_number,
402                   name = task.name,
403                   state = format!("{:?}", task.task_state),
404                   priority = task.current_priority.0,
405                   stack = task.stack_high_water_mark,
406                   cpu_abs = task.run_time_counter,
407                   cpu_rel = if self.total_run_time > 0 && task.run_time_counter <= self.total_run_time {
408                       let p = (((task.run_time_counter as u64) * 100) / self.total_run_time as u64) as u32;
409                       let ps = if p == 0 && task.run_time_counter > 0 {
410                           "<1".to_string()
411                       } else {
412                           p.to_string()
413                       };
414                       format!("{: >3}%", ps)
415                   } else {
416                       "-".to_string()
417                   }
418            )?;
419        }
420
421        if self.total_run_time > 0 {
422            write!(fmt, "Total run time: {}\r\n", self.total_run_time)?;
423        }
424
425        Ok(())
426    }
427}
428
429#[derive(Debug)]
430pub struct FreeRtosTaskStatus {
431    pub task: Task,
432    pub name: String,
433    pub task_number: FreeRtosUBaseType,
434    pub task_state: FreeRtosTaskState,
435    pub current_priority: TaskPriority,
436    pub base_priority: TaskPriority,
437    pub run_time_counter: FreeRtosUnsignedLong,
438    pub stack_high_water_mark: FreeRtosUnsignedShort,
439}
440
441pub struct FreeRtosUtils;
442
443#[derive(Debug, Clone, Copy, PartialEq)]
444pub enum FreeRtosSchedulerState {
445  Suspended,
446  NotStarted,
447  Running
448}
449
450impl FreeRtosUtils {
451    // Should only be used for testing purpose!
452    pub fn invoke_assert() {
453        unsafe {
454            freertos_rs_invoke_configASSERT();
455        }
456    }
457    pub fn start_scheduler() -> ! {
458        unsafe {
459            freertos_rs_vTaskStartScheduler();
460        }
461    }
462
463    pub fn scheduler_state() -> FreeRtosSchedulerState {
464      unsafe {
465        match freertos_rt_xTaskGetSchedulerState() {
466          0 => FreeRtosSchedulerState::Suspended,
467          1 => FreeRtosSchedulerState::NotStarted,
468          2 => FreeRtosSchedulerState::Running,
469          _ => unreachable!(),
470        }
471      }
472    }
473
474    pub fn get_tick_count() -> FreeRtosTickType {
475        unsafe { freertos_rs_xTaskGetTickCount() }
476    }
477
478    pub fn get_tick_count_duration() -> Duration {
479        Duration::ticks(Self::get_tick_count())
480    }
481
482    pub fn get_number_of_tasks() -> usize {
483        unsafe { freertos_rs_get_number_of_tasks() as usize }
484    }
485
486    pub fn get_all_tasks(tasks_len: Option<usize>) -> FreeRtosSystemState {
487        let tasks_len = tasks_len.unwrap_or(Self::get_number_of_tasks());
488        let mut tasks = Vec::with_capacity(tasks_len as usize);
489        let mut total_run_time = 0;
490
491        unsafe {
492            let filled = freertos_rs_get_system_state(
493                tasks.as_mut_ptr(),
494                tasks_len as FreeRtosUBaseType,
495                &mut total_run_time,
496            );
497            tasks.set_len(filled as usize);
498        }
499
500        let tasks = tasks
501            .into_iter()
502            .map(|t| FreeRtosTaskStatus {
503                task: Task {
504                    task_handle: t.handle,
505                },
506                name: unsafe { str_from_c_string(t.task_name) }
507                    .unwrap_or_else(|_| "?").to_string(),
508                task_number: t.task_number,
509                task_state: t.task_state,
510                current_priority: TaskPriority(t.current_priority as u8),
511                base_priority: TaskPriority(t.base_priority as u8),
512                run_time_counter: t.run_time_counter,
513                stack_high_water_mark: t.stack_high_water_mark,
514            })
515            .collect();
516
517        FreeRtosSystemState {
518            tasks: tasks,
519            total_run_time: total_run_time,
520        }
521    }
522}