Embedded Software Engineering 101: Microcontroller Basics
We’ll start our Embedded Software Engineering 101 journey with the humble microcontroller. The microcontroller (or microprocessor) is the fundamental building block of all computer systems, embedded or otherwise.
A microcontroller seems quite complicated, but it’s made up of three simple things: instructions, registers, and memory. Instructions are the things a microcontroller knows how to do. A simple microcontroller doesn’t know how to do much - it might only have 20 or 30 instructions. I’ll use the MSP430 microcontroller made by Texas Instruments later in this series, and it has only 27 instructions.
These 27 instructions are the only things the MSP430 microcontroller knows how to do. It can add two numbers, subtract two numbers, move numbers around, and 24 other simple operations. 27 different operations might not sound like enough to do anything useful, but it’s more than enough to run any conceivable program.
Okay, so a microcontroller has instructions that do something with numbers. But where are the numbers? Registers and memory! Instructions operate on numbers that are stored in registers and memory.
Registers are very fast storage that holds the numbers that instructions operate on. One way to think of it is that a register is a scratchpad for instructions to use. A microcontroller has a small number of registers, typically 8-32. The MSP430 has 16 registers, for example.
Memory is also storage for numbers, but memory is much more plentiful and slower than registers. A microcontroller might have 64KB, 256KB, or more than 1MB of memory. One MSP430 microcontroller (the MSP430F5529) has about 128KB of memory; that’s more than 8000 times the number of registers it has!
Before we get started with some examples, I encourage you to get out a sheet of paper and a pen or pencil and work them out as you read them. Working them out on paper is harder than just reading what I’ve written, and so you will pay more attention to what you’re doing and you stand a better chance of remembering what you learn.
Let’s look at an example of a fictional but representative microcontroller.
Let’s say our microcontroller has 4 registers and 8 memory locations. Registers are usually named creative things like “R0,” “R1,” and so on, and so we’ll do the same. Memory locations are usually referred to by the location number, also called the memory address, starting at 0. Here’s what our registers and memory look like:
And now I’ll put some values into our registers and memory:
Now our fictional microcontroller needs some instructions. The collection of instructions a microcontroller knows is called its instruction set. Let’s say its instruction set has three instructions: ADD, SUB (short for “subtract”) and MOVE. Instructions have to get the numbers they operate on from somewhere and also put their results somewhere, so part of an instruction set tells us where the instructions’ source data and destination data are.
For instance, let’s say our ADD instruction has two sources and one destination that must all be registers. The manual might describe our ADD instruction like this:
ADD regSrc, regDst The ADD instruction adds the value in register “regSrc” to the value in register “regDst” and stores the result in register “regDst” Summary: regDst = regSrc + regDst Example: ADD R1, R2 performs this operation: R2 = R1 + R2
It’s common for an instruction to use one of its sources as a destination as well, as the ADD instruction does by using regDst as both an source and a destination.
“ADD R1, R2” is the assembly language for the microcontroller: it is the microcontroller’s native programming language.
Let’s define SUB in the same fashion:
SUB regSrc, regDst The SUB instruction subtracts the value in register “regSrc” from the value in register “regDst” and stores the result in register “regDst”. Summary: regDst = regDst - regSrc Example: SUB R3, R0 performs this operation: R0 = R0 - R3
And finally let’s say the MOVE instruction has one source and one destination, and either:
- both arguments are registers, or
- one is a register and one is a memory location.
The instruction set manual would say:
1. MOVE regSrc, regDst 2. MOVE memSrc, regDst 3. MOVE regSrc, memDst The MOVE instruction copies the data from its src argument into its dst argument. Summary: There are three types of MOVE instructions 1. regDst = regSrc 2. regDst = memSrc 3. memDst = regSrc
Example: I’ll show examples of the MOVE instruction later in this post.
One note about the word “move” used for this instruction: most instruction sets use the word “move” even though the data is really copied, not moved. Calling it “move” might suggest that the source operand of the instruction is destroyed or cleared, but the source is left alone and only the destination is modified.
Let’s run through some examples using our fictional microcontroller.
Our registers and memory look like this when we start:
Now let’s run this instruction on the microcontroller:
ADD R1, R2
This ADD instruction takes the value in R1, adds it to the value in R2, and stores the result in R2. The processor does most instructions in a single operation, but I’ll break down each ADD, SUB, and MOVE instruction execution into several steps with “⇒” leading you through the steps and (register/memory ⇒ value) substitutions:
R2 = R1 + R2 ⇒ R2 = 37 + 100 ⇒ R2 = 137
After this instruction runs memory is unchanged, but the registers now look like this, with the changed value drawn in red:
Notice that R1 is unchanged; only the destination register R2 has changed.
Let’s try a SUB instruction next:
SUB R3, R0
This SUB instruction takes the value in R3, subtracts it from the value in R0, and stores the result in R0:
R0 = R0 - R3 ⇒ R0 = 42 - 2 ⇒ R0 = 40
After this instruction runs memory is unchanged, but the registers now look like this:
And finally let’s try out a couple versions of the MOVE instruction:
MOVE R2, R0
This MOVE instruction copies the value in R2 into R0:
R0 = R2 ⇒ R0 = 137
And now the registers look like this:
Next we’ll copy a register into memory:
MOVE R3, [3]
This MOVE instruction copies the value in R3 into memory location 3. The square brackets are how our instruction set shows memory locations.
[3] = R3 ⇒ [3] = 2
The registers are unchanged, but memory changes to this:
And for our final example we’ll copy a value from memory into a register:
MOVE [6], R0
This copies the value in memory location 6 into register R0:
R0 = [6] ⇒ R0 = 1
Memory is unchanged, and the registers now look like this:
Believe it or not, if you’ve followed most of what we just discussed about instructions, registers, and memory then you understand the basics of microcontrollers and assembly language.
Of course, I’ve left out many details. For instance, how does the microcontroller get the instructions to run? Are there more interesting instructions than just simple math and copy instructions? Is memory the same thing as RAM, or flash, or something else?
We’ll answer these questions next week - see you then!
Please ask any questions in the comments and I’ll try to answer them as they come in.
This post is part of a series. Check out the complete Embedded Software Engineering 101 series here.