Welcome to the Embedded Systems Programming course. My name is Miro Samek and in this
lesson I'm going to show you how computers count.
Here and throughout this course I'm going to use the free evaluation version of the
IAR toolset that we have downloaded and installed in lesson zero.
To create your first project, lauch IAR EWARM and select "Project->Create New Project" menu.
Expand the C project types, select main, and click OK
This opens the file explorer, where you need to choose the directory for your project.
I recommend that you create the "embedded_programming" directory on your C: drive for the entire
couse, within which you will add sub-directories for each lesson.
To do this, click on the C: drive and then click on "New folder".
Next, double-click on the newly created folder and create a subdirectory "lesson1" for this
lesson.
Double-click on the lesson1 sub-directory to get inside it, and type the name of the
project. For this course, name this project generically "project".
Finally, click Save.
As you can see, the project contains a generated main.c file, which I will explain in a minute.
But first, let's check how the directories and files look on your disk.
To finish configuring yor project, you need to adjust a few parameters.
Click on the "Projects->Options" menu.
In the Target tab, you need to adjust the processor variant. Select Device and click
on the selection button. Choose Texas Instruments, LM4F family, and LM4F120H5QR device from the list.
Next, select the the C/C++ Compiler category.
Here you can see that the default language is C and that the C dialect is C99. In this
course, I will teach you this newer dialect of C.
Finally, click on the Optimizations tab and make sure that the optimization level is set
to low, which is the default. A couple of our first programs won't work correctly at
higher optimization levels, until I show you how to write code that works even if highly
optimized.
As you are running the IAR toolset for the first time, you should also spend a minute
to tailor it to your preferences. Click on Tools->Options... menu.
Here, you can change the fonts, for example I prefer my fixed-width font to be Lucida
Console.
In the Editor section, increase the indentation to 4 spaces and select spaces instead of tabs.
I strongly recommend avoiding tabs in your code, because they are rendered differently
on different devices, such as printers.
So, finally we can get to the code generated by the IAR toolset. Let's try first to validate
that this indeed is a valid C code. You do this by compiling the code, that is running
it through a program called the compiler.
In the Project menu select the Make option and note that its keyboard shortcut is F7.
You will use the F7 shortcut quite frequently.
Since it is the first time you bulid this project, the tool asks for the workspace name.
Type a generic name "workspace" and hit return.
The build completes with zero errors and zero warnings, so congratulations for your first
legal C program.
Now, let's reformat the generated code to use the "line-saver" bracing placement and
indentation of 4 spaces, which I prefer. The compiler doesn't really care about any of
this formatting and would accept just as well the code formatted as one long line. But the
formatting makes quite a big difference to humans who need to read and maintain the code.
But obviouisly not everythig you do to the code is legal C. For example, let's type something
illegal and compile again by pressing F7.
This time, the compiler reports errors. When you double click on the error report, the
toolset brings you right to the offending place in the code, which is rather cool.
After removing the problem, it's a good practice to ask the compiler to check you again by
pressing F7. I'd like to convince you from the get go that the compiler is your best
friend constantly watching over your shoulder. All you need to do is give him a chance to
help you by frequently pressing F7.
Now, let's define a counter variable, which I will use to show you how computers count.
A variable is a location in computer memory to store a value, such as a number. In C,
you have to define a variable, before you can use it. You do this by specifying the
type of the variable followed by the name and optionally the initial value.
According to my own advice, let's check ritght away if the compiler accepts the variable
definition.
Well, kind of. The are no errors, but there is one warning that the variable counter has
been declared but never referenced. That's true, so let's move on.
Now, let's increment the variable counter by one from it's current value. C has a special
operator ++ for it, called pre-increment. As usual, I let the compiler check that the
code still compiles.
As we want to watch how the computer will count, let's increment the counter several
more times.
Let's compile one last time by pressing F7.
Now I will show you how to run this program. First, make sure that the project is configured
for the Simulator. Actually, you can tell it by the presence of the Simulator menu at
the top, but to double-check, click the Project->Options menu.
In the Debugger category, you should see the Simulator selection.
You have two options to run the program. Either through the "Project->Download and Debug"
menu, or by the toolbar button, which I will use.
Now, the IAR toolset has switched to the debugger mode. Let's make sure that the following debugger
views are visible: Disassembly, Memory, Register, and Locals.
Let's re-arrange the debugger windows so we have the best view inside the simulated ARM
Cortex-M4 processor.
First, let's take a look inside the Disassembly view. This view shows the machine instructions
that the compiler generated from your program. The processor is stopped at the highlighted
instruction, which is the beginning of your main function.
The machine instructions, as anything else inside a computer are just numbers. The symbols
to the right of the instructions are so called instruction mnemonics, and are added by the
debugger to improve readability.
The column of numbers left to the instructions are the memory addresses of the instructions.
Memory addresses are simply numberes assigned to bytes in memory.
To convince you that the instructions are really just numbers in memory, let's take
a look at the Memory view. You can think of memory as a big table of bytes, which are
numbered sequentially starting from zero. These sequential numbers, called the addresses,
are shown in the column of numbers along the left edge of the memory view. To better recognize
the machine instruction in the memory, let's change the view to 2xUnits, because most of
the ARM Cortex instructions occupy 2 bytes in memory. Now, you should easily recognize
the instructions. For example, at addresse 0x40 you see the same number as in the dissassebly
view. The same is true for instructions at addresses 0x42 throgh 0x50.
All right, so let's step through the code one C line at a time. To do this click inside
the code view and click on the step-into button.
As you can see the current instruction advanced by one, and the value of the "counter" variable
changed to zero. Also, if you look carefully, the value of the PC register has changed to
0x42. PC stands for "Program Counter", because it counts the instructions and always holds
the address of the current instruction.
Now, let's click the step-into button again to execute the next instruction. This time,
the value of the variable "counter" has incremented to 1. The Locals view tells you also that
the "counter" variable is located in the R1 register. Indeed, when you check the register
view, you can see that the value of R1 is 1.
But what are these registers, anyway? Well, if you ever used a simple calculator you already
should have a good idea, becase registers in a microprocessors are very similar to the
caluclator's memory register. Typically, you can add, recall, and clear the memory register
of a calculator.
The ARM Cortex-M processor has 16 such registers, which are named R0 through R15, whereas R15
is the other name of the PC. All these registers can hold 32-bit numbers. The importance of
these registers lies in the fact that machine instructions can manipulate the registers
directly, typically in just one clock cycle. You've already seen two examples of instructions
manipulating registers: moving a zero to R1, or adding 1 to R1.
Let's keep stepping through the code and watch the "counter" variable increment. Everything
seems to work as expected, but something interesting happens when the couner reaches 10. As you
recall, the counter is located in the R1 register, but at value 10 the Locals and the Register
views seem to go out of synch, because R1 shows value A. This requires some explanation.
The Locals view shows the value of the Counter in the decimal system, which people adopted
because regular people have 10 fingers.
However, C programmers grow 16 fingers after a while on the job. Here, for example, is
the picture of my hands after programming in C for over 20 years.
Programmers find it much more natural to work in the hexadecimal system, because it maps
perfectly to the binary system underlying all computers. As you can see in this comparison
between decimal, binary, and hexadecimal, a single hexadecimal digit represents a group
of 4 bits, called a "nibble". In contrast, decimal system needs two digits for numbers
above 9. The strange looking "0x" prefix in front of every hexadecimal digit is the C-language
convetion of encoding hex numbers.
At the bottom of the picture, you can see an example of applyting the table above to
encode a string of 32-bits into hexadecimal. The bits are grouped in 4 packs of 8, which
are called bytes, each byte contains two 4-bit "nibbles". As I just explained, the nibbles
map directly to hex digits. For example, the nibble 1010 maps to the hex digit A, the nibble
0101 to hex digit 5, and so on. In the end, the whole 32-bit binary string is equivalent
to 0x260F3E5A.
So, now I hope that the debug views make much more sense to you becasue most of them show
hex numbers, as you can tell by the 0x prefix.
Now, before letting your program increment the "counter", let's cheat a bit to test what
happens when the number gets really big. That's actually very easy to do in the debugger,
because you can manually change any variable, by clicking on it and typing a new value.
Type in 0x7FFFFFFF and press return. This turns out to be a big number in decimal.
Now, let your program increment the counter by one.
Oops, something very strange happened. You counter becomes a huge negative number in
decimal, while it is 0x80 00 00 00 in the R1 register. Again, this requires some explanation.
The counter variable has been declared as an integer (int), which in C is a signed number,
meaning that it can hold both positive and negative values. It turns out that computers
represent negative numbers in a rather percular way, called the "2's complement representation".
This circular graph explains how it works. Every arrow in the diagram represents increment
by one. For small positive numbers, everything works as expected until the number fills all
the bits but the most significan one. This is the largest positive value a 32-bit signed
number can represent.
When you increment this number by one, you overflow to the most significan bit, at which
point the number becomes negative. In fact, it becomes the smallest negative number you
can represent with 32-bits. From there, when you keep incrementing, the value becomes less
negative, until you fill all the bits, at which point you reach -1. When you increment
-1, you get zero again and the cycle repeats.
So, back to our debug session, let's set the counter value to -1 and watch the content
of the R1 register. Next, let your program increment the counter one more time, and close
the cycle by making the counter zero again.
Exit the debugger by pressing the 'X' button.
As a homework I'd like you to study counting of unsigned integers. To do this, you need
to modify the type of the counter variable to unsigned int, recompile and start the debugger,
just like we did earlier in this lesson.
Finally, as the last step of this lesson, I promised to show you how to run the code
on the Launchpad board. To do this, you need to modify the project options. Click on Project->Options
menu. In the dialog box, choose the Debugger category. From the drop-down list, select
the TI-Stellaris option.
Next, click on the Download tab and check the Use Flash loader option as well as the
Verify download option. Click OK.
At this point, you need to connect the Launchpad board to your computer by means of the provided
USB cable. If this is the first time you connect the board, allow a minute or two for the installation
of the USB driver for the USB debugger. The board is pre-loaded with a demo program that
blinks the LED.
Now you can download your code to the flash memory of your board and start debugging in
the exact same way as you did on the simulator. This is rather cool, when you think about
it, and is only possible, because both the USB debugger on the board and the microcontroller
itself has a special ciruitry to provide this capability for you.
Please note that your program stays permanently programmed inside the microcontroller, so
it count but it won't blink anymore. Don't despare, though. we will make it blink and
much more in the future.
This concludes this lesson about counting. In the next lesson I'll show you how to replace
the repetitions of instructions with a loop and I'll show you all sorts of ways to change
the control flow through your code. Stay tuned and visit state-machine.com/quickstart for
the class notes and project file downloads.