 Hello, in this part we'll focus on Thread Flux, which are called WoVn3R2S API Task Notifications. It is very similar to Event Flux, which we covered in a different part of this training, but this time the Thread Flux are not a separate object WoVn3R2S, but those are included into each task control block. It allows us to directly communicate between tasks. So one task can, let's say, set some bits within those Thread Flux of another task. So let's create a new project within existing, let's say, workspace. I would use STM32Q by DE, but you can work with STM32Q by MIX as well. So I start new file, new STM32 project. I will select STM32L476RG as this is the heart of a nuclear board, which we are using within this training. The name of this hand sound would be 8 underscore Thread underscore Flux. Click Finish. And within this exercise, so we need to specify two tasks. One task will wait for some combination of its own Thread Flux, and then the second task will set part of it. And we will need as well one interrupt, which would set those Flux as well. So as you can see, there are a lot of similarities to Event Flux described in another part of this session. So we will start from the interrupt. So this is PC13, a blue button is connected over there. So I configure this line, clicking left button on mouse, and I select GPIOXD13, I can set a label, clicking on right button on pin, and I can set this name, button. It will be not using the exercise, but just worth to always to present how we can do it. Please remember that all of the labels are defined within main.h file after code generation. Okay, let's continue. So within system core and sys group, I'm selecting as a debug trace asynchronal SSW. So standard SWD plus SWO, so serial wire output, which is used for single wire tracing. We will use this to send the data from our tasks and interrupts on the console within our environment. It's a very nice supported debug tool. Okay, next point is let's say the change of time-based source, which is used for a HAL library. By default it is SysTik, but it is good to change it to any other timer as SysTik is reserved for three years twice. I'm changing it to timer six, as this timer do not have any input nor output channels, so it will be quite natural selection. Then the next point is selection of the three-year toys. So I go to middleware, three-year toys, I select interface CMC's V2. And from this, I need to check first whether the task notification has been enabled. I use task notifications, yes, so we can continue. All the API functions within three-year toys sources will be available for us. Okay, so as I told you, we need two tasks. I go to tasks and queues, I double-click on this default task. I would rename it to task one, priority normal, stack size 256 words, and function name, I would name it start task one. Okay, then I will add one more, so I click add button, I would rename it to task two, priority will set to priority normal, 256 bytes, start task two SNM3 function, and that's it. We've got all the configurations within three-year toys, let's have a look into the config parameters, and we can see this library max syscall interrupt priority is set to five. All of the interrupts we would like to use to call operating system functions should be within this range from five to 15 in terms of the priority of the interrupt. This is due to the fact that the critical sections within operating system is operating, it's blocking all of the interrupts from this level, some from level five down to level 15. Other interrupts, so 0, 1, 2, 3, 4 can be still executed without any problem. So it is important that once we are in a critical section, there should be no call to operating system functions. This is why all interrupts which are interacting with the operating system should have the priority within this range. So knowing this, I go to nvc and I'm selecting first enabling this interrupt, external interrupt, changing this 0 to level five. After this, I can generate the code, so I click this gear icon, okay, and I can open main.c file and perform further code modifications. As I told you, the task notifications are, let's say, the thread flags do not need any creation of the component within three-year toys or CMC toys. Those are parts of existing tasks, control blocks. So we can go directly to our tasks implementation. And as we discussed, the task one will be used to wait for some specific combination of the flux. So I would use OS thread flux wait. And then let's say the flux. So I would like to wait, for example, for one, then there is only one flux. So this option is not, let's say, that important. So I can put a zero over here and the timeout OS wait forever. So this is an example how we can wait for the particular, let's say, combination of the flux. After this, I would use sign of life. So task action as it is task one, I would send one. And that's it. So we need some other task or interrupt to set these thread flags within this task one. In the first part of this hands-on, we will use for this an external interrupt. So I go to core source stm32l4xx underscore it.c file. And I would scroll down to my external interrupt handler. And I would reuse, just opening the declaration, I would reuse this callback. This callback is called at the end of execution of this external interrupt. It is defined as weak, doing nothing. So I can define it myself in my code. So I would copy paste it within part four of this code. And here I would just set those flux. So always thread, thread, flux set. And it will be, now I need to select which task I would like to set the flux. So in our case, it is task one. And the flux. Flux, it will be just one. This is the combination of the flux on those the task one is waiting for. After this, we will just send the, let's say, the sign of life, task action and exclamation mark. Okay, we can define as well this task action. So I would use itm interface to do this as we declared this additional line, serial wire output. So for this, I would use itm send char and it will be message and the second line, it would be, let's say, sign of new line. Okay, the missing point is, of course, declaration of this function when this private function prototypes part, the user code. And now we are ready to compile the code. And what is an expected result? An expected result is the following. Task one will be blocked waiting for this, its own flux set to one. And till we'll press the button, nothing should be visible within the, let's say, the data console. Once we press the button, we should see first an exclamation mark, and then we should see this task, the one, which is the task action from task one, because this task would be unblocked for, let's say, the single occurrence of this flux combination. After this, let's say, task will receive this combination of desired combination of the flux. In this case, it's on the one bit. It will be cleared automatically, it is specified within the second argument, and it will wait again for the next button press. So let's compile the code and check whether this is working like we are expecting. Okay, code is compiled. Let's click on this back icon. I go to the debugger, I'm enabling serial wire viewer, and I'm changing the core clock to four megahertz, which is the case of our application. Then I need to display our ITM data console. In case you do not see it, please go to the quick access and press SWV and select the line of this monitor icon. Once it is done, please activate it and configure. To configure it, we need to press this configure trace button. Select zero with an ITM stimulus ports, which is our SWO line. And we need to press this icon, which is start tracing. After this, we can start our application. And as you can see, there is nothing expected because task one, let me go to the code once again, task one is waiting for the code for, let's say, those flags. And there is no button press, which could send it. So I press the button and I can see that there was, let's say, a single occurrence of the button press, of the interrupt. This is this exclamation mark. And after, as it has, let's say, coast setting the, let's say, the thread flux of task one, it is unblocking for a single, let's say, execution task one as well. This is why we can see this one afterwards. If I press button once again, I can see more pairs like this. So one button press and single occurrence of exclamation mark and one, which is a single activation of task, task one. This is the first exercise. In our second part, we will work on a combination of the flags. And the part of it would be set from the interrupt, like now. And that the rest will be set from the different task. So let's terminate our debug session and continue with the coding. Okay. So let's do some modifications to have, let's say, more extended example. We will not touch this callback. So in this exercise, in this part, our external interrupt will set one within, let's say, task one, thread flux. And it will send its sign of life, which is an exclamation mark. So there is no change. We will change the function body of entry code for task one. So this time we will wait for 51 hexadecimal. So as you can see, we need to set this missing 50 hexadecimal as let's say the thread flux are all together from different sources. And we would specify as well one of the flags, the flags combinations, telling that we would like to wait for all of the flags. So for this, we've got OS, flux, wait, all. If you are not sure what you should specify within this part, you can go to the definition of this function. So it is within source, CMC's RTOS V2 in middle words, and the CMC's underscore OS dot OS2 dot C file. If we find this function, we can see that we've got in fact two flags. OS Flux no clear, which is in fact two, and OS Flux wait all, which is one. No clear means that after success execution of this function. So once all the desired flux will come, flux will be not cleared within the control block of the task, which is operating on those bits. Usually we would like to clear them. So this is the default action, but in any case, we can set that it should not be done. Then OS Flux wait all means that we would like to wait for all of the flags specified within this function. If both flags are not specified, so the second argument is zero, like in a previous exercise, all the flags will be cleared if any of the bits within the thread flags will come. So in case of 51, any of the bits from this 51 would be set. It will unblock this function and clear all the flags within the control block of the task we are working on. So in our case, we will use OS Flux wait all, and we will not use this option. So this is why I selected this argument and OS wait forever. Then the sign of life of our task one, no change. And we will add some code within task two. In task two we will do similar operation like from our interrupt, but in this case we will set 50 hexadecimal. And of course our task action would be two instead of an exclamation mark. And we will put some delay afterwards. So we will send our task for three seconds into the blocked state. Let's try to compile it and check whether it will work like expected. As you can see at the moment, we should see once per three seconds two on our, let's say, terminal, as it will be the only active task once per three seconds. This one would be blocked because task one will wait for the combination of thread flags 50 hexadecimal and one coming from interrupt. And till we will have, let's say, button press setting one within the thread flags and 50 hexadecimal, there would be no action from our task one. Let's compile it. Okay, there is no errors, no warnings. So this is the time to start a debug session. I press, let's say, the debug icon. My board is already connected. Okay, so as it is, let's say, we already configured the ITM interface and the data console from a single wire viewer. I would just switch it to be active window and I started tracing and I run my application. So now I can see once per three seconds activation of task two as expected. There is no action from task one as it is blocked waiting for the combination of the flags set by task two and external interrupt. Now I'm pressing the blue button, which is causing this external interrupt occurrence. And now I can see that there was an exclamation mark informing us that there was the callback from the rise by the interrupt and single occurrence of task one. As we have not specified no clear afterwards, this function will clear all the flags within the control block of this task one and it will wait for the next button press. So let's do a small modification and we will add one more flag within these OS threat flags wait for task one. So let's switch off the debug part. Okay, and within our task one, let me come back to this code. I would add this flag OS flags no clear. So come back to main and we can concatenate those flags together using the R button. Let's see the or sign and now we'll wait for all of the flags and we will not clear them. So now I would build a code once again and I will start a debug session. Data console start tracing and run. So no button press. So once per three seconds, I can see these actions from task two only. Now I press the button. And now what we can see? We can see that both tasks, task one and task two are activated regularly. So the flags has not been cleared after this. And now let's say both tasks can be executed regularly. So this is the difference. That's all for this part. Thank you for watching this video.