This video presentation is the third installment of the Essentials of MQX RTOS Application Development training course. In this session, you will be introduced to the MQX task scheduler. You will learn how tasks are scheduled and how to select a scheduling policy and set task priority levels for your system.
This training was created by Embedded Access Inc., a Freescale sponsored training provider and proven partner.
|Session 3 Course Line||Lab Outline|
SESSION 3: LAB ASSIGNMENTFirst, watch the video for Session 3: Task Management and the Scheduler.
Then, follow through with the interactive lab assignment below.
In the lab for session 1 we were using round robin scheduling and we saw how tasks can share the CPU time efficiently. In this lab we'll switch to priority based scheduling and learn about how to share CPU time efficiently using this scheduling policy. We then get to finally start adding some more code to our application and the first task we'll start with is the User Interface task. Here we'll add in an interface over the serial port where the user specifies how many instances of the CAN Task there should be in the system. The interface will be enhanced to allow the user to destroy, abort, and restart the CAN Tasks giving you experience with the task creation API.
The objectives of this lab are to:
- Understand the task template list
- Create tasks dynamically using _task_create
- Use and understand the differences between _task_destroy, _task_abort and _task_restart
These objectives will be accomplished by modifying the UI task to accept the following commands:
- Create command - create the specified number of CAN tasks. Each CAN task should be started with a parameter that uniquely identifies the instance of the CAN task.
- Destroy command - destroys all CAN tasks.
- Restart command - Restarts all CAN tasks.
The CAN task should be modified to:
- Install an exit handler
- Print an initial message when it starts
- Print a periodic message (different than the initial message) once a second
New functions/ structures you will use:
_time_delay, _task_create, _task_destroy, _task_abort, _task_restart, _task_set_exit_handler
IMPLEMENT PRIORITY BASED SCHEDULING
- Currently all tasks are set to priority level 9 which invoked Round Robin Scheduling and now we'd now like to switch over to priority based scheduling. Re-organize the priorities of the tasks to have high priority tasks, medium priority tasks, and low priority tasks. When assigning tasks to relative priorities keep in mind that user information tasks are typically low priority because a user won't notice or care if the update to a display is delayed by a fraction of a second. On the other hand, more time critical tasks need to be high priority to ensure that they are as responsive as possible. Think about the priority level you'd assign for each task and then look at our list of suggested priorities.
- Update the priority levels in the Task Template List to reflect your selected priorities. Remember that a lower number is actually a higher priority level and that it will unnecessarily use more memory if you leave gaps in your priority levels. Assign the highest priority tasks to level 9, medium to level 10, and low priority tasks to level 11.
- Run your code and observe the output on the terminal. Pause the execution and observe the Ready Queue in TAD.
UNDERSTANDING THE SCHEDULER
- With Round Robin Scheduling we saw that a schedule yield from the active task allowed the next task in the ready queue to run. That doesn't work though when you have tasks at different priorities and the only way a higher priority task will yield the CPU to a lower priority task is to enter a blocked state. So our use of a time delay before wasn't such a bad idea after all. Change the sched_yield call in the Health Task to a one second time delay and see what happens. You may have to put a break point on the time_delay in the Health Task in order to catch its printout.
- We now have a means for the high and medium priority tasks to run so we need to extend this to allow the low priority tasks to run as well. Since we currently don't have a means of unblocking from any of the blocking calls other than a time delay we'll have to stick with that for now, but these will all eventually be replaced. Update all tasks now to use a one second time delay in lieu of the schedule yield. Now see what results you get.
UPDATING THE USER INTERFACE
- Now we finally get to add some meat to one of our tasks. The next step is to upgrade the UI task to accept from the user a command to either create CAN Task (by entering "c"), destroying all CAN Tasks (by entering "d"), or by getting help on the available commands (by entering "h"). If the user wants to create CAN Tasks they will enter the number of task to be created (up to a max of 10). The task_create() function will be used to create the CAN Tasks. Here are some additional requirements:
- Task Creation: When you create a CAN task pass to it an ID number. For example, the first CAN task will get ID number 0, the next one will get ID number 1, and so on. In order to confirm the results update the CAN task to print a message to say that it has been created and once a second it should print a message identifying which CAN task it is. In order to avoid the periodic printouts from the other tasks interfering with this interface you can comment out their printf statements or move them to be above the while (1) loop. Finally, since the UI Task will be creating the CAN tasks, they should not be auto generated at MQX start up so you need to reflect this in the Task Template List.
- Task Destroy: To verify the operation of this command use a printf to output a message that identifies which CAN Task is being destroyed.
- Help: Print out a series of messages to identify the available commands. For example "c - create CAN Tasks", "d - destroy CAN Tasks".
- To try out your changes create a number of CAN Tasks using your new interface and then pause the program. Check that the requested number of CAN Tasks exist in the task summary window in TAD. Delete all CAN Tasks using your interface and ensure that they are removed from the Task Summary window.
- Now add an additional command to your UI to allow the user to abort the CAN Tasks instead of destroying them by entering in an 'a'. The abort function allows the task being terminated to run an exit handler before termination so we'll add an exit handler to the CAN Task. The exit handler is a separate function outside of the can_task() function and it should be put in CanTask.c. For now all that the exit handler will do is print out a message that the task is shutting down. In order to access the exit handler the CAN Task will need to install the exit handler in its initialization code using the task-set-exit-handler() function.
- Run your code and verify that when the abort command is entered all CAN Tasks will print out the shutting down message but they don't print this message when the destroy message is used.
- Finally, add one more command to your UI to restart the CAN Tasks by entering an 'r'. In order to see when a task has started add a printf in the initialization section of the CAN Task code that indicates that the task has just started with the ID parameter passed to the CAN Task.
Need more help? The full source code for this lab can be found in the 'Lab Source Code' folder here.