Introduction to Bare Metal Programming in Arduino Uno

Introduction to Bare Metal Programming in Arduino Uno

A short tutorial to start programming Arduino Uno without using the Arduino IDE.

  • 36 respects

Components and supplies

About this project


In this tutorial, we are going to see how to program the Arduino Uno without using the Arduino IDE. We will see how the Arduino IDE works under the hood.

How Arduino IDE works

The Arduino IDE uses the avr-gcc compiler and avrdude to upload our program in the microcontroller. So, we are going to compile using avr-gcc the source code (written in C) to obtain the corresponding object file.

Then through avr-gcc, we link the system libraries to the object file to produce the executable or the ELF file.

Using avr-objcopy, we can translate the executable into a binary file that can be uploaded in the Arduino board using avrdude.

Install the tools

The commands to install the tools are for Ubuntu/Debian machine.

First, we update the package.

>$ sudo apt-get update
>$ sudo apt-get upgrade -y

And then we install the package required by avr and avrdude.

>$ sudo apt-get install gcc-avr binutils-avr avr-libc
>$ sudo apt-get install avrdude

Type in the terminal avr- and press the tab twice (do not press enter) to see all the tools installed, and type avrdude -v to see the version of avrdude installed.

Before coding, a little bit of theory

Before going deep into the c code that will blink the build-in led of the Arduino Uno board, we need to understand what we will do.

The avr-gcc toolchain does not know the Arduino Uno layout, but we need to deal directly with the microcontroller mounted on it.

In particular, the Arduino Uno has the Atmega328p on it. Here we can find the official datasheet of the Atmega328p, which is quite large and full of details, but we do not need all this info.

Otherwise, the Arduino Uno reference design is particularly useful to fulfill our task.

On the bottom-right of the image above, we can see three blocks labeled with IOL, IOH, and AD. The AD block represents the analog pins of the Arduino Uno board. In the other two blocks (IOL, IOH), we have the digital pins.

We want to blink the built-in led that is attached to the digital pin 13 of the Arduino board. Try to locate the pin 13 (written in green) in the IOH block and following the circuit (we see that the built-in led is attached to that pin) we "arrive" to a label PB5.

That means that the Atmega328p maps the pin 13 of the Arduino Uno board to the PORTB, in particular to the fifth bit of the PORTB. So, setting the fifth bit of the register PORTB of the microcontroller means setting HIGH the pin 13 of the Arduino Uno and vice versa.

But, that is not sufficient to control the pin, because we also need to specify if we want to use that pin as input or output.

For this purpose, we have different 8-bit registers in the Atmega328p called Data Direction Register (DDR). In particular, we set to 1 the fifth bit of the register DDRB, in this way, we say that we want to use the pin associated with the fifth bit as output.

Go back to the Arduino Uno reference design we can see that each port register maps to a particular block of pins, the IOH block to PORTB, the AD block to PORTC and the IOL block to PORTD.

In this file "/usr/lib/avr/include/avr/iom328p.h", we can see all the definition useful to manage the registers of the Atmega328p. To handle each block of pins, we need three registers: the data direction register (DDR), the data register (PORT), and the input register (PIN). We have already seen the first two registers work, the third one (PIN) is used, as the name suggests, to read the input value when we use that pin in input mode.

Now, let's code

Here's the code to blink the built-in led (blink_led.c).

#include <avr/io.h>
#include <util/delay.h>

#define MS_DELAY 3000

int main (void) {
/*Set to one the fifth bit of DDRB to one
**Set digital pin 13 to output mode */
DDRB |= _BV(DDB5);

while(1) {
/*Set to one the fifth bit of PORTB to one
**Set to HIGH the pin 13 */

/*Wait 3000 ms */

/*Set to zero the fifth bit of PORTB
**Set to LOW the pin 13 */

/*Wait 3000 ms */

We need to include "avr/io.h" that contains all the utilities to manage the I/O of the microcontroller. For further information on this header file, I suggest you read the official documentation, which is useful and well-done, and I do not think that I can explain it better than the documentation does.

The other header file we include "avr/delay.h" is used only for the wait function.

When we include the I/O header file, we also include the "avr/sfr_defs.h" file in which are defined some useful macros we are going to use in our code. In particular, the _BV macro that executes a left bit shift of 1 by the number of positions specified as the argument.

#define _BV(bit) (1 << (bit))

Do not forget the official documentation!

We are going to use also three logical operator bit-wise:

  • |= the bit-wise logical OR
  • &= the logical bit-wise AND
  • ~ the bit-wise logical NOT.

So, let's see how we say to the microcontroller to set in output mode the pin 13 of the Arduino Uno board. We need to set the fifth bit of the register DDRB to one:

DDRB |= _BV(DDB5);

Then we set to HIGH the same pin setting the fifth bit of PORTB to one:


And we set to LOW the pin 13 setting the fifth bit of PORTB back to zero:


The io328p.h header file defines DDB5 and PORTB5 as 5.

Compile and upload

Now we need to compile, so we first create the object file from the source code specifying the microcontroller in which we will run the program:

>$ avr-gcc -Os -DF_CPU=16000000UL -mmcu=atmega328p -c -o blink_led.o blink_led.c

We create the executable:

>$ avr-gcc -mmcu=atmega328p blink_led.o -o blink_led

And we convert the executable to a binary file:

>$ avr-objcopy -O ihex -R .eeprom blink_led blink_led.hex

Finally, we can upload the binary file:

>$ avrdude -F -V -c arduino -p ATMEGA328P -P /dev/ttyACM0 -b 115200 -U flash:w:blink_led.hex

Look at your Arduino Uno! The led is blinking!


My first tutorial is complete. I hope that you enjoyed it.

Like you, I want to improve and learn new things, so leave some feedback in the comments below.

See you soon in my next tutorial on bare metal programming in Arduino Uno.

LinkedIn profile

Hackster profile


Similar projects you might like

Programming ATtiny13 with Arduino Uno

by Tauno Erik

  • 39 respects

Programming Arduino Pro Mini Using UNO

by Harsh Dethe

  • 8 respects

Add WiFi to Arduino UNO

by Jeff-Paredes

  • 269 respects

Programming ATtiny85 with Arduino Uno

Project tutorial by Arjun Ganesan

  • 272 respects

Programming Arduino Using Python!!!

by Smart Technology

  • 24 respects

RGB LED Snowflake with Arduino Uno

Project in progress by James Cameron

  • 32 respects
Add projectSign up / Login