Moving Beyond Arduino Part 3

At this point you have working code, an LED that you can control and hopefully a better understanding of how to work with a microcontroller.  But there’s still a lot to cover so lets talk about the elephant in the room… your code.  First off, learning how exactly your code is doing what you want and whether it’s doing it in the best way possible (spoiler: it’s not).

Fair warning this is going to be a longer post with a lot of code.

I’m going to skip over basics like, what are variables and things like binary and hex.  I’m assuming you’ve come from the Arduino environment which should have exposed you to those things.

#define (and no it’s not a trending Twitter hashtag)

So let’s start with the first part the #define line.  What does it do?

#define LED_DIR       ((uint8_t *) 0x24)

I said before you could think of a #define like a variable, like int a = 5.  That’s not really true but for the last example it was close enough.  Really it’s a macro and it’s more like a placeholder than a variable.  For example:

//Define NUM1
#define NUM1 5 

//Define variable a
int a = 0;

int main(void) {
  a = NUM1 + 2;  //a will equal 7
  a = 5 + 2; //same as the previous line but we wrote out 5 here
}

You might look at this and say, “OK I see the use there but why use a #define and not just a variable?”.  A valid question, and you might ask “Also that looks a lot like a variable to me”.  Another valid point.  Lets look at some more examples where they start to diverge.

#define NUM1 5 + 2

//Define variable a
int a = 0;

int main(void) {
 a = NUM1 + 2; //a will equal 9 (same as 5 + 2 + 2)
 a = NUM1*2; //a will equal... 9?  Why?
 NUM1 = 4; //This will give an error.
}

First look at our define statement where we say 5+2, if you think of NUM1 as a normal variable then you’ll think that NUM1 = 7, but that’s where things start to diverge.  NUM1 always represents what you put next to it; so it represents 5+2.  The first line of code inside the main function seems normal enough but the second line seems off.  If NUM1 equals 7 wouldn’t a = 14?  No, because NUM1 doesn’t represent 7 it represents 5 + 2, so wherever you put a NUM1 it replaces that value with 5+2.  So really what the compiler sees is a = 5 + 2 * 2.  So 2*2 = 4, then plus 5 you get 9.

This is where things can get tricky and confusing.  It’s why you’ll usually always see define values with () around them like the following code.

#define NUM1 (5 + 2)

Now it would be a = (5 + 2)*2, which is 14.

Hopefully you’ll see why now NUM1 = 4 is also not valid.  It’s not a variable, it’s just a placeholder that’s more like a constant.

So that’s all well and good but why use them?  Well for one thing it’s easier to write small pieces of code that couldn’t be variables which we’ll see later on.  Sometimes it’s just preference as whether you want something as a variable or as a #define.

While we’re talking about defines I’ll show another example that show a use case that can be handy for simple pieces of code.

//The following code almost acts like a function
#define NUM1(x) (x + 2) 

int a = 0;

int main(void) {
  a = NUM1(3); //As you guess this equals 5 but remember this isn't a function
              //This is a placeholder so it's not "returning" 5 but putting
              //the following in it's place: (3 + 2).
}

Pointers

Now lets talk easily the most important part of this lesson today.  Pointers.

