Developing in C for the ATmega328P: Setup Introduction

9 minute read

Updated: Where I describe the optional steps to setup the Standard C toolchain for the ATmega328P on Windows, Linux, or macOS using a command line-based approach.


In my efforts to continue to refine how to develop Standard C code for the AVR family of microcontrollers, I’ve determined using a Raspberry Pi along with VS Code can provide the optimal experience.

The Raspberry Pi OS is a reliable and steady operating system on which one can build the latest GCC tools for developing code for any microcontrollers in the AVR family. This includes the latest in the DA, Tiny or DD families.

With that said, I recommend using the Raspberry Pi and VS Code instructions and skipping the rest of this page.


Some of the information below is outdated, however, it might be useful in some edge cases.

Command Line Required

Executing the tool chain and performing this work, will require you to work in your operating system’s command line interface. If you aren’t familar with the command line, it need not be a hindrance, there are only a few commands you will need to execute. I recommend reviewing these command line tutorials before you start. Practice a little bit prior to performing the commands below.

As a guide, here are the commands that I believe you will need to know and understand:

# present working directory - find out where you are 
$ pwd
# list - find out what is in the current directory
$ ls
data   layouts     public      resources   static      themes
# make directory - create a new directory
$ mkdir temp
# change directory - in this case go "up" one level
$ cd ..
$ pwd


Setting up the tool chain for C for the AVR family of processors isn’t difficult, if you are using a Linux-based operating system or one that is quite similar to Linux, i.e; Unix-based, macOS. With this in mind, this tutorial will use the following approach for installing the tool chain required for developing in Standard C for the Arduino Uno (AVR ATmega328P):

  • Raspberry Pi - similar to the Linux approach, I recommend this method
  • Linux - use apt as the package manager and use the command line interface (Console)
  • macOS - install homebrew, a Linux-like package manager and use the command line interface (Terminal)
  • Windows 10/11 using a third party download - install a special tool chain packaged designed for Windows

To test the tool chain is working, I’ll use a simple program from Freedom Embedded. The program is basic and doesn’t require any other additional libraries (outside of the standard C library, avr-libc) The goal is exactly the same as that of the Arduino, demonstrate one can get the LED to blink.

Once, its confirmed the tool chain is working, I’ll use a Makefile from Elliot William’s book (GitHub site) (the book is recommended if this topic is interesting to you.) which works extremely well. You might have to make slight changes for your specific setup, however, the Makefile offers an easy and complete automation tool.

In summary the steps will be:

  1. Install the tool chain.
  2. Test the tool chain using specific commands for each step.
  3. Automate using a Makefile, so that the compile/link/load step is one command.


It can be helpful to ensure the Arduino Uno is working propeerly. If you are experiencing problems with the Raspberry Pi and the Uno, download the Arduino IDE and test it on your PC (Windows/macOS). To use the Arduino Uno and the Arduino IDE to confirm the specific port we will be using as well as ensure we have a good physical connection between our Windows system and the Arduino.

Connect your Arduino Uno to your computer and open the Arduino IDE. Go to Tools -> Port -> ???(Arduino Uno) to confirm the port for the Uno. In Windows, it was COM3, in macOS, it was /dev/cu.usbmodem3101 and in Linux, it was /dev/ttyACM0. Write down this port name as you will need it for the step later using the averdude command.

Go to File -> Examples -> 01. Basics -> Blink to open the Blink sketch. Click on Upload and ensure the file is uploaded to your Uno and the built-in LED is blinking. If everything is working, close the Arduino IDE. Note: The IDE must be closed for the command line application avrdude to access the port. If you have connections issues with avrdude connecting, confirm the IDE is not running.

Makefile Notes

The Makefile uses two variables from the env.make file, TOOLCHAIN = and OS =, which allows you to use either, a system-installed toolchain (default) or the toolchain installed by the legacy Arduino (1.8.x) IDE.

In order to use the latter, perform the following steps in the env.make file:

  1. Add arduino to the TOOLCHAIN variable as in TOOLCHAIN = arduino
  2. Add either mac or windows to the OS = line to indicate the OS your PC is running

If either is missing, make will assume you are using the GNU tool chain.

ifeq ($(TOOLCHAIN),arduino)
    ifeq ($(OS),mac)
        BIN = /Applications/
        AVRDUDECONF = -C /Applications/
    ifeq ($(OS),windows)
        BIN = 'C:\Program Files (x86)\Arduino\hardware\tools\avr\bin\'
        AVRDUDECONF = '-CC:\Program Files (x86)\Arduino\hardware\arduino\avr\bootloaders\gemma\avrdude.conf'
    ifeq ($(OS),raspberry)
        BIN = /usr/local/arduino/hardware/tools/avr/bin/
        AVRDUDECONF = -C /usr/local/arduino/hardware/arduino/avr/bootloaders/gemma/avrdude.conf

	BIN =

