Level Up Your Arduino Code: External Interrupts

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
[Music] when you're learning to use microcontrollers some of the most powerful functions available to you are excuse me hello when you're learning to use microcontrollers some of the most powerful functions available to you are interrupts that what okay we're all pretty familiar with the idea of an interrupt you might be working on your own thing something outside of your control happens and you have to stop whatever it is you were doing to handle that event for example I might be explaining a concept when I hear my phone rings so I take that call I put my phone away and I go back to whatever it was I was talking about let's see how this relates to microcontrollers when we write code for many basic microcontrollers like our Arduino we use a programming scheme known as a super loop here we run a section of code once in order to configure any pins serial ports global variables etc the program then enters into a while loop that runs forever known as a super loop if you're programming something on a computer having an endless loop is usually considered bad practice but on a microcontroller it's pretty normal in a basic super loop structure we might check to see if a button is pressed each time through the loop this is known as polling as we pull or check to see if our event has occurred if the button has been pressed then we can perform some action such as toggling an LED in this case once the event has been checked and handled as necessary the processor is free to perform other actions polling is pretty simple to do and often it's all you'll need for most projects however you might run into problems if that other stuff portion takes up more than a few milliseconds and you need to respond to button pushes right away asking a user to hold down a button for a few seconds is not great interface design to solve this we can use interrupts somewhere in our code like during setup we can tell the processor that were open to interrupts from then on or at least until we disable interrupts whenever a button push is detected the program stops whatever it was doing and handles the interrupt usually we need to tell the microcontroller how to handle the interrupt and we do this by writing a special function known as an interrupt service routine or ISR in this case the ISR will simply toggle an LED whenever a button is pressed notice that in our main loop we no longer need to worry about checking the state of the button as we've told the processor what interrupt to look for and what to do when one occurs note that we're not asking the microcontroller to do two things at the same time when an interrupt occurs the microcontroller stops saves any bits of information it might need to memory goes and handles the interrupt restores that information and then continues whatever it was working on when working with microcontrollers there are two types of interrupts internal and external internal interrupts refer to anything that can happen inside the microcontroller to trigger an interrupt for example we might set up a timer to fire an interrupt every 10 milliseconds then there are external interrupts these are caused by things outside of but connected to the microcontroller like a button in the rest of this episode I'm going to show you how to take an Arduino program that pulls for a button push change it into an external interrupt handler using Arduino code and then convert that into code that manually reads and writes registers so you can see what's really going on as one commenter put it I'm gonna show you how to level down your Arduino code we're going to use the exact same circuit as last time an LED with a 330 ohm limiting resistor connected to pin 5 and a button connected to pin 2 we are going to start with a simple polling example here we've defined the button and LED pins as 2 & 5 if you're not familiar with the UN Tate T data type it stands for unsigned integer 8 bits it's great for storing small positive numbers and it works the same as the Arduino byte data type however if you plan to work in platforms other than Arduino byte generally isn't defined so you'll see a lot of these fixed width data types which help save space and memory we then need a couple of global variables to help us remember the states of the LED and button between loop iterations in setup we're using an internal pull-up resistor on the button and setting the LED as an output in order to detect a button push we read the state of the button and see if it has gone from high to low since the previous iteration in other words we're looking for a falling edge on the pin connected to the button if that falling edge is detected changed the state of the LED from low to high or vice-versa save the state of the button for the next loop through the iteration and add some obnoxiously long delay to pretend the processor is off doing other things if we run this we see that we can turn the LED on and off by pressing the button however notice that you have to hold down the button for a while in order to let the loop finish that long delay before pulling again this would definitely annoy most users let's pretend that we can't do anything about that long delay the processor is off performing some complex calculation that takes half a second we can however interrupt that process in order to handle a button push at any time if we look at the 80 mega 328p datasheet we can go to the interrupts section at the top of that section is the interrupt vectors table this tells us what things can create an interrupt in the processor also note that this table actually exists in the program memory of the microcontroller starting at address 0 if we were writing assembly we would have to fill out this table to tell the processor where to jump when a specific interrupt occurs we are only concerned with external interrupts right now so we need to look at in zero and into 1 as the sources for these interrupts go to the pin configurations section if you remember from the last episode pin 2 on the Arduino is connected to port D pin 2 as you can see here PD 2 is tied to int 0 that means we can't make other pins B external interrupts 0 only PD 2 can trigger the into 0 interrupt back to the interrupt vectors table we see that if PD 2 triggers an interrupt execution of the program will stop and jump to address 2 as the source of the interrupt was in 0 the next instruction would be a jump to somewhere else in the program memory ideally this would be the beginning of our interrupt service routine let's take a look at a theoretical example at the top of our program memory is the interrupt vector table below that is where our program lives and at the bottom is the arduino bootloader when your program runs the processor begins executing instructions sequentially in your program space let's say an external interrupt on port d pin 2 occurs which is int 0 program execution stops whatever it was doing and jumps to the int 0 interrupt vector an interrupt vector is the memory location where an interrupt service routine can be found in the case of our 328p this is just another jump command to somewhere else in program memory the interrupt service routine is a function we write in code once that routine has been completed execution jumps back to where we left off in our normal program and things just continue running if we go to the arduino reference guide we see that there is a handy little function called attach interrupt note that this function only works with external interrupts on pins 2 & 3 with the uno which correspond to the int 0 and int 1 sources we saw in the datasheet to use it we tell the function which interrupt source we want to use which will be int 0 for us the name of the function to use as the interrupt service routine and whether we want the interrupts to trigger on a pin change falling edge rising edge low or high state there's also a nifty function called digital pin to interrupts which we can give it a pin number and it'll return the correct interrupts source for us to use this function in Arduino right attach interrupts open parenthesis digital pin to interrupt open parenthesis button pin closed parentheses comma toggle comma falling in all capital letters close parenthesis do this in setup under our pin mode calls notice that we're giving button pin which is pin 2 to the digital pin to interrupt function which returns the int 0 number for us we're saying that we want the function toggle to be used as the interrupt service routine and that we want toggle to be called whenever there's a falling edge on pin to delete everything in loop but leave the delay 500 we want this to simulate the processor doing something intensive for half a second underneath loop write void toggle open parentheses close parentheses open curly brace LED state equal exclamation point led state digitalwrite open parentheses led pin comma led state closed parentheses and then under that close curly brace when the int zero interrupt occurs this function is called which switches the led state from 0 to 1 or from 1 to 0 and then writes it to pin 5 you can also delete the button pre variable declaration since we don't need it anymore when you run this code you can see that the LED changes when you press the button just like before however you don't have to hold it down for long periods of time anymore Arduino is awesome in that it gives you an easy way to set up interrupts but note that it's really only good for external interrupts to set up internal interrupts you're pretty much on your own to see how to manually set up an interrupt let's dig deeper since we learned how to manually control registers on the last episode let's replace these pin mode and digital write functions in setup remove the pin mode functions we can write D D Rd ampersand equals tilde open parenthesis 1 less than less than button pin closed parentheses to clear bit 2 in the data Direction register for port D note that the ampersand equals operator has the effect of reading the value from D D Rd performing the bit clearing operation and then writing that value back to register D d rd we then write port D vertical bar equals open parenthesis 1 less than less than button pin closed parenthesis to set bit two in the port D register which has the effect of enabling the internal pull-up resistor below that write d dr d / d KO bar equals open parenthesis 1 less than less than LED pin closed parenthesis to set bit 5 in ddr d which turns port d pin 5 into an output finally in the toggle function remove the two lines and replace them with port d caret equals open parenthesis 1 less than less than LED pin closed parenthesis this reads the value from port d flips bit 5 and writes it back to port d you can also remove the LED state global variable sense we don't need it anymore we can flip an individual bit by performing the exclusive or operation with the number we left shift 1 over 5 times to get zero zero one zero zero zero zero zero we then read in the value of port D which will give it some arbitrary value like one zero one zero one one zero zero we then perform a bitwise exclusive or on these numbers 0 X or 1 is 1 0 X or 0 is 0 1 X or 1 is 0 and so on here we can see that all the bits remain the same except for bit 5 which we flipped from 1 to 0 had bit 5 been 0 1 X or 0 is 1 so we would still flip that bit by flipping bit 5 from 0 to 1 or 1 to 0 we can toggle the LED in one line of code and if we run our code we see that it works the same as before now comes the fun part figuring out how to set up an interrupt by manually writing to registers for an interrupt to trigger three conditions must be met first global interrupts must be enabled Arduino automatically enables global interrupts for us but if you're not working in Arduino you will have to perform this step manually second the individual interrupts must be enabled and this is usually accomplished by setting a bit in a register somewhere and finally third we need to actually meet the trigger condition for that interrupts and in our case this is a falling edge on a pin so the pin goes from logic high to logic low let's look at that datasheet again underneath the interrupts section we find a section devoted to external interrupts that looks promising when you're trying to figure out how to set up an interrupts on a new architecture it can be helpful to read through these interrupt sections a few times scroll down to the register descriptions here it tells us that the lowest two bits in the external interrupt control register a allows us to set the trigger condition for int 0 note that this registers abbreviated e ICRA we'll need that later in our code take a look at the interrupts sense control 0 table we want to trigger on a falling edge so we'll need to write binary 1 0 to the last two bits note that these bits also have names is C 0 1 and is C 0 0 we'll be able to use those names in our code keep scrolling down to the external interrupt masks register also known as iím SK which has only two usable bits these are our interrupt enable bits for the external interrupts so we'll want to set bit zero in order to tell the processor that we want to allow interrupts to trigger for int zero we don't need to worry about the other registers in this section as we've already set up the trigger condition and enabled the individual interrupts we just need to find where to enable global interrupts if you read the description for int zero in the eim SK register you'll see that we also need to set the I bit in the status register which is abbreviated s reg if you start searching for s reg you'll find that it's described in the AVR CPU core section under status register here we see that the I've it is bit 7 in the s reg and that's what allows us to control global interrupts if you read this part you'll see that we can set and clear this bit with the SEI and CLI commands which have been implemented in C for us by the AVR framework get rid of the attach interrupt call and write e IC are a vertical bar equals open parenthesis one less than less than is C 0 1 close parenthesis this sets bit 1 in the external interrupt control register a notice that we're able to call both the register and bit by name here thanks to some header files in the AVR libraries under that right e IC RA and percent equals tilde open parentheses 1 less than less than is C 0 0 close parenthesis to clear bit 0 now the lowest two bits in E I CRA are set to 1 0 which means that the int Z row interrupts will trigger on a falling edge then write E I m sk vertical bar equals open parenthesis 1 less than less than int 0 close parenthesis to set the int 0 bit in the external interrupt mask register which enables interrupts for the into 0 source finally write SEI open parentheses close parentheses which has the effect of setting the I bit in s reg to enable global interrupts note that while Arduino turns on global interrupts by default it's help to see here what you would need to do if you weren't using Arduino if you want to turn off interrupts you can write CLI but we don't need to do that for this example finally we're not calling the toggle function directly anymore so we need to set up a block of code that gets run from the int zero interrupt vector AVR has a special function called ISR that tells the compiler we're making an interrupt service routine we need to supply this function with the name of our interrupt vector which is based on the interrupt vector names in the datasheet change void toggle to ISR in all capital letters open parenthesis int zero in capital letters underscore vector closed parenthesis open curly brace this tells the compiler that this is the service routine that should be run whenever an int zero interrupt occurs upload this code as before you should see the LED toggle when you press the button now that you've seen how to setup interrupts I want to cover two more very important concepts the first is that you should always keep your service routines as short as possible by default an AVR you can't interrupt and interrupt and you usually want to get back to your main program as quickly as possible if you're finding that the ISR takes too much of your program time you can always handle interrupts with a flag that means setting a global variable and changing it in the interrupt somewhere in your loop you can check that flag execute whatever you need to do and then reset the flag this is similar to polling but has the benefit of not missing the interrupt the second lesson is that if you use a global variable in your main program and in an interrupt like our flag here you should always declare it as a volatile variable this tells the compiler that the variable might change outside the main execution thread which it does in the ISR many times compilers will be set to optimize away variables like this it sees that we set the flag to zero and assumes that it'll never change again because the compiler doesn't see it changing in the ISR so it'll assume the if statement is always false and remove that chunk of code by declaring the flag variable as volatile we're telling the compiler that it should not optimize out parts of our code with that variable when you run this you'll see that you can still toggle the LED but it might take up to half a second to respond the good news is that you still don't have to hold down the button to make it happen interrupts open a new way to respond to events outside the control of your main program loop they can definitely get complicated pretty quickly but they do offer more precise response times when you need them there are many different types of interrupts available and we just looked at one next time we'll look at how to set up timer interrupts if you want to stay up to date with more videos like this one from Sparkfun make sure you subscribe to our Channel and happy hacking we know pepper did it was adapted up whatever it is man nope so you're let a little doubt is the example these are into zero number phone oh I guess you heard that one led state equals what does that called an exclamation point it's like a tongue twister what did I write
Info
Channel: SparkFun Electronics
Views: 128,944
Rating: 4.9405546 out of 5
Keywords: diy, diyelectronics, arduino, engineer, engineering, coding, programming, microcontroller, embeddedsystems
Id: J61_PKyWjxU
Channel Id: undefined
Length: 18min 54sec (1134 seconds)
Published: Mon Feb 12 2018
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.