 Hello, in this part we will continue with Kills. In this exercise I will prepare an example where we will have two senders and one receiver. So I'm starting with STM42Q by DE, but you can use as well as the STM42Q by MiX and here select the toolchain. So starting from STM42Q by DE, I will create a new project, so file new STM42 project. I will select my microcontroller L476RG, which is located on Nucleo L476RG board which we are using within this training, but you can easily use any different STM42 based board for your exercises. So I press next. Name of my exercise example would be 3 underscore Qs, underscore 2, underscore senders and I press finish. Okay, after a while I can see the empty pin out of my selected microcontroller. You can reuse any of previous exercises as well here, but just for those of you who started with us within this part I will just briefly repeat all main steps. So within system and core and SIS section I'm selecting the debug interface to trace asynchronous SW to have three lines, so standard SWD plus SWO, so single wire output, which is used in our case to make the communication between the board and one of the terminals to display the messages from the tasks. Next point is to select the different time-based source for HAL libraries. By default it is SISTIK and SISTIK is used for free R2S and the best practice is not to share it with other functions, so I'm selecting time-based source to forwarding it to timer 6, which is standard time-based timer without an input nor output channels. So this is the first step. The second step in our case would be to add them in within the middleware's free R2S. We are selecting CMC's V2, as it is much more extended, and then within this CMC's V2 we will add three tasks. One is already defined, so we'll just rename it by double-click and I'm changing its name to, for example, Sender1, as priority normal I would keep, then the stack size I would extend to 256 words, this is important, and the entry function would be start Sender1, the rest I would keep default. Then we need two more tasks, so I click add within tasks and kills and task section, and if I would click add I have a next window for next task. So next second task would be Sender2, so priority would be the same, so normal, 256 as stack size in words, and the entry function would be start Sender2. Okay, and the third task is the receiver, so again, add a button, then receiver, always priority normal, 256 words, and entry function it would be start receiver. That's it, so we've got three tasks and we would need as well one queue, so I go within queues tab, and I add one queue, so my queue have a name queue1, it will be eight components long, and each component should be 8 bit unsigned type, and that's it. We've got new components within our 3rd task, and please have a look, we've got some warning message on 3rd task middleware, and it is present as well within 3rd task heap usage, so most probably we are over our limit, and total heap used is 3500, so it's bit more than we declared, so we need to go to config parameters and extend our heap size from 3000 bytes in this time to, for example, 4000. Now we can see that everything is corrected. After those operations, we can generate a code either using this button or going via project and generate code. Okay, main.c file should be opened automatically, if not, we can find it within core, source, and main.c. Within main.c file, we can see some header files which are included, so main.h file contains, for example, the definitions of the labels of the pins, and cmcs underscore os contains, let's say the interfaces, name the declarations of the functions and macros use the within cmcs os layer, which we are using within this complete training. Below, we can see the definition of the handlers selected of our tasks, so sender1, handle, sender2, handle, and receiver handle, and as well the handler for our Q1. There are as well attributes for each of them, so those are let's say the part of attributes, the rest with the pointers to the memory structures like control blocks and private stacks would be defined upon the creation of those components later on. If we go below within main function, we can see the standard hardware initialization, starting with hull underscore init function, then configuration of the system clock using system clock config function, and then from the peripherals we are using calling GPIOs, this is why we've got only mx underscore GPIO init function. Just below, there is an initialization, the memory allocation for the operating system, and below this we can see the creation of operating system components, starting from the queue, then three tasks, and after this on line 116, in my case, there is OS kernel start, which is starting the operating system. If we go below, we can see the entry functions for our tasks, and here we would need to add some coding. Okay, so let's continue with code processing. We'll start from sender1 entry function, at the moment there is only OS delay for one millisecond, so we'll start with something different. Before our endless loop within this task, we'll create local variable, as simple as possible, so it will be adb to unsigned, I will name it x, and initial value 1, and we will use this value to pass it to the queue from this particular task. So I would start my endless loop with sign of life, so in my case, it is sending a simple sign over selected interface. In all of the exercises within this training, we are using SWO line, so over instrumentation trace, macro cell interface, we are sending the data, which are synchronized with the system clock. I would demonstrate in a while how this task action looks like in our case, but you can define it on your site differently. So this is our sign of life, just to display something from the task. Then I would try to send some data to the queue, so I would start message queue control space, and I would like to put some data over the queue. If I press control space, I can see that there is queue handle, then there is a pointer to the data, so in our case, I would put address for this x value. Priority is not used here, so I put 0 and timeout. This is the time which would be used for the system, for the task in fact, to send those data to the queue. After this, if it will be not successful, the function will return some error value, so negative value, and it will continue execution. Then OS delay. OS delay, I would increase here to two seconds, so after sending data to the queue, we will send this task to the blocked state for two seconds. So this would be sender1 code, and for sender2 it will be almost the same. The only difference would be that this local variable would be equal to 2. So I would just do copy-pasting, and the rest is almost the same. Another difference is our sign of life, so instead of small s, I would send big s for sender2, and the last point is to specify to fill the receiver code. So we need to have as well data which are taken from the queue, by default it's 0. Then a sign of life, big r from receiver, and then we will try to read something from the queue. So it will be OS message queue get. So the first argument is a handle. The second argument is a pointer to the message, so it will be our address of our local variable. The priority is not used, so I would vote null, and timeout I would try to do this for seconds. Important is to execute below code only in case it was, let's say, successful to get anything from the queue. So only in this case I would perform another task action to display what I just received, plus 48. In this case we will send to our terminal either one or two in ASCII code. After this I would send this task for two seconds to blocked state. Okay, and this is our code. So let's define now this task action code. Okay, so our task action code will locate within this user code section four. It will return nothing, so it will be void type task action, and it would accept one char variable, one byte. Then we would send one char actor, so it will be our message and sign of new line. Okay, so it will send one signed and sign of new line. So in the terminal we should see messages from our queues one after one in each one in the new line. And just to have everything correct, I need a prototype within user section as well. So this private function prototypes over here, and we can test our code. So now I would try to build it. My board is already connected. Okay, now this build, we will start the debug session. Okay, so let's start the debug session. I'm clicking this bug icon just to have a look whether everything is correct. I'm just enable the serial wire viewer and correct the clock to four megahertz, which is used in our example. SWD. Yes, we can apply and start the debug session. Okay, before we run the code, I would just start my ITM data console. In case you don't know how to start it, please go to access and select SWV interface, and select this ITM data console. This with the monitor on the left side as an icon. Once you select it, you need to configure it. So configure trace and select bit zero, which is in our case SWO line. It is BB3 pin. And then once you're ready, you can start tracing using this bullet icon. And you can start your code. You can see the time delay. It's quite long. I would pause it. And if we analyze what is happening, the first has been run the receiver task, then there was a sender one and sender two. There is a difference with size. After this, receiver was executing the code once again. So we can see the one coming from first sender, wasn't because it was the first component. So receiver during its time displayed this message from the queue. And then the time of receiver ends up and sender one has been selected as for the second time. So we can see sender, then again, receiver has been selected. And then receiver displayed the second component from the queue, which was sent by sender two. So this is number two. As you can see, in this particular example, it is important to define precisely the timeouts, which we will use for our code execution. Please have a look that both tasks, sender one and sender two are sent after sending the data to the queue for two seconds into the blocked state. Then receiver is waiting two seconds, two seconds in a blocked state as well. So it may happen that let's say the timing with among those three tasks will not be perfect. So always please remember about the timings among the tasks and the timeout, which is specified within the queue get or queue put. We can finish this exercise here. So thank you for watching this video. In next video, we will analyze the situation when the queue, the receiver task has higher priority than both senders.