Moving Beyond Arduino Part 2

Now that you see some of the advantages of using a development platform I’ll show some basic example code using the ATMEGA328PB Xplained-Mini dev board.  First, I’ll go over reading the datasheet and extracting the information that matters to you.  This is a key skill to have when starting out.

This tutorial is a lot longer than anticipated but maybe 10% is actually going over the code itself.  So don’t think that coding your board is going to be a painful process.  At the end the code we only end up writing 4 lines of code.  That’s it!

So don’t be intimidated by what you see, I’ll go through step by step.  Of course you don’t have to use the Atmega board and if you’re using another MCU, such as TI, the code I write won’t work exactly for your board but the fundamentals are the same and at the end of the day it’s still C coding.  So once you learn certain coding techniques then you can bring them into any MCU.

First things first, download whatever IDE your MCU uses.  I’ll provide a link for the Atmel Studio IDE which I’m using.  I won’t go over installation just follow the instructions it should be a painless process.

While that’s downloading figure out what chip you’re using on your dev-board and download the datasheet for it.  It should be a large pdf (typically a couple hundred pages) that goes over every detail you need to know about a chip… mostly.

After you’re all set with your IDE we can start coding.  Typically you can go to File-New and select Project.  There should be options for what board/chip you’re using, in my case it’s atmega328pb which I select and named the project Tutorial_Part2.  You can name your project whatever you want.  NOTE: If you already know your way around an IDE you can skip datasheet/coding part below.


Every IDE is set up a little differently but typically there will be a project navigator, an output/error window and a bunch of other stuff you might need.  For now let’s just go over the project navigator.  The other windows will be used a bit later.  No matter what, focus on what you need to use and not everything they provide you, otherwise you’ll get overwhelmed.  Just focus on what I’m describing for now.

Quick Definition: Function – I use this term a lot and you’ve already had experience with this in the Arduino IDE with things like setup() and loop().  Those are functions which just house code and can be called by just using typing the words loop() into your code.  You probably made functions yourself in Arduino to help keep you code neat.

Rather than describe something that’s been already talked to death in other forums/blogs.  I’ll post a link to the Arduino tutorial on functions.

Back to the IDE.

The project navigator just shows the files associated in your project and there folders.  If you’re not using an example code it should be pretty bare.  This is where you can addAtmelStudio_PN folders to separate different libraries you write (or copy) to keep things nice and neat.  The files you care about now, are files that have the extension “.c” or “.h”.  The “.h” is for a header file which defines constants, functions, etc. that can be used by it’s “.c” counter part.

For example, I can have a “led.h” that defines the pins that the LED are on and the names of functions that will be declared in “led.c”.  So I declare that the function’s name will be “led_on()” but don’t write any code to define what it does in my “led.h” file.  Then I create my “led.c” file and fill in the detail there.  This is a standard C coding format for creating separate files that house code.

A quick note before moving on.  A header file doesn’t need to have “.c” file associated with it.  You can just use a header file to declare variables or define other types as well.  Don’t worry about that now but just so you don’t go thinking every header file needs a “.c” file too.

Looking at your project you’ll likely see a main.c file, which is common but not required.  They can be named something else but it still has to house the “main(void)” function.  You can think of the main.c file as the main program code.  This is what it’ll program into your chip.

You can write a million lines of code in other files you make and it won’t do anything unless it’s referenced inside this main.c file.  If that doesn’t make sense it will by the next lecture or two.


Now for some coding. (finally)  Don’t worry we’ll go over the other windows as they come up.

For now delete all of the code inside the main(void) function and everything before it so all you have is this:

