Low Level Arduino Programming — LED Blink (Part 1)

Required Tools

To get started, we need to install the AVR toolchain for building applications, and avrdude for installing them to the Arduino.

sudo apt install gcc-avr avrdude


Low level microcontroller program involves reading from and writing to registers. A register is a single byte variable, which when accessed causes the microcontroller to perform a predefined action. Individual bits in each register are used for different functions. As an example, the following image from the datasheet describes the PORTB register (we will be using this shortly):

Modifying Registers

Setting a bit to 1:

PORTB |= (1 << PORTB5);
PORTB |= (1 << PORTB5) | (1 << PORTB2);
PORTB &= ~(1 << PORTB5);
PORTB &= ~((1 << PORTB5) | (1 << PORTB2));
PORTB |= _BV(PORTB5); // _BV(x) is the same as (1 << x)

Controlling I/O Pins

Pin Layout

There are three banks of I/O ports on the ATMega328p. These are described using the letters B, C and D. The following image labels each pin on the Arduino Uno. Each pin has multiple names as many pins have multiple uses.

The pins on the Arduino Uno.

The I/O Registers

The I/O pins are arranged in three banks, labelled B, C and D. Each bank has three registers associated with it for controlling the pins on that bank. Each bit in the register controls a single pin. Since we will be working with bank B, all following examples will use it. Banks C and D work identically.


The DDRB register is used to set the direction of a pin. Writing a 1 to the relevant bit will set the pin to an output and writing 0 will set it to an input.

We will need to set DDB5 to 1 to configure the LED as an output.


The PORTB register has two uses, depending on whether the pin being controlled is an input or an output.


Writing 1 to a bit in the PINB register toggles the corresponding bit in the PORTB register.

Bringing It All Together

Let’s bring everything together into a file: blink.c.

#include <avr/io.h>
#include <util/delay.h>
int main(void)
DDRB |= _BV(DDB5); // Set LED as an output
while (1)
return 0; // We will never get here!
while (1)

Building and Programming

First we must build the binary:

avr-gcc -Os -DF_CPU=16000000 -mmcu=atmega328p -o blink.elf blink.c
avr-objcopy -O ihex blink.elf blink.hex
avrdude -p atmega328p -c arduino -P /dev/ttyACM0 -b 115200 -U flash:w:blink.hex



Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store