Developing in C for the ATmega328P: Setup (All Platforms)

Where I setup the C toolchain for the ATmega328P on multiple platforms.

Sources

Introduction

Setting up the tool chain for C for the AVR family of processors is well-supported on the internet. I’ll walk through how to do it for each platform, however, if you run into problems, using search will probably find the answer. To test the tool chain is working, I’ll use a simple program from Freedom Embeded along with the commands they use. 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 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.

Note on Package Managers

Linux has led the way with package managers since its inception. A package manager is typically a command line application which can update a system or be used to install new applications. Ubuntu has apt, Debian uses dpkg and Red Hat uses yum.

macOS has had package managers due to its foundation in Unix, we’ll be using homebrew for the macOS installation.

Windows is just now getting package managers as a standard operation and we’ll use the msys2 version which is called pacman. To this point, there will be a step 0, with macOS and Windows to install the package manager.

Linux

For the Linux installation, it can’t get much easier. Balua has done a nice job of describing each of the steps. I’ll review them here as well.

1. Install the tool chain

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

sudo apt-get install gcc-avr binutils-avr gdb-avr avr-libc avrdude

Yes, its that easy. Don’t you just love Linux?

2. Test the tool chain using specific commands

Here is the program we are going to use to test the tool chain. I won’t go into detail on it right now. If you want to understand it more, please review on Balau’s page.

#include <avr/io.h>
#include <util/delay.h>
 
#define BLINK_DELAY_MS 1000
 
int main (void)
{
 /* set pin 5 of PORTB for output*/
 DDRB |= _BV(DDB5);
 
 while(1) {
  /* set pin 5 high to turn led on */
  PORTB |= _BV(PORTB5);
  _delay_ms(BLINK_DELAY_MS);
 
  /* set pin 5 low to turn led off */
  PORTB &= ~_BV(PORTB5);
  _delay_ms(BLINK_DELAY_MS);
 }
}

In this step, we’ll setup a specific folder for developing C. I called it PureC. I add a subfolder called led for this specific test. The trailing “cd” command is a cute trick I learned while setting this up. It means take the return from the previous command, which in this case is the name of the folder created and cd to it.

I’m using nano as my editor to keep this simple, this is a copy and paste from code above (from Balau’s page) into a file on your system.

cd ~/Documents
mkdir PureC
cd PureC && mkdir led && cd $_
# copy the file from above and we'll call it led.c
nano led.c
# paste the file, save it and exit
avr-gcc -Os -DF_CPU=16000000UL -mmcu=atmega328p -c -o led.o led.c
avr-gcc -mmcu=atmega328p led.o -o led
avr-objcopy -O ihex -R .eeprom led led.hex
avrdude -F -V -c arduino -p ATMEGA328P -P /dev/ttyACM0 -b 115200 -U flash:w:led.hex

If the avrdude command gets an error of “can’t open device…” then confirm your device is the one shown “/dev/ttyACM0” and use sudo. If your device isn’t the one shown, use the command “dmesg” to determine the device name. Follow this for more information.

3. Automate using a Makefile

On this GitHub site for Elliot William’s book, he has a Makefile in the folder setupProject. This Makefile is comprehensive and delivers an Arduino IDE type of simplicity with significantly increased speed. On the page for the file, click on the Raw button, just above the first line. With the raw text, select all of it then copy it. Paste it into a new file on your system in the led folder and call it Makefile.

There are some changes that need to be made. Using the line numbers below as the line numbers in the original, change the text as reflected below. Do not add line numbers to the lines.

 7 MCU   = atmega328p
 8 F_CPU = 16000000UL 
15 # LIBDIR = ../../AVR-Programming-Library
23 PROGRAMMER_TYPE = Arduino
25 PROGRAMMER_ARGS = -F -V -P /dev/ttyACM0 -b 115200	
48 TARGET = led
50 # TARGET = $(lastword $(subst /, ,$(CURDIR)))
60 CPPFLAGS = -DF_CPU=$(F_CPU) -DBAUD=$(BAUD) -I.
  • Line 25, be sure to confirm you use your serial port, which worked in step 2.
  • Line 60, simply remove the " -I$(LIBDIR)" text at the end of the line. This text exists for an additional library, which we won’t use at this time.

Once you have made the changes, please try the following:

  1. Manually delete all of the files EXCEPT the led.c and Makefile in the folder led.
  2. Run “make flash”, this will perform all of the tasks required to compile/link/load the program onto the Arduino Uno.

Hopefully, it all works the first time. If not, look at the errors to discern what needs to be fixed. For me, I had not changed my serial port (line 25 -P /dev/ttyACM0) to be the correct one.

Now that it is working, I noticed the complete time from pressing enter to “avrdude done.” was 2-3 times faster than what the Arduino framework would take on a similar file.

macOS