int main(void){ 


Now lets turn on an LED… but how?  You need to read the datasheet, and before you go “Well that was fun but I’m not reading 700 pages of a datasheet just to turn on an LED” you don’t have to read the entire thing.  It’s split up into sections each a few pages long that go over every segment on how the chips works.

If you’re interested, it always good to read the pages that describe the chips architecture just to know for your own knowledge.  A good datasheet will have all of the information you need to do each task in it’s own section and if it’s not there, it should reference previous sections so you know what to read.

For the Atmega328pb the section that we care about is 17 (I/O port) other datasheets might say GPIO instead but it’s the same thing in this case.

I recommend you read it, get an understanding of how to configure the output and comeAS_IO_DS back to go over coding it.  Just be aware that a lot of that section goes over register descriptions, which you need to know but can be a bit confusing so when you get to that part feel free to return here.  Or you can just not listen to me and skip reading the datasheet and move forward with the tutorial, hey it’s your life.

Ready?  Ok let’s move on.

For this simplified 8-bit MCU, all we have to do to turn on the LED is configure the pin to be an output, something you should be used to, and set the output high.  Nothing crazy here, but the hard part (at least starting out) is how?

How do I tell the CPU to do these things?  Well look at your datasheet and go to the register description.  What are registers?  They are places in memory that can either just hold generic values for your personal use or specific ones that have very specific uses.  In this case these places in memory tell each pin what it’s “job” is (to be an input or output) and what value it should be.

How do we write to them?  Simple, at least in C code, we “point” to the memory address and write to it.  We just need to know what it’s address is, which is stated in the datasheet, and use that.  Easy!

Our LED is on Port B pin 5 (that info should be in the schematic provided by Atmel).  So we look for Port B and see the Direction register’s offset is 0x24.  If you don’t know HEX then google it, it’s easy and I can’t explain everything!  In our case that’s just the address but in other MCUs the offset might have a base address.  So the base address might be 0x10 but then we have an offset of 0x08, so the address for the register is really 0x18 (0x10 + 0x08, simple enough).

Now the register’s size is 8 bits (surprise, surprise), what do all those bits represent?  The datasheet tells you right there.  In this case each bit represents a pin.  So in order to set Pin 5’s direction we change the 5th’s bit value in the register.  Here a ‘1’ represents an output.


So, just to recap this info, some places in memory have specific uses and each bit in that memory location has a use.  In our case we care about memory location 0x24 and the 5th bit.  NOTE: Be aware when I say the 5th bit it’s using bit 0 as a starting part.  So really it’s the 6th location if you started counting from 1 but that’s not how binary is used.  We always start from 0.  This is the same everywhere including Arduino so this shouldn’t be new to you, hopefully.

Coding For Real

Now let’s code, no seriously this time.

Let’s write a piece of code that access this point in memory first we start with this, which we put before the main(void) function.

#define LED_DIR        ((uint8_t *) 0x24)

….What is that?  Think of a #define as a variable (like int a = 5) it’s not exactly like that but for now just use that as a reference.  I’ll go into details later.  Don’t even worry about the * for now, let’s just say it’s used to access memory.  The uint8_t is easy.  It’s like a integer (AKA int) but defines that it’s unsigned (don’t worry about it for now) and it’s 8-bits long.  That last bit is what really matters.

Everything we’re dealing with at the moment is 8-bits but things like integers can vary in length.  Sometimes they’re 16 bits or 32 bits.  Our memory address are 8bits wide and it’s good practice to use the right bit size.  Yes you could use “char” too but I prefer this method which clearly defines how long the data type is.  There is uint8_t, uint16_t and uint32_t.

This LED_DIR that we just defined, is going to be used to access that register we read about.

Inside the “main(void)” function type the following:

*LED_DIR = 0x20;

All this is doing is saying, put 0x20 into memory address 0x24.  Why 0x20?  If you know HEX this translate to the following in binary: 0010 0000.  You’ll notice a 1 in the 5th bit (remember the bit all the way on the right is the 0th bit).  Per the datasheet, putting a 1 in that location tells the MCU that pin 5 needs to be an output in Port B.

So the code should be as follows:

#define LED_DIR ((uint8_t *) 0x24)

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

Now lets do the same thing for the Data register.  We need to tell the output to be “1”, or high.

Take a min to look over the datasheet and figure out the data locations and what bits to change.  Write the code in the same format as the LED_DIR (use LED_DATA) and see if you get what I have.

Here’s what you should get:

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

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

Pretty easy right?  Might not be clear how exactly the code is doing what we want but for now that doesn’t matter.

Another Day, Another Error

Now compile the code.  In Atmel Studio it’s under Build-Build Solution or just use F7.  And you get …. an error in the error window.  This is pretty common, not this error necessarily but making a simple mistake that causes a failure like this.

Most of the time it’ll be a syntax error like missing a ; or something like that.  In this case it has no idea what uint8_t is.  The error window will point them out to you and provide a location, if there is one.  If there is you can double click it go to the problem source.

If you used libraries in Arduino before then you know you need to include them.  Here it’s no different but how do you know what library defines the uint8_t variable?AS_Error.PNG

Easy, simply right-click it and hit Goto Declaration (Alt-G in Atmel Studio).  Every single IDE has this, it might not be exactly the same (it might be Declaration with a sub menu) but no mater what you can find where it started from.  Then simply look at the name of the file (near the top) and simply “include” it at the top of your code (outside the main(void)) like so:

#include "stdint-gcc.h"

Depending on your IDE it’ll be different but you get the idea.  This is huge though.  If you’re having trouble figuring out how to do something.  Taking standard example code from the chip manufacturer and using this method to backtrack, is vital to saving yourself a headache.  I’ve hit road blocks where I was misreading the datasheet. looked at the example code and this simple method helped me figure out where I was going wrong.

Now compile and you should see no errors.

This might seem like a lot compared to Arduino but consider this.  You just learned how to program an MCU the way every other engineer does.  They go through the datasheet figure out how to access the write registers, write to them and go.  Plus the code you wrote is actually really simple (just 4 lines) and you have everything you need to turn on an LED.

Plus this code can be used anywhere else too.  It’s not Atmel dependent (minus the memory locations), that is another big advantages of this.  As you learn more coding tricks you can bring them anywhere.

The hardest part was going through so you understand, on a basic level, how to look at a datasheet and figure out everything you need to know.

Programming the MCU

Now lets code it into our MCU.  Getting to them to work on the IDE should be easy but there might be a little set up that can be annoying.  Read your dev-boards user guide or starter guide (if they aren’t the same thing).  That should go over how to setup your board if there is any weird things you have to do.

For the Atmega board there is some setup up.  After you plug it into your computer and it installs the right driver (should be automatic) then go to Project and Properties.

Go to the Tool tab and you should see the following:


Change the debugger/programmer to what you see below or your equivalent:


You should be good to go.  If you see this error:


Hit yes and it should go away.

Now hit the Debugging button, the top green arrow that is completely filled in (or just F5).  There is another green arrow that’s hollow, don’t hit that one.

If all goes well you should have a nice orange LED.  If you’re still having problems let me know, provide some details and I’ll see if I can help fix the problem.


Now lets look at the memory, which is great when you’re debugging a problem with your code.  Hit the stop button on the top to stop debugging and then hit the Step-Into button near the green arrow up top (or just F11).  This goes through your code one line at a time.

It should have started to debug again but now you get this arrow in your code.


When you see this (plus the green arrow is no longer grey’d out) then you know the MCU has paused at that part of the code.  Here’s where the magic happens.  Here you can look memory which should be below where the error window was.


You can see a lot going on here.  Let’s tackle it one part at a time, notice there are three major columns here.  On the left column is the HEX value for the memory address and it’s values are in the center.  To be clear all of those values in the center are not for the value on the left.  The left most value (which is two numbers/letters) is associated with the value on the left column.  The rest are associated with next memory locations.  So 0x20’s value is 0x0C, 0x21’s value is 0x94, 0x22’s value is 0x64, etc.  Take a second and make sure that’s clear.

The right most column is the ASCII value for what’s in memory.  Don’t worry about that for now.

So let’s say you wanted to look at memory location 0x24.  In the address box above memory type in 0x24,data.  This will take you to the data memory area and location 0x24.  The data at that location and 0x25 should be 0x00.

Why do you need the ,data at the end?  These MCU’s have memory split between program and data, it’s called a Harvard Architecture.  So 0x24 in program memory is different than 0x24 in data memory.

Now hit F11 (or the step-into button) and the arrow up top should move the next line of code and you should see 0x24’s value change to 0x20.  Notice how it went from white to red as well.  That’s just showing you what changed in memory since the last time you paused the MCU, a really helpful tool.

If you hit F11 again it should also change 0x25’s value to 0x20 and you should see the LED come on.

This is an incredibly powerful tool; being able to go step by step through your code and watch it work in real time is amazing.  It makes debugging so much easier since you can see immediately if your code isn’t doing what you think.

Wrap Up

Just like that you’re looking at the bare metal of a MCU change right in front of you.  It’s not as low level as you can go.  We’ll go over assembly next time because this went on way longer than intended, which is really as low as you can go.

With these tools you can do w/e you want with your dev-board and access all points in memory.  It’s not the cleanest method for accessing memory but we’ll go into better coding practices after we go over more fundamentals.

The next tutorial I’ll go over looking at the assembly that is created from your C code, the registers in the CPU itself and then actually go into C coding.  Specifically what I just did in this code and maybe go into better coding practices for accessing memory.

Thanks for reading,