 3RTOS tasks. What is the task? It is a C function. It should be run within the infinite loop like we see on the screen. So it is like a menu while one loop from the main.cfile main function. It can be, this function can be used to generate any number of the tasks, so separate instances. The task has its own part of the stack. So each instance of the task has its own stack and its priority. And the task can be in one of the four states running, only one at the moment, blocked, suspended and ready. Blocked, suspended and ready states are grouped into so-called lists which are used by the scheduler to select the task to be executed and to manipulate the rest of the tasks and switch between the modes. The task is created and deleted by calling the dedicated API functions. Within the CMC-SOS we have either OS-Thread create, within CMC-SOS v1 or within CMC-SOS v2 which we are using within this training, OS-Thread-New. To delete within CMC-SOS v2 we will use OS-Thread-Terminate. Let's have a closer look on the task function. Here on the screen you can see the typical function template and within this template we can specify three areas. The first one, green one, it is so-called task initialization. It will be called only once during the task creation, the first execution of the task. Then the gray zone, it is an endless loop and it can be executed constantly during the time when the task is in a running state. So we can have a several rollover of this loop or we can execute part of it depending on the code which we've got inside and of course the length of the time slice dedicated for the particular task. Then the last part, the pink one, it is the part where we should not land at any time because if we land in this part it means that there is some problem of the memory allocation and we should have a closer look on the memory resources, especially on the stack areas of the tasks. Let's have a closer look on the task structure. A task consists of three parts, the program code located in Flash and two RAM areas, a stack which can be accessed by the stack pointer and it holds all the temporary variables used by the task during its execution. The second RAM area which is used by the task is the so-called task control block, TCB and it is in fact the data structure assigned to the task during its creation. It contains its status, information of the task including the stack pointer task priority current task status and as well the connection between the task and other operating system components like for example mutex's. We need two calls of pvport malloc, so two memory allocations. During the fast task creation the first one allocates the task control block the second one allocates the task stack. The process of saving the context of the task while it is being suspended or it is sent to ready state from running state is called context switching. Here we can see the example of the task control block TCB. It is taken from the tasks.c file from the freeRTAS implementation in version 80.1.2. We will focus on the main components on the next slides. Let's have a closer look at the task control block structure. It is defined within task.c file. First thing worth to remember is the scalability of this structure. If you enable more operating system components like mutex's, software timer, some task notifications you may expect that each task control block will be increased as well. Please have a look below. If you use mutex's by setting control use mutex's to 1 within freeRTAS config.h file you will find two more words within each task control block. The second example is using a task notification which is adding one word and one byte per task within its TCB. Next important point is the first component of the task control block which is widely used during the context switch. It is the current location of the task stack. It is used during the context switch process to know where it is the last state of the task stored within its own stack. Then we can find the name of the list so the group of the tasks where the task is assigned its current priority and pointer to the beginning of its own stack. There is as well a task name it's a text string used for the descriptive name of the task which has been used during its creation. As you remember from the previous slides the list of the fields within TCB is much longer I just focus on the most important ones here. Task can be present in one of the four states. After its creation it is always in a ready state. Task is ready to be executed but it is not currently executing because a different task with equal or higher priority is running. The task can be selected by the scheduler to be running and in this case it is changing its state to run state. It can come back to ready state once its time slice elapsed or it will decide to come back to the ready state or it is preempted by the task with higher priority. In all of the cases scheduler is making the decision which task should be executed. So we've got ready state we've got run state. The third one is a blocked state. Block state is a state of the task when the task is waiting for something. Either on semaphore or mutex or some data on the queue or it is waiting for some particular time so-called delay and after those components will appear it is unblocked and it is coming back to the ready state. The last state of the task possible state of the task is a suspended one. Suspended is a task when it is not available for scheduling but it is still kept in the memory. To put task into the suspended mode we need to execute APA function from a different task on the data task itself and to come back from the suspended mode we need to call a function as well. There is a fifth state not mentioned here it is so-called delete state. In delete state the task is removed from all of the lists and in the next execution of so-called idle state idle task the memory of this task is released from the complete memory area. Task states are coded within os thread state underscore t enumeration. Here in the table we can see the coding of each state with value so we can see thread inactive ready running blocked terminated so mentioned deleted. There is as well the thread error in case of an error of the task and there is a reserved field as well. Let's focus a while on a treat or task attributes which are defined within a structure of type os thread at underscore t within cmcs underscore os2.h file. So this is a typical for cmcs os in version 2 and it contains all of the information which are necessary to create a new task and some of them we need to specify by our own during the task creation and some of them are filled by the code after task creation. So let's say the first one is a name of the task of the thread then the second one which we need to specify is a stack size which will be dedicated for the given task and its priority. So those three arguments we need to specify within this structure before task creation and then use an address of the structure to create a new task. Then during let's say task creation the rest of the fields will be filled. So for example attribute bits then pointer to the thread control block memory area which would be known after task creation during allocation of two memory areas for the task. So the test control block needs stack. So this pointer would be filled bit later on after creation of the task then the size of the task control block would be filled here as well. It can be done after creation of the task because it will be calculated based on the rest of the components which has been selected within freeRTas. Usually within freeRTas.config.h file. As you remember if we add for example mutexes, software timers, task notification the task control block will be increased. This is why this size is added bit later on. Then we've got the pointer to the thread or task stack memory area so the dedicated stack for the task and in case we are working with the microcontroller based on a core with a trust zone there is as well the trust zone module identifier place. So those are the main components within this structure which are defining the task we are just creating. And just to highlight during the task creation we are filling manually or specifying manually three components name, stack size and the priority of the task. As you remember the task can be in one of the states. By default after the task creation it is in ready state it's already to be executed. It can be as well within the blocked state while the task is waiting for something. Then it can be in a suspend state where it is not taken into the consideration by the scheduler. It can be as well in a run state. Run state is only for one task at the moment. Then we have not specified it before. There is as well the termination group which contains the list of the task, group of the tasks which has been terminated which has been deleted. But the memory blocked by those tasks are not released. So all of the modes has its own list which contains the group of the task which is within current group. So for example we've got the ready tasks list. We've got tasks waiting termination list. We've got the suspended task list. We've got the pending ready task list. We've got the delayed task list. And we've got the overflow delayed task list. So this is the group of the lists which is accessible by the scheduler. And this is the role of the scheduler to manage the tasks and to migrate the tasks among those lists and move them from one list to the other depending on the current state of the task and the effect of the interrupt or the context switch. This is how it is managed within three years. The management of the lists is handled within list.c and list.h file. During task creation we are specifying as well the importance of the task. It is specified by its priority. Lower number means lower priority within three years twice. And the priority is set during task creation and can be changed during the execution of the code. We can read the priority of a given task using its handler and we can decrease and increase the priority level. Please remember that the big number of the priorities within the operating system means that we will increase the memory usage of the operating system itself because we will need a bigger amount of memory for the stack. The scheduler activates the task that has the highest priority of all of the tasks within the ready state. The order of execution of the task depends on its priority, like we said before. And what is important is that the task with higher priority can pre-empt the running task if config use preemption within three years as config.h file is set to one. Otherwise we will work in so-called cooperative mode and we will need to wait till the currently executed task will finish its job, will decide by himself to finish the job. So this is important in case of any issues with the preemption to check whether this config use preemption is set to one or not cleared. Then the each priority, which is assigned to the task, should be in a range between zero and max priorities minus one. So this is defined within three years as config.h file. This is max priorities. And the important message is that the idle task has the priority called tsk idle priority. It is defined as well within task.h file. Here is the list of the priorities which are available within CMC's OS V2. It is much extended list versus CMC's OS V1. And in terms of the naming it is much more complex than native three-year OS API. So the priorities are stored within the enumeration called OS priority underscore t. We can find it within CMC's underscore OS.h file. And we are starting from OS priority none, which is not initialized. It is value zero. Then we've got OS priority idle. This is value one. It is reserved for idle thread. Then we've got, let's say, the space between two and seven not used. And number eight of the priority is used for OS priority low. Then we've got OS priority low one to seven. And we've got the group OS priority below normal. Then there is OS priority normal above normal high real time. And OS priority ISR. It is reserved for interrupt routines and its level is 56. If there is an issue with the priority we will have the value minus one. So the system cannot determine what is the priority level or the level used for the creation of the component is illegal one. So it is good to monitor the value which is read for example from the task whether the priority is on a correct level. When we are using the CMC OS it is converting in fact those naming conversion into the free R2S API ones. So please monitor carefully which values are returned by the OS functions. The values which would be returned by CMC OS would be, of course, within this OS priority enumeration list. But if you will go into the API functions of free R2S please remember that in between we've got the conversion between those priority list and end numbers and free R2S APIs. Let's have a look how we can create a new task within the code. Usually once using stm32cubemix or stm32cupid code generators those operations are performed for us within generated code. But as it is possible to add the tasks from the code it is good to know how to do it. To create a task we should first define and fill the structure of type OS thread at ATTR underscore T which contains main parameters of the task like its name, its tag size, given in bytes and its priority. There are more components within this structure but those components, those fields are filled automatically during the task creation. Then we should declare a function which would be assigned to the task and executed when the task will be in run mode. This function should accept single argument, pointer to any type and return no value. Please remember that within this function body there should be an endless loop implemented as we should never exit from the task function. Then we should execute a function which would create a task. For this we will use so within cmc-size-v2 OS thread new function as arguments we will specify. Task function name which would be called during task execution argument we would like to pass to the task function. Usually we will not send an argument so we can specify null over there and address of the structure with task parameters. It is this OS thread ATTR underscore T type structure which we have discussed few minutes ago. As a return value of OS thread new function used for task creation we should receive an ID for the new task. It is type OS thread ID underscore T. In case we will receive null it means that there was an error during task creation. Usually it is an information about issues with memory allocation. So the first thing we should verify whether we have enough memory with an unparrying system heap. Please remember that during task creation two memory areas will be allocated. tcb task control block which is more or less 100 bytes and stack of the task. Let's focus for a while on an API dedicated for tasks within cmc-size-v2. So the task has its own handler. It is OS thread ID underscore T type value and it is returned why we are creating the task and we are trying to get the currently running task ID. To create the task we need a function OS thread new which is accepting three arguments. The first one is a pointer to the function which would be called during task execution. Then the second one is a pointer to any type of the argument which can be passed to this function during its execution. Usually it is not used so we are specifying null in this field. And the third one is a pointer to the structure with the attributes specified for the task we are creating. Those attributes are the name, tag size and the priority of the created task. Then to delete the task we need to execute the function OS thread terminate and there is only one argument it is an ID of that thread we would like to terminate. If you would like to terminate the task which is currently executed it is enough to specify the null argument instead of ID of the task. Important message is to have a look on the status returned by this function so this is OS status underscore T it is an information whether the function has been successfully finished or there were some issues. So it is a good practice to monitor this OS status underscore T value after the call of the function. Then the next function is a function which is returning back the task ID so this is the function OS thread get ID without any argument because it's returning back the ID of currently run task. And then if you would like to have a task name we have a dedicated function OS thread get name and then we need to specify the ID of the task the handler of the task you would like to have the name. Next function is dedicated to tasks within CMC SOS V2 API yield task. So the function OS street yield without argument it is the function which should be called by the task within its function and it is sending the task from run state to the ready state. It is a clear information that the job has been done by the function and there is no need to block the time dedicated within the time slice and task would like to give the space to the for the next one from the ready list. Please remember that this is movement of the task from run to ready state. So in case of the task with higher priority the effect would be the following that we will go for a while to the ready state and come back immediately to the run state as a task with the highest possible priority to be executed. So this yield yielding is effective in case we've got at least few tasks within ready state on the same priority and we just would like to split the time the most effective way. Then the second function always treat resume it is the function which is allowing us to to send the task from suspend mode suspend group to the ready group ready mode. We can execute it from other task body function and it requires from us only the thread ID so identification of the task you would like to send from suspend to ready group. So it could be visible by the scheduler again. What is important is that both of the functions yield and resume are returning always status underscore t value which is giving us an information whether the function has been successfully executed or there were some issues within its execution. So it is important to monitor the return value and check whether it was successful or not. Then next function is always treat get state which is returning back the state within which one is the selected task. So the only argument is a task ID which we are checking. And then the next one is a suspend task which is the complementary function for the resume task. Suspend task means that we would like to send one of the tasks from ready group or from blocked group into the suspend group. So to remove it from the interest of the scheduler because for example the task is not effective for a longer period of time. So to suspend the task to send it to the suspend group we need to execute always treat suspend with the task ID. Then we've got two functions which are suspending and resuming all of the tasks which are in the ready state. So the first one resume all the tasks we should execute always treat resume all without argument and to suspend all the tasks so we need to execute always treat suspend all without any arguments. So this is in fact freezing for a while and interrupting operating system without blocking the interrupts of the system. Next group of the task API within CMC science video. So we've got a dedicated function to get the stack size of the task. So this is all the threat get stack size where we need to specify the handler or let's say the ID of the threat of the task we would like to check its stack size. Then we can check the available stack space for a threat based on a stack watermark. So for this we've got always treat get stack space. So this is amount of the space remaining for the given task. Then we can change the priority of the task using function always treat set priority. And then we need to specify the ID of the task of the threat. And then we need to specify its priority the new priority. An important is to really control the value of this new priority and control the return status of this function to be sure that the value we just specified is in line with the settings of the complete system. Especially when the maximum value of the priority specified in three or two is config.h5. Then we can check what is the current priority of the given task using always treat get priority function. And the only argument is an ID of the task of the threat we would like to check the priority. And the return value of this function is the priority value. Then we can terminate execution of currently running task by deleting it. So for this we've got always treat exit function. And if you would like to terminate any other task I would need to use the function always treat terminate when the argument is a threat ID. Here you can see the summary of the functions available in different APIs for three year ties. So in the first column you can see the functions which are visible within CMC's OS API in version one. In the middle there is currently used in this training CMC's OS API version two. And on the right side there is a three year ties native API. So as you can see the list is quite complete and most of the functions from the currently used CMC's OS is calling early the functions from original three year ties API as an example. OS kernel start is calling vtask start scheduler. OS treat new it is calling xtask create. OS delay is calling vtask delay. Thank you for watching this video.