Introduction: AVR Assembler Tutorial 5
Today we are going to take a closer look at the various memory locations in the Atmega328p microcontroller and see if we can better understand how things are stored, where they are stored, and how the pointers and lookup tables work.
The tutorial will be separated into two parts. The first part is getting a grip on where and how data is stored in the microcontroller and the second part is about how computer memory works at a fundamental level using SR-Latches (and, in this case, D-Latches, which are pretty much the same thing)
You will need the following items:
- your prototyping board
- 8 LEDs
- a 220 ohm resistor
Part 2: (if you choose to do it)
- an LM7805C voltage regulator https://www.sparkfun.com/products/107"
- a 3-pole switch https://www.sparkfun.com/products/9609
- two push buttons https://www.sparkfun.com/products/97
- two LEDs https://www.sparkfun.com/products/9590
- 4 BC547 NPN transistors https://www.sparkfun.com/products/8928
- 6 1kohm resistors https://www.sparkfun.com/products/10969
- 2 resisters around 100 ohm to 330 ohms or so https://www.sparkfun.com/products/10969
- a bunch of connecting wires https://www.sparkfun.com/products/124
- the Instruction Set Manual: www.atmel.com/images/atmel-0856-avr-instruction-s...
- the Datasheet: www.atmel.com/images/Atmel-8271-8-bit-AVR-Microco...
Here is a link to the complete collection of my AVR assembler tutorials: https://www.instructables.com/id/Command-Line-AVR-T...
Step 1: Part 1: Register Analyzer
The first part of this tutorial is to build what I will call a register analyzer. The picture shows the circuit. Please ignore the white wires and red LEDs at the top right of the picture as those are part of the dice from Tutorial 4. Here we want to concentrate on the green LEDs. You see that I have 8 green LEDs in a line. All of them have the cathodes connected to the GND rail of the breadboard and that GND rail is connected to the power supply ground through a 330 ohm resistor. The anodes of each LED are connected to separate ports of the microcontroller. I have used PB1 through PB4 for the first 4 LEDs and PC1 through PC4 for the last 4 LEDs.
Here is how it is going to work:
We will set all of these pins to OUTPUT at 0V. Then when we want a particular LED to light up we will send a 1 to that pin which will put it at 5V and current will flow through the LED and then through the 330 ohm resistor to GND.
Once you have set up the circuit then we can move on to the next step.
Step 2: Test Program
Let us first write a quick test program to make sure that the setup is doing what we want it to do.
;************************************ ; written by: 1o_o7 ; date: <2014|11|03> ; version: 1.0 ; file saved as: analyzer.asm ; for AVR: atmega328p ; clock frequency: 16MHz ;************************************
; Program funcion:---------------------- ; analyzes the bits stored in a register ;---------------------------------------
.nolist .include "./m328Pdef.inc" .list
.def temp = r16
.org 0x0000 rjmp Init
Init: ser temp out DDRB,temp out DDRC,temp clr temp out PortB,temp out PortC,temp
main: ldi r20,0b11111111 ; register to analyze ; ldi r20,0b10101010 ; ldi r20,0b01010101 rcall analyze rjmp main
analyze: clr temp out portb,temp out portc,temp sbrc r20,7 sbi portb,1 sbrc r20,6 sbi portb,2 sbrc r20,5 sbi portb,3 sbrc r20,4 sbi portb,4 sbrc r20,3 sbi portc,1 sbrc r20,2 sbi portc,2 sbrc r20,1 sbi portc,3 sbrc r20,0 sbi portc,4 ret
As you can see, we are simply testing the LEDs here: that they all work and that they are wired correctly. I loaded 3 different patterns of bits into register r20 and then output them to the LEDs to make sure that the LEDs correctly represent the byte that is in that register.
Now we can use this code to analyze the byte that is stored in any register that we want.
We are going to use this to understand how the X, Y, and Z pointers work but first lets do a couple more tests...
Step 3: SREG Analysis and Bitwise Logic
As you recall (from page 11 in the datasheet) the AVR Status register, called SREG, stores information about what is going on in the cpu. What we are going to do in this step is simply analyze this register and see what is in there and how it changes when we perform a few tests.
The first test we want to perform is a simple one. Let's test the zero flag, which is bit 1 of SREG
main: ldi temp,1 cpi temp,1 in r20,SREG rcall analyze rjmp main
First we loaded the number 1 into temp, then we "compared" what is in temp with the number 1. Recall what "cpi" is actually doing. If you take a look at the instruction set summary table you will see that cpi actually subtracts the two numbers and if the result is zero it sets the zero flag in SREG. So if the zero flag is set after this instruction then we know the two guys are equal. We then copy SREG into r20 and analyze the contents of r20. So run the above program and you should find that bit number 1 (i.e. LED number one) turns on signifying that the zero flag of SREG is set. Great! Now look again at the instruction summary line for "cpi". In the "flags" column you will see that this instruction actually deals with a bunch of the flags in SREG. It says "Z, N, V, C, H" and looking at page 11 we see these are the zero flag, the negative flag, the two's compliment overflow flag, the carry flag, and the half carry flag.
Let's see if we can turn on a few more flags. Let's try this:
main: ldi temp,1 cpi temp,2 in r20,SREG rcall analyze rjmp main
This is the same as the last one only this time cpi should subract 2 from 1. We have 1-2 = -1 and so we shouldn't get the zero flag, but we should get the negative flag, Now try it.
Wow. We got a bunch of flags. We got C, N, S, and H. On page 11 next to bit 0, the carry flag, it says it indicates a carry in an arthmetic or logic operation. Then it says to turn to the instruction set description for details. So let's do that. If you open up the Instruction Set manual that I suggested you download in the last tutorial
On page 63 of the instruction set manual it discusses the cpi instruction. Let's take a look at what it says. It tells us that the operation performed is actually subtraction. Then it lists the flags and the calculation it performs to decide which flags to set. Under the "C", or Carry flag, it says that it is set if the absolute value of K (which is 2 in our case) is larger than the absolue value of Rd, which is R20 in our case and equal to 1. Since 2 is greater than 1, the Carry flag should be set -- which is exactly what we found. What about all that mathematical equation stuff they have there? What does that mean? Let's do the calculation. Here is a summary of how bitwise operations are done http://en.wikipedia.org/wiki/Bitwise_operation We don't have the symbols to write it precisely the same way, so we will use a * to denote a dot (which means "logical AND"), and an underline, to denote an overline, Here is the equation they give next to the Carry flag:
C: Rd7 * K7 + K7 * R7 + R7 * Rd7
In our case Rd is R20 which has the number 1 in it. This means R20 = 0b00000001. Also in our case K = 2 which means that K = 0b00000010. Also "R" means the "Result". So in our case the result of subtracting 2 from 1 is -1.
What is "-1" in binary? Well here is where things get interesting. If you take a look at the Wikipedia page here: http://en.wikipedia.org/wiki/Signed_number_repres... you will find a discussion of the different ways to represent negative numbers in binary. There are three main possibilities. First, maybe bit 7 denotes the sign? That would mean that -1 = 0b10000001 another possibility is called "one's compliment" which means that the negative of any number is just the compliment of that number. So in that case -1 = 0b11111110. The third possibility is the "two's compliment" representation. In two's compliment, the negative of a number is found by inverting all of the bits and then adding 1 to the result. So -1 would be 0b11111110 + 1 = 0b11111111.
So how do we figure out which one the atmega328p uses? Well we can easily find it in the datasheet, but instead, let's treat it as a detective mystery and figure it out "experimentally", i.e. by performing the calculations for each flag in SREG and seeing which representation gives the answer we have lit up on our breadboard analyzer.
Here is what we have for the above equation for the Carry flag: Rd7 is a 0 since the 7th bit in R20 is a zero in our case and K7 is a 0 also since the 7th bit of K is a zero. R7 means the 7th bit of the "result" and in our case, the result of 1-2 is -1. So our three posibilities are:
-1 = 0b10000001 (sign bit representation)
-1 = 0b11111110 (one's compliment representation)
-1 = 0b11111111 (two's compliment representation)
So let's try it with each:
An overline means "compliment" which means you change all the 0's to 1's and all the 1's to zeros. Thus Rd = 0b11111110 and hence Rd7 = 1. K7 = 0, and R7 is a 1 in all three representations! So here is what we have:
C: 1*0 + 0*1 + 1*1
The way that * or "AND" works is if both are 1's you get a 1, otherwise you get a 0. So in our case we have
C: 0 + 0 + 1 = 1
So our result for the "Carry" flag is 1. Hence the C bit in SREG should be on. Which is what we found.
However, in the Carry flag calculation, all three representations of negative numbers gave the same result since R7 is a 1 in all three cases. So let's look at the other ones. What about H? Its says that H is the "half carry" flag and it says that it is a 1 if we "borrow from bit 3". How do we figure that out? The instruction set manual gives the following as the calculation:
H: Rd3*K3 + K3*R3 + R3*Rd3
In our case we have Rd3 = 1, K3 = 0, but R3 is a 0 in the first representation, and a 1 in the other two. If we use the sign bit representation we get:
H: 1*0 + 0*0 + 0*1 = 0
But our breadboard says it should be ON! Hence the atmega328p is not using the "sign bit" representation. Let's try the other two where both say R3 = 1. Then our calculation gives:
H: 1*0 + 0*1 + 1*1 = 1
Good! So we know that it must be one of those two representations. Let's try the N flag. The calculation says that it is a 1 if the MSB (most significant bit, i.e. bit 7) in the result is set and 0 otherwise. Well the result is -1 and in ALL three representations the most significant bit is a 1. So N = 1 and this flag isn't going to help us. So we turn next to the S flag which is the last one we have that has lit up an LED in our analyzer. This is called "the sign bit" and the calculation is a straight up "exclusive or" which is what the plus sign with a circle around it means. An "exclusive or", or XOR, means that if both bits are a 1 you get 0, if both bits are 0 you get 0, but if one of them is a 1 and the other is a 0 you get a 1 as the answer. In other words if the first "or" the second is a 1 but not both. The calculation is:
S: N XOR V
and in our case N = 1 (since bit 2 LED is on) and V = 0 (since bit 3 LED is off) hence the answer is 1 XOR 0 = 1. Which means in all three representations the S flag will be on.
So we still haven't decided experimentally whether the atmega328p uses "one's compliment" or "two's compliment" to represent negative numbers! So let's look at another bit from SREG. Let's try the V bit. Which is called the "two's compliment overflow bit" which sort of tells us what our answer is going to be doesn't it? Oh well, let's continue to try and verify it. The calculation is:
V: Rd7*K7*R7 + Rd7*K7*R7
We have Rd7 = 0, K7 = 1, R7 = 1, and R7 = 0 in both one's and two's compliment. Hence this flag is also not going to distinguish between the two. Here is what we get:
V: 0*1*0 + 1*0*1 = 0
There is only one flag left in SREG that we can test! The zero flag Z. Our LED's say that this flag is off. The calculation is
This is a series of AND operations. It means that Z = 1 if ALL of bits in R are 1 and zero otherwise. But this means that ALL of the bits in R must be 0's (since it's compliment R has all 1's). We have
R = -1 = 0b11111110 (one's compliment representation)
R= -1 = 0b11111111 (two's compliment representation)
So in "one's compliment" we would get Z = 0 and in "two's compliment" we also get Z = 0.
Well that sucks. We have tested all of them and we still haven't proven which binary representation of negative numbers the atmega328p uses. However, we have verified that our LED's are giving us an accurate reading of the SREG register.
Well let's end this once and for all. W know that the number -127 in each representation is given by
0b10000000 = -127 (one's compliment)
0b10000001 = -127 (two's compliment)
So the same negative number is equal to a different binary number in each representation. So a simple experiment will decide!
main: ldi r20, -127 rcall analyze rjmp main
There you have it! Test it yourself. Which binary representation is in r20 when you analyze this on your board? Now you know the answer.
The point of this step was not just to show you how to test the contents of registers, but also to show you how to understand the workings of the flags in SREG, to understand how to calculate bitwise logical operations in binary and therefore to understand how to read the instruction set. Each of the operations in the instruction set has an "operation" that it performs which is shown in the operation column of the summary table. You now know how these are computed and why they set and clear various flags in SREG. You also now know which binary representation of negative numbers the Atmega328p uses.
Exercise 1: You will recall when we discussed the TCNT0 timer/counter and used it in the past two tutorials there were various bits that needed to be set in order to make the timer and the interrupts do what we wanted them to. Do a quick check with our analyzer and see what is currently in the TCCR0A, TCCR0B, TIFR0 and TIMSK registers (don't forget that TIMSK0 is not in the i/o register range so you have to use a more expensive command than "in" to load it's contents into a register!)
Step 4: Another Look at Pointers and Lookup Tables
Let us now see if we can use our analyzer to get a better idea of how these X, Y, and Z pointers and lookup tables work. I realize that our discussion in the last tutorial may have been a bit confusing to the newcomer. I think that this will help make it all more clear.
Let's start out by modifying our code as follows:
;************************************ ; written by: 1o_o7 ; date: <2014|11|03> ; version: 1.0 ; file saved as: analyzer2.asm ; for AVR: atmega328p ; clock frequency: 16MHz ;************************************ ; Program funcion:---------------------- ; analyzes the bits stored in a register ;--------------------------------------- .nolist .include "./m328Pdef.inc" .list .def temp = r16 .org 0x0000 rjmp Init numbers: .db 0b01111111, 0b11011110, 0b01011110, 0b11010010 .db 0b01010010, 0b11000000 Init: ser temp out DDRB,temp out DDRC,temp clr temp out PortB,temp out PortC,temp main: ldi ZH, high(2*numbers); ZH is the high byte of the address of numbers ldi ZL, low(2*numbers); ZL is the low byte of the address of numbers mov r20,r30 rcall analyze rjmp main analyze: clr temp out portb,temp out portc,temp sbrc r20,7 sbi portb,1 sbrc r20,6 sbi portb,2 sbrc r20,5 sbi portb,3 sbrc r20,4 sbi portb,4 sbrc r20,3 sbi portc,1 sbrc r20,2 sbi portc,2 sbrc r20,1 sbi portc,3 sbrc r20,0 sbi portc,4 ret
We have simply added a lookup table at the top (which is exactly the same one we used in our dice program in Tutorial 4) and then we added the ZL and ZH lines in our Main routine to initialize the Z pointer.
We then load r30 into our analyzer register r20 and analyze it on our board. You should do this. You should find that r30 contains 0b00000010. Now do the same for r31 and you will get 0b00000000.
The address of "numbers" is 0x0001 which in binary is 0b0000000000000001 so
ldi ZH, high(2*numbers)
means exactly the same as
ldi r31, high(0b0000000000000010)
ldi r31, 0b00000000
ldi ZL, low(2*numbers)
means exactly the same as
ldi r30, low(0b0000000000000010)
ldi r30, 0b00000010
Recall that r30 and r31 combine together to make Z.
main: ldi ZH, high(2*numbers) ldi ZL, low(2*numbers) mov r20,ZL rcall analyze rjmp main
Now what happens if we look at Z? Well lets try it:
main: ldi ZH, high(2*numbers) ldi ZL, low(2*numbers) lpm r20,Z rcall analyze rjmp main
Notice that, in order to use Z, we need the command LPM. This will load whatever data in sitting in the address pointed to by Z. The address Z is pointing to is found by starting with what is in r31 r30, which is 0b0000000000000010 and then shifting it back again to the right to get 0b0000000000000001. So try it and see what you get....nifty right? You get 0b01111111 which is exactly what we stored at that address. Let's continue to try and make this more obvious. Let's do the following:
main: ldi ZH, high(2*numbers) ldi ZL, low(2*numbers) adiw ZH:ZL,1 mov r20,ZL rcall analyze rjmp main
You see we added 1 to ZH:ZL which makes ZL = 0b00000011. If you replaced ZL in the above with r30 you would get exactly the same thing. They are different names for the same number. If, instead of using "adiw ZH:ZL" in the above you used "inc ZL" or even "inc r30" you would get the same thing again. The instruction adiw means "add immediate to word" and a "word" is 2 bytes or 16 bits. So you need to give it two 8-bit registers, ZH:ZL, or equivalently r31:r30 and it adds k to it. In our example above k is 1.
Let's add 1 to r30 and then see where Z is pointing:
main: ldi ZH, high(2*numbers) ldi ZL, low(2*numbers) inc r30 lpm r20,Z rcall analyze rjmp main
So Z contains ZH:ZL which is the same as r31:r30 which is 0x0003 since "numbers:" starts at 0x0001 and we times by 2 to initialize ZL and ZH which meant that Z contained 0x0002, then we incremented r30 (which is the lower half of Z) so that now Z = r31:r30 = 0x00:0x03 = 0x0003. When the LPM instruction is asked to Load program memory from Z to r20 it takes the number in Z, which is 0x0003 = 0b0000000000000011, and says, "okay, the 0th bit is a 1, that means I want to grab the second byte at the address 0b0000000000000001 which is the address I get by shifting Z to the right one position (or dividing by 2)"
I want to do one last thing here to try and make it less confusing. Let's go back to the very top of our program and stick an .org statement in there. So you have
.org 0x0000 rjmp Init .org 0x0008 numbers: .db 0b01111111, 0b11011110, 0b01011110, 0b11010010 .db 0b01010010, 0b11000000
Can you see what I have done? I have explicitly told the PC to stick my table of numbers at the Program Memory address 0x0008, which in binary is 0b0000000000001000
(Recall that since I am not enabling any interrupts I can can use those memory spaces for whatever I like.)
Since the numbers: table starts at the above address, what will get loaded into r30 and r31 this time?
Well multiply that address by 2 and you get 0b0000000000010000 which separates as 00000000:00010000 so that tells us that r31, or ZH if you like, is 0b00000000 and r30, or ZL if you like, is 0b00010000.
Exercise 2: Check this with your analyzer by outputting r30.
Now try this:
main: ldi ZH, high(2*numbers) ldi ZL, low(2*numbers) adiw ZH:ZL,3 lpm r20,Z rcall analyze rjmp main
You should be pointing at the 4th number in our list since we started at the first one and added 3 to the address. What is the actual Program Memory address of this number? Well, the program memory address for the first one in the list is 0x0008, which is 0b0000000000001000, and the program memory of the second number in the list is ALSO 0x0008. The program memory of the third number in the list is 0x0009 and the program address of the fourth number is ALSO 0x0009. The way we get that fourth number is we start with it's address 0b0000000000001001 and we shift left by 1 bit (i.e. times by 2) to get 0b0000000000010010 and this will give the ZH:ZL values for the first number at that address, i.e. the third number in our list, to get the second number stored at that same address (in other words the upper half of that 16 bit register at address 0x0009) we just add one to the shifted number. Which is 0b0000000000010011. This is the number 0x13 in hex, or 19 in decimal. So we want a number stored at Program Memory address number 9 and so we set ZH:ZL to 0:19 Let's prove all this. Lets just stick this number in to r30 and r31 and see what comes out of Z.
main: ldi ZH, high(2*numbers) ldi ZL, low(2*numbers) ldi r31, 0 ldi r30, 19 lpm r20,Z rcall analyze rjmp main
Lo and behold. Your LED's are showing the 4th number on our list.
I hope that this has helped people understand what is going on with the Z pointer, how Program Memory is laid out, how ADIW and LPM work and why they have to do this multiplying by two stuff to get the correct byte data out of a given memory address. Notice there are two different 16 bit things here. The Program Memory address itself is a 16 bit number, AND the register AT each of those address is 16 bits wide. The first fact is why Z has to be made out of 2 of our 8-bit working registers so it can hold a complete memory address, the second fact is why we have to multiply by 2 so we can get the low or the high byte of data stored at each address.
Exercise 3: Play around with placing data in various memory locations, incrementing and decrementing the r30 and r31 registers, what happens when you start at r31 = 0xFF and increment it? Also try using the X, and Y pointers as well.
Whew! Let's take a break and do something fun...
Step 5: Part 2: Logic Gates
We have spent this entire tutorial talking about Program Memory addresses and registers and all that. Now I would like to take a few minutes and delve down into what is going on at these addresses. In other words, how do I store a byte of data at some address? What is going on there? What is a "register"? I.e. what is the electrical circuit that I can build which will store an 8-bit number like 0x03 or a 0b10101011 for example?
By now you have noticed that all of the instructions in the instruction set can be reduced to two types. The first is storing, retrieving, or copying bits from and between registers. The second type is performing logical operations on those data. For example adding, subtracting, ANDing, ORing, XORing, etc. That is really all that is going on here. Various combinations of storing numbers, and then performing mathematical operations on those numbers is what results in all of the cool things computers and microcontrollers can do. It is pretty amazing when you think about it.
In Part 2 of this tutorial we are going to go down one final step and look at one of the most important components of this process. We are first going to construct a certain type of "logic gate" called a NOR gate, and then we are going to use NOR gates to construct an SR-Latch.
Take a look at the picture of the logic gates I have given above. They are schematic diagrams which denote a particular mathematical operation. You recall that these kinds of operations are all that we need to make instructions for an instruction set. When we were discussing SREG before you saw that all of the flags depended on some logic calculation using the bits in our registers and performing OR's, AND's, etc on them. If you look at the diagram you see that each of these "logic gates" has two inputs (which are essentially wires) and one output (also a wire). So If you had an AND gate for example, you see that if I put 5 V on each of the input wires, then I will get 5V on the output. On the other hand, any other combination of input voltages (0's or 1's) would result in 0V on the output. Similarly for the other logic gates. So these are a way to make decisions and perform calculations on the bits in our registers, just like the calculations that are done by the CPU to figure out which flags get turned on in SREG.
So let's look at the NOR gate. The NOR gate is simply an OR gate with an N in front of it. That means take the "truth table" of the OR gate and take the compliment of all the answers. So a 1 becomes a 0 and a 0 becomes a 1. Notice something in particular. The only way I can get 5V out the other side of a NOR gate is if there are NO volts coming in from either input! This is kind of cool. It is why we are going to use this baby to make something even cooler, an SR-Latch.
An SR-Latch is constructed by taking two NOR gates and setting up a feedback loop so that the output of one becomes one of the inputs for the other and vice-versa. Take a look at my second diagram showing an SR-Latch. See if you can see why the logic table is what is shown. For example, say I put 5V on R and 0V on S. Well you know how a NOR gate works, since there is 5V on R we know that the output at Q will be 0V because the only way to get a signal out of a NOR gate is if both inputs are off. So that means that 0V is coming out a Q and being fed back into S. So the other NOR gate has 0V coming in from S, and also 0V coming in from the feedback loop. Hence it will set the other output Q to 5V. So now you see how it works right?
The reason it is called an SR-Latch is that S stands for "Set" and R stands for "Reset". Say I connect an LED1 to Q and another LED2 to Q and I start out with S set to 1 (i.e. at 5V) and R set to 0 (i.e. 0V), then LED1 will be on and LED2 will be off. Then Suppose I release S so that it goes back to 0V. We still have 5V at Q because R is 0V and the feedback from Q is also 0V. But now S is at 0V instead of 5V so what will change? Nothing! We can keep switching S between 0V and 5V and nothing will happen. The LED at Q will stay on and the LED at Q will stay off. We have "set" the latch ON. (Q is lit means ON)
Now what happens if we put 5V on R? Well then since 0V is coming from the Q feedback and 5V is coming from R, the NOR gate will shut off and Q will shut off. Then the feed back from Q will be 0V and since S is 0V we see the other LED at Q will turn on! Now convince yourself that if we keep switching R now nothing will change the LED at Q will remain on regardless of what we do to R. The only way to shut it off is to push S again. The net result of this SR-Latch is that pushing S will light up one of the LEDs and it will stay on even after you let S go back to 0V, the latch remains "set". The only way to get it off is to push R which will shut off that LED and turn on the other one. Then you can let R go back to 0V and the Latch will remain "reset".
So an SR-Latch is a way of storing one bit of data. If you define it to be a "1" when the Q LED is on and a "0" when the Q LED is on you have effectively stored a single bit. You can send a voltage pulse to the latch and set it to 1 and then you can just leave it be and it will remain a 1. Then if you send another voltage pulse to R it will be reset to a 0 and will remain that way as well.
This is the basic element of storage in the computer. There are slightly more sofisticated storage methods like Flip-flops and D-latches, but they are all made from an SR-latch with some added features like a clock signal input which sets and resets the latch according to the clock pulse and another gate or two on one of the inputs. But the basic unit is the SR-Latch.
We could go further into the theory and show how you can take 8 of these Latches and make an "8-bit Register" out of them. I.e. you can store 8 bits, one in each of the 8 SR-Latches. Our general purpose working registers in the atmega328p are essentially a collection of SR-Latches. In fact, someday I may grab a breadboard and build 8 SR-Latches like the ones we are constructing here in Part 2 of this tutorial and then hook them together to make a Register. It might be fun to then perform logical operations on it and stuff like that. If I ever did build a register like that I think I will name it "r16" :)
Step 6: How Do We Make an SR-Latch?
I made the diagram above on my Android phone using an app called "Droid Tesla". It is a way to construct circuits and test them before physically building them on a breadboard.
As you can see by the diagram I have simply taken an SR-Latch made out of two NOR gates and wired the inputs to 5V each through a pushbutton, and then I wired the outputs each through an LED, then a resistor, and then GND. Then I tested it on Droid Tesla and found that it works exactly as I want. If I press one of the buttons an LED comes on and then the only way to switch LEDs is to push the other button.
Probably you are saying, "Sure. We can all see that this is a great way to store a bit of data, but we can't build this circuit on a breadboard because you still haven't told us how do we make a NOR gate!!"
The way to make a NOR gate is using transistors.
Take a look at the second picture. This is an NPN transistor and we can make a NOR gate using two of them. The way a transistor works is that you put 5V on the Emitter pin, 0V on the Collector pin, and nothing will happen until you put a small current in the base pin. If there is current in the base pin then the transistor just acts like a plain old wire. If you look at the last diagram I have given you can see that using the bases as my inputs and connecting the emmiter to ground and the collector to 5V then if either of the inputs is on then the Vout will be brought down to GND, or 0V. If neither of them are on, then the Vout will be at the supply voltage 5V. This is the truth table of a NOR gate.
So now we know how to make a NOR gate out of two transistors right?
Exercise 4: How would you make an AND gate out of two transistors?
Exercise 5: How do you make an SR-Latch? You can make two NOR gates and then hook up the feedbacks right? Draw it out on a piece of paper.
Step 7: Circuit Diagram
You can see the circuit diagram of an SR-Latch that I made using Droid Tesla.
I know it looks sort of complicated but it isn't that bad. If you did Exercise 5 in the previous step you will find it pretty obvious. The only things I added in were larger resistors to limit the current going to the bases of the transistors.
Exercise 6: Take a breadboard and build and SR-Latch!
Step 8: Conclusion
In this tutorial you have made a circuit that allows you to analyze the registers in the atmega328p and find out which bits are on and off. You then used this to learn more about the AVR Status Register SREG and how to perform bitwise logic calculations to find out how all of the instructions in the instruction set perform their functions. Then you used your register analyzer to take another look at the X, Y, and Z pointers and how they work with lookup tables. Hopefully you are now a lot more confident that you understand what is going on there. Finally we learned more about the logic gate circuits that the cpu uses to perform its bitwise logic calculations implemented in its instruction set. We then looked at NOR gates and how to construct them using transistors and then how to build our own SR-Latch which is the fundamental basis of memory storage that make up our registers.
In the photo you can see the SR latch that I built.
Anyway, that's all folks! I hope you enjoyed Tutorial 5.
See ya around!