ISP programming

Now that we succeeded in creating our first standalone Arduino board, we are ready to program the microcontroller in a new way!

Actually we can avoid placing it on the mcu socket on the Arduino UNO board, and we can also avoid the use of an USB cable and an USB/serial breakout board (like this one from SparkFun).

Instead we can use the In-System Programming (ISP), also known as In-Circuit Serial Programming (ICSP) (here’s the Wikipedia page on the topic)!

What we need is:

The hardware programmer

We choose the Adafruit USBtinyISP (v2.0) as our hardware programmer in particular because it is well documented. We will use these informations not only to build the programmer itself (it comes as DIY kit), but also to start out with ISP programming.

Adafruit programmer

If you are reading this article and you need to build the programmer yourself too, we strongly recommend to follow the instruction from the Adafruit website.

As said before, the website documentation contains lots of fundamental information; for example there are some components you may want not to solder on the PCB depending how you want to use the programmer. There are also some considerations about the power jumper and when you need to use it! So, be sure to have read the Adafruit documentation before going on.

The circuit

First of all we create a 3 pairs male header to be placed on our breadboard circuit. As you see in the image below we need some soldering to extend the headers’ legs. We also need pliers to gently bend them in order to make them perfectly match the rails of the breadboard.

ISP header solo

Once we place the ISP headers on breadboard we need to connect them to the Atmega328p, but what’s the correct way to do that? If we take a look at the top view of standard ISP headers, we can immediately label all the 6 headers!

ISP top view

We also need to pay attention to the Atmega328p pin-out (The image below is taken directly from the Atmega328p datasheet but you can use also refer to this beautiful illustration from phigixx if you prefer):

atmel pinout

You can see the complete circuit here: ISP header bend on breadboard

Connecting the programmer

It’s now time to connect the programmer to the ISP headers on the breadboard.

The Adafruit programmer comes with a 6-wire cable, with the red marked one being the MISO wire; this is the connection:

programmer->ISP header on breadboard

programmer->ISP header on breadboard 2

We also want to provide power to the mcu via the programmer itself so we need to use the jumper (see here for more information on how to use the jumper).

Using the programmer

We then upload a program using the programmer; there are at least two ways we can achieve this:

Pay attention, all we are going to say now on has been tested on a Linux Debian machine. Maybe you need to do something different if you are using another OS or configuration. Please refer to Adafruit tutorial for more informations.

Usign the Arduino IDE

If we want to use the Arduino IDE we can follow these Arduino instructions.

Because our ATmega328p came with the bootloader preinstalled we weren’t able to test it but if yours is a fresh factory micro-controller, according to the Arduino “Upload Using Programmer” method instructions, you must first burn a bootloader to it. We think this is needed in order to properly set the mcu fuses to be able to upload programs.

If you are facing an error, maybe you don’t have all the necessary permissions to use the USBtinyISP device. We also faced the same problem the first time we tried and we solved it by creating an appropriate udev rule in a new rule file placed in /etc/udev/rules.d/ (read this section of the Adafruit tutorial, it can be very helpful).

This is the content of the rule file (on a single line):

SUBSYSTEM=="usb", ATTR{product}=="USBtiny", ATTR{idProduct}=="0c9f", ATTRS{idVendor}=="1781", MODE="0660", GROUP="dialout"

After adding this rule, you need to reboot the computer in order for this modification to take effect, but after doing this, you will be able to use the particular usb device without the need of root permissions.

Usign the terminal commands

Instead of using the Arduino IDE to upload programs to the mcu via the hardware programmer, we can use the terminal but first we need to install some software tools if we don’t have them yet.

The toolchain is composed of:

This is the workflow we are using: workflow

All these operations are executed automatically by the Arduino IDE each time we press the Play or the Upload button. Now we need to perform them manually!

It is worth saying that this resource was critical in the understanding of the steps to be taken, so, many thanks to the AVR-libc development team!

Even if you are not using the Arduino IDE, don’t forget to create the udev rule file in order to have the right permissions to use the USBtiny device! See the section above for more details.

