Interrupts on Cortex M - NVIC (demonstrated on STM32) | VIDEO 34

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hello everybody welcome to another video today i will talk about very important peripheral that is used on all embedded systems across different platforms and devices i'm talking about interrupts this topic is fairly large so for the first time this part of the video will be scripted to ensure coherency and good pace after introduction and explanation i will guide you over the example i have prepared for you you can also skip this part if you want to go to directly to the code examples and functions timestamps will be in the description another term you will hear in the documentation is isr which stands for the interrupt service routine which basically means function aka routine executed as an interrupt interrupts in and of themselves are not a peripheral but more a special signal inside of most microcontrollers however these signals are processed by integrated peripheral on most modern processor in the case of arm embedded series m it's called nvec which stands for nested vector interrupt controller this integrated peripheral receives different input signals within the microcontroller and processor core and then with additional parameters decides on what the processor should do at other times the functions that are executed by specific interrupts are called isr or just an interrupt function running code you will find mostly as a peripheral underscore rqn the function being triggered by an interrupt or an exception can also be referred to it interrupt exception handler because of the nature of how this function is executed we cannot really say that interrupt functions are called like a programmed call would be but rather invoked invoked by an nvic because of an interrupt signal that came to nvic as the name suggests nvic is a built-in peripheral in most cortex processor that handles interrupts as a core peripheral is somewhat fixed by the core designer therefore the implementation is universal for all arm cortex devices i will be showing you and linking pages regarding nvic and other documentation on interrupts the term nested as the first letter of the nvic means that a number of interrupts can be defined and given a priority number where 0 means the highest priority and that this interrupt cannot be interrupted by another the lowest priority value is somewhat variable like i started before that peripheral is somewhat fixed different manufacturers like st and xp microchip all that include arm cortex cores can choose to implement different number of priority level or bits that are available from the maximum of eight on this image you can see that two different microcontrollers from different companies have different amounts of priority levels or bits to set so for your stm32 microcontroller you can check this information in the reference manual by going to the nvc chapter in my case i have a 407 vg on my discovery board and it's chapter 12 and reading how many levels of priority it supports you can see that for my microcontroller it says 16 priority levels which means it has four bits of priority able to set the last term vector refers to the way the processor finds the program or function to execute when receiving an interrupt this is by having a vector table or a list of all interrupts that are predefined for a microcontroller every vector entry is on a memory location or an address to the appropriate function in the memory that the processor needs to execute you can find this table defined in your startup file for a microcontroller it's the one that ends with dot s and you can find it in the core slash startup folder and it includes definitions of all vectors for exceptions and interrupts you can observe such a table and you can find that some of these vectors are more universal than others across different microcontrollers the first part are the core vectors and they're called exceptions because they have negative place numbers in the table that's because they're linked to the operation and interrupts by the processor core whereas others are external and thus called just interrupts number of this can vary and there are different amounts of external signals that can come from different amounts of prefrost that are integrated in your microcontroller this is in the control of the microcontroller manufacturer for sdn32 you can see the vector table in the reference manual under interrupts and events chapter you can see that this table is then saved at the memory location 04 so this is the first reset exception at 0 location is sorted for initial stack pointer so only at 0 4 it can start and then 4 bytes for each pointer nvic remembers each interrupts like a number like a position in the table and then orders the processor to go that interrupt address number in the vector table where the address is stored and for that location it just jumps to the appropriate function so this is the isr interrupt handler or an exception before going forward i want to talk to you about function calls and interrupts routines and how they compare as far as processors are concerned i want to address this topic so you have some reference knowledge for the future and any additional reading when we talk about code execution function calls and interrupt routines we have to discuss some core ideas like program counter stack and stack pointer these are important in understanding why interrupts work the way they do program counter or pc or also commonly called the instruction pointer ip is a core part of the processor and holds the address of the next instruction this is how the processor fetches instructions that need to be executed normally a pc is incremented sequentially from memory but sometimes this value can be modified to be changed so the processor sort of jumps to another location and executes something else that it should be you can see where we're going now the stack is a type of memory structure used in most operating systems and processing ics it's a part of the memory that stores all sorts of variables and parameters so the execution of the program and its function can be dynamic and reentrant if a code would not have any functions and interrupts so a linear code then stack would not be needed but because we wanted functions that we call multiple times with different data going in and out of them and we also need interrupts there we need a stack stack is a lifo type of structure and i'm sure that you heard of fifo which stands for first in first out it's a common buffer type memory structure for storing data that is written and read asynchronously whereas lifo on the other hand is stands for last in first out this means that the last piece of data that was placed on top of the stack is the first piece of data to get off the stack this way the stack breeds up and down and the location of the next available data piece is stored as a stack pointer this way when we say the data goes off from the stack we do not erase it but we just move the stack pointer lower therefore declaring the space above the stack as free this way the stack memory grows upwards in the same area where heap which is a free part of memory grows downwards but that's for another time so why is this nature of stack useful well because this way we can store all local variables and parameters on top of each other without them interfering with one another let me show an example we see in this program we have a few static variables global variables we have two functions and two function calls and one function also calls the other different points in time or execution will be denoted by the program counter value pcx in the beginning the stack is empty firstly the program calls function drawbox with two arguments and a return value in order to preserve those processor we store those variables on the stack by their name as h w and return in this case next we want to jump to the memory location where this function is defined but we do not want to forget where we left off so to remember when we have to return to back when we finish executing this function we load the address of where we left off on top of the stack this address represents the program counter value before the jump to the function now we just have to allocate additional space for local variables of this function that we're calling therefore reserving space on the stack for variables i j and k now the stack pointer is on top of the stack ensuring integrity of data below the group of the data that we just put on the stack including return value arguments and local variables it's called a stack frame sometimes instead in the function drawbox the program calls function drawline therefore without finishing function dropbox you want to jump to another function this is quite common the processor loads up another stack frame for function draw line on top of the stack and the stack pointer follows here comes the best part function draw line is not a word that is being called by another function and it's not aware of the variables inside of it and in case of draw line below them this is why we can have the same local variable names as the function below it in this case it's the variable i this is also the reason the function can be re-entrant meaning multiple instances of the same function can be running on the stack each one with its own separate set of variables without disturbing each other this is the only way the function can access data below them on the stack is to use arguments that pass the reference to a variable instead of the value so let's say a pointer in which case we want to use a higher stack function to modify the value of a variable in a function below it on the stack right after the function line finishes its execution the data is popped off from the stack using the stored address to program that can return where it left off after calling the set function when we say we popped off we again mean that the stack pointer just changed its value to a lower one and therefore declaring the data above it to be free sometimes after this function drawbox also completes and stack frame also pops from the stack too now the stack is empty again therefore the stack pointer points to the beginning of the stack right now we can say that the data on the stack is still there but if we were to fetch it we could still get it but the stack pointer is the one that declares which data is present and usable and which is empty and rewritable now you know how the stack works with regularly so-called planned function and now we can apply this logic to interrupts looking from a program point of view an interrupt is just another function with its own set of local variables but no arguments or return value for this example we will add a few parameters to our processor these are in the form of some kind of registers from r1 to rn these are certain cpu registers that are used for its operation and their value is unique to certain points in execution of the code therefore we will backing up those on the stack as well when an interrupt is invoked we want to jump to that function that is pre-assigned to that interrupt do whatever you need and then go back to a regular program as if nothing had happened we do this by copying the address of where the program was interrupted on top of the stack adding these processor registers and then starting execution of the interrupter theme also popping on the local variables of this interrupter theme in this case the interrupt routine has two local variables which are stored on top of the stack after the interrupt routine is finished we pop its stack frame from the stack replace the current processor register go back to the address where we left off at this point the state of the processor is the same as we never left for the interrupt this is the magic of interrupts of course we can have interrupts called other functions and maybe functions that are already executing on top of the stack due to the nature of the stack we can have re-entrant function with no problem as they are not aware of each other we can have a function executing and then having an interrupt invoked that calls the same function again no problem processor will load up a new stack frame so that function on top of the stack is not the same as the one below and when we finish the interrupt and its function we go back to that function and continue its operation as if nothing happened this is in a nutshell how program works with and without interrupts of course some parameters and information have been excluded from the explanation so you know if you want to know more i will add some articles and videos in the description now that you know how interrupts blend into the normal operation of the program the only thing to discuss is how to label and assign priority levels to different interrupts we already talked about the available priority levels in a microcontroller with our new knowledge of interrupt execution we can also determine that interrupt priority on its other parameters if an interrupt will be executed often synchronously or asynchronously the interrupter thing has to be short in order to take the least amount of time from the regular program this does not dictate how important an interrupt should be regarding its priority but does give us an explanation on the frequent heard advice that says that interrupts should be short the worst thing would be if this interrupt was very long happens very often and has a very high priority with this we can deduce that that interrupt with a high frequency of invoking has to be short in order to maintain that high priority therefore for a lower priority we assign interest that might take longer to execute or just not that important just to be sure that give to low frequency interrupts the priority that they need so maybe assign priorities to less important interrupts give shorter interrupts higher priority than longer ones so they can reach the appropriate deadline another functional peripheral is systek timer you probably heard of it this is a 24 bit down counting timer that is built into the nvic peripheral it is used for keeping system time and for triggering task schedulers for real-time operating systems you could get systick to trigger its interrupt routine periodically to either increment a global millis counter or trigger the said airtos scheduler this is how all stm32 projects created with cuba max are initialized systick interrupts is used to increment a global millis counter which is used for timeout and delays functions of the hull library with an arm cortex you're guaranteed at least one timer for system applications so timing like ertos for including systick therefore we don't need to rely on any additional timers implemented by the microcontroller manufacturer a few words on nvic documentation its documentation can sometimes be interdegraded into microcontroller's reference manual or datasheet in the case of s132 only a small amount of microcontroller specific information is provided along like vector tables and the priority bits i have worked with some old samd processor from atmel and i had integrated nvsu documentation therefore it's a really manufacturer specific case so if your micro documentation does not have nvic documentation then you can just access it on kl.com under core mc's documentation the link will also be provided in the description another useful documentation is the format of an article on arm's webpage that i found and it's explaining the difference between two types of priority and configuration called priority grouping these two include previously mentioned preemptive priority and new sub-priority we have already talked about the first one but not the second one this is because even arm advises against using the second one in most cases the preemptive priority is just normal priority as we were calling it up until now and it's called that because it distinguishes between different interrupts on whether they can preempt each other based on the priority level the second one distinguishes interrupts between two or more with the same preemptive priority if they are pending like if interrupts are disabled for a period of time and then when you re-enable multiple interrupts with same priority level might be waiting but this however is ill-advised and recommended you do not use by arm this is also important because pre-emptive and sub-priority bits are shared between the two so if you only have four available out of eight then their split either four zero three one two two one 3 or 0 4. so this is selected with priority grouping if we take a look in the hull init function we can see that by default s32 are indeed configured with amvic priority group 4 which gives all 4 bits to preemptive priority and 0 to subpriority functions that we'll be using are called the nvik enable req in order to enable the set interrupt and envik set priority to set interrupt priority and these are also used by the cube mx and we'll be showing those two in the actual examples with those two settings we can enable nvik to receive and acknowledge incoming interrupts from certain peripherals so the next step is to configure the physical peripheral to trigger these interrupts from it usually you're able to set what kind of condition or status trigger such interrupt from a peripheral to nvic so you can see there are two parts to the story you have to set up nvic in order to receive an interrupt and assign its priority but you also have to set up a peripheral in order to fire those signals on certain condition so now it's time for you to show my demo program and how to set up interrupts on your stm32 from cuba max or just manually we'll be setting back to the non-scripted live recording and we're back to the demonstration part of this video so as you can see on the screen i have my project setup and my discovery board on my desk if you take a look at it you can see that i'm using all four leds where the blue one is the only one that is running in the while loop all the other three are the result of their separate interrupts as you can see there are two additional wires going into the board and these are connected to my external uart interface so we're gonna use putty to send messages to this board i'm also gonna be using timer which is connected to the red led to trigger and create ourselves our own millis counter and we're also going to be using the internal sensor for the accelerometer which is in the middle of all the leds and we're going to using this one to trigger an interrupt in order to read the data from its registers so let me show you how i configured everything in the cube so for this i have configured firstly the spi for the accelerometer and the pins for it can be accessed via the schematic pack for this discovery board this chip is not the same that the one that i have on my board but the pinot is exactly the same so all the pi6 5 and 7 are for spi peripheral pe3 is connected for the chip select and p0 is connected to the interrupt pin of this chip so this interrupt will be connected internally for the data ready flag so when the data will be converted and ready to be read this pin will go high and trigger an interrupt so the first thing we're gonna do is to enable this one as because this one is connected to the ps0 all the zero pins of all the ports are connected to the xt 0 interrupt so here we want to click this one and it will enable the interrupt for this one for the chip select i'm just using the pin that is connected to this microcontroller for the uart over here i have connected two pins that are available for so if you go to the connectivity and uart i have configured it as asynchronous so this is just normal configuration and the parameter is for one 115 200 bits only 8 bits and no parity and i'm also using the nvx settings over here to enable the global uart 5 interrupt so this will enable the local interrupt for the uart peripheral this will not enable the nvic peripheral for that we have to go to the system core and vic and then manually tick this one so it will enable this interrupt also for xt line zero interrupt for the timer we can go to the timers and i'm just using one that is available in this case timer 5 ticking the internal clock and configuring its own timer 5 global interrupt i also take this one and make sure that it's also ticked in the nvx settings because sometimes it's not by default for the timer for those who are curious how to set this up as a millis counter well we have to firstly see what frequency is going into this timer and if we go to the clock configuration register we can see that in my case all the timers are apb 2 timer clocks or at least the timer 5 is so in this case the apb2 timers are connected to the 2 times the frequency of the apb2 peripheral clock 84 megahertz so if you want to get a 1000 kilohertz out of this we firstly divide this by the maximum around now whether we can in this case 8400 so the timer will be receiving a frequency of 10 kilohertz if we want the timer to roll over every millisecond we also have to make the counter count to 10. in this case the counter we roll over every millisecond therefore rolling over thousand times a second putting out a one kilohertz update interrupt we want to make sure that the trigger event selection will be update event which means every time the timer overruns or something like that also in mvic settings enable it in the end excellent we can also observe some additional settings that we also talked about so you can see the priority group can be selected over here in this case by default it's four bits for preemption and zero bits for sub priority you can also click this to show only the ones that are enabled and in this case you can see that the hardly enabled so you cannot disable them are the system ones so in case like system timer and hard fort error now let me show in the code where these settings are plotted and how you can do it yourself so for the xt interrupt we can go to the gpio init function and in the last part over here here is the local peripheral initialization of this pin so this pin is configured as interrupt rising so this is the local designation meaning that this pin will be connected to an interrupt now in order to enable this interrupt from the nvx point we have to fire these two functions over here one sets the priority and one enables the interrupt and if you go into one of this function which is hull and we can enable rq is just a mask for the envic enable irq and same for the function set priority it's just a mask for the envic set priority it just does a little bit of overhead from selecting a priority group from preempt and sub priority all together to give it the proper priority number depending on your settings so it's a little more universal so you don't have to think about it that much for timer 5 we can go into the timer 5 in it and this one is a little bit hidden but actually inside of time 5 base init is call of the function which is defined over here which is hull time base msp in it and this one enables the timer and also enables the interrupt for it as well the same can be said for uart five so if you go to unit five inside of the whole uart in it you can see that here is where it calls the hull uart msp in it and if you go back to the uart this is this function over here it checks all the pins and enables the nvx set priority and enables the interrupt you can access this interrupt routine functions when they're configured in the core source and the one that ends with it and this one houses all the interrupt functions so in the beginning we have all your defines and then the core interrupt functions like hartford handlers and if you go to the bottom the last one is the systec you can see that the already here is the hull increment tick and if you click on it you can see it's adding the tick freak which is just a macro if you go back and this is a macro for number one so all the way over here this is how increments the timer if you go back so this function increments the global variable by one so here are our own interrupt handlers this first one is the external zero so this is the one where when it goes high triggers this interrupt meaning that the data on the accelerometer is ready to be read so first thing i do is toggle the led so i can see it visually then it calls the hull xp rq handler so this is the hull handler for handling all of the things for interrupts and then i'm reading the data from two different registers in this case the z-axis data filling in into an array and then combining these two values into one big 16 bit value and we can observe this value later to show you how also configure this accelerometer in case we have this board we can go to the data sheet for this accelerometer you can see that out register is on 2c and 2d so this is where i'm reading the data you can observe that a few configuration register can be altered so in this case there are six of them and we're going to be configuring the register 4 and 3. so we go down the register four in this case has to be entered because these four most significant bits enable the sensor itself so in this case i've configured it as 12.5 hertz output rate so these two bits are at one by default it's powered down we also want to enable these three accesses by default they're already enabled but when you write into the register it's gonna overwrite if you don't have this set at one in the incoming data as well the next register we want is the number three so the reduces number three is the one that actually enables the interrupt for the sensor itself the first one enables the data ready signal and enables it so it's wired to the interrupt 1 pin then we need to set this interrupt to be high when the interrupt happens instead of low and then having it interrupt signal passed this is depending on how you configure your own microcontroller interrupt but for my case i want to have it paused and be high when the interrupt happens we also want to enable interrupt 1 pin with setting this pin 1 as well in the beginning i want to reset the chip in order to erase any settings from before so the first thing i only write one to this register in order to software set it and the next thing that i write is this one not zero but this one on one and this three on one as well to configure it for this operation you can see this in the beginning of my main code so here i have declared all the addresses for these registers i have configured all the registers data and then i'm writing all the data here i'm just resetting that a bit and then writing the data for three different registers i was before using the control register five for testing but it's not really necessary i'm also reading the data from the chip and in this case reading the id register in order to confirming that i'm indeed talking to the chip other interrupts include the timer 5 that we have configured and i'm using this one exactly the same as the systick one just a little more bare bones i have a global variable called my millis and i'm just incrementing it by 1. in order to start the timer you have to firstly call the hull time base start but in case of interrupts you have to call the one that has underscore i t in this case you just start the timer and enable the local interrupt trigger for the uart interrupt we want to trigger it when data comes into the receiving registers but i had a few problems doing this with hull and i can show you also another web page where another man had the same problem with this and having some function lock up and this is the same thing that i observed it triggered the first time but then it locked up and didn't want to trigger anymore so i'm doing this manual way i'm configuring the interrupt for the uart directly from the main so the first thing i do is just send this text called happy and with the new line and carriage return so i'm sending this text to the terminal and then i'm enabling the bit in the control register one that says the receiving bit so if we go to the control register one the control register one is the one that is responsible for this we can check this by going into the data sheet for my processor and if you scroll up this is the control register one and the bit 5 on the bottom over here is the receive register not empty so the bit 5 is the bit that sets the receiving not empty register interrupt so when some data comes into the uart peripheral the the said data register is not empty which triggers a flag and this flag will also trigger an interrupt so we're getting an interrupt on the local settings for this peripheral for data coming in after i've configured these things i want to enable the spi prefer using low level driver and then start the timer as said before this is just applying the settings for the accelerometer and then there is just the while loop so only thing that happens in the while up is toggling of the led so everything happens in the interrupt things so the the memory reading is done over here from the accelerometer the millis counter is over here and then for the uart it's over here so again i toggle one led in this case is the green one and then it's the hollywood handler and then i just read the data from the data registers of the word five peripheral and then put it back into this register so the uart will send it right back because if you remember uart uses the same data register for incoming and outgoing data so this should just throw back the data that we type into the terminal so to demonstrate this i'm just gonna pause reset it and then i'm gonna open putty and i have also already configured it for my usb interface and if i start the code you can see it wrote happy and a new line and carriage return and now i'm typing on the keyboard typing one two three four five six seven eight nine zero and you could as you can see this is directly going in from the microcontroller well we can confirm this because put it doesn't actually type whatever you're tapping on the keyboard it's just blank and i can confirm this by going this to the side and enabling my debug window and is if you can see here on the right we have all the live expressions going so the uart is the one that is over here so this uart buffer variable and if i write a you can see a asd as d f g h j k l so you can see that all numbers are coming into the microcontroller being saved into eur buffer which is already pushed back into the router register to be sent back to the uart into the computer for the accelerometer you can see the raw data over here in this case it's 16800 and if we go and say that this accelerometer has a 16 bit resolution and it's plus minus 2g so divided by 2 and then 2 gs divided by this number is the resolution per bit and multiplying it by sixteen thousand one eight hundred it says that it's one point zero two five g's in my room right now which is a little bit over the actual data but it's not calibrated because it also has some offset registers and calibration values so if i were to the trouble to use this accelerometer to get absolutely consistent and accurate data then i could do that but for demonstration this is clearly working as you can see all the data is being refreshed all the time so here you can see the low and high bits for this register and then combined into the 16 bit over here the buffer is being refreshed over here so if i type into the console you can see it being refreshed and the millis counter my mill is over here well it fell to expression this is sometimes happens in this id so if i just write it again you can see it's being refreshed and as you can see it's almost the same value as the ue tick which is the one being incremented by hull and it's because the systick timer starts a little bit before our timer because the systick is in it initialized over here and our timer is initialized and started over here so there's a little bit of gap a few microseconds i'm sure and then you can see it's roughly the same so 101 607 000 and here it is this is the demonstration of all the interrupts actually working so nothing is actually going on in the while loop everything is going on in the interrupter team so i hope this video was very informative for you so you can start your own project with interrupts and set everything up and again like in my case when hull or something else doesn't work you can just read the data sheet and do it manually this way it's really no problem so thank you for watching and i'm gonna see you in the next time bye
Info
Channel: Matej Blagšič
Views: 4,133
Rating: undefined out of 5
Keywords: stm32, stm32f407, programming, linux, gcc, data, dma controller, mint, linux mint, timers, st, delay, PWM, OC, stlink, gdb, debugging, systemworkbench, openstm, jlink, segger, stm32cubeide, ide, hal, tutorial, i2c, stm32 i2c, iic, serial interface, serial, spi, spi interface, interrupts, stack, NVIC, arm cortex m, cortex
Id: FYOi9QQn5XY
Channel Id: undefined
Length: 34min 23sec (2063 seconds)
Published: Wed Sep 16 2020
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.