STM32 Guide #3: PWM + Timers

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
greetings everyone and welcome back to my third stm32 video before we dive in i really want to review some of the things we covered in the last video the last video is incredibly important to this whole series i'd really recommend going back to watch it if you haven't already in that video i talked about how registers control every aspect of a microcontroller for example i talked about this gpioa output data register which directly controls pins tied to the stm32 now if we had to set every single register on our own we'd spend a lot of time reading data sheets and writing really ugly code so to help us out there's tools like cubemx this helps a lot with the initial setup anywhere from configuring which pins are inputs and outputs to naming pins which make them easier to use later as well as configuring the clock and any other peripherals that we might need just as a reminder this tool only generates code you don't even have to use it if you don't want to everything that this tool does you can do in code later you can even look at the code that it generates as inspiration for your own once all the setup and configuration is done we move on to writing code and once again to save us from reading data sheets and manipulating registers directly we can use the hardware abstraction layer or hal it lets us do things like toggle this pin and wait one second without actually knowing what registers are changing behind the scenes and once again if you want to change the registers directly you can you don't have to use this at all that's all for the review now let's get down to business before we jump into any of the advanced features of an stm32 we need to lay some groundwork so i've gathered a small list of things that i consider essential to learning a new microcontroller i'm sure a lot of you are already familiar with arduino so i'm listing things here in the way that they're used in the arduino ecosystem first up we have the hello world of microcontrollers which is being able to control a single pin on and off this is what we did in the last video we can turn a pin on and off using digital write and conversely we can detect if something else is pulling our pin high or low using digital read but now if you want more control then on and off and you want to have a sliding scale for something like brightness then we're getting into the world of analog so the next two bits of essential functionality is being able to do an analog read or an analog write now if you don't have any sense of timing with a microcontroller you're going to get stuck quick so we also want to know how to get our timing just right we can do that with blocking commands like delay or delay microseconds these lock up the processor for a certain amount of time and don't let anything else happen or we can use things like millis and micros which allow us to keep time without actually locking up the processor after this we want to be able to debug our code if we're having any problems we usually want to step through our code in small chunks to figure out where the problem is starting with arduino it's really common to just use serial to print things off to the screen you can do this with an stm32 but there's better ways to debug and lastly the three most common forms of communication that i come across are serial once again as well as i squared c and spi we already covered digital right in the last video and i don't want to waste a whole video on just digital read so today we're going to jump into the world of analog specifically analog write so let's figure out how to do analog write but on an stm32 in arduino it's pretty simple we just say analog write and then pass in a byte which is any value from zero to 255. if we give it zero we'll have a zero volt output if we give it the maximum number 255 we have five volt output but what happens if we give it something in between like 128 are we going to get something around 2 and a half volts well let's try it out for ourselves i'm making a quick arduino program to do analog write 128 and when i hook it up to my multimeter it looks like it's actually around two and a half volts but if i hook this same signal up to my oscilloscope we can see what's really going on we're not really creating a true two and a half volts instead we're just toggling the pin on and off really fast to simulate it the average voltage over time is around two and a half volts which is what my multimeter is measuring but if you're to look at the pin at any moment in time it's either zero volts or five volts turning the pin on and off really fast to simulate an analog voltage is referred to as pwm or pulse width modulation we control how long the pin is on or off and then the average voltage over time will be our fake analog output now there are microcontrollers that can do a true analog output this is often referred to as dac which is a digital to analog converter and if you had one of these fancy microcontrollers it would have some internal circuitry that looks similar to this which would allow you to have a true analog output microcontrollers are digital so it's really easy for them to do something that's either off or on but it's very difficult for them to do something that's somewhere in between and that's one of the reasons why pwm is so popular for a lot of cases turning the pin on and off really quick does a good enough job for example you could use pwm to control the brightness of an led or the speed of a motor in my last example i turn the pin on and off at equal intervals and this means that our average voltage is halfway between zero and five volts which was two and a half volts in my example and if we want an output voltage that's lower all we have to do is leave the pin on for less time so in this example the pin is on for 25 percent of the time and off for 75 percent of the time by changing the width of this pulse we're changing our output voltage and that's where the name pulse width modulation comes from how long you leave the pulson is referred to as the duty cycle this first example here has a 50 duty cycle and the one underneath it has a 25 duty cycle and if you're curious this is what it looks like when you change the duty cycle on the fly you can notice how the width of the pulse increases or decreases as well as how my multimeter is measuring the average voltage let's revisit how arduino handles pwm they give us this analog write command which only asks us for a pin and a duty cycle that's pretty straightforward stm32 on the other hand asks us for quite a few more things so before we jump into how to configure this let's talk a little bit more about how a microcontroller generates pwm signal let's start by counting to eight over and over again one two three four five six seven eight we did it now let's do it again one two three four 6 7 8. we're going to do that same thing again but this time i'm going to give you a thumbs up or thumbs down i'm going to give you a thumbs up if i say a number that's 7 or higher if it's not 7 or higher you're getting a thumbs down let's do this again one two three four five six seven eight one two three four five six seven eight what we just did there is created a pwm signal specifically one with 25 duty cycle where you had a thumbs up 25 of the time by giving a thumbs up for any number that's greater than or equal to 7 we have a 25 duty cycle now since i'm only counting to 8 there's a very small number of possible combinations here's all of them listed depending on what number i decide to start giving you a thumbs up on that affects the duty cycle and here's a list of all the possible duty cycles that we could get to by counting from one to eight what if we wanted a duty cycle that's not on this list for example what if we wanted something around 92 percent that would fall somewhere in between 100 and 87.5 well that's pretty easy we can just count to a higher number now we have much more control over what our duty cycle actually is it's nice that we have more control over our duty cycle but the drawback is that it takes longer to count to that number both of these are examples of 25 duty cycle but counting from 1 to 32 takes 4 times as long as counting from 1 to 8. that means that in the time that it takes us to count from 1 to 32 we could instead count from 1 to 8 4 times over the number of times that we repeat this cycle per second is referred to as the frequency of pwm and the number that we count to is often referred to as the resolution there's almost always a trade-off between frequency and resolution the more precise your duty cycle is the longer it's going to take to count here's an example of what i mean these are two pwm signals both outputting around nine percent duty cycle the blue one has four times the resolution but it also takes four times as long for it to count that high let's zoom out to get a better look for every blue pulse there are four yellow pulses we can count this by picking a starting point like the rising edge of a blue pulse and counting how many yellow pulses there are until we hit the next rising edge i set my oscilloscope to measure both the period and frequency of both of these signals the period is how long it takes for the signal to repeat so here we can see that the period of the blue signal is four times that of the yellow the frequency is how many times this pattern repeats per second since the yellow signal takes less time to repeat the frequency is four times that of the blue cool now we understand the basics of pwm let's quickly run over the terms that we just covered the first is duty cycle this is how long the pin stays on compared to how long it stays off the higher the duty cycle the higher the analog voltage next we have resolution resolution is the number that we count up to this affects how granular we can make our duty cycle if our resolution is small we can run really fast but if we have a big resolution it takes a lot longer and this directly affects the frequency which is how many times we start the cycle over every second one other buzzword that i'm not going to cover in this video is mode the only thing worth mentioning is that this video has been focusing on something called fast pwm with this information let's try out a math problem we're going to try to figure out what the pwm frequency would be for a microcontroller running at 72 megahertz with an 8-bit pwm resolution let's reword this problem if we assume that we can count up one number per clock cycle that means that with a 72 megahertz clock we could count up 72 million times every second and an 8-bit resolution means that there's 256 numbers so each pwm cycle consists of 256 counts so we're really trying to figure out how many of these counting cycles we can do every second this is how i like to write out my math problems we're able to do 72 million counts every one second and there's one cycle for every 256 counts so doing the math this gives us just over 218 000 cycles per second this means that our pwm frequency is 218 250 hertz and we'll just simplify this to 218 kilohertz the stm32 blue pill runs at 72 megahertz so this means that if we had a resolution of 256 our pwm frequency would be 218 kilohertz for comparison an arduino uno runs at 16 megahertz if it followed these same rules it would be able to run at a maximum of around 62 kilohertz however by default it only runs at 490 hertz or 980 hertz depending on which pin you use i've cranked this up to the max speed before but you have to play around with registers manually now at this point we should have enough knowledge that we can get pwm working at a basic level on our stm32 just like in the last video i'm going to use the stm32 cube ide to set up this project if any of this doesn't look familiar i'd recommend going back to my previous video because i cover all of this setup in great detail this first time i'm going to do this with the blue pill and then i'm going to come through and do it a second time with the nucleo i'm creating a blank project and i'm going to call it blue pill pwm just as a reminder this part of the program is called cube mx it gives us a graphical way to set up the microcontroller and then when we're done it will generate the code that actually sets the registers for us to get pwm set up we need to go find one of those magic counters and those magic counters are actually called timers and we can get to them from this drop down menu right here on the blue pill we can see that we have four built-in timers called timers one two three and four at their core each timer is just a counter however sometimes timers are designed with specific functionality in mind so it's not uncommon to see different settings for different timers additionally each timer affects a different set of pins so the timer that you choose will dictate which pins you're allowed to use with it i'm not going to use timer 1 and i'll show you why later so instead we're going to start with timer 2. as soon as we open it we can start to see some configuration we want our counter to increment once every single time that the system clock ticks so because of this we're going to set our clock source to be the internal clock as soon as we do that we can see some configuration that shows up down here these channels are going to be the output pins but we'll come back to those in a sec for now let's look at this configuration i'm going to drag this so we can see the full name of these pins and expand my view a little bit so we can read everything the first thing that we have is this prescaler and this means how many clock cycles do we want to wait before incrementing our number once and for the sake of simplicity i want our number to count up once every single time that the system clock ticks so i'm just going to leave this as the default zero if i wanted to slow this down i could set this to a number like four which means that we'd wait four system clock ticks before incrementing our number once counter mode as you can imagine is whether or not we're counting up or down next we have our counter period which is the maximum number that we're going to count up to before we start over to follow my example that we did the math for earlier i'm going to set this to 255. this means that our counter is going to count from 0 up to 255 and then start over next we have the internal clock division which is another way to divide our clock signal if we want this to run slower the next thing is auto reload preload and this will automatically restart our counter when we reach that maximum number when we're using pwm we just want that cycle to keep going and going and going and we don't have to want to restart it with code every single time so by setting this to enable it's going to automatically start back at zero when it reaches that maximum number of 255. these next two parameters down here i've only ever used when i want to use one timer to trigger another timer so it's not applicable to pwm so now at this point we've set up a timer which is just going to count from 0 to 255 and it's going to count up one number per system clock cycle and then restart when we get to the end now it's time to actually set up the pwm output pin so we're going to drag this down so we can see i'm going to select channel 1. the channel doesn't really matter it just affects what pin we're going to be using by default it's set to disable meaning that the pin is going to be doing nothing we want to set this as an output pin so we're actually driving the pin high and low and that gives us these four options near the bottom here that are outputs well actually only two of them are outputs because two of them literally say no output right in their name i'll touch back on output compare in a bit but we're going to use pwm generation now obviously we don't want to use the no output because it's not actually going to drive the pin so far i've never actually had a use for this if we did select it it would generate a pwm signal but it just wouldn't actually drive the pin so i guess if you're doing something really weird you could maybe use this as a source to another timer or something but of course we actually want to drive our pin high and low so we're going to select pwm generation on channel one the moment we select that we can see that this pin here went green and that means that pin pa0 is our pwm output pin now also when we did that it added more configuration here at the bottom this configuration is specific for this pin whereas the counting stuff that we did earlier affects all four of those channels let's make this bigger so we can see everything that's going on this mode is that thing that i told you that we don't need to worry about right now if you're curious you can look up mode 1 versus mode 2 in the data sheet this next value here affects our duty cycle and this is saying how long do we want our pulse to be if i wanted to have somewhere around a 10 duty cycle i could set this value to be about one tenth of what this number is so i'm going to set this to 25 and we should get pretty close to a 10 duty cycle i rarely ever touch these next two settings but every once in a while i like to change the polarity which just flips the entire graph so rather than staying on for 25 out of 255 clock cycles we'll stay off for 25 and then be on the rest of the time but we're just going to leave this as the default value high so that's it the only thing that really mattered for this channel was to set this pulse value right here and this will give us a roughly 10 duty cycle output okay let's see what happens if we push this code onto the microcontroller we're going to want to start by clicking on this save icon which will generate our code do we want to generate code yeah and then we're asked if we want to switch perspectives which is just a different window layout so sure so here's the code that it generated and if we scroll down we can start to see some code that has to do with our timer such as this right here if we go down a little bit further we can see where it's actually called which is right here the mxtim2 init and if we actually want to jump to the function itself we can either hit control on windows or command on mac and then click and then it jumps us to the actual implementation now if we peruse through this we can probably see all the things that we set through the configurator such as our period of 255 and a little bit lower is our pulse of 25 and then pretty much everything else was set at its default value once again to demonstrate what's actually happening here i'm going to strip out all these comments to make it look a little simpler so here's our main function with all the comments stripped out it starts by initializing the hardware abstraction layer followed by the clock configuration next we can see where it has the gpio initialization which does things such as setting a pin as an input or an output and then we have our timer 2 configuration which is what we just ran through so that means any point beyond this our timer is already set up now the question is is this all we need to do to get pwm to work let's see what happens if we push this onto our microcontroller we just need to click on this little play icon here since this is the first time launching for this project it's asking us how we want to debug all the defaults are fine here so i'm just going to click on ok and i just remembered that i haven't plugged everything in yet so i'm going to do that before i click ok here i'm going to plug in the stlink v2 programmer the last code i had on this was a blink example which we can see here and now i just need to move the jumper into program mode and reset the microcontroller and we can see that the led stopped blinking now that it's in program mode now that everything's plugged in we're ready to keep uploading so when i click ok it's going to save and then continue pushing the code under the microcontroller you can see down here the progress and it looks like we're done i lied now we are done now we just need to switch the jumper back and press reset this puts us in run mode i hooked up my oscilloscope to pin a0 to see what's happening but it doesn't look like pwm is working the reason we don't see any pwm output is because we haven't actually started the timer yet i really wish that they added like a check box to the mx configuration to say auto start timer so that they could just generate this code for you but since that doesn't exist we're going to do it manually we can do this by typing in hal underscore tim underscore and i think it's pwm and i'm going to hit control space for auto complete it's pwm start yep there we go so there's three options here we have pwm start regular we have start with dma which is direct memory access or addressing or something and then start with an interrupt we're not doing anything complicated so we're just going to use the basic start function the first argument it wants to auto complete for us which is the htim2 variable so we're going to do that this is a reference to the actual timer itself it's defined near the top of our code right here and we can see where it's used down in our generated code down here where it says htm2.instancestim2 and all of the other variables that we set during the configuration so we've got this reference to the actual timer and now we just need to tell it which channel to turn on and we can do that with tim underscore channel i'm going to hit control space for autocomplete and we use tim channel one now i did actually learn this little trick if you highlight this then it will tell you what this is actually expanded to so in this case tim channel one is really just the number zero we could put the number zero in here and it would do the exact same thing so we're going to end this with a semicolon and now i'm going to put the blue pill into program mode so we can push the code all right it's in program mode so i'm going to hit play save our code and let's see if it works gucci hey look we have pwm i'm going to play around with the zoom and the horizontal scale so i can get two pulses in the view and now if we look down at this bottom corner here we can actually see where my oscilloscope is measuring the duty cycle and it looks like it says it's about 9.718 if you take our pulse width which was 25 and divide it by 256 which is the resolution that we set it to that should come out to about 9.7 percent duty cycle so it looks like this is working just how it's supposed to okay everyone i'm going to wrap things up here this video is getting a little bit long and there's still a handful of things that i want to talk about so i'll cover these in pwm part 2. i specifically want to show you how to change the duty cycle through code rather than just leaving it at that one fixed value and i also want to talk about why you can't use timer 1 for pwm and then of course i promised that i would do this with the nucleo board so i also want to do all of this again with the nucleo thanks for being patient while i put this together it took me six days to film this well that's all see you next time for comparison an arduino uno running at 15 i saw a cat you
Info
Channel: Mitch Davis
Views: 20,420
Rating: 4.9831047 out of 5
Keywords:
Id: AjN58ceQaF4
Channel Id: undefined
Length: 20min 24sec (1224 seconds)
Published: Mon Feb 15 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.