Getting Started with STM32 and Nucleo Part 6: Timers and Timer Interrupts | Digi-Key Electronics

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
[Applause] [Music] [Applause] [Music] timers are one of the most important features in modern microcontrollers they allow us to count clock pulses and have a variety of uses from creating delays generating periodic interrupts measuring time between events and setting up pulse width modulation signals in this episode i'm going to show you how to use a basic timer with stm32 timers start with your main cpu clock which you'll see referred to as hclock in the datasheet this signal is connected to one or more prescalers which simply act to divide the clock we'll assume one prescaler for our simple example here let's say we run our nucleo board with a main clock speed of 80 megahertz if we set the prescaler to divide this clock by 80 then we get a one megahertz clock signal coming out of the prescaler this then feeds into our timer our timer is simply a running counter that counts from zero to some maximum value that we set thanks to the prescaler this timer increments once every microsecond which is equivalent to 1 megahertz most stm32 microcontrollers have several timers available for you to use we'll work with one that has a 16 bit counter which means that the most it can count to is sixty five thousand five hundred thirty five for this demo i'll be using a nucleo l four seven six rg we won't need any additional hardware since we're using an stm32l476 let's look at its data sheet we can see that we have a bunch of different timers at our disposal timers 16 and 17 are good general purpose timers with limited features so we'll use one of those note that this microcontroller has other special purpose timers available such as watchdog timers the hal api relies on systick for its delay functions so we don't want to mess with that systick was originally included to assist real-time operating systems like free rtos with task scheduling the clock tree is a very useful diagram for figuring out how all these timers and clocks are connected we can see that timer 16 is connected to some kind of clock multiplier but is also connected to the apb2 prescaler which is fed by the cpu clock each timer also has its own individual prescaler so if we set the apb2 prescaler to 8 set the timer multiplier to 2 and set the timer 16 prescaler to 4 we would end up with a total clock divider of 16 which means that timer 16 would count at a rate of 5 megahertz if we look in the reference manual for the l476 we can see all of the registers needed to control the timer since we're going to use cube mx and hal to set everything up for us we don't need to worry about these too much however it can be useful to know that the timer's counter value at any given moment can be found by reading that timer's cnt register for timer 16 this is a 16-bit register in memory in stm32 cube ide let's start a new project find our nuclio board and give the project a good name in the cube mx perspective if you go to system core cis you can see that hal defaults to using the cystic timer for things like its delay function if you plan to use a real-time operating system then you'll want to change this however we can leave it alone for now go to the clock configuration tab and take a look at the prescalers you can see that we have an 80 megahertz cpu clock we can change the apb2 prescaler but you can see that the timer multiplier is automatically changed when we do so pay attention to that if you're messing around with these prescalers we'll leave everything at its default of 1 for now back in the first tab we can activate timer 16 to use for our own purposes take a look at the channel 1 drop down list these are the various hardware connections we can make with timer 16. input capture allows us to automatically store the timer's value when an event happens on a pin output compare lets us toggle a pin whenever the value in the timer reaches a certain amount pulse width modulation or pwm toggles a pin at different duty cycles based on a timer's counter value note that these require very specific pins to use but it can be super helpful to have the hardware control these functions for us without needing to worry about the cpu wasting cycles to run code we won't get into these functions in this episode so leave this option disabled at the bottom let's change the prescaler to 80. note that the prescaler register wants one less than the desired clock division number that's because a value of 0 here means use a prescaler of 1. a value of 1 means use a prescaler of 2 and so on so we put 80 minus 1 to enter a value of 79 in this option i like to explicitly write the minus 1 as it helps me remember what the prescaler actually is this timer can only count up so we'll leave the counter mode setting alone the auto reload register is where we set the maximum counting value of our timer note that if we leave it at 0 the timer will basically not run at all we want the timer to count to its maximum 16 bit value so we'll enter 65535 here once again i'll put the number of counts and a minus one to remind me how many ticks the timer actually performs in one period including resetting back to zero with a cpu clock of 80 megahertz and a prescaler of 80 the timer runs at one megahertz giving us one tick every micro second that means the maximum time we can measure with this timer is about 65 000 micro seconds or 65 milliseconds we'll leave the other settings as default ckd is used to adjust the d glitching filters rcr allows us to count multiple reload events which means we can effectively time events that take longer than 65 milliseconds assuming we use the other options we've set the auto reload preload is something you can play with to get more precise timing but we'll leave it off save to generate code and open main.c to start we're going to make a very simple application that times how long something takes to process in our microcontroller because we want to output the timer value to the serial terminal will include the standard io library in main we'll declare a few variables the first two are our uart buffer and buffer length which we've been using throughout these episodes next is a simple unsigned 16-bit integer variable used to hold our timer value and elapsed time calculations we'll start by outputting a simple string to the serial terminal to let us know that the program is working then we need to call the hal timer start function to make our timer start counting notice that cube mx automatically put the timer 16 init function in here for us if we check the definition of that function we can see that our settings were filled out for us and stored in this init struct it then calls the init function which sets all the register values that enable the timer to work the way we specified back in main in the while loop we'll use the hal wrapper to read the cnt register for our timer next we'll put in some demi functionality i'll just tell the processor to wait for 50 milliseconds but this could be any code that you want to measure how long it takes to execute just remember that with the way we have our timer set up we can't measure anything longer than about 65 milliseconds i'll take another reading from the timer's cnt register and subtract the original timestamp to get the time elapsed i'll then print out that elapsed time to the serial terminal and wait one second before doing it again we'll build the project and go into debugging mode i'll connect to my nucleo board using a serial terminal program and press the play button to start running code you should see the elapsed time being printed to the console interestingly enough it seems that hal delay takes about one millisecond longer than what we specify let's stop the program next we're going to rewrite the famous blinky program but we're going to do so with our timer go back into the cube mx setup and click on timer 16 to open its settings because we want to be able to measure time spans of at least one second we need to change the prescaler so let's make it 8000 which means that the timer will tick once every 100 microseconds now save to regenerate code we won't need our serial output in this example so let's get rid of the standard io library and the uart buffer variables we'll also delete our initial serial output lines of code we still need to start the timer so we'll leave the hal timer base start function in we'll immediately get a timestamp by reading the cnt register just like we did in the previous example we'll delete everything that we had written in the while loop all we want to do is get the current value of the timer and subtract out the previous value in our timestamp if that's over some value we'll toggle our led like we did in the very first episode note that the way we compute elapsed time here will work even if the timer rolls over to zero because we adjusted the timer's prescaler it should be ticking every 100 microseconds or at a rate of 10 kilohertz now so if our time difference is equal to or over 10 000 we can say that a second has elapsed it's also important that we update the timestamp after toggling the led so that we can toggle again a second later this creates non-blocking code for our blinky example as we are no longer waiting for some delay function that consumes our cpu you could write your own code after this if statement and have it run in between led toggles let's build the project and start running it in debug mode press the play button and check your nucleo board you should see the ld2 led blinking on and off each second if you connect an oscilloscope probe to the led pin you can see that it toggles almost exactly once per second next let's see how to do this same thing using interrupts which will allow us to run no toggling code in the while loop let's start by looking at a plot of our timer value over time when we call the timer base start function the timer value will begin to increment at the rate specified by our cpu clock and prescalers at some point we want it to reach a maximum value and start over however at the moment it returns back to 0 we want the timer to trigger an interrupt which we can write some code to handle in this interrupt service routine we'll toggle the led this will continue forever so long as our timer is running to mimic the last program's behavior we want these interrupts to occur once every second so the trick is to choose a maximum timer value that allows this to happen all we need to do is multiply our desired interrupt period by the cpu clock speed over our total prescaler value in this case we want one second interrupts with an 80 megahertz clock and a prescaler of 8 000. that gives us a maximum timer value of 10 000. if we look at the hal api documentation for our timers we can see the available functions for us to use note that we need to use the start underscore it function if we're using interrupts additionally there are a number of callbacks that can be implemented depending on the type of timer event that has occurred we care about the timer period elapsed callback as that gets called whenever our timer resets back in stm32 cube ide open the cube mx perspective for our project we need to change the timer period to 10 000. remember to keep the minus one in there since the interrupt occurs when the timer resets from 9999 back to zero which is the 10 000 tick you also need to go into the nvic settings and enable the update and global interrupt flag without this interrupts will not trigger save to generate code and go back into main.c delete everything in the while loop this is useful to show how we can toggle an led without any code in our super loop delete the line where we get the current timer value as we don't need that anymore remember from the hal api that we need to change the base start function to base start it so that it runs in interrupt mode finally head to the bottom of main.c so we can implement our callback function note that the hal library handles the interrupt service routine which you can look at in stm32l4 xxunderscoreit.c in the source folder somewhere in that service routine you'll find a call to our callback function here note that this callback function is fairly generic as any timer can trigger it so we're past a handle to the timer struct that caused the interrupt as a result it's a good idea to verify that the timer that triggered this interrupt is indeed timer 16. once we know that we'll toggle our led save and build the project start the debugging perspective and press the play button to begin running the program if all goes well the ld2 led should toggle once per second just like before and we can verify that the toggle rate is almost exactly once per second by using an oscilloscope i hope this helps you get started with timers i'll make sure a link to the code for these demos can be found in the description please subscribe if you want to keep seeing videos like this and happy [Music] hacking [Music]
Info
Channel: Digi-Key
Views: 62,797
Rating: undefined out of 5
Keywords: DigiKey, STM32, STM32CubeIDE, interrupt, timer, microcontroller, embedded, electronics
Id: VfbW6nfG4kw
Channel Id: undefined
Length: 14min 39sec (879 seconds)
Published: Mon Aug 17 2020
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.