Programming, Compiling & Linking

After having all these softwares installed, it’s time to compile and upload our program.

First let’s write a simple C program to replicate the legendary Arduino “blink” sketch:

#include <avr/io.h>
#define F_CPU 16000000UL
#include <util/delay.h>

int main(void)
    unsigned long interval = 1000; /* ms */


        PORTB |= (1<<5);
        _delay_ms( interval );
        PORTB &= ~(1<<5);
        _delay_ms( interval );
    return 0;

First we include some header and define a macro:

Going on we enter the main: here we set the local variable interval to 1000, because we want 1 second to be the time during which our LED will stay on or off alternately.

Now we are about to enter inside the while loop but first just another statement:


The DDRB macro identifies the Data Direction Register of port B of our mcu. Take a look at this tutorial for an introduction to the mcu ports prior to continue.

… now, speaking in terms of the Arduino IDE, setting the 5th bit of the port B register on the ATmega328p means to set the 13rd digital pin! So when we say DDRB|=(1<<5); is like we are saying pinMode(13, OUTPUT);.

The same is for the instructions PORTB |= (1<<5); and PORTB &= ~(1<<5); inside the loop: they respectively correspond to digitalWrite(13, HIGH); and digitalWrite(13, LOW);.

Now we need to compile the program so we use this command on a terminal window:

avr-gcc -g -Os -mmcu=atmega328p -c filenmane.c

This instruction tells avr-gcc to create an object code file - filename.o - named after the program source file filename.c. It also tells the compiler the type of mcu we need to build the program for (mmcu option).


Then we need to create the .elf file from the .o, so we use the command:

avr-gcc -g -mmcu=atmega328p -o filename.elf filenmane.o


We can go on playing around with avr-gcc telling it to create also the .map while creating the .elf:

avr-gcc -g -mmcu=atmega328p -Wl,-Map, -o filename.elf filenmane.o

We can even examine the object file using the avr-objdump to create a readable .lst file:

avr-objdump -h -S filename.elf > filename.lst

These last two commands are not mandatory in contrast to the next one, which is needed to create the definitive binary .hex file:

avr-objcopy -j .text -j .data -O ihex filename.elf filename.hex



Now that we have a binary file in the Intel hexadecimal format we have almost finished. The last thing to do is to upload the file on the mcu memory and here’s where avrdude comes in handy!

Here’s the command:

avrdude -pm328p -cusbtiny -C/etc/avrdude.conf -U flash:w:filename.hex:a


To fully understand how avrdude works and what all these arguments stand for, we recommend you to read the command manual (man avrdude), but here’s it’s a little recap:

If we like it, the command can be simplified in three ways:

  1. We can get rid of the -C option because the /etc/avrdude.conf is the default configuration file, so we don’t need to explicitly specify it;

  2. We can create a new file on our home directory and place a line inside it telling that the USBtinyISP is our default programmer. This way we can omit the -c option. The file we create is named .avrduderc and it contains this single line:
    default_programmer = "usbtiny";
  3. We can simplify the -U option syntax because of its default behaviour;

So the command eventually becomes:

avrdude -pm328p -U filename.hex

After running this last command we see plenty of log messages in the console :

console log

In particular there are three progression bars, showing us, respectively:

  1. the reading process;
  2. the writing process;
  3. the verification process during which avrdude compares the original .hex file with what has just been written on the mcu memory;

If everything goes fine, we are able to see our sketch working: the LED is blinking!

Future Experiments

An improvement we can make is to gather our entire workflow inside a single makefile to do everything quicker; this will be extremely useful when we have tens or hundreds of micro-controllers to program.

Another interesting thing about this project is that if we are planning to print out our circuit on a custom PCB, we can simply include a 3 pair male headers in it in order to program the mcu without any other components being needed. In this way we can certainly reduce costs (and size) of our project!;

Note that the Arduino UNO board can be programmed in this very way, so why don’t try it.

If you find this article useful and you like it, please leave a comment below: let us know what do you think about it, we'd really appreciate it. Thank you very much and, as always, stay tuned for more to come!