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.

Trackback

22 comments

  1. Hi, great articles, im a computer science student interested in mcu programing, and its hard for me to undestand concepts by reading datawsheets only. Keep going 🙂

  2. spiffer00 @ 2011-03-28 04:04

    Your articles are quite instructive. I like your approach to explaining these ideas.

    I have a question: Say I have a pointer pointing to an external memory address and I want to access another address relative to the pointer using the pointer. How do I go about it?

  3. Hi there, thanks!

    OK — so let’s say you have:

    volatile uint8_t *ptr = 0x1234;

    To access an address relative to ptr, you can use the + or – operator directly on it, adding or subtracting the number of “units” you want to jump. What I mean by units is the size of whatever the pointer points to. So in this case the unit is 1 (a uint8_t is 1 byte), and if you do:

    *(ptr + 2) = 5;

    You would be writing 5 to the address 0x1236. I’ll give you one more concrete example with a 32-bit integer to make sure you understand what I mean about the size of what the pointer points to:

    volatile uint32_t *ptr = 0x12341234;
    *(ptr + 1) = 7;

    This statement stored 7 into the 4-byte unsigned integer beginning at 0x12341238 (we only added 1, but since a uint32_t is 4 bytes, we skip ahead 4 bytes for each number we add). I hope this helps!

  4. spiffer00 @ 2011-03-28 12:29

    I’ve got it. Thanks a lot.

  5. spiffer00 @ 2011-03-30 07:14

    From the question I asked above you could probably tell I’m not experienced with C programming. I’m trying to migrate to C from assembly language programming.

    One thing I’ve always wondered is how the compiler can tell which ports and pins to output to when you use an IO function like printf? Say I’m using a dot matrix LCD to output messages. How does it know whether I’ve connected the LCD to Port 0 or another port and which port pins are connected to which data or control pin on the LCD? Again, how does the compiler know things like the specs of the LCD like whether it’s 20 x 4 character or 20 x 2 character for LCD initialisation purposes?

    Or will I have to interface an LCD as manually as we do it in assembly?

  6. Hi again spiffer00,

    I did, but luckily since you’re already familiar with assembly, it should be pretty quick for you to pick up on C. Congrats on deciding to move to C–I’m sure you will like it!

    As far as printf goes, it’s not really the compiler that is in charge of that–it’s the C library that provides all the standard C functions. How that works is going to depend on which C library you use. However, different compilers have their own bundled C libraries, so it’s not entirely wrong to say it will depend on which compiler you use.

    In general, a C library isn’t going to know how you’ve hooked up your LCD to the port pins. It doesn’t know the size of your display or whether you’re using 4- or 8-bit mode with it. (For anyone joining in from Google or wherever else, search for HD44780 to see the LCD controller we’re talking about.) You will have to write your own (or use someone else’s) driver for sending commands to the LCD. So you got it correct at the end of your comment — you’ll need to do the initialization manually.

    However, as far as writing text to the display, you may be able to get printf to work depending on the C library. In some C libraries, printf may call another function that you implement each time it needs to write a character out. You could write this function to use your LCD driver to write that character to your display. You’d probably still have to manually set the cursor location before calling printf, though.

    It would probably be easier to just use sprintf, though. Make a buffer big enough for the width of your display + 1 (to ensure there is room for a null terminator):

    char buf[21];

    Now use sprintf (actually, you should use snprintf to avoid buffer overflows) to do what printf would do, but store it in the buffer:

    snprintf(buf, 21, "Hello %d", blah);

    Now just use your driver to write it to the display at a certain position (first two parameters are row and column, and the driver determines the string length based on the null terminator):

    LCD_WriteString(0, 0, buf);

    Just a few thoughts…you may want to make sure I used snprintf correctly. It’s an evil function that is known to behave differently depending on the implementation, but it’s usually safer than sprintf. In particular, some implementations of it (Microsoft’s?) are not guaranteed to null-terminate the string when it’s too long. You could try it out with various string lengths to be sure.

    Wow, this probably could have been its own blog post…haha. Hope this helps!

  7. spiffer00 @ 2011-03-31 12:50

    Thanks. I get the general idea. About the statement:

    snprintf(buf, 21, “Hello %d”, blah);

    shouldn’t it be “Hello %s” if you’re printing strings? Another question: What would happen if instead I just used:

    char buf[21] = “Hello”;

    then called

    LCD_WriteString(0,0,buf); ?

  8. Hi spiffer00,

    I was actually assuming that blah was an int variable. I should have clarified that. You’re correct that %s is for strings (in that case blah would need to be a char * or char array).

    You could definitely do what you asked in your second question. You could also do:

    LCD_WriteString(0,0,"Hello");

    without defining a char array at all. If you did decide to define a char array anyway, you would only need a 6-byte array to hold the string “Hello” plus the null terminator, so you could define it as:

    char buf[6] = "Hello";

    In fact, the compiler knows the size, so you could let it decide on its own:

    char buf[] = "Hello";

    In that case,

    sizeof(buf)

    will evaluate to 6.

  9. spiffer00 @ 2011-03-31 15:51

    Okay, but if you do

    LCD_WriteString(0,0,”Hello”);

    I imagine you would still need to instantiate an array in the definition of LCD_WriteString to hold the third parameter then send it’s elements (which should be ASCII values) one by one to the port connected to the LCD. Am I on the right track?

  10. Actually, when you use a string constant in C (I’m referring to gcc here, other compilers will be similar), it stores it in a section called .rodata and provides you with a pointer to it. Here’s a basic implementation of LCD_WriteString (you’re definitely on the right track as far as sending the ASCII values one by one):

    void LCD_WriteString(int row, int col, const char *string)
    {
        LCD_SetPos(row, col);
    
        while (*string)
        {
            LCD_WriteChar(*string);
            string++;
        }
    }

    So rather than having to allocate your own buffer, the parameter “string” will be filled with a pointer to the string constant (which is stored in the .rodata section in flash), and you can refer to it via the pointer. The ++ operator moves the pointer on to the next character in the string. The loop continues until the pointer is pointing at the null terminator (a C expression is true if nonzero, false if zero, and the null terminator is ASCII value 0). If you’re planning on modifying the string, then you would want to copy the string to your own buffer first, though, using something like strcpy()–then the copy will be stored in RAM so you can modify it.

  11. spiffer00 @ 2011-04-01 03:16

    Thanks. I had been wondering how you store values in non-volatile memory with C. Guess I need to spend some time with a book on the language.

  12. You’re welcome! You should also look into using the “const” keyword on a normal variable:
    const int blah = 5;
    This kind of variable will also be stored in flash. Anyway, I agree–you should just grab a book and a compiler and play around with C, even if it’s not in an embedded program. With assembly I’m sure you’re used to being able to specify exactly where in memory/flash/etc everything belongs, and with C it’s pretty much handled automatically–you just have to get familiar with how it does it. It can also be handy to write something simple in C and disassemble the binary just to see how it’s implemented underneath.

  13. spiffer00 @ 2011-04-02 06:03

    That’s helpful.

    The other thing I find tricky with high level programming is taking control from one point in the program to any other point you wish. With assembly I was happy with the idea that at any point I could just say for example:

    “jp ProgramPoint1”

    i.e jump to the label ProgramPoint1. That made life easy but I don’t think you have that flexiblity with a language like C or Java.

  14. Yep, this is mostly just the paradigm shift of getting used to structured programming. Everything you do with jumps can also be done with if, while, etc., but sometimes it looks quite a bit different and it’s tough changing your mindset over from one way to another. I actually have the opposite problem: I have a hard time when I have to code in assembly because I wish I could just use if/else if/else blocks instead of having to create them with jumps. Especially when the jump I’m using has to be within +127 or -128 bytes of the current instruction (ahhh, gotta love 68HC11 assembly).

    It actually turns out that C has a goto instruction along with labels. It will let you jump to another place in the same function (but only in the same function). So you can actually do that in C, but it’s generally considered bad form (except in certain cases–see the link below). When I learned C, my instructor actually told us to never use the goto keyword. In fact, one of the questions on a midterm exam was to list the C keywords, and he said if we listed goto, we’d get 0 points for that question. One of my friends wrote “goto (just kidding)” on his exam, and the instructor wrote “F (just kidding)” for his grade! LOL.

    It’s a little extreme to say it should never be used, but I can also see where my instructor was coming from because it’s easy to end up writing a spaghetti code mess. Here’s a great link demonstrating legitimate uses of goto in C. It also has links to other useful discussions. The comments are also pretty interesting to read. As you can see, it’s a controversial topic. I personally avoid it unless I’m doing error handling like he discussed in the link.

  15. spiffer00 @ 2011-04-04 08:27

    That makes it challenging to shift from assembly to C but I hope I’ll pull through. The other issue is debugging. In assembly when things aren’t going as you expect you can step through instruction by instruction looking at values changing on the ports, registers and external memory to see if the expected values are arrived at. This makes it easy to isolate a problem. But with C (I stand to be corrected) the compiler is handling where it wants to store variables so debuggung should be more demanding. Or isn’t it?

  16. True, the compiler is handling where it wants to store variables, but it knows where it’s storing each one of them. It passes that information to the debugger (if you do a debug build), and you can do the same kind of thing that you already do while debugging assembly programs. You tell the debugger the name of the variable you want to look at, and it will look at the debug information, figure out where in memory it should be looking, and give you the result. I wouldn’t really say it’s more demanding. You can even tell it to step line-by-line through C instructions (or instruction at a time through the generated assembly, if you wish). Pretty much every debugger will allow you to step over a function call (so it will execute an entire function that you call, and then pause the program again once it’s finished) or step out of a function as well. Another advantage of C debugging is that it is extremely easy to work with variables you have on the stack (i.e. variables local to a function that don’t fit into your registers, which you have to store on the stack). You can refer to them by name and it knows exactly where they are in relation to the stack pointer.

    The only real drawback I can think of off the top of my head is that when you compile with optimization, sometimes stepping through the C code doesn’t work too well, at least in the case of GCC and GDB. Other than that, there are plenty of useful ways to debug C apps. You can also specify a memory address by hand if you want (useful for the registers, as you said earlier).

    I can give you more info on how C debugging works, at least from the perspective of GCC. When you compile a program, GCC doesn’t generate a binary file. Instead, it generates an ELF file which can contain many different sections, not just the code and data that will be flashed. You can generate a BIN file from the ELF file, but you will need that ELF file later. If you tell GCC to generate debugging information, it will stick that debugging information into the ELF file. Although you only need the raw binary or hex file (which won’t contain any debugging information) to flash your program to the microcontroller, you won’t be able to debug it unless you have the ELF file containing the debugging information. The debugger reads the debugging sections in the ELF file and uses it to determine memory locations of all your variables.

  17. spiffer00 @ 2011-04-11 15:21

    That’s enlightening.

    I have another question that’s troubling me: When you want to use library functions in stdio you include stdio.h in your code. Now the thing is when you inspect stdio.h it only has declarations of the prototypes for functions such as printf. Where are the functions actually implemented?

  18. Very good observation. It turns out that the header files only inform the compiler that those functions exist somewhere — until you do the final linking, it doesn’t yet know where they are. The functions themselves are already compiled into binary form inside the C library. Depending on the compiler vendor, the source code to the C library functions may be available somewhere, or they may be completely closed-source (with only the compiled versions available to you). Usually, when you do the final linking of your program, the linker looks not only at all of your compiled code, but it also looks at the C library to find if any functions you used are in it. It grabs whatever functions it needs from the C library and sticks them into your final binary.

    A related side note: if you are compiling a project and forget to tell the compiler to compile and link one of your C files (or if you explicitly tell it to not compile one of your C files), you will get errors at link time similar to “undefined reference to my_func”. That would mean that the linker looked through everything you compiled along with the C library, but couldn’t find a function called “my_func”. Whichever file that referred to my_func was made aware of its existence by the corresponding .h file, but since the .c file was not compiled, the linker couldn’t find it.

  19. spiffer00 @ 2011-04-12 14:40

    Thanks. That makes sense. The reason I asked this question is the library I have has an include file called string.h that declares a function:

    size_t strlen (const char *);

    where size_t is an unsigned integer. I call the function in my code and at the linking stage I get the report that “strlen frame definition not found”. I’m puzzled. Can’t figure out where the function is supposed to be defined. I’ll keep struggling with it. Finding the problem should be worthwhile.

  20. Ah, ok. Well, strlen() is definitely a standard function that should be located in the C library, so if printf() is working, strlen() should as well. Beyond that, you might want to try making an extremely simple program that does nothing other than strlen() to see if you can get that to link, and otherwise, it might be worth talking to the compiler vendor to see if they have any clues. Good luck!

  21. Muhammed @ 2012-07-13 12:16

    Excellent article , can we contact via email or skype?

  22. Hi Muhammed,

    Thanks! I’d rather stick to answering questions from people in the blog comments so the knowledge can be shared by everyone in the future. If you have some questions feel free to ask them right here!

Add your comment now