Welcome to the Embedded Systems Programming course. My name is Miro Samek and in this lesson I'm going to talk about variables and pointers. Let's start with making a copy of the previous lesson2-project and renaming it to lesson3. If you don't have the lesson2-project, you can get it online from state-machine.com/quickstart. Get inside the new lesson3 directory and double-click on the workspace file to open the IAR toolset. If you don't have the IAR toolset, go back to lesson 0. So, here is the C program you've created in lesson 2. Let's clean it up a little bit and have a quick look in the debugger to see where the counter variable lives and how it is accessed. As you step through the code and watch the Locals window, you see that the counter variable lives in the R0 register and is accessed by the machine instructions directly. Now, let's move the variable definition outside the main function, recompile, and go back to the debugger. Interestingly, the variable counter is no longer visible in the Locals window. This is because it is no longer local. To see the counter variable now we need a different view. Select the View menu and click on Watch, Watch1 view. When the watch window opens, click on the first line and type the name of the variable: counter in out case. As you can see, the location of the counter variable is now a big number starting with 0x2. This address is the beginning of the Random Accessed Memory in this particular ARM Cortex-M microcontroller, so the counter variable lives now in RAM. If this is really the case, than the variable should be also visible directly in the memory window. To check this, set the memory view to 0x2000000 and change the memory setting to 4x unit, to conveniently see 4-byte integers. Now, let's single step through the code and watch the debugger views. Note that the STR instruction caused change from zero to one in the e Watch 1 view and the memory view. Now to 2... Now to 3... Let's try to understand the machine instructions that the compiler has generated to access the counter variable in memory. The first interesting instruction is the LDR, which stands for load from memory to a register. The LDR.N instruction loads something from the label ??main2 to the R0 register. You can actually Scroll down to this label to see what's being loaded. Hey, this looks familiar. The value loaded to R0 is the address of the counter variable. Let's execute the LDR.N instruction to watch R0. The next LDR instruction loads R0 again, but this time the value comes from the address that the R0 currently holds, which is the address of the counter variable. Single step and verify that R0 now has the value 3. The ADDS instruction does the actual work of incrementing R0 by 1, so R0 becomes 4. The next LDR.N instruction loads the address of the counter variable to R1. And finally, the STR instruction stores the value of the R0 register to the memory pointed to by the R1 register. Please note how the Watch1 and memory views change after this instruction executes. At this point, I hope that you start to see the general pattern of accessing memory in the ARM processor. ARM is an example of the so called Reduced Instruction Set Computer (RISC) architecture, where memory can be only read by the specical load instructions, then all data manipulations must happen in the registers, and finally the modified register values can be stored back into the memory by the special store instructions. This is in contrast to the Complex Instruction Set Computer (CISC) architecture, such as the venerable x86 inside the personal computers, where some of the operands of complex instructions don't need to be in the registers, and can be still in memory. But regardless of the processor architecture, I hope that you start to appreciate the role of memory addresses, because every access to memory necessarily requires the knowledge of the address to load the data from or to store the data to. All this leads to an interesting question. If those memory addresses are so fundamental to the CPU, can they be somehow represented at the C-language level? The answer is "yes". In the C language, the addresses can be stored inside variables called pointers. Here is an example of a pointer variable in C. Like most C declarations, the best way to explain this one is to read it backwards. So, p_int is a pointer--which is what the star means after the type--to integers. In other words, p_int is a variable that can hold addresses of integer variables. If so, then p_int should be able to hold, among others, the address of the integer counter variable. Indeed, this can be very easily achieved in C. The operator ampersand gives the address of the counter variable, and this address can be legally assigned to p_int. Finally, it is also very useful to get the value stored at a given address from the pointer, which is called de-referencing the pointer. The C operator for this is the star. Star-p_int means the value at the address currently stored in the p_int pointer, which is the value of the counter variable in this case. Because of this equivalence, you can replace counter with *p_int, and the program should work exactly as before. Let's see if the compiler is happy with this program. All right, so now let's go to the debugger and prepare the views. This time we need both the Watch1 view to see the counter variable and the Locals view to watch the p_int pointer. Before you step through the code, let's take a look at the disassembly view and compare it side-by-side with the code before introducing the p_int pointer. As you can see, the LDR instruction, which loads the address of the counter varible to the R0 register has been moved to the top, and another copy of the same instruction has been removed altogether. In other words, introduction of the p_int pointer has simplified the machine code and improved its efficiency. This code also suggests that the p_int pointer is now located in the R0 register. Now you can single-step through the code and watch the counter variable increment, both in the Watch1 view and in the memory view, exactly as before. This confirms that the pointer is indeed an alias of the counter variable. Finally, if you want to execute the loop to the end, but are getting tired of single-stepping through the code, you can set a breakpoint after the loop and click the Go button to execute the program at full speed. After you stop at the breakpoint, you can verify that the final counter value is 21, as expected. In the last step of this lesson, I'd like to demonstrate the incredible power of pointers. At this point, it will be just a horrible hack, but it will show you a technique that is actually used all the time in embedded systems programming. As I said before, a pointer variable, like p_int, holds the address of an integer. But this can be almost any address, not just the address of the counter variable. If so, then let's try to assign a fabricated address to p_int. In your first attempt you might try to use an address expressed as a hex number, just as you saw it in the debugger. But when you compile by pressing F7, the compiler rejects the code. In your next attempt, you might try to use an unsigned number by pre-pending the U suffix to the number. But the compiler doesn't like this either. At this point your negotiations with the compiler really break down. But the C language has a mechanism to enforce a type by using type casting. You perform such type casting by placing the name of the type in parentheses in front of the cast expression. Now, the compiler has no choice, but to accept it. All right, so now let's de-reference the pointer and poke some easily recognizable integer value into it. Embedded programmers seem to like DEAD BEEF for this purpose. Obviously, this hack needs to be tested. But to preempt your objection that a simulator can take anything, I'd like to run this code on the Stellaris Launchpad board. So, if you have the board, plug it into the USB connector of your PC. Next, set the debugger to the TI Stellaris interface. Also don't forget setting the "Use flash loader" option under the Download tab. If you don't have the board, just skip this step and follow along in the simulator. Either way, click the download and debug button to get to the debugger. Make sure that you have a breakpoint at the re-assignment of the p_int pointer and also make the Watch view visible so that you can see the counter variable. Press Go to run to the breakpoint. Click in the disassembly window to step one machine instruction at a time. Execute the LDR instruction, which loads the fabricated address to the R0 register, and verify that this address appears int the p_int variable in the locals view as well as in R0 in the register view. Execute the next LDR instruction, which loads the value hex-DEADBEEF to R1. And finally, execute the STR instruction, which stores DEADBEEF in memory at the p_int address. The effect of this is kind of scary. Due to the intentional misalignment of the fabricated address, the 0xDEADBEEF value gets written partially over the counter variable and partially over the next word in memory. The Cortex-M4 processor accepted this misaligned address, but Cortex-M0 would have a problem with it. So, now you see how pointers, as any powerful mechanism, can also be dangerous, if used carelessly. This concludes this very quick introduction to pointers. In the next lesson you will put this knowledge to use to blink the LED of the Stellaris Launchpad board. If you like this channel, please subscribe to stay tuned. You can also visit state-machine.com/quickstart for the class notes and project file downloads. --- Course web-page: http://www.state-machine.com/quickstart YouTube playlist of the course: http://www.youtube.com/playlist?list=PLPW8O6W-1chwyTzI3BHwBLbGQoPFxPAPM