To sum up a pointer in the most basic terms, it’s used to “point” to an address.  To get a basic understanding I suggest you use a C compiler on your computer to test out code.  There are ones available via your web browser so you don’t even have to download anything.  I’m using the following (https://www.tutorialspoint.com/compile_c_online.php)

Now here’s the standard use of a pointer:

int *p; //This is my pointer declaration
int a = 10; //This is a variable

int main(void) {
 p = &a;      //This says that p should point the address of a
 int b = *p;  //b will equal 10
 b = p;       //b will equal the address of a
 *p = 12;     //This changes the value of a to be 12
}

Pointers are where everyone always gets confused but so long as you understand the fundamentals you’ll be fine.  Syntax can always be looked up remind you how to set things up.  So let’s tackle what’s going on here.

We define pointers using the ‘*’ key.  That says this variable will be a pointer and a pointers value is supposed to be a memory address.  We can assign them manually like we did in the last section (I’ll go into more detail later) or define a variable and point to it using the ‘&’.  The ‘&’ symbol means you want the address of that variable.

Remember everything is stored in memory.  So when you write a=10.  What you’re really saying is go the memory address of ‘a’ and replace it’s value with 10 (0x0A in hex).  So to assign the address of a pointer you simply put the following:

pointer = memory address;

Again we could define the memory address ourselves (0x03) or use the ‘&’ symbol (&someVariable).  To get the value that p is pointing to, use *p.  So if we were using some basic c compiler on your computer or on your web browser, we can test some code.

#include <stdio.h>

int *p;
int a = 10;

//DON"T WORRY ABOUT PRINTF

int main()
{
 p = &a;
 printf("%d\n", &a); //Will print address of a
 printf("%d\n", p);  //Will print the value of p (which is the address of a)
 printf("%d\n", a);  //Will print the value of a, 10
 printf("%d\n", *p); //Will print the value of whatever we're pointing to
                   //in this case 'a', 10.  This is the actual value of 'a'
                   //If you change 'a' the value of *p will change as well
 a = 5;
 printf("%d\n", *p); //Will print 5 now
 
 return 0;
}

You should see a large value when you print ‘p’ or ‘&a’ (that’s the address) but then when you print ‘*p’ or just ‘a’ you get the variable.

So what are these used for?  They provide you the ability to access exact memory addresses which is key for coding a MCU.  Let’s look our previous example from the last section:

#define LED_DIR       ((uint8_t *) 0x24)
#define LED_DATA      ((uint8_t *) 0x25)

int main(void) {
   *LED_DIR = 0x20;
   *LED_DATA = 0x20;
}

The first line we use is our define, to make a pointer that points to 0x24.  It may look different but look at it like this:

int * p; //NOTE: int *p and int * p (with the added space) are the same
((int *) p) //The exact same as the above
((uint8_t *) 0x24) //Now with a static address and uint8_t rather than int

You can see they look similar and in reality what we’re defining is a pointer but rather than one that can change value it’s static at 0x24.  That’s the power of #define.  Sure we can also do int *LED_DIR = 0x24; but I’ll show you something you can’t do with a standard variable.

#define LED_DIR (* ((uint8_t *) 0x24))

What does this do?  Now rather than have *LED_DIR = 0x20 you can just write LED_DIR = 0x20.  Is there a difference as far as end result?  No, but it’s simpler to write and cleaner.  That’s an important lesson in coding that there a lot of different ways of getting the same result.  What matters is it clean/easy to read, easy to update and is it a method you’re comfortable with.

So given what I just wrote we can simply the code to the following:

#define LED_DIR    (*((uint8_t *) 0x24))
#define LED_DATA   (*((uint8_t *) 0x25))

int main(void) {
   LED_DIR = 0x20;
   LED_DATA = 0x20;
}

Not a major change but one that’ll make your coding a little easier to type out and neater to read.  Of course this begs the question, are we currently writing out our code in way that’s easy to modify or change later than?  The short answer is no.  Right now you have to memorize, or always look up, what your register does every time you come back to this code.  For LED_DIR and LED_DATA it’s easy since it’s just defining direction and data.  You could simply add some comments that say each bit represents a pin on PortB.

You might notice with that last sentence that there’s a problem.  I’ve defined DIR and DATA for only PortB.  Really there DIR and DATA registers for each port, so I need to specify that it’s PortB and name out the rest of the ports.

To add some more information on the #define macro you could rename LED_DIR to PORTB_DIR so you can have different names for various ports.  However, it’s not dynamic.  For example, PORTA and PORTB share the same register types(DIR, DATA, etc) but with different offsets.  So wouldn’t it be nice to just declare them once and just define register offsets?

Also, as registers get more complex they’ll have multiple uses.  Just to keep it simple you could have a register that has bits 0 to 3 as DIR and bits 4 to 7 as DATA.  Unlikely, but you get the idea.  If you don’t remember that register memory allocation you have to look it up all the time.  It’s much easier to put in the work in the beginning so you save a headache later; which is what we will do now using two things Structs and Unions.

Struct

A struct is a way of grouping together information.  Here is an example:

//This defines what the struct will be
struct Shapes {
 int sides;      //An int called sides
 char name[100]; //A char array (aka a string) for the shape name
};

int main(void) {
 struct Shapes shape; //Declares our struct here 
 shape.sides = 3;  //Define the variable sides for this shape
 shape.name = "triangle"; 

 struct Shapes randomName; //Can have multiple Shapes with different values
 randomName.sides = 4;
 randomName.name = "square";
}

You can think of Shapes as a type of variable with different parts.  First you define the struct name and its parts.  Then declare a type of struct almost like any other variable but state it’s a struct prior.  From there you use ‘.’ to get to it’s local variables and assign them values.

Seems simple enough but where does this come into play for us?  We’re going to use a different type of struct called a typedef struct.  That’s to just simplify the use case.

A typedef is used to define a new type of variable, like an int or char.  This along with a struct can be used the following way.

typedef struct {
 volatile  uint8_t DIR;
 volatile  uint8_t DATA;
} PORT_REG;

#define PORTB_REG ((PORT_REG *) 0x24)

Don’t run away, this code is surprisingly easy to understand once you take the time to look at it.  First we’re defining a struct but not just a regular struct, a typedef struct.  You’ll notice there’s no name, that’s because we don’t plan on declaring new structs of these types in the future.

So rather than do what we did before (define a new struct then declare a new one with the following code “struct Shapes shape;”) we define and declare it at the same time.  That’s what that last line in the typedef struct is (PORT_REG).  It’s defining a new struct for us.

Then using our #define and pointer we can take that struct and point it’s first variable to the address 0x24.  Which, coincidentally, is the offset of the DIR register for PortB.  Here’s where this struct really shines.  Since we defined DIR as only 8 bits long, DATA will be the next value over in memory, aka 0x25.  If we made DIR a uint16_t (16 bits long rather than 8) then we would have had DIR start at address 0x24 but also take up the address of 0x25.  Then DATA would have been 0x26.

By writing out our PORT_REG struct properly we’ve made it easy to define specific port register by simply pointing it to the lowest address value and properly sizing each register.  Now if we wanted to do the same thing for PortA (assuming it was the same as PortB in that DIR was first, then DATA, etc) then we could just use:

#define PORTA_REG ((PORT_REG *) 0x2...)

Before moving on to unions, I bet you’re also asking what is volatile?  It lets the compiler know that this register value can change from an outside source.  Why use it?  Here’s a basic example:

int a = 1;

while(a==1) {
}

From a compiler point of view this will loop forever so it’ll replace this check of a==1 with just 1.  But what if ‘a’ is changed by the press of a button outside the MCU?  How would the compiler know that?  If you label it as volatile you’re telling the compiler, “Hey this value can change at anytime so don’t assume you know it’s value”.

The final code would be “volatile int a = 1;”

Unions (minus the giant inflatable rats)

With a little leg work we got more dynamic and easy to work with code.  This doesn’t solve the problem of what happens when a register has bits with different uses.  That’s where we get into unions.  Easiest way to get started is look at our use case:

typedef union { 
 struct { 
     uint8_t PMUXEN:  1; 
     uint8_t INEN:    1; 
     uint8_t PULLEN:  1; 
     uint8_t:         3; 
     uint8_t DVRSTR:  1; 
     uint8_t:         1; 
   }bits; 
   uint8_t reg;
} PORT_CFG;

What is all of this?  First things to make life easier think of a union as a struct but it’s memory footprint is only as big as it’s largest value.  I.e. a struct with 32 different 8bit values in it will bit 8*32 bits long, but a union with 32 different 8bit values will be 8bits long.

Another example is a union with an 8bit variable in it and a 16bit variable in it.  If that was a struct it would be 24bits long but in a union its 16.

So what does this buy us in our case?  It’s easier to show you.

PORT_CFG.bits.PULLEN = 1;
PORT_CFG.reg = 0x00;

Now we can select individual bits and assign them values without affecting the rest of the register.  Which we could do before but you would have to use techniques that aren’t hard to do (OR’ing/XOR’ing values) but isn’t as easy to read.  Now it’s clear as day we’re taking the PULLEN bit of PORT_CFG register and setting it equal to 1.

We also don’t lose the ability to set the value of register in one fell swoop as you can see with the ‘.reg’.  That’s the advantage of the union, and is something we couldn’t do with a struct.

Now that we understand what it does for us, let’s go over the code.  First we define a new typedef union (much like a struct) and then define one of it’s variables to be a struct itself.  We define them as uint8_t variables but they aren’t actually.  Their sum total makes up 8 bits but we define their length by using the “:  1” after their name is declared.

uint8_t PMUXEN: 1;  //This says it will make up 1 bit of a final 8 bit value
uint8_t :       3;  //This says it will make up 3 bits of a final 8 bit value

You’ll notice a few values of that internal union struct don’t have names.  That’s because those bits are reserved and cannot be changed.  By not having a name they become inaccessible but still take up the proper space.

At the end of the struct we define a name “bits” so we know those are the bits and then declare a new 8 bit variable called “reg” to access the entire register all at once if we wanted to.  Now lets put together this union stuff with our structs and turn on our LED.

#define _REG volatile  //So we don't have to write out volatile every time

typedef union {
  struct{
    uint8_t bit0: 1;
    uint8_t bit1: 1;
    uint8_t bit2: 1;
    uint8_t bit3: 1;
    uint8_t bit4: 1;
    uint8_t bit5: 1;
    uint8_t bit6: 1;
    uint8_t bit7: 1;
  } bits;
  uint8_t reg;
} STD_PORT_8;

typedef struct {
  _REG STD_PORT_8 DIR;    //1: OUTPUT, 0: INPUT
  _REG STD_PORT_8 DATA;   //1: HIGH, 0: LOW
} PORT_REG;

#define PORTB_REG ((PORT_REG *) 0x24)

int main(void) {
  //The -> is used to access a part of the struct pointer
  PORTB_REG->DIR.bits.bit5 = 1;
  PORTB_REG->DATA.bits.bit5 = 1;
}

Well that’s a ton more code for doing something we’ve already done before!  That is true but now we have a code we can use easier.  As you move on from just turning on an LED to using things like i2c, spi, uart, pwm, etc. things can get really complicated fast if you don’t organize your code properly.

Typedef

Now let’s get into why typedef is so helpful by using a function.  So let’s just go over one of my use cases for now.

void gpio_toggle(PORT_REG * port, int pin) { 
  port->OUTTGL |= PIN(pin);
}

This function takes in two variables,  our typedef struct PORT_REG and a pin.  NOTE: The PIN(pin) is just a #define that will provide the proper pin for this function, don’t worry about the details.

Here we have piece of code that will toggle the output of a GPIO.  The best part about this is it’s dynamic to any port (PortA, PortB, etc).  To use it just simply type in the following:

#define PORTA_REG ((PORT_REG *) 0x20)  
#define PORTB_REG ((PORT_REG *) 0x24) 

//Simplified to assume that the gpio_toggle function is already delcared
//and that the DIR is already an output
int main(void) {
  gpio_toggle(PORTA_REG, 3);  //Will toggle pin 3 of port a
  gpio_toggle(PORTB_REG, 7);  //Will toggle pin 7 of port b
}

Even if you forget how to toggle a pin all you have to do is know how to properly use this function (which is easy) and you’re all set.  A simple example but as things get more complex you’ll see the advantages of doing things like this.

Let’s look at another example that’s a bit more complicated:

void uart_initialize(SERCOM comm, SERCOM_REG uart) { 
 SERCOM_REG * reg; 
 reg = (SERCOMBASE_OFFSET + (0x0400 * comm)); 
 *reg = uart;
}

This function takes in two variables, the one we care about is SERCOM_REG.  Because I made it a typedef I can use it like any other variable and pass it into this function easily.  This allows me to set up UART parameters first then pass it to the proper UART register without having to look up every comm port address.  Plus it lets me put error detection in it.

void uart_initialize(SERCOM comm, SERCOM_REG uart) { 
 if(comm > 5) { 
   return;  //ERROR COMM CANNOT BE LARGER THAN 5!!
 } 
 SERCOM_REG * reg; 
 reg = (SERCOMBASE_OFFSET + (0x0400 * comm)); 
 *reg = uart;
}

Again not complex examples but they do provide you some insight on how they can be used.

Wrap Up

That was a long post…. Hopefully you got an understanding of what your previous code was doing and how we’ve improved upon it.  While it may seem like we went from a pretty straight forward coding solution in the last section to a more convoluted one, remember that doing things the quick way can lead to headaches down the line.  That said also keep in mind that just because you can use a car to drive from your house to your neighbors doesn’t mean you should.  Why go through the effort of finding the keys, starting the car, finding parking, maybe having to move it later, etc. when you can just walk?

This was more of an example of how you can access register and set things up better but it’s not the best for everything you code.

Get a feel for your project and code to a format you think makes sense.  Sometimes doing the simple solution is the way to go.  Like I said before, at the end of the day there are a lot of different ways to get to the same solution.  While it’s great that my code is easier to understand, and build upon, if it’s only job will always be to power on an LED.  Is it worth typing out 30 lines of code when 4 will do just fine?

Where that becomes a problem is when a project starts with just turning on the LED, but soon they want to toggle it.  Then they want to control it via UART, and have a status of whether it’s on or off.  Maybe they say they want an interrupt that toggles it if it’s been on for too long.  Projects tend to get more complex not simpler, so a bit of planning and foresight goes a long way.

I keep saying the next section will be Assembly but this time I mean it.  It’ll be an overview of what Assembly is and how to use it for debugging.  The section after that will likely be organizing your code into separate files for reuse and to keep it all nice and neat.

Thanks for reading and I’ll see you next time.

-Ozzie