I couldn’t resist jumping into my “microcontroller programming for high level programmers” series as soon as possible, so I’d like to go into a bit more detail about where I left off in my last post–memory-mapped peripherals.
Like I said in my last post, I missed out on memory-mapped peripherals during my CS education (which is not too surprising). My mental model for how memory addresses work was missing a chunk of knowledge. I thought that every memory address was a storage location–either RAM (readable and writable) or ROM (only readable). It turns out that memory addresses in computers can also be used for other stuff, like telling an external chip or a built-in peripheral to do something.
A memory-mapped peripheral works exactly how I just described. The peripheral reserves a portion of the address space on the computer. You write to (or read from) somewhere inside that address range, and the peripheral does something in response. Let me give you a simple example:
Let’s pretend that a microcontroller has an “LED” peripheral. By LED I mean a light-emitting diode–a little green or red or whatever other color light. Get used to the acronym “LED” because it’s very common. An example of an LED is the power light on your computer. Anyway, back to the pretend world. Imagine that the physical microcontroller chip has eight pins you can hook up to LEDs to light up, and your program can control them. The “LED” peripheral is mapped to memory location 0x1234, and it’s one byte long. Each of the eight bits in the byte controls one of the LEDs. If a bit is one, its corresponding LED will be turned on, and if the bit is zero, its corresponding LED will turn off.
In a C program, you would then turn the LEDs on and off by changing the value at address 0x1234. I’m going to assume you understand pointers. I’m creating a pointer so that I can manipulate the LED peripheral. Remember that uint8_t is usually typedef’d as unsigned char–an 8-bit integer that has no sign bit, so you can mess with all eight bits without worrying about side effects. Here’s some sample code:
#include <stdint.h> /* Get a pointer to the LED peripheral */ /* Note that you cannot do this in Linux in a user program */ /* because each user program has its own virtual address space */ /* and other sections of memory are protected from direct access */ volatile uint8_t *LED = (volatile uint8_t *)0x1234; /* Turn all LEDs off */ *LED = 0; /* Wait for 1 second -- just pretend this is already implemented */ wait_milliseconds(1000); /* Turn every other LED on -- 0x55 == 01010101 in binary */ *LED = 0x55; wait_milliseconds(1000); /* Turn on the LED associated with bit 7 -- 0x80 == 10000000 binary */ *LED |= 0x80; wait_milliseconds(1000); /* Turn off the LED associated with bit 0 -- 0x01 == 00000001 binary */ *LED &= ~0x01;
Ok–I hope that wasn’t too much to start out with, but I’ll try to explain in detail what I did.
First, I created a pointer that points to the LED peripheral’s address (0x1234). I said earlier that the peripheral is one byte long. That’s why I picked a uint8_t as the type — because a uint8_t is 8 bits, or 1 byte, in size.
You may have noticed that I defined the pointer as volatile. Why did I do that? I’ll tell you as soon as I finish explaining the rest of the code. It will be easier to describe what it does as soon as you understand exactly what the code does.
After that, I write a zero to the LED peripheral. Remember that since the variable “LED” is a pointer, I have to use the * operator to tell it to write to the address to which LED is pointing. Since every bit of a zero byte is zero, this turns off all the LEDs (if any were on already). Next, I call an imaginary function to wait for 1000 milliseconds, or in other words, one second.
Then, I write 0x55 to the LED peripheral, turning on the LEDs associated with bits 0, 2, 4, and 6. Of course, I wait a second here too.
For the last few operations, I turn on the LED associated with bit 7 (without affecting the other LEDs), wait another second, and then turn off the LED associated with bit 0 (again without affecting the other LEDs). If you haven’t seen the bitwise operations |, &, and ~ used much in C, you should definitely learn them before venturing onward. They will be used endlessly to manipulate individual bits in memory-mapped peripherals. Sometimes you want to turn on a single bit at an address while leaving all the rest of the bits unchanged. The | operator is perfect for this. Likewise, the & operator (combined with ~ for a bitwise NOT) works great to turn off a single bit without affecting the other bits. Study those two lines in my example code carefully until you understand exactly what they are doing.
If you were to run a program like this on a real microcontroller that had a real “LED” peripheral, you would physically see the LEDs changing each time the corresponding code wrote to the LED memory-mapped peripheral.
Back to the volatile keyword for a minute. Declaring a pointer as volatile forces the C compiler to always access the memory address a pointer points to, no matter what, with no optimizations allowed. You’ll notice that I write several different values to the LED peripheral in this program. A good compiler might notice that I first write 0 to it, then write 0x55, then turn on bit 7 (effectively writing 0xD5) and then turn off bit 0 (effectively writing 0xD4). It might think “hey, why should I do all these intermediate operations? I know that it will effectively end up with the value 0xD4, so why not just write that in directly?” For normal variables in a program, this could very well be a great idea to make your program run faster, because it would save unnecessary arithmetic operations from needing to be performed. For memory-mapped peripherals, it’s not such a great idea, because those intermediate values being written to the peripheral actually have a meaning. The volatile keyword simply makes sure the compiler does not make any optimization decisions like that. Whenever you’re accessing a memory-mapped peripheral, a pointer to it should be defined as volatile.
I will finish off this first real post of the series with a reality check. In my experience, there’s not really such thing as an “LED” peripheral. Instead, what you end up using is a “GPIO” peripheral. GPIO stands for General Purpose Input/Output. A microcontroller might have several “ports” — port A, B, C and D, for example, each eight bits. The physical chip would have pins corresponding to each port. Port A pin 0, port A pin 1, and so on. There would also be corresponding memory-mapped peripherals for each port. So if you had LEDs hooked up to port A pins 0 through 7, you could then do exactly what I showed in my “LED” example, except you would write to the GPIO port A peripheral, which would be memory mapped similar to how I described before.
It’s a little more complicated than that because I only really described the “output” capabilities of GPIO ports. You can also configure GPIO pins as inputs, so they read data from an external source and your program can see whether they are “high” or “low”. I hope that makes sense, and I’ll go into more detail about GPIO in the future. The memory space of a GPIO peripheral is more than just a byte wide. There are other sections of its memory space for configuring things like whether a pin is input or output, and also pull-up/pull-down resistors for inputs. In general, you will see these various sections of the memory-mapped address space referred to as the peripheral’s “registers.”
Anyway, that’s all for this first post. If you happen upon this post and have any questions, feel free to ask in the comments and I will try my best to answer them.