For the macOS installation, it is similar. The installation will require using a package manager, in this case homebrew. If you don’t have it installed, you will need to do so before continuing. homebrew will require xcode command line tools to be installed as well.

0. 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 https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
# get the latest versions list
brew update

1. Install the tool chain

I followed these instructions for installing the AVR tool chain. It took a fair amount of time to install, I had no issues throughout the process. To install the tool chain, you will need to perform:

brew tap osx-cross/avr
# removal needed before upgrading
brew remove avr-gcc avr-binutils avr-libc
# avr-libc is now included in avr-gcc
brew install avr-gcc avr-binutils
brew install avrdude

Not as easy as Linux, however, it works well.

2. Test the tool chain using specific commands

Here is the program (same as above) we are going to use to test the tool chain. I won’t go into detail on it right now. If you want to understand it more, please review on Balau’s page.

#include <avr/io.h>
#include <util/delay.h>
 
#define BLINK_DELAY_MS 1000
 
int main (void)
{
 /* set pin 5 of PORTB for output*/
 DDRB |= _BV(DDB5);
 
 while(1) {
  /* set pin 5 high to turn led on */
  PORTB |= _BV(PORTB5);
  _delay_ms(BLINK_DELAY_MS);
 
  /* set pin 5 low to turn led off */
  PORTB &= ~_BV(PORTB5);
  _delay_ms(BLINK_DELAY_MS);
 }
}

In this step, we’ll setup a specific folder for developing C. I called it PureC. I add a subfolder called led for this specific test. The trailing “cd” command is a cute trick I learned while setting this up. It means take the return from the previous command, which in this case is the name of the folder created and cd to it.

I’m using nano as my editor to keep this simple, this is a copy and paste from code above (from Balau’s page) into a file on your system.

cd ~/Documents
mkdir PureC
cd PureC && mkdir led && cd $_
# copy the file from above and we'll call it led.c
nano led.c
# paste the file, save it and exit
avr-gcc -Os -DF_CPU=16000000UL -mmcu=atmega328p -c -o led.o led.c
avr-gcc -mmcu=atmega328p led.o -o led
avr-objcopy -O ihex -R .eeprom led led.hex
avrdude -F -V -c arduino -p ATMEGA328P -P /dev/tty.usbmodem3101  -b 115200 -U flash:w:led.hex

If the avrdude command gets an error of “No such file or directory” then confirm your device is the one shown “/dev/tty.usbmodem3101”.

The easiest method to determine the serial port is to use the Arduino IDE (if installed). Go to Tools -> Port and look at the dropdown for which port the Uno is using. You may also do an “ls /dev/tty” to find a similar named port (sometimes the numbers change, such as 4101 instead of 3101.) Ignore the multitude of /dev/tty names with a two letter designation after, such as “/dev/ttysc /dev/ttyu7 /dev/ttyw2”.

3. Automate using a Makefile

On this GitHub site for Elliot William’s book, he has a Makefile in the folder setupProject. This Makefile is comprehensive and delivers an Arduino IDE type of simplicity with significantly increased speed. On the page for the file, click on the Raw button, just above the first line. With the raw text, select all of it then copy it. Paste it into a new file on your system in the led folder and call it Makefile.

There are some changes that need to be made. Using the line numbers below as the line numbers in the original, change the text as reflected below. Do not add line numbers to the lines.

 7 MCU   = atmega328p
 8 F_CPU = 16000000UL 
15 # LIBDIR = ../../AVR-Programming-Library
23 PROGRAMMER_TYPE = Arduino
25 PROGRAMMER_ARGS = -F -V -P /dev/tty.usbmodem3101 -b 115200	
48 TARGET = led
50 # TARGET = $(lastword $(subst /, ,$(CURDIR)))
60 CPPFLAGS = -DF_CPU=$(F_CPU) -DBAUD=$(BAUD) -I.
  • Line 25, be sure to confirm you use your serial port, which worked in step 2.
  • Line 60, simply remove the " -I$(LIBDIR)" text at the end of the line. This text exists for an additional library, which we won’t use at this time.

Windows

For the Windows installation, we’ll use msys2. In the last few years, there has been a considerable amount of work to make Windows look more similar to Linux. For example, Microsoft has created Windows Subsystem for Linux (WSL). We won’t be able to use WSL in this case as WSL can’t access the Windows serial ports. In our case, we’ll use a similar approach called msys2. It is a package manager which we will use to install the tools we need in a very similar manner as one does in Linux.

0. Install the package manager, pacman:

Go to msys2. In the section Installation, you will see the steps to install the package manager. Follow the steps on that page, below is an abbreviated version of the steps:

# Download the installer shown in Step 1.
# (Optional) To verify with the SHA256 checksum (be sure to get the right location for the file)
certUtil -hashfile Downloads\msys2-x86_64-20210725.exe SHA256
# Compare the checksum calculated against the one on the web, typically I look at the first 5 chars and the last 5 chars, they need to be identical
# Double-click on the installation file to begin execution
# Accept the defaults such as the installation location c:\msys64 and "Run MSYS2 64bit now" at the end