The BIN = ... and AVRDUDECONF = ... lines must be indented with two tabs at the beginning of the line for make to accept the lines.

There is only one Makefile and it sits at the root level of the folder, along side env.make. There are symbolic (soft) links inside of each example to this Makefile. This makes it easy to propagate changes to all examples simultaneously. It also means there is only one Makefile.

In order to account for multiple projects which use this library in different folder hierarchies, each local makefile has a variable DEPTH which is defined as the required relative nesting to reach the root folder.

For example, in AVR_C, the depth is two, therefore DEPTH = ../../, while in another project where there is one more level of folders, it is DEPTH = ../../../.

If you are getting make errors, stating it can’t find the target, more than likely the DEPTH variable is incorrect.


The method of changing parameters from local environmental variables such as AVR_PORT and AVR_MCU has changed. Instead of local enviromental variables, I have found it easier to maintain a top-level file called env.make, which contains all of the local customizable options. This file is added to the make process by an include at the top of file. It is ignored by git, so it must be created and updated, outside of the git process.

The file, env.make is not tracked by git and it looks like this: (*macOS SERIAL parameter)

# Arduino UNO environmental variables
MCU = atmega328p
SERIAL = /dev/cu.usbmodem14101
F_CPU = 16000000UL
BAUD  = 250000UL
LIBDIR = $(DEPTH)Library
PROGRAMMER_ARGS = -F -V -P $(SERIAL) -b 115200
TOOLCHAIN = arduino
OS = mac

As shown, this one is for the Arduino Uno board and on a Mac. For Make to work, you need to perform the following:

  1. Copy the contents above and paste them into a file called env.make
  2. The file needs to sit at the top level, the same level as this README and the programming folders Library and examples. You will need to set the following:
  • SERIAL = to the serial port you found using the Arduino IDE
  • TOOLCHAIN = arduino delete the word ‘arduino if you wish to use the locally installed GNU C tool chain’
  • OS = mac replace ‘mac’ with ‘windows’ if on a Windows PC
# Arduino UNO environmental variables
MCU = atmega328p
SERIAL = /dev/cu.usbmodem14101
F_CPU = 16000000UL
BAUD  = 250000UL
LIBDIR = $(DEPTH)Library
PROGRAMMER_ARGS = -F -V -P $(SERIAL) -b 115200
TOOLCHAIN = arduino
OS = mac

I’ve found it best to include full sections per board, then comment/uncomment a section based on the board I’m using. A full version of the env.make file I’m using is below.

Note: This repository has the new version of Makefiles which uses this file, so no other changes other than creating the proper env.make file are needed.

The nice part about this change, is once the variables have been updated for your system, you no longer have to do special programmer types such as make flash_snap or make flash_xplain, as make flash will be automatically updated for your specific programmer. (Provided you give it the right parameters.)

Install Make

THe Arduino IDE uses a custom program to compile and load programs on to the Uno. While this program uses the standard GCC tools, it isn’t something that is standard to the industry. We’ll use a standard program in the industry called Make. It is designed to use the tools installed either via the Arduino IDE or via the optional step below. Here are the steps to install both git and make per operating system, both git and make are important to using the AVR_C code.



This is the abridged version of what you would do to install the complete toolchain found here. In this case, you will install homebrew, a package management system for macOS and our two applications, git and make.

Install the package manager, homebrew:

# this command installs command line tools only, not all of xcode 
xcode-select --install
# this command installs homebrew, a package manager for macOS
/bin/bash -c "$(curl -fsSL"
# get the latest versions list
brew update

sudo Request

You might get a request: Checking for sudo access (which may request your password). This is required to setup the initial folder for installation. This github issue covers why:

“You’re correct that Homebrew does not need sudo when used. The installer however does so that it an create the necessary install directory /opt/homebrew and set up its permissions. The installer also uses sudo to install and setup the Xcode Command Line Tools if it is not installed. Once installed, Homebrew no longer requires sudo ever again.”

“If you can’t use sudo to install Homebrew, you still can install it without! Check here for alternative install instructions: Note however that if you can’t install Homebrew to the default location (/opt/homebrew) due to permissions then you won’t be able to use some pre-built bottles and as such installing some formulae may build from source (which is a lot slower and more prone to errors).”

Install the tool chain

To install the tool chain, you will need to perform:

brew install make git

(OPTIONAL) Next Steps: Specific Operating System Installations

This set of steps are for installing the newer GNU version of the avr tools. If you are serious about development, I recommend following the step for your operating system. If you are beginning programming, follow the steps to using the Arduino IDE toolchain on the AVR_C repository.

Comments powered by Talkyard.