This is a step-by-step tutorial for everyone, who is interested in STM32 programming. I had a call with Greidi, and in this video, he will explain everything important you need to know to start writing software for STM32 microcontrollers. In this video, we will write a code which will control an LED, we will write a code to handle an interrupt, and also, we will write a code which can send some data from STM32 to your PC. After you watch this tutorial, you will be able to continue writing your own code all by yourself, so in this tutorial, you will find everything important you need to know to continue writing the code by yourself. If you will be following this video, don't forget to install CubeIDE, which we will be using. So, let's do it, here is my call with Greidi, and he just started the CubeIDE. So, we will be using STM32 CubeIDE for this project. You can configure, or like whoever's working here or doing the project, you can always configure the workspace as whatever location you want. Usually as a rule of thumb is I don't use any spaces for any of the project or workspace names, I've historically there just been some issues with using spaces, so just using underscores instead is is my go-to. So, this is the project name basically and location. Basically yeah, the workspace location, so we will still be setting up the the project itself, but in a workspace, you can have as many projects as you want essentially. So, we’re gonna go ahead and just Launch and this should pop up. Usually when you’re, when you’re starting a STM32 CubeIDE for the first time, you might have like more stuff running there, it starts to install some extra packets and so on, but right now it’s good to go right away and. Back to workspaces, so do you need to use like different workspace for different project? So, typically that’s what I’ve done in the past, yes, so, but I could have like multiple sub-projects in one workspace essentially. Okay. So, typically yes, I would keep separate projects separate, but whatever goes underneath it, you might have like, like one project that’s for like one specific board, but it has multiple microcontrollers on it, so you could do multiple Greidi: projects in that workspace essentially for that specific board. Yeah.
Robert: Okay, so they will open all at once. Okay, I understand. So, where did you click, or this is just default screen? So, yeah, I clicked here on the left side, there’s an STM32, a Create New STM32 project or usually if you open up for the first time this STM32 CubeIDE, you should be able to see right here in the middle like another one that create a project or something like that. So, in this case we are working with an STM32 Nucleo board, so it’s an STM32 Nucleo F4 board to be more specific, it looks something like this, and all the information will be in the description below as well for, for this specific board itself. But usually when you are doing, when you have your own custom board, you can on the same tab where we are at right now, you can use the MCU/MPU Selector, and you can just type in your commercial part number or whatever you might be using like if it’s STM32, and whatever might come after that like it could be like the F4, right. So, you could select and start off your project here if you have your custom board, or if it’s an STM32 provided board, which is our case right now, here we can go on the top here to Board Selector and type in the Nucleo board, so we can just say like “NUCLEO-F4” and this board was specifically F401RE, so it’s the first one here on the list, and also, if, if you’re not sure what type of Nucleo board you have, it’s usually like the product packaging when you order the board, you should be able to see it from the product packaging or the MCU chip itself like this, this big chip here there’s some letters on it and you can, you can read off that the serial or the commercial part number. But here we can just select the board and click Next, and then we can create our project. So, in this case, we are not gonna name it anything fancy, but we can just say it “getting_started” maybe. And again, I’m using an underscore, so instead of using a space, I, it’s just a personal preference, and. I know exactly what you are talking about. In different kind of softwares, I had so many problems like too long path for example, and and the software will not tell you actually. Yeah. They will, it will just cut it off or something and then you don’t know what’s wrong. Exactly. Some, some softwares will, will recognize the spacing as the end of the path or something like that, so it won’t find the project again or, or just a bunch of issues when the compilers are compiling the code and so on, so it’s better to use this underscore for that reason. But for the target language we’re gonna use C, all the settings essentially, we’re just gonna keep it default and just go ahead and go to the Next. Typically, there’s nothing really here that you would need to change, unless for for whatever reason you wanna use like an older firmware package, name and version. In this case, I only have like two packages installed, one of the newer ones, but on an STM32 webpage, you could always go and it’s. But what is this like firmware package? So, it's essentially, the firmware package will include all the STM32 custom drivers essentially, so whatever, they call it HAL or STM32 HAL libraries, so all of those libraries for this F4 product are essentially… Okay, so you, we will not be starting like from complete scratch. Basically, our project will, because we selected board, they know exactly what is on the board, so they will also include some kind of drivers, which we can easily use to talk with different peripherals. Yeah, you're absolutely right, that's exactly what's gonna happen. So, an STM32 has done or STM32 in general has done a pretty good job with just making this happen in the first place, and it's, it's pretty pretty user friendly, so from here I can just say Finish, and now you technically have an option to say to the STM32 CubeIDE do I want to start from completely scratch or like do I want to initialize some peripherals right away based on this Nucleo board. And in this case, we can go ahead and say Yes to like initialize all that stuff, and we will be loading, so that it will be loading this say ioc file, which is this kind of a GUI interface once it pops up, so. And if you don't have this board, if you are starting from scratch, then you can like manually include these peripherals from some kind of library or? That’s how it works? Yes, so typically, okay I misclicked here, but yeah, what’s, even if you have your custom board, you will still have this whole interface, so technically, if you want to do in, on your prefer, like on your personal board or whether it’s for a commercial reason, or just hobby, or whatever you’re doing, you can still use this STM32 CubeIDE interface that we’re using here. There’s, there’s, I guess there’s some downsides to using STM32’s own libraries that they provide, which is the HAL libraries. One of the specific downsides to those custom or like their their default libraries is that they can slow down our interface, so if you are using something that’s very much timing limited and timing critical system that you’re building, you might want to consider using your and building your own drivers essentially. But they are not like most optimal, they are like more universal than optimal. Yes, so, when they are building the drivers, they have to take into account all these corner cases and so on, that your project might not have at all, so when you're using those libraries, you have to keep that in mind that that specific STM32 driver for a GPIO interface or an SPI interface might be just doing some extra steps in order to achieve the same result. So, but little bit later we can also show the place where people can like. Exactly. Okay. Exactly. And, and also, something that I have done in the past is I've actually used STM32’s provided libraries initially, and kind of tested the concepts of like is this feasible, even can I do this on an STM32 chip? And once I improved it I'm like okay, it's feasible, now I can take those STM32 libraries and kind of build my own custom libraries based on them and throw out all the code that I don't really need anymore, so and make them more efficient, make them faster, so. But yeah. So, we see this GUI interface here essentially, which, which shows the whole STM32 chip that we are using here. I really like this specifically for the reason that it makes it so easy or, for someone to visualize, especially a person who is just getting into this, to visualize what's going on on the chip. So, we have all these things configured here right. So, this is already configured. Exactly. So, it's all configured, we have our oscillators coming in, we have a low-speed oscillator, high-speed oscillator, UART interface, a green LED right here and debug lines. Also, something if you are getting started with STM32, I would highly recommend going into and looking at STM32 and searching for a specific Nucleo board. So, when you go on the STM32's website, you can go to CAD Resources, you can download STM32 Nucleo’s schematic, so all of this is open source information, you can go there, download this information, and for whatever designs you're doing, so this is the PDF file here, you can actually look at, look up and everything how all these connections are done and so on, so if we are literally comparing this, this MCU on the GUI interface that we saw, we can, we can make this comparison kind of with the schematic itself as well. Let's check LED. Yeah, so the LED was, where was the LED? The LED was on a PA5 pin, so we can go ahead and check from that same schematic and say like okay, so PA5 is right here, it doesn't look like something's here right now, but you can always to copy this name and search for it. So, you can go PA5, and somewhere on the schematic PA5 should be connected to an LED. So, we can see this it has a bridge there, another bridge there, and it's connected to green LED. So, that would be for, for that LED right, and then the USART interface we already saw, so those are connected right away here. Okay. And we also have a switch here, so that was configured at PC13. That's what we will use in this video. Yeah. And exactly, so for, for someone starting off with an embedded software or if you come already from a background from like electrical engineering, done already like a bunch of layouts and so on, you're probably familiar with pull-up resistors and pull-up, pull-down resistors, right? So, that's some, that's something that to keep in mind as well, so in this case, this switch is connected directly to a pull-up resistor essentially here, so and if we would like press down the button, it would short it down to the ground, right? It's something we wanna keep in mind for later on when we're creating our interrupt interface for example. Can we click on, can we go back to the software and click on PC13 to see how it is configured? Yep, yep. So, when you press or start configuring any of these pins, you will see like a list of options of how to configure them and so on, so in this case yeah, it's configured as a general peripheral input output interface for an external interrupt 13. So, that's the configuration and yeah, so the oscillators are configured similarly as well. What about like the pull-up and pull-down resistors? They're configured in different. So, yes, so on the left side we can see here there's a system core for example or analog timers, all these interfaces that we can configure. So, we can jump into GPIO for here, and here we can see okay, so we have PC13, you can see it's actually flashing here, so you know which pin you're working on, and there's a GPIO pull-up or pull-down, so you can configure that GPIO pin itself to pull-up or pull-down as well, right now we can just leave it as no pull-up, no pull-down. Because there is external pull-up anyway, so we don't need it. Exactly. So, there's no need for that, and then we can configure all these different things here, so we would have like DMAs or we have a watchdog timer if we wanna activate it or we wanna configure an ADC or like how many channels we want to have running there for a real-time clock or a bunch of connectivity interfaces, so in for this project here what we're doing we'll be using UART, but there's also like SPI, I2C and USB. And if there are dependencies you will get warning or something? Like for example, I remember when I was using for example UART, then automatically I had to use, I don't know, a timer or… I don't know, that's an example. Yes, so when you are configuring something, you will typically see, let's say I'll configure the ADC, but I will see this yellow or like orange warning sign kind of. In this case, it's indicating that there are some channels that can't be configured. So, it doesn't mean like you can't configure ADC1 at all, it just means like these lines can't be configured. Okay, I understand, okay. So, let's move next. And green are the interfaces what we are using? Yep, those are the ones that are already configured in, in some way or they're being set up already, so the green one is that things are good essentially. Essentially, the RCC would be the clock configurations here, so and the cool thing about it is as well like you can visualize the clock configuration on this STM32 CubeIDE. This is very nice. This is super useful. It is, it is very useful. So, in this case actually, everything is configured on the low-speed internal oscillator here, so the 32 kilohertz oscillator is the internal one, and the external one is not used, but I mean, I think neither one of them are really used right now, because we don't really have anything to run. Like there's only two things you run with RDC in this case. So, and then that we have our high-speed internal oscillator, which is 16 megahertz, and there's the high-speed external oscillator that we talked about a little bit earlier. And I think if we look at this schematic here, we should be able to see both of these oscillators, so this is one of them. That's the 32 kilohertz, yeah. This is the 32 kilohertz, exactly. And then this is the 8 megahertz external oscillator, so there are the external and external oscillators are there, so if you wanna use them instead of the internal ones that's fine, and in this case I'm just not gonna mess around with them, but the PLL interface is using the high-speed internal as an input, we will be dividing the clock, multiplying the clock and dividing again to achieve a higher frequency, in this case 84 megahertz as the system clock frequency. Whereas and the really nice part is like you can actually go here and say like okay, I have 84 megahertz here, but maybe I want my APB peripheral clocks to run at a lower speed, well which in this case it is already running at 42 megahertz, but what if I wanted to run lower, like what if I wanted to run at 10.5 or and this is a way you can actually save power in the system to mess around with these or, or even actually lower the system clock frequency. Technically whatever we'll be doing here today will not require this high system clock frequency at all, but your whatever applications you might be doing in the future, this is something you'd want to familiarize yourself on, how to reconfigure and configure the system clock and the peripheral clock speeds, so especially if working in power efficient systems, so… Can you rewrite the number where the output is? Like yeah. Let's try it. I mean, you, I don't think you can do anything, it would search for a solution. Yeah. It found solution, okay. Yeah, it found one. So, but I'll put it back to 84, there we go. It has changed also the other numbers, I see it. Yeah, it found like a different solution here, I think you can always just pave it to do, nope can't do Edit, Undo, you can't do Undo whatever you did there. So, yeah so, whenever you have something configured here, and I can close this, well actually do you have any other questions for me here before I jump? So, initially we only will be using the GPIO for LED, and we need to have the clocks configured. What is in SYS? In SYS here, it is a debug interface essentially, and time based source for system check, so there is this thing when you're, this is like a later on but if you start working with real-time operating systems, SMT32 by default recommends to use a different time-based source for a SysTick if you're using a real-time operating system essentially. But it's not really needed, the really like why they suggest that is if you use their internal like built libraries like the HAL libraries that we talked about earlier, so if you use those then you would want to have two time bases, because they, you don't want them to essentially cross each other and influence each other in a negative way. But that's again for a way later topic, but maybe we can talk about it in the future, so… And NVIC, what is NVIC? This is the all the interrupts that are generated from the Arm CPU for this one. So, we will configure this little bit later again. Yep exactly. So, we will get to that part, we will get to that part. Okay. So, first we would like to control an LED, which is connected to the PA5. What do we need to do next? We have the pin is configured, microcontroller has the clock, so what is the next step? Well, the next step will be to, you can close out, in this case I'm not gonna change any of the changes or anything like that because we messed around there so, but all the code is generated already and on the left side you have your project explorer essentially, so you have the main CubeIDE project, you have includes, your core interface and the core interface will have the header files for your projects whatever you're using, and it also has some generated modules there. You will see like your your main file, which is where we will be writing our code, and it is quick question, is the text big enough or should I assume? Yeah, it is okay. Okay, that's good. So, and then we also have our HAL MSP, interrupt interface here or like modules here. So, the MSP module is already generated to initialize some of the HAL module instances there essentially to to configure the GPIOs in a certain way. But I guess we don't have to do any changes here because these are automatically generated when you change the settings in the previous, I don't know what was it, wizard or something, module configurator. Exactly, you're you're absolutely correct, so no need to do any changes here like the same thing is for the interrupt module, it has some fault handlers and so on if you if you ever wanna use them, but there are always like sections to add user code here. And those sections are, like the user code sections, are identified as “user begin” and “user end” essentially. If you, let's say I write something here like, like I don't know, like say “uint8” or something like that, and I'll, I just say like this is a hello or something like that like. Like if I say something like this here within the user code, it's fine. It means it will probably not change it when you reconfigure. Exactly. So, if you were to, if you put it outside of these command sections, if you put your own code outside of the command section, and you regenerate the code through that IOC file that we had open before, then you will lose that piece of code essentially. So, you want to make sure that it's really to keep your code, look maybe it helps to even organize your code a little bit better, but it also will help you to keep in mind that hey, you know you gotta use these areas if you wanna keep using the auto generation functionality. So, and there is another folder here where startup, so this is, whoever is a big fan of assembly, this is their favorite place probably. But essentially, there's a bunch of interrupt mapping here, there's mapping for what memory areas you're using, how much memory using for certain areas and so on, so this is loaded into the MCU, so the MCU knows that hey, okay, what should I do, which interrupts can I use, where are they located, where are the address locations and so on, so that everything… We actually head all these at the university. Yeah? Nice. So, and if we go to a driver section, we have, we have the STM32 drivers here that we talked about before, so all of these are auto generated essentially based on the module, so this is typically an area where you don't necessarily need to modify anything unless there's a need or unless you wanna use these HAL drivers as a baseline, and then later on modify them for your own good or own needs, right? So, you can always do that, so if we're looking at the GPIO for example, you can always go and surf around and kind of look what the source code is doing here and so on. Usually, I use it to also check what kind of functions I can use. You can, you can have a look in the header files, but here sometimes you can get like better idea what it is actually doing. Yep, there's always that part as well. Okay. So, let's say we would like to, yeah, we would like to use the GPIO or did I interrupt you? You wanted to say something. Yeah, so here on the in the main.c file, we'll have the GPIO initialize, so we can actually go and see how the GPIO is initialized here. So, if we open the declaration for this, we can jump here and say like okay, all these different GPIO clocks are enabled, a pin is initialized in a certain value, so we can see actually how GPIO write pin, it's using, it's actually writing that LED2 pin value as a reset value or 0 if we highlight here. So, and I think one of the good points you just mentioned Robert is that you, one of the ways you can check is like what kind of functions can I use or what they do is if you like to read code, this would be a great place, you can go and open declaration and jump into and see like okay, how do I need to like, how do I use this code, like what do I need to include here, right? I need to use a GPIO peripheral area whether it's an A, B, C, D or however many letters your STM32 or MCU might have, and then you specify the pin and the state for that pin, whether it's 0 or 1, and then you can configure that pin. And you can do the same with maybe a toggle function or read pin function, so you can kind of read through the code. Or, if you are more of a document based person essentially, and you can see STM32 Nucleo F4 HAL documentation is what we're looking at, so there's a UN1725 documentation and we can see the same thing that we were doing here before, so HAL, so HAL GPIO, and then we can see all this function that we can say write pin, and we can go and check what this write pin is doing and see the other functions as well. So, all these HAL driver functions are defined here and kind of what is the expected input or what is the expected return value and so, very typical like a software or a firmware documentation that… Can you open this from the right-click? I think there was go to documentation or something when you were, go back to the software and on, yeah, right-click there or maybe I… Okay, maybe I went wrong. I don't think, I think there is like under Help you've, there might be a way, Information Center, I don't know, I typically, I honestly, Robert I usually do what you do, I jump into the code and I'm like okay, what other functions are there, how do I use them, what what else could I use to like better, I don't know, write this pin or like send a message to the PC or something like that. So, that's what I typically do. Are there examples like how they, how they use it? In that documentation? I don't recall. There might be, I know there are some, there's a lot of documentation that they have here, but this is a generic overview of how like or what the function would be doing. Are there specific examples? I've actually never looked myself for a specific example from this documentation. Okay. I know there's like some other ones that are like specifically focused for UART or ADC or even GPIO that have like an example set of functions, so hey maybe someone is in the comment section, and they know better, so leave a comment about it. So… I'm only asking all these questions because I was doing different video with, I think like PIC microcontroller, and they included actually examples directly in the c files and in the header files. Maybe, are they in headers? No maybe there is nothing in header files. I don't think they have specific examples like that. They have I'm pretty sure it's just descriptions of how how to do things. So, what is in header files, can we have a look on the header files? Of course. Of course we can. So, on the left side here on the project menu we can jump into include folder and yeah, if we look at the GPIO header file, we most likely won't see these main functions. But we can open it up but and and see like okay there's some structures defined, there's the GPIO pin state that we had there before and then, so GPIO, some GPIO pins that they're defined as some unsigned integer. Yeah. And then there will be yeah, so there's there's some macros here, and we will see some functions, so there's the write pins, toggle pins and so on so… But there there's no examples within the code itself so. Okay, so, how do you do that let's write something to control the GPIO, I'm curious how you find out how to use it. Let's do it. So, I'll just minimize this stuff here but… So, what I ended up doing here actually, I don't know if, I don't think I brought it up, but so if I'm usually what I do here if I'm working from directly from the code, I just copy this essentially this function statement here, and I just jump into like the main.c file, and in the, in the main we have in the main.c module essentially we have the main function itself where essentially the processor knows okay, I gotta jump in here and start executing some code. We will be initializing some HAL stuff here, configuring the system clock that we saw on the, on the GUI interface there before that we talked about, and configuring those GPIOs and the UART interface. So, once everything's configured, we can go in and say like okay let's do our LED, let's turn the LED on or actually let's take it a step further, let's make it blink, so if we're looking at this section here, user code begin while, user begin or end while, so we wanna stick to that area essentially. I just go ahead and take this interface or like take the same code essentially that I copied from that function statement, so we need to use this GPIO, and what was the, what was the GPIO peripheral that was for the LED. I think it was GPIOA, right? PA5, I don't know. So, I mean we can, we can hop on to this documentation, I think it was PA5, yep. You are, you're correct, you are correct. PA5, so it's a GPIO pin 5 then, so it should be GPIO. How do you know you need to write this? So, in this documentation, okay, I’ll jump here. So, we have defined here the GPIOx, so essentially the parameter definition here is saying like okay it has to be defined as GPIOx where the x is A to K essentially, so I could define this as GPIO whatever it might be, right? And then… And the next one is GPIO_PIN_x, okay. Yep, there we go, exactly. So, I can define those in in that way, so I can say this is GPIO pin 5, and if I jump back to here, I can set the pin state, and there were two options here, so it was GPIO… Reset and set. Exactly. So, in this case, we wanna turn on the GPIO. I think the reset is default, yeah. Yep, exactly. So, but if we wanna do this blinking interface, we need to turn the pin off as well, right? So, if we rewrite the pin and say that it's turned off here, so let's say this is LED on, LED off, and you probably noticed this right away and you're like okay like I turn it on but I'm turning it off right away, there's gonna be no difference there, you're not gonna see a change in state essentially for the LED, it happens so quickly at 84 megahertz. So, we need to create a delay before we can actually go ahead and write the next value on that GPIO pin. So, so this is, there's a function for within there, there's a function in the STM32 libraries that is called HAL_Delay and if. Can we find it? It will be somewhere in timer? Come on. There we go. So, it opened up. So, HAL_Delay and you can, reuse this code wherever it is, and the idea is yes, you have the system tick timer that's running in the background, and it's getting some timer value initially, and it's essentially comparing and checking like okay, how much time has passed, like is, have I, has the clock counted enough times from the point where it started for to meet that delayed expectation essentially. Have I waited enough? I understand, I understand. I see it is in “stm32f4_hal” okay, so, no timer, nothing, just underscore. So, there are some other useful functions here? Yes, there are some functions here for example there's a HAL_SuspendTick and ResumeTick. So, you can like, you can like pause the delay for example, yeah? Exactly, and and this is something that if you ever get into low power, kind of low power modes on an STM32, this would be something you will find useful, because the low power modes often are configured, so that you can wake up with an interrupt or or even yeah, you know, essentially, so in this case an interrupt occurs for or like if the system tick is still counting, it will interrupt the this sleep mode essentially or the low power mode, whatever it might be right? Or whatever it might be called, so essentially you wanna suspend the system tick for the duration of time that you're sleeping. So that also your system clock is not running while you're trying to save energy. So, that's something that I've used a lot in the past is the suspend and resume essentially. And I mean like you can if for whatever reason you wanna build a configuration of like or send information out what is the HAL version or revision ID, so you can, you can fetch that information as well. Okay, we can go back to our main code. Yeah. So, it was in milliseconds, I've seen in description it's in milliseconds. You are absolutely correct. So, delay in milliseconds. Do you have a preference, how, how long… 1 000. Okay, let's do 1 second on. So yeah. Now what's happening is like we're turning the LED on, we're pausing or essentially delaying for 1 second or a 1 000 milliseconds and I'm gonna copy this code again and say that okay, we're gonna delay again for another 1 000 milliseconds or 1 second, so we're gonna turn the LED off, delay, turn the LED on, delay, and so on, and keep repeating. So, we are in this while loop or super loop or infinite loop or, there's many ways people call this, but yeah. So, this is what we should be expecting now. Okay, so this is our first example, now we are going to run it. Correct? Exactly. So, but usually I just wanna keep, I just wanna let you guys know that whenever you're running or trying to program for your first time the STM32 board, here's a quick tip that I would recommend doing if it's your first time plugging in your STM32 board, you wanna go to Help, and you want to select ST-LINK Upgrade, so it's pretty common that the boards that are sent to you or mailed to you have a very old firmware version installed on the board. So, in order for you to have like the best experience or less buggy experience essentially, that it wouldn't crash, or your board just doesn't burn, hopefully it doesn't burn, but but anything like that, so you can, when your ST-LINK is plugged in, you can open it in update mode essentially. So, now it's updated. Plugged in you mean you connected your board to USB? Yes. So yeah, essentially I'm saying that let's upgrade the debugger interface, because the debugger is the essential element, which for us to get the code to our chip. So, I showed, I showed the board before, but you kind of see yeah you kind of can see that okay, so this is the main MCU here that we're talking about right there, and there's another STM32 chip, which it happens to be a STM32 F1 series chip, so it's a smaller F1 series chip, but this chip is acting as a debugger interface. That’s the JTAG, yeah? Yep, yep. So, in this case, so this, I don't, you can probably see the board is kind of divided into two sections, right? So, one part is the debugger part and that's the part that we wanna upgrade, because our whole programming experience is gonna be dependent on this debugger side, right? So, if we plug it in, open it in update mode, we should be able to see that okay we have this connected, technically, mine is already upgraded because it did that but what you would do here is you would just press upgrade and it will upgrade your debugger interface to the latest and hopefully you will have a fun experience. This is good to mention, because you don't have to buy any JTAG anything, you have it on the board. Exactly yeah, and that, and another thing to keep in mind here, I've run into this issue a lot, sometimes I'm not able to program it until I upgrade that board, so I cannot like even, it's it's just gonna say like okay like some error or like can't find the interface or something's not working and it's not able to program the board just because it's not updated to the latest version, it's not compatible with the CubeIDE version and everything that it's using versus the the debugger itself there. If you are designing your own board, can you use the this JTAG or debugger to connect to your board? No. You can't use this one. I, realistically I think it's not gonna be feasible, but I can quickly show I think it's called STM32 link version 3 I think, this is the one of the newer ones, yep, so STM32 STLINK has a V3SET. How much this costs? Okay, let's see if we're looking at, we can probably look at Digi-Key, right, or Mouser. One is available for VSET, so the V3… Oh 35, that's not really, that's cheap. It's not bad, it's not definitely not bad. It has like a bunch of extensions as well, so I think if you are, like STM32 typically the MCUs can work in a 3.3 voltage setting or 1.8 voltage setting. The V3SET by default provides only support for a 3.3 volt, you would have to buy like an extension board, kind of like an Arduino shield board, you like put it on top of this one, and it will give you the support for 1.8 volts as well, but it's, the V3SETs are the ones that I've used in the past and I’m the most familiar with. But I think there is an option for you to use also the v2 set, which should be cheaper, I hope. I would always say like it's like I should use the latest one. It's exactly, so it's, the v2 is out there just for a record if someone wants to buy something cheaper, so yeah exactly. Anyways and the v2 set will provide that support from like straight out, but I think the difference was also on the speeds of the debugger itself, I think the v3 is newer version, faster speeds and so on so all compared to the v2. So, this is something you would use when you have your own board or unless you want to put a debugger on your custom board which very unlikely that you would ever want to do that, but yeah, that's that would be another option. This is good to include in the video, because that would be like next thing what I would be looking for, what kind of they might get to use. Exactly, so now we should be at a point where we can actually launch this on the board. So, what we are going to do next? We wanna build the project, so you can either do it in in a step that you can just say like Run As and just run the board or and it starts building, but I wanna take time and actually build the project first, so what you can do is either right-click here or press this little hammer here to start building, and you can build a project. And you should be able to see this. No errors, no warnings! That's an always good sign. It should be very satisfying if you, if you've been doing this for a long time, you you know. It's very very satisfying. Okay, now hold the board, I would like to see when it starts flashing. Okay, so if I, I'll grab the board right here and I don't think right now you should be able to see anything running. There are two LEDs, two red LEDs, yeah. And they are on. Okay. And then if I press, I gotta actually select for the first time, there we go. Run and you will see some configuration window here. I usually never mess around with this, when I do it for the first time, I simply in the configuration window I am, especially if I'm working with a Nucleo board, I'm not doing anything here. Okay. The only reason why you would change anything here is when you have a custom board typically. When you have your own custom board, you might have some weird cases there on how the debugger is connected, it might be like an active low setting or you wanna connect it under reset or whatever it might be, you would have to like figure that out if you have your custom board, but usually yeah, you're good to go with default settings here. And we should be able to see, we see some getting started, GDB server error, which is not a good sign. So, let me do it, restart on the board. That's exactly what always happen. Yeah? This is, this is real life here, so… Maybe we can just connect and disconnect it because we upgraded the firmware. Yes, I just re-plugged it in and let me see if this did something. There we go, and now it worked, should be able to… Is it flashing? Which one is flashing? A green one! I see it, yeah. So, it's yeah, it's not focusing in but I could manually. I can see it. Change the focus. It's good. So, yeah, we can see flashing one second on, one second off. So, there we go. We made our LED flash. There we go. So, it's always a satisfying thing to see that. We saw a real life example of having a problem here as well. But I think it was because, because we updated the JTAG. Yep, you're, I'm pretty sure that was the, that was the issue. So, now we were able to make this LED blink and we also had given few hints here and there before about talking of this interrupt system of like using this button and creating an interrupt, so let's go ahead and do that, let's do it, so that when we press the button, we're gonna toggle the LED for example, we we turn the LED on or turn it off and so on. So, for interrupts we can jump back into our ioc file. Okay. I was curious where you are going to or how you are going to start this, so you just click there, okay. Yep exactly. You double-click on the ioc file and you jump there and we had mentioned before the GPIO here. So, when you open this up, you can either do it through the NVIC tab here or you can go to GPIO and you should be able to see NVIC here. And since this, wait a second, this one here. Yes. So, since this one is configured as external interrupt mode, the interface or the software knows here, that the NVIC, we can actually enable an interrupt for this one. So, both of these configurations need to match, so we need to have the GPIO mode as external interrupt, but we also need to enable the actual interrupt within the CPU, so… What about the other option, go back to the GPIO, can you? Yep. What are some other options here? So, we can do either falling edge trigger, we can do rising edge, we can do dual edge, right, rising/falling edge either one of them, and then we can do them as events, so… What's different between interrupt and event? Event is, how do I explain this? Okay, for this example we would like to use interrupt, that's… Yeah. So, I'm just gonna go forward with that. Yeah, that's fine, I'm gonna ... in that one right now. So, external interrupt mode with a falling edge trigger detection, so… Falling edge it means from plus to ground, so when we press the button, that's the moment when the interrupt will occur. Yes and and the reason why usually like our, I guess our general intuition would be that, you know, you press the button, it it should turn on then, right? Or like we should have a high signal on, but in this case falling interrupt is indicating, a falling interrupt is indicating that we are going from a high signal to low signal, right? And that's for the reason what we kind of discussed before and showed before here was that we have a pull-up resistor on this PC13 line, so technically if we press the button, we're going from a high signal to a low signal once the button makes the connection. So, that will make the falling edge trigger for us. And I see there is also small capacitor, I guess that's filter the possible spikes when you press the button. There we go. I wouldn’t, I personally, I'm not that of an expert of electrical designs and so on, I know the capacitors are used for filtering, so that's that's what I know, but so I'm assuming that would be for debounce, is that, is that correct? To filter some of the debounce out. Okay. Because if you don't have some kind of way to filter this, then you can end up with like multiple multiple events basically, could happen. Okay. That makes sense, that makes sense. Okay. So, we had this Blue PushButton set up here essentially, and now we can go forward from here by saying that… Okay, you enable the interrupt, okay. Enabling the interrupt here right, and once we have done that, technically all we need to do here is just save, save the file and it should prompt us to generate the the code here. So, Yes, I wanna generate the code, so now it's gonna go change some perspectives here. There we go. Okay and now it updated the libraries. Exactly so something, something changed here. And I know specifically where something, so in GPIO initialize we did configure the pins before, right, we had the the pin here configured as ... falling and then we have the LED output as a output mode, and we were setting at some reset value. Now we see two extra lines of code here essentially, so this external interrupt line from for specifically for GPIOs from 15 to 10 or like the pin numbers 15 to 10 has been enabled now, so now we can actually go and look into this interrupt file here, we we opened this before, we looked at it, we kind of saw that everything kind of ended around here, so there was no more code here. So now we should be able to see another piece of code here, which is this one. So, this is an interrupt handler that essentially when an interrupt happens the the system knows where to jump within the code, what address location it needs to jump to. So, this handler is for all the pins or from 10 to 15? This is for pins 10, 15 to 10. Only, it’s specifically for GPIO pins 10 to 15. So, if you're using this, it would be for that. So now, since we are handling this interrupt, it's calling this a GPIO function here, so that's what it's really using and what we wanna do is we wanna jump here and see what's kind of happening, right? So, it's actually using, it's checking if the pin interrupt value was the correct one, if it's, if it is a true interrupt, if it's not then it's just gonna move forward and move on with its life essentially and do whatever else, but, and then it's gonna clear an interrupt. Okay this is checking the specific GPIO, because the first one was for GPIO 10 to 15 and this one is checking this specific GPIO what we are using for the button. Exactly, thanks for clarifying that. Yeah. Yep. So, we are passing this pin value here, and that's what it's comparing. It's checking for if the interrupt was on for that specific pin. So now that we have this, it's going into this function called external interrupt callback. So, and callback functions in embedded software are very typical, so whenever you have like an interrupt or something that you typically wanna use callback functions. In this case, we are gonna use a callback function as well. This one here is actually defined as weak, so what does weak mean? It means it's not strong enough to stand. I guess if we write it again, then our new will rewrite this one, that's what it means? I don't know. Essentially yes. So, if you put this function anywhere in the code, you can essentially like overpower this whole definition that was called weak. So, what we can do is we can go to this user code begin section in here and essentially call this function as our own now. So now whenever the interrupt happens, it's actually going to this function here. So, we can go ahead and write our code here, so whatever we wanna do. We would like to turn on the... Oh no. Yes. We have, we would like to change the status of the LED. Yes, so we we already looked that into GPIO write pin and there was this other cool function called toggle. So, we can use this function. Okay. And I think there are a couple couple rules to keep in mind whenever writing anything or doing anything in an interrupt. Yeah okay. Do you know some, do you know some rules? We should keep it as short as possible and as simple as possible. Exactly, exactly. And there's there's another, kind of ties to the first one that you just mentioned, but never ever do delays in a interrupt. So, which will mean that you're making it longer than it should be, but also there are actually some compilers that would throw it out as an error or a warning that hey, you have, you have a delay in your interrupt, it should not be there, or they won't let you compile or it's a complete error or, however you configured a compiler, that's actually something that can cause some issues on your, on your code down the road, so good one, good one for sure. So, we have some of this code already written here, do, we won't need anything in the while loop anymore, because we are working as an interrupt now, so… And just to clarify the concept actually… Oh, we don't... Yeah, we don't need to, we don't need to blink, yeah. I just copied the code too. Okay so… Make myself lazy. Okay. So, I can copy this over and just say this is GPIOA, pin 5, and now whatever the state is it's just gonna toggle it every time I press the button. So what if there are like more interrupts enabled, so the this callback will be called different name? I see what you're saying. Yes, so in that case, we had this function here, I just wanna jump to the right place, so I don't need to look it in the code, but if we jump here, so yes we were looking at this pin value, right? So, if we have multiple interrupts, we will have multiple ways this pin value could be here, right? Okay. If it was an actual interrupt and so on, we could either define more callback functions or we can use the callback function as you were kind of saying here, I think that this is what you were going with like hey, we gotta check if this, if this pin is the correct one, but right now we kind of know that this is the only one that is out there, so yes we could add a section here and say like okay. There should be like condition maybe or if. Yeah like an if statement, if this GPIO pin… Is the power button, power button.. Is the button, then control the LED. Okay I understand. Exactly, exactly, or if it's something else, do something else. Exactly like that. And yeah, and that's how we can do the interrupt, so let's let's give it a try, let's see what happens. So, we can run this code, should be able to build, run it on the board, there we go, we can see verified, everything is verified successfully, and we should… Let’s have a look. There we go. So. It should be off. Okay, so the green LED is off, okay. And then if we press the button, we should be able to see it turn on, yep, there we go. Perfect! Yeah, so it works, it works. It's always a good sign, always a good sign. And now something really really difficult, we are going to communicate with PC. Yeah, there we go, so we will communicate with a PC. So, if we are jumping into a UART interface, which is what we're gonna be talking, we kind of talked about, and we actually showed on this ioc file that hey, our UART is configured or in this case it's configured as USART or it is at least a TX USART, because it is. But USART can be used as UART as well, it just gives an additional option of either having it as a synchronous or asynchronous, so in this case, we will be using an asynchronous version. And as a whole like a system level, kind of what we will be doing is we're essentially configuring this main chip here as a UART interface, and just saying like hey, send data to this small UART interface, and this small UART interface is like okay, I'm getting data from the big chip, I'm gonna send it to the PC. So, it's like UART to USB converter. Yeah, basically yeah. Can you, can you still use it, can you still use the JTAG for, I don't know, debugging if you are using also… Oh yeah, yeah. Okay. Yeah, you can actually, if you want, this will take an additional code space on your board, but you see this there's an SWO, so I don't really remember what the letters itself stand for, but you can essentially configure this as a third pin or this debugger interface, and it's a print pin essentially. You can send and print out like debug data if you want to. So, but it would take an additional code space in your code like, if you're already limited on a code space on RAM, then you probably don't want to use it, but if you don't and you want to print out like some, I don't know, if you are a person who writes code as they are like doing like the print if statement somewhere and so on, you can always do that and and configure that. That’s interesting, because, so this is good to know, because I know in some chips you actually have to use UART interface for debugging. I didn't know you can use the SWO for this. Yep, yep, so in this case yes, you can use that if, as as long as you have the flexibility to do that, so. Okay, so what do we need to configure or use the UART? Let's have a look how it is, okay it's already configured. Yeah exactly, so as I was talking about the USART, the synchronous and asynchronous mode, you have like options to select it as asynchronous or synchronous or single wire or multiprocessor communication or however you wanna do it, and it's in this case we're just doing a simple asynchronous UART interface, standard UART interface, and the baud rate is a, I think it's a pretty much default as standard as well 150 200 bits per second, and the word length, parity, stop bits like all of this you can essentially define here. You have some advanced parameters for data directions right, so we are working with both receive and transmit, so we could do only transmit if we want to or we can do both either way, and then we have oversampling as well. So, oversampling is simply a way to, you know, maybe correct some errors within the software itself and say like okay like, I got like 16 samples already matching up with kind of like the ..., I don't know what algorithms are behind it, but the kind of the idea is to just minimize some errors or maybe some, eliminate some of the noise that might come on the data lines. So, it's kind of confident, yeah, exactly, exactly. So, and there are some options here as well, so we could either set it up as a interrupt if we want to, we could connect a DMA interface to it which is a more later on like an advanced concept, but and the GPIO settings for them as well. So, if you configure interrupt, it means it kind of reacts on interrupts like, you receive data then you do something. If you don't configure it as an interrupt, then you just have to like wait in wait function to receive data or something like that. Yes, so it's, yeah we kind of like talked about the concept or like show the concept of interrupt right like if we can do whatever we want and then we press the button and we interrupt the whole system and then we handle that button press essentially what happen. So yes, if we enable the interrupt for UART, we would have an option to essentially start sending and receiving data on on an interrupt, whenever we see something on the data line to happen, whether it's like typically you would wait for a receive signal, right, you would wait until you get something on the receive line and start reading the data from the receive line. And the same way if you transmit somewhere or some data. And in this way, you would eliminate the effect of polling, which you would otherwise do which is like you keep waiting for something to happen right. If you would order a piece of pizza, you don't go stand by the door or like you don't go to the door like every two minutes and check if the pizza person is here or like or it is the pizza is here, you wait until the doorbell is like rang and then you go like, so it's like the concept of like minimizing the effort of the CPU itself. So, I mean this whole configuration for it, what, for the UART itself like you don't really need to like calculate any of the clock signals, you just specify the baud rate and it, the system itself will make it happen, like essentially it will be like okay, this is the baud rate, I should be interfacing on. You need to meet a certain clock frequency to achieve that in the first place, but we are way beyond that. What is in the user constants? User constants, so if you want to, you can instead of, so in this case it's used I guess USART_TX or USART_RX, you see LD2 is defined as green LED. I was thinking where it is, yeah. So yeah, it's a really good point, so we should be able to see like, so there's a user, I think I need to, I spaced this out a little bit more, you see there's a user label, so you can specify this. So, it's easier for you to actually know what is for example connected there. Yeah, I personally, I do name them in a certain way but if I'm doing something as like just testing out, like some testing out some concepts, I typically just stick to the USART and so on and and just be like okay, try them out and see what happens, so. Okay, so let’s have a look how to use it. Okay. So, this is configured, we didn't make any changes so. We don't even need to configure any timers, anything, everything is configured automatically. Exactly, everything is configured for us, it's right here, so we can, we can see all the same information right here for the USART configuration, and we. Where is it located? Where did you click? So, this is in the main.c file, so it's, so if we go here, this is the USART, UART initialize, if you open declaration, we should get here and see all the code here. So, we have all this interface here, and now okay, we wanna see, how we can send data with UART right? And we kind of both had the same method of like going into the code and see what functions are there, and what can I use or or actually or maybe even using the header file here, right? So, if we go to the HAL drivers on the left side here, and let me, there we go, we have the hal_uart.h file, so from here we should be able to see some functions that we can use to make our communications, to send data to the PC itself. So, there we go, HAL_UART_Transmit. Upstairs there are Init functions, I think. If you go a little bit up, yeah, I think these are Init. Yeah. So, we already have UART initialized. Yeah, I’ve seen it. So, this is the one that's initialized here. And where was I? I was here. And then the UART_Transmit is the function we would want to use right now. Okay. So, I'm gonna keep things very professionally, how would I say, inappropriate code wise, so not a clean code, not a clean code is what I'm saying here essentially. I'm gonna make it as simple as possible essentially. So, if you notice here, the first parameter that the function requires is a UART handle type definition. So, a lot of these functions and peripherals and STM32 HAL libraries will have these handles, so in this case, we have a UART handle and if I go and actually check the definition and see we have all these different register base values and pointers and all these things that are part of that handle, so it's essentially a way to… It’s pointed to the structure or something like that. Exactly, exactly. So, all of these values are used and we we can configure them in a certain way or however we want to use, so now, we can see actually if we go here, some of these values for the UART2 handle are defined here, so we have the instance defined, we have, so this would be like a memory location for the where this USART is located for example, and the rates and so on, so now we can use the same handle that we had defined here and initialized in the code itself and say that okay, well let's put it here and we're gonna use this UART handle and send data over it. Do we need to add and (&)? Exactly, so another thing I wanted to mention is this is a pointer, so we want to pass it in at some specific memory, not coming out, and command. Yeah, I notice they use it also in the Init function, they use it exactly same way. And now we can we can pass some data here, I mean we could technically write some just, yeah, we could just write here “Hello World!” and do this for example. This is a way to do it and I'm gonna give a line break here as well in the end. Okay. And the size for this, I mean if you want you can count the characters or you can just put a nice number that looks good, I mean I'm assuming it's not gonna go beyond 15 characters, it doesn't look like it's more than 15 characters. So, or 15 bytes in this case to be more specific. And the timeout value is simply as like hey, I'm, I can delay and wait until we are able to make UART transmit connection. For example, we start transmitting and for some reason the UART line is busy or it's, maybe it's not even available right, the system is gonna the driver’s gonna go and check like hey, can I send the data, oh no, okay, I'm gonna keep waiting, can I send the data, can I keep waiting, and it's gonna keep waiting and counting like how long it's been waiting until it times out essentially until it's like okay, there's, I can't make any connection here.
Robert: Okay, so this is timeout what you are going to put there. And I'm just gonna put like 100 milliseconds. Let's do it properly, let's make a variable and then put there some kind of function to count the length. Let's do it. Let's do it. So, we have all these local variables here, so they are nothing big here, so usually something like that you could say that this would be “t” and you can say that this is your message you wanna send for example, and we can use the same code here and say that, there we go. And we can, we can send this message now. Okay, and now we can, can we use the function which will count the letters? Yes, we can do that. Do I remember from top of my head? That's another question. I don't usually count letters, and see. Wasn't it like string length? Yeah something like that. Yeah usually it would say I think like… You can write it directly in the transmit. You don't need to create it as in or you can't? I don't know, I don't know what is, what does it return. You mean to just use site. Yeah, just put it directly there. Let's see if it works. Are we going to also… I think this is a C++ function, I'm pretty sure, and usually you can define like a timeout value, so this is something you would define as a… Sorry. “User code begin” okay, there we go. So, yeah, so this would you do a “#define” here and you could say like timeout and 100 for example. And something like, it's like, it would be like a typical timeout value you can use across the board for other functions or whatever you're using, and I'm pretty sure… We may need to include, for strlen we may need to include something. Yeah. So it's stdlib or something, I don't know. No stdlib. I don't know, I was just guessing, I don't know. I don't know where is it located. Search on internet. There is… There we go. That should have. Yeah. Well is this, it is strlen. And what, the string, string.h. Std, string.h, yeah. Usually, yeah, okay. It's fine, it's fine right now. Usually like these functions or this includes our stuff that you typically like depending on what kind of microprocessor and stuff like that, you have, you might not want to use libraries like string.h or something like that, because it depends on how much code space you have available, right? So, if you have only like 32 kilobytes of code space available, and you have your very tight constrained essentially like on that code space, your own code is already like maxed out like at 30 kilobytes, you most likely won't have space for the string.h library to bring in, so I think that's like very like corner cases essentially that you would ever encounter something like that are pretty rare, especially that nowadays like all these MCUs are just getting so much better with the power efficiency and having that code space and that RAM, that is like it's crazy like how what kind of technology is coming out, so. That’s a very good point. It's getting better and better, so, but usually I think we want to define this as a char, and message, strlen message, timeout. Let's see if that works. I'm curious. So… Let's build it first, if there are errors. There you go. Build Project. Did I build it already? There is a triangle, yellow triangle. Something is wrong in our. Okay okay there we go. “Makes pointer from integer without a cast”. I don't know why it's, yeah, it's being weird. Okay there we go. Now it's, now it built. I was just, it was just weird like I kept pressing build and I, it didn't even try, it didn't even try to build so. Perks of playing with these tools. So, it's okay, it built, it built, so let's see we have that built. But you know this is good, because this is exactly what everyone would be doing, because you have to find these things, you need to go back to function declaration, have a look what is wrong. Yeah, you're right, you're right, that's true. Okay, so I'm gonna open this console here, which is trying to, there we go, I'm gonna expand this a little bit to make some space here, I'm gonna move this here. So you can open console directly there, okay. Yes, so it's essentially what you can do is like on the windows you can just drag and drop essentially, you can move them around. So, I just make some more space here for this console. But once you click on console you should be able to go here and there's this little little arrow right next to the console, or actually this plus button here is that not that little arrow, and you wanna open up a command shell console. And if you're doing this for the first time, you probably have some configuration here like, I'm running on a macbook, so I would have here a, you can define a connection name but on a mac specifically something like this is defined as like a “cu.usbmodem” and like so on, and this is the configuration we have there, we have a 115200, 8, none and 1. So, this would be a connection name. I technically have like if you would type here some name, you would press Finish, I have it defined here already and it's STM32F4 connection. So, I know that specifically this will be the remote connection for for this board itself. And if I press Ok, okay it connected to this board and actually it showed up on two tabs, which is very nice, I guess I'll close one. But now, we can actually go ahead and run our code. Now if you are using Windows or something like that, you could use like a program called like Tara Term or what else is there? PuTTY. PuTTY, yeah exactly, so all these programs will work as well, and honestly I think PuTTY is like one of the main ones that a lot of people use, it's pretty good one, so I would personally use that one if I would have access to that. Right now, but… Okay, one of the things we haven't talked about either is the debugger itself or the… Yeah, I think we will do this and then we will do debugger then. Okay. We can, okay. First I would like to see if it works, you know. Okay. I’m curious. Let's run it and let's see what happens. Okay, let's run and see what happens here. It's going to write “Hello World” forever hopefully. So, I just, so we wanna do this one. It works! There we go and it works. So, we're running this “Hello World” everywhere and we are doing our print statements and obviously it's gonna do this for a long time. So, if we want to stop the “Hello World” here or actually. Yeah. Yeah let's fix, let's fix our code for a little bit right. So, let's move it out of the for loop or like, sorry the while loop, and if we run this, we should be able to reprogram it and let's debug it, and to make sure that I guess we don't make the same mistake and we're not constantly running this here right, so this is good. So, if in order to debug you have a bug button here and you can just go ahead and select that and go here as debug. Did you, and did you put there like stop. Oh right, right. Thanks for clarifying that. So, whenever you're debugging, you can specify these little break points essentially, and you can specify them and if you go to this line of code where you're at and you double-click on that line essentially, you can say that okay there's a breakpoint here, breakpoint here, you can put these little blue buttons everywhere. We obviously don't want them everywhere, but we want them before, right before the HAL_UART_Transmit, so we can do this, and now if we go and start the debugger, we start Debug As, we go, we are stopping whatever the code was running there right now, but once we're in a debugger, usually what happens is that we are stopped right away by default, we're stopped at the first line of code essentially. In this case it's our like defining this message that we're about to send right, but if we are going through here and we have few options here in the debugger itself. We can either step through, which is this arrow here, so we can step here and go here, or we can step into a function, so if we press this. Yeah, we go inside of the whole Init. Yeah. So, we go into a specific function, and we can actually step through this function if we want to, and see like what's happening here or we'll step into another function inside that function and kind of trace down what our code is doing, and it's such a great capability that all these debuggers are providing. So, we can go through, so this is gonna return some value and we are gonna continue off where we were, where we jumped in before into that function. Now if we, since we added this breakpoint before, we can instead of like trying to like one by one like walked in there, we can just press this play button, here in this little green play button, and it's also like a stop button at the same time or pause button, so if we press this play button, we run the code until the next breakpoint. So, in this case we reach the breakpoint, now that we have a clean page here, we can go and check how our transmit function is kind of working. What do you think, should we jump into the function itself? No, no. You can just go over. So, if we go and step over this function here, we should be able to see “Hello World” printed, which we did. Yay! So, essentially now and if we keep going forward or if we run our code or resume our code, when we run this, we are actually not printing the “Hello World” million times anymore, so now we're just, we printed it once, we're done and that's it. Okay and that's everything for this video. I would like to say thank you very much to Greidi for finding time to prepare this very useful tutorial, thank you Greidi, and of course I would like to say thank you for leaving your comments, liking this video and subscribing to this channel. It helps a lot. Thank you very much for watching and see you next time. Bye!