1. Install the tool chain

# You need to be in the window which opened up from the msys2 installation
# Update the package list
pacman -Syu
# Hit return to accept the default answers and the window will close at the end of installation
# Run "MSYS2 MSYS" from Start menu and update the package manager again
pacman -Su
# Again, accept defaults by hitting return
# When it finishes, we install our tool chain
pacman -S mingw-w64-i686-avr-toolchain
pacman -S mingw-w64-i686-avrdude
pacman -S make
# Close this window by entering exit
exit

So we have the programs installed, now we need to tell Windows where to find them. In the Search bar, enter “Environment” and a window will open for editing Environmental Variables, click on the Environment Variables near the bottom, then in User Variables, select PATH and click Edit.

When a new window opens up, click New and add two new paths:

  • c:\msys64\mingw32\bin
  • c:\msys64\usr\bin

Now open a command window (cmd in the search window) and attempt the following commands:

avr-gcc --version
avrdude -v
make --version

If successful, all three will run and show the version numbers of the software.

2. Test the tool chain using specific commands

Here is the program we are going to use to test the tool chain. I won’t go into detail on it right now. If you want to understand it more, please review on Balau’s page.

#include <avr/io.h>
#include <util/delay.h>
 
#define BLINK_DELAY_MS 1000
 
int main (void)
{
 /* set pin 5 of PORTB for output*/
 DDRB |= _BV(DDB5);
 
 while(1) {
  /* set pin 5 high to turn led on */
  PORTB |= _BV(PORTB5);
  _delay_ms(BLINK_DELAY_MS);
 
  /* set pin 5 low to turn led off */
  PORTB &= ~_BV(PORTB5);
  _delay_ms(BLINK_DELAY_MS);
 }
}

In this step, we’ll setup a specific folder for developing C. I called it PureC.

I’m using Notepad++ to keep it simple, this is a simple copy and paste from code above (from Balau’s page) into a file on your system. I recommend using Notepad++ as you are able to set the file type to “.c” instead of “.txt” and NPP will recoginize it as a C Language source code file.

cd ~/Documents
mkdir PureC
cd PureC && mkdir led && cd led
# Copy the file from above and open notepad++ and paste the code
# Save the file calling it led.c (be sure to check the dropdown file type as "C Source"
# in the command window, in the led folder execute the following
avr-gcc -Os -DF_CPU=16000000UL -mmcu=atmega328p -c -o led.o led.c
avr-gcc -mmcu=atmega328p led.o -o led
avr-objcopy -O ihex -R .eeprom led led.hex
avrdude -F -V -c arduino -p ATMEGA328P -P COM3 -b 115200 -U flash:w:led.hex

The easiest method to determine the serial port is to use the Arduino IDE (if installed). Go to Tools -> Port and look at the drop-down for which port the Uno is using. If the Arduino IDE is not installed, go to Control Panel via the Windows search bar, select Device Manager and then Ports (COM & LPT). If the Uno is plugged in, the COM port will show which port number. Use it for the command above and make sure it is in the Makefile below.

If all goes well, you will see the following lines:

.....
Writing | ################################################## | 100% 0.04s

avrdude: 176 bytes of flash written

avrdude: safemode: Fuses OK (E:00, H:00, L:00)

avrdude done.  Thank you.

3. Automate using a Makefile

On this GitHub site for Elliot William’s book, he has a Makefile in the folder setupProject. This Makefile is comprehensive and delivers an Arduino IDE type of simplicity with significantly increased speed. On the page for the file, click on the Raw button, just above the first line. With the raw text, select all of it then copy it. Paste it into a new file on your system in the led folder and call it Makefile.

There are some changes that need to be made. Using the line numbers below as the line numbers in the original, change the text as reflected below. Do not add line numbers to the lines.

 7 MCU   = atmega328p
 8 F_CPU = 16000000UL 
15 # LIBDIR = ../../AVR-Programming-Library
23 PROGRAMMER_TYPE = Arduino
25 PROGRAMMER_ARGS = -F -V -P COM3 -b 115200	
48 TARGET = led
50 # TARGET = $(lastword $(subst /, ,$(CURDIR)))
60 CPPFLAGS = -DF_CPU=$(F_CPU) -DBAUD=$(BAUD) -I.
  • Line 25, be sure to confirm you use your serial port, which worked in step 2.
  • Line 60, simply remove the " -I$(LIBDIR)" text at the end of the line. This text exists for an additional library, which we won’t use at this time.

This page (look for the Notepad++ commentary near the bottom) has a great automation tip for adding Make macros to Notepad++. I’ll leave it up to the reader to determine if they want to go the extra step.

Comments powered by Talkyard.