 Hello! In this part, we'll focus on counting semaphores within 3R tests and CMC's RS version 2 layer. I'm starting with STM32Cube IDE. You can use as well STM32Cube MX and your selected tool chain for this exercise. We will do it on Nucleo L476-RG port, but again, you can select any other STM32-based hardware and with some small modifications, you can use it freely. Okay, I'm starting the new project, so file new STM32 project. I'm selecting my microcontroller, so L476-RG. I click next. Name of my project would be 6 underscore sem underscore cnt. Finish. Okay, finally we can see our pinout, so what we would need for this exercise. The first thing would be the button, so PC13 again. We are selecting GPIOexternallyinterrupt13. We will not use any label. And then within system core and sys, we will select Debug interface, trace as it announced SW, to have a single wire output additionally, to have single wire tracing. Then time-based source. This is for library. We will use timer 6 instead of timer in SysTick. SysTick is used for 3-year toys and it should not be shared with other roles within our application. Then the next point is within middleware and we are selecting 3-year toys. It's interface CMC's V2. Then within config parameters, we need to check whether the counting semaphores are enabled. So we can see it's enabled. We can continue in this case. So we go to tasks and queues and we will rename the existing default task by double-click on its name. We will rename it to task1. Task1 was priority normal. 256 words. Start task1. One sentry function. We need to let's see two more tasks within this exercise. So the second one would be task2. Priority the same. Normal. 256. Start task2. As an entry function. And again the third task. So it would be task3. The same priority. So priority normal. 256 words as a stack size and start task3 as an entry function name. Then we need to create a counting semaphore. So for this we switch to timers and semaphores. And within the counting semaphores I click add. I keep, let's say, default name. At the count we keep default 2. Press OK. This is, let's say, all the modifications at the moment. We can see that most probably we got not enough memory allocated for the heap. Let's have a look on this freeRTAS heap usage. Yes. So within config parameters we will extend this 3000 bytes to 4000. 4 kilobytes. To be sure that everything will fit into our dedicated heap for freeRTAS. OK. Next point. Last one would be checking of interrupts. So I come back to this NVIC. And I need to enable external interrupt handler. Yes. And I need to modify the preemption library. Preemption priority. Priority should be within, let's say, borders specified within freeRTAS configuration. So let me come back to freeRTAS settings and config parameters. And at the bottom we can see library max syscall interrupt priority set to 5. So all of the interrupts with the priority between 5 and 15 can execute operating system functions. All other interrupts with priority 0, 1, 2, 3 and 4 shouldn't because it can, let's say, cause an issue with proper operations on the operating system. This is why we will specify our external interrupt priority on 5. It can be 5 to 15 and a number of those. We will specify 5. And now we are ready to generate the code. So I click icon to generate the code. And I can open my main.c file. OK. Let's continue with code modification. So now we are within main.c file. If I scroll down, I can see the definitions of my operating system objects. So free tasks and our counting semaphore. Then if I go a bit below, after the initialization of the kernel, I can see the creation of this counting semaphore. You remember that the max count of the semaphore has been set to 2. So this is the first argument. The second argument is, let's say, a number of available tokens within the semaphore. In my case, I would like to start from 0. So semaphore would be not available at the beginning. This is why I clear it. And then the attributes are specified by us by the name of the semaphore. The rest of the attribute fields will be filled after this function execution. So it will be the pointer to the memory structure which contains the information about this semaphore. Let's go below. We will start with definition of our task action function which would be used to, say, send information from the tasks and interrupts to the console. We'll use ITM interface, instrumentation trace macrosol, but you can use, of course, for example, you start for this. There is one argument, but no return value. And it is using ITM interface which doesn't require any configuration. The only required thing is to configure this SWS additional line during debug selection. So I would just send one character which would be sent by the tasks and interrupts. And after this I would send, let's say, the new line, the assigned character. After this, there is a time to put some code into the tasks entry functions. We'll start from task one. The role of task one would be to release the semaphore. We will release the semaphore within this function. So it will be release. Now semaphore ID, this is discounting semaphore and no other arguments. So it will try to release the semaphore in case of any trouble. It will return error value, which is negative, but there would be no timeout. So it is, let's say, trying to release the semaphore and in case of any trouble, it is immediately going to the next line. In our case, this next line would be task action, so sign of life. And as it is task one, we'll just send one over ITM. And then we will send this task for two seconds into the blocked state. This is the code for task one. What is the role of task two? The role of task two would be very similar to role of task one. So we will try to not to release the semaphore, but the difference would be that the number we would like to send during this sign of life would be two. Task three will be different. So within task three, we would like to wait for two occurrences of the semaphore and how we can do it. We've got, of course, the semaphore, let's say, the dedicated function semaphore acquire, counting semaphore. And this time we've got the timeouts. I would use quite long one for seconds. And here is the first, maybe let me say this way, the limitation of, let's say, the counting semaphore acquiring. There is no function which is, let's say, trying to acquire the semaphore more than once. In case you would like to acquire the same semaphore twice or more times, or you would like, you are waiting for more than one semaphore, you need to specify those acquire functions one by one. And there is a problem, determinism problem, sometimes you are not sure in which order the semaphores were, let's say, pop up. Just an example. If you would wait for semaphore two, and let's say the first one would be, you will wait for semaphore one and two, and first one would be semaphore two, you will be still blocked, and you cannot be, let's say, unblocked till, let's say, the first one would be given. So in case you would like to wait for multiple events, so from different tasks, like in this case, there are better mechanisms which will touch a bit later on, like event flags or event threats. So you can have a look in next parts of this training. Okay, so coming back to our topic, the last action I would like to specify within this task would be sending the sign of life. Okay, and what we would add here as well is releasing the semaphore from the interrupt. So to do this, I go to core, source, interrupt file, so this one, l4 underscore it, let's see. And here within external interrupts, I will go to the definition, not the declaration, sorry, not this one, sorry, this one. And I will use this callback. This callback is defined as weak within how underscore GPIO.c file. So once the interrupt is coming, there is automatic clearance of the flag within this line. And then there is a callback, a call to callback, which is empty function. As it is weak, I can override it within my code. And during the compilation, my version would be taken into consideration. So I'm doing this and here I will start with, let's say, sign of life. So it will be exclamation mark. And then I would release the semaphore, osmaphore, release A. And that's it. We are done with the coding. I will try to build it. And the next point would be, let's say, the debug session. So let's wait for the compilation. Okay, no errors, no warning. We can start the debug session. I start the debug session. I will check with the debugger and I would enable the serial wire viewer. I would limit the callback to four megahertz, which is used in my system, and then apply and okay. Single wire viewer is used as this IDM interface. So instrumentation trace macro. So and for this, we are using this single wire output, this third pin with an SWD interface. It is very good because it allows us to quite easily send data from the code over this line with system clock speed. So in our case, synchronous four megahertz. So it's quite fast and efficient. To display it, we need a single wire viewer ITM data console. In my case, it is already enabled as I'm using it for different projects. In case you cannot see it within quick access, please just select SWV and select this line with the monitor icon. Once you will do it, you need to configure it like myself and enable this line number zero. This is SWO interface line. Once done it, you need to start it. This icon starts trace and you can run the application. You can see one, two, three, one, two, three. Yes. And if I press the button right now, I press, I press, I press. We can see that I would pause it for analyze. We can see that once I press the button and then task one and task two has been executed. Task three is executed again because the semaphore has been released. As you can see, once I press, let's say the button a bit more frequently, task three was able to execute more frequently as well because it was possible to do this task to take two counting semaphores without waiting for both of the tasks. Okay, we can end this session on this point. So thank you for watching this video.