Level Up Your Arduino Code: Registers

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
I've had a few requests to cover interrupts on Arduino before we do that we need to talk about registers so what is a register to answer that we have to look at the data sheet for our microcontroller which on the Arduino Uno r3 is the microchip AVR 18 mega 328p the datasheet is a 660 page monstrosity that tells you everything you need to know about the 328p and its siblings if we look at the cpu core overview we find a block diagram showing how the main processor is configured the ALU the arithmetic logic unit is responsible for performing calculations like addition subtraction bitwise logical operations and so on data can come from or be stored to 32 general-purpose registers each holding eight bits or one byte other registers exist like the instruction register or within IO modules to assist with pin control think of the processor like a kitchen the ALU is the chef whose job is to follow instructions and prepare meals the person that's responsible for executing all the actions the chef is following a recipe which is like the program you wrote a series of instructions for the ALU the pots pans and various bowls for prepping food are your registers there's simply containers for holding food in the processor there are storage vessels for data the general-purpose registers are used to hold data for calculations the kitchen analogy breaks down when we start talking about special function registers these registers have special hardware connected to them in the microcontroller die let's pretend that these tiny bins represent bits in a special function register in our 328p each register has eight bits and we are able to read and write to many but not all of these registers directly from code for example I can set a bit to one which I'll represent as a closed lid the open lids represent the binary value 0 the register has the binary value 0 0 1 0 0 1 1 0 to calculate the decimal value from this binary number we start from the rightmost bit and assign it the value 1 the next bit to the left has the value 2 then 4 then 8 16 32 64 and 128 notice that the value doubles each bit we go up for the bits that are set to 1 we add up their assigned decimal values for example this would be 2 plus 4 plus 32 which is 38 if we were to write this value to a certain special function register in the microcontroller it might cause corresponding Hardware pins to go from 0 volts to 5 volts in fact we're going to do just that special function registers are used to control pins set up interrupts send and receive serial data and so on note that registers are unique to each architecture that means if you're developing on a 328p and you move to an ESP 32 you'll need to learn a whole new set of register names the 328p has three different types of memory the first is program memory this is where your code lives when you write code for the 328p it is compiled into machine code and stored here it's the set of instructions or recipe for your processor to follow next is SRAM or static random-access memory the interesting part is that our general-purpose registers as well as the special function registers at least the ones we have read or write access to live in the first 256 spaces of SRAM on the 328p so to write to a special function register we need to first find its address in SRAM and then we specify what value we want to write to it in our program the rest of the SRAM is used to hold values for things like variables when you define them in your code note that when you power down or reset your microcontroller the SRAM is cleared so you lose all your data here finally there is electrically erasable programmable read-only memory or a EEPROM it's often not considered part of the memory of the processor since it's usually on a separate bus it also has much slower read and right times but it's useful for storing data that needs to be saved between resets or when power is lost if we look at the i/o ports overview section we can see what a digital input/output pin looks like in a general sense note that on the 328p every IO pin can be configured to act as a digital input or a digital output although some pins can adopt other functions like serial transmit Hardware interrupts or analog to digital converter go up to the pin configurations section and take a look at the dip package which is the same one found on the Arduino Uno you'll see that IO pins are given a label P for port a letter like B C or D and a pin number for example we may want to toggle an LED on pin pd v which stands for port d pin v to use a pin as digital input or output we need to be concerned with three registers port DDR and pin let's navigate to the i/o ports register description section to take a more in-depth look first we need to set the DDR the data Direction register for our port for D in this case by writing a 1/2 a bit in the DDR we're telling the microcontroller that we want the corresponding pin to be an output we want to be able to control the voltage on that pin with our code if we leave the bit a zero then we want the pin to be an input which means we can read whether the voltage is high around 5 volts for our Arduino or low around zero volts next we have the port register not to be confused with the hardware port which is a collection of pins note the all capital letters for the register name if a pin is configured to be an output from the data Direction register then writing a 0 to the corresponding bit in the Port register will cause the pins voltage to be 0 volts and writing a 1 to the corresponding bit will cause the pins voltage to go high 5 volts in the case of our Arduino if however the pin is configured to be an input then writing a 1 to the same bit in the Port register will able to pull up resistor on that pin leaving it zero for an input pin will disconnect the pull-up resistor as you can see the port register performs two duties depending on if the pin is an input or output finally the pin register should generally be treated as read-only and its purpose is to give you the input values on the pins of that port we'll look at an example of how to do this in a minute but first let's see how these registers are organized in memory if we navigate to the register summary section of the datasheet we see the registers listed with their addresses in SRAM note that they're in reverse order special function registers can have functions other than controlling pins for example TCC r0a is used to control timer zero which is useful for things like setting interrupts and creating pulse width modulation signals some bits are unused or reserved as noted by a dash and likewise a hole register may be marked as reserved these usually have special functions internal to the processor and generally should not be written to let's start with a very simple Arduino example whenever a button is pushed an LED lights up it's trivial but it serves as a great way to see how to use registers to control Hardware starting with an LED and button on a breadboard put a 330 ohm resistor in series with the LEDs anode connect a wire from the resistor to pin 5 on the Arduino connect another wire from the led's cathode to one of the buttons pins as these pins will share a common ground connection then connect those pins to ground on the Arduino finally run a wire from the buttons other pin to pin 2 on the Arduino we've defined our button and LED pins to be 2 and 5 we've set the button pin to be an input with the pull up resistor enabled and the LED pin to be an output pin in loop we read the state of the button pin which gives us a 0 if the button is pressed and a 1 if the button is not pressed if the button is pressed turn the LED on otherwise turn it off the Arduino framework gives us a layer of abstraction functions like pinmode and digital read are reading and writing values to the necessary registers for our 328p let's upload this code notice that this arduino code takes 902 bytes of program space this will be important later just to test as we press the button the LED lights up everything seems to be working what we want to do now is rewrite our program so that we have the same functionality but we're directly manipulating registers rather than relying on arduino code we need to look at the uno r3 schematic and find the 328p controller here we see that our button is attached to Arduino pin 2 which is PD two-port deep into on the microcontroller likewise Arduino pin 5 which is attached to our LED is actually PD 5 this means we need to manipulate the port D registers to read and write to these pins let's say that we have our three registers that control port D on the microcontroller first we'll set the DVR D register to binary 0 0 1 0 0 0 0 0 which tells the microcontroller that we want to use pin 5 on port D as an output for our LED and leave the rest of the pins as input then we want to write 0 0 0 0 0 1 0 0 2 the port D register this will enable the internal pull-up resistor on pin 2 as that pin was configured as an input let's make these changes in our code remove our pin mode functions and replace them with DDR D equals capital B 0 0 1 0 0 0 0 0 semicolon note that starting a number with capital B indicates that we're using binaries so binary 0 0 1 0 0 0 0 0 is really 32 and decimal you could replace this number with just 32 and it would mean the same thing it looks like we're setting a variable named D D Rd in reality we're calling a predefined macro for the 328p that assigns the value - to the register at the DDR d/s if we dig into the Arduino source code we find a file named IOM 328 pH which defines all of our registers for the 18 mega 328p in it we see that DDR D is defined as a macro used to manipulate special function registers and given the address hex 0a if we look at the register summary section in the datasheet again we can confirm that SRAM address hex 0a is indeed the data Direction register for port D back in our code we want to add port D equals binary zero zero zero zero zero one zero zero this enables the pull-up resistor for pin to note that it also causes in v to be set to zero volts as we've already told pin v to the output in the previous step upload your code to the Arduino press the button and we see that our circuit is behaving the exact same way as before now let's try replacing the digital write functions with direct register writes in the first one we want to turn the LED on so replace it with port D equals binary zero zero one zero zero zero zero zero and we want to turn off the LED in the second one so replace it with port D equals binary zero zero zero zero zero zero zero zero upload the code when I push the button the LED comes on but wait it doesn't turn off what's going on here remember how setting or clearing a bit in the port D register can enable or disable the pull-up resistor if that pin was set to an input well it looks like we're disabling the pull-up resistor for pin two when we're trying to turn the LED on and off oops we could change that bit to a one in both writes to port D but this is a good time to talk about setting and clearing individual bits in a register other parts of your code may try to set or clear bits in the register and we want to leave those alone to just make the single bit change we need so how do we do that first we need to use the left shift operator this shifts all the bits to the left by the number of positions specified for example the number 50 is 0 0 1 1 0 0 1 0 in binary if we wrote 50 less than less than 2 it would shift all bits to the left by 2 positions resulting in 1 1 0 0 1 0 0 0 which is 200 note that zeros are appended on the right side during the shifting operation so to set bit 5 we need to left shift the number 1 over by the number of times equal to the bits position 5 in this case this gives us binary 0 0 1 0 0 0 0 0 we then want to read the current value of the port deregister which will likely be something like 0 0 0 0 0 1 0 0 as pin 2 is configured for a pull-up we want to use the bitwise or operator to perform the or operation between each of the corresponding bits 0 or 0 is 0 0 or 0 is 0 1 or 0 is 1 and so on by doing this we keep all the same bit values that were in port D except for bit 5 which we've changed to 1 we then write this value back to the port theme register in our code where we want to turn on the LED we change it to port D equals open parenthesis 1 left shift LED pin close parentheses vertical bar for the for operation port D semicolon this shifts 1 to the left by 5 bits as led pin is equal to 5 performs a bitwise or operation with the value currently in the port D register and writes that result back to port D the method of setting a bit doesn't quite work for clearing a bit meaning we want that bit to be 0 to do that we start again by shifting the number 1 over by the number of times equal to the bits position which will want to be 5 we then perform a bitwise not operation on this result to invert all the bits ones become zeros and zeros become ones so in our example we have 1 1 0 1 1 1 1 1 we then want to perform a bitwise and operation on this number with the current value in court d this is likely to be 0 0 1 0 0 1 0 0 as the LED on pin 5 will probably be on and pin 2 still has the pull up set move down the bits again and perform the and operation 1 and 0 is 0 1 and 0 is 0 0 & 1 is 0 only bit 2 is kept set in this instance as 1 + 1 is 1 notice again that the bit values in port D are kept the same except for pin 5 which we've successfully cleared in code this looks like port D equals tilde for the not operation open parenthesis 1 left shift LED pin close parentheses ampersand port D semicolon this shifts the number 1 over to the left 5 spaces performs a bitwise not operation on that result and then performs a bitwise and on that with the value in port D ultimately this clears bit 5 and writes that value back to port D the last piece we need to consider is the digital read function remember that we want to read the value of the pin D register but we need it so that we get a 0 or 1 for the state of that pin if we were just to read pindy we'd get the state of all the pins on port D so how do we isolate just pin 2 we first want to left shift the number 1 again by the number of times equal to the bits position 2 in the case of our button we then read the value of the pin D register which I'll write is a series of X's as we don't know what state the pins will be in and X can either be high or low 1 or 0 we don't know we use the bitwise and operation on these values which is known as masking here we're defining which bit or we want to keep and the only bit we care about is bit too when we perform the bitwise and the result is zero for each bit in the first number that is zero it doesn't matter if the X is 0 or 1 0 and 0 is 0 as well as 0 and 1 is also 0 the only bit that stays the same is bit 2 as 1 and 0 is 0 and 1 in 1 is 1 so whatever value was in that bit in pin D is kept if this X happens to be a 1 the value would then be binary 0 0 0 0 0 1 0 0 which is 4 we want the results to be 1 if that bit is set so we right shift this result by 2 the same number we shifted by before now if bit 2 of pin D is high we get 1 as a result or if bit 2 is low we get 0 in our code we replace the digital read line with int BTN equals open parenthesis pindy ampersand open parenthesis 1 left shift button pin close parentheses close parentheses right shift button pin semicolon this will set the BTN variable to 1 if pin 2 is high or to 0 if pin 2 is low upload this program we see that the circuit is back to working again to make life a little easier the Arduino library also has the underscore BV macro which is defined for the AVR family of microcontrollers and our 328p is in that family the BV macro just makes the 1 left shift by bit number operation a little easier to read and faster to execute we can replace our left shift operations in our code with underscore BV bit number it's slightly easier to read but it's also important to understand the BV macro as you'll often see it in C code examples for AVR we can upload the code to see that it works exactly the same but here's the interesting part remember how our Arduino code was 902 bytes in size our newly optimized program now compiles to just 400 sixty-two bytes we nearly have to the size of our program by manually reading and writing to registers if you're making a product and you need your code to be as small and fast as possible learning to deal directly with registers can be very helpful and get rid of the extra code overhead that Arduino introduces however if you're making a project or a prototype and you just need something to work that extra development effort can be quite a pain to learn new registers and register names for your architecture in that case the abstraction layer that Arduino introduces is a wonderful blessing being able to call digital right instead of having to figure out which bits to flip in which register is fantastic all that being said if we want to do more advanced things like set up interrupts then learning how to deal directly with registers is the way to go we'll talk about that next time we look at leveling up your Arduino code see you then
Info
Channel: SparkFun Electronics
Views: 128,558
Rating: 4.9527273 out of 5
Keywords: IoT, Arduino, engineer, engineering, coding, electronics, HowTo
Id: 6q1yEb_ukw8
Channel Id: undefined
Length: 21min 8sec (1268 seconds)
Published: Wed Jan 10 2018
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.