Developing in C for the ATmega328P: Linux Setup

Where I setup the Standard C toolchain for the ATmega328P for Linux.

Test with the Arduino IDE!

After performing these steps on multiple machines, I’ve found it best to install and test using the Arduino IDE before going forward with the installation instructions below. This will reduce the errors to something more manageable and having the Arduino IDE is handy for its Serial Monitor as well it provides an easy method to which port the Uno is connected.

Linux Serial Permissions

Linux also has permissions issues with its serial ports. My recommendation is this (also shown at the bottom of this page):

Linux Setup and Tool Chain Installation

For the Linux installation, it can’t get much easier.

1. Install the tool chain

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

# Bring up the command line and enter
sudo apt install gcc-avr binutils-avr gdb-avr avr-libc avrdude make git


Once the installation completes, perform the following commands to confirm all applications were installed correctly. The application averdude provides a lot of output, only some is shown.

avr-gcc --version
make --version
git --version

Check and Confirm the tool chain is installed

Check and Confirm the tool chain is installed

Large Version to see detail

Now, we have the same capability as the Arduino IDE, however, we are able to use it via the command line!

2. Test the tool chain

Test Code

You will want to select the code in the box below, copy it then you will paste it into a nano editor window.

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

Testing the Code

In this step, we’ll setup a specific folder for developing C. I called it test. We’ll add a file called main.c, the we’ll compile/link the file on to the Uno.

mkdir test
cd test 
# copy the file from above and we'll call it main.c
nano main.c
# paste the file, save it and exit

Notes on the steps

  • I’m using nano as my editor to keep this simple, this will be a copy and paste from code above into a file on your system.
  • Using the command cd without folder descriptor, will take you to your Linux home folder. Using cd test will take you to the new folder test you just created.
  • nano main.c will open a file called main.c in the nano editor. The window will be empty as main.c is a new file. Once you paste the file into nano, it will look like this:
    main.c in the nano editor

    main.c in the nano editor

    Large Version to see detail

To save and exit nano, press Ctrl-X, y (to confirm saving) and Return (to confirm file name). Once you have saved the file, you will be back at the WSL prompt. Enter the commands below to compile/link your file.

avr-gcc -Os -DF_CPU=16000000UL -mmcu=atmega328p -c -o main.o main.c
avr-gcc -mmcu=atmega328p main.o -o main
avr-objcopy -O ihex -R .eeprom main main.hex

Your window will now look similar to this (both sets of operations shown:

main.c in the nano editor

main.c in the nano editor

Large Version to see detail

Our last step is to load the main.hex file on to the Uno. We use averdude to upload code to the Uno. We’ll use the following command:

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

Notice the "/dev/ttyACM0"? You will want to change the “ttyACM0” to the port name that you wrote down in the Arduino IDE port assignment in the earlier step Install the Arduino IDE. When you run the averdude command above successfully, you will see the following:

Successful averdude upload

Successful averdude upload

Large Version to see detail AND your Uno will be blinking at a much faster rate!

If you see this instead:

Typical averdude failure

Typical averdude failure

Large Version to see detail

  • Is the Arduino IDE installed, have you tested it, and it worked?
  • Have you closed the Arduino IDE?
  • Are you sure you are using the right port? Double check the number using the Arduino IDE and make sure the port name matches the port name on your averdude command. Ex: -P /dev/ttyACM0

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 test folder and call it Makefile. Make sure the filename is Makefile without an extension.

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
25 PROGRAMMER_ARGS = -F -V -P /dev/ttyS3 -b 115200  
48 TARGET = main
50 # TARGET = $(lastword $(subst /, ,$(CURDIR)))
  • Line 7, change the processor type to the Uno processor, atmega328p
  • Line 8, change the clock speed to 16MHz
  • Line 23, we’ll be using the Arduino to program itself
  • Line 15, add a “#” at the beginning of the line to comment the line out
  • Line 25, be sure to confirm you use your serial port, which worked in step 2.
  • Line 48, change the name from blinkLED to main
  • Line 50, add a “#” at the beginning of the line to comment the line out
  • Line 60, remove the " -I$(LIBDIR)" text at the end of the line. This text exists for an additional library, which we won’t use right now.

Once you have made the above changes, please try the following to test your setup:

  1. Manually delete all of the files EXCEPT the led.c and Makefile in the folder test.
  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/ttyS3) 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.

Finish up

Once you have tested the Makefile and the program main.c, the folder test is no longer required.

Linux Serial Permissions

Steps to follow: (actual steps and output below)

  1. Run dmesg to determine idVendor and idProduct of the USB interfaces (in this case there are two.)
  2. Use sudo and your favorite editor to create a file: “/etc/udev/rules.d/50-myusb.rules”
  3. Using the idVendor and idProduct numbers, create two lines in the file using this format (replacing the numbers shown with your numbers):

SUBSYSTEMS==“usb”, ATTRS{idVendor}==“2e8a”, ATTRS{idProduct}==“0004”, GROUP=“plugdev”, MODE=“0660”, TAG+=“uaccess”

  1. Save and close the file then restart your system.
# use dmesg to determine idVendor and idProduct
# be sure to use the right USB devices (such as usb 1-1 and usb 1-2 below)
# ...
[62603.487834] usb 1-1: New USB device found, idVendor=0403, idProduct=6001, bcdDevice= 6.00
[62603.492032] usb 1-1: FTDI USB Serial Device converter now attached to ttyUSB0
[62607.143187] usb 1-2: New USB device found, idVendor=2e8a, idProduct=0004, bcdDevice= 1.00
[62607.145394] cdc_acm 1-2:1.0: ttyACM0: USB ACM device
# ...
sudo nano /etc/udev/rules.d/50-myusb.rules
SUBSYSTEMS=="usb", ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6001", GROUP="plugdev", MODE="0660", TAG+="uaccess"
SUBSYSTEMS=="usb", ATTRS{idVendor}=="2e8a", ATTRS{idProduct}=="0004", GROUP="plugdev", MODE="0660", TAG+="uaccess"
# save the file then reboot your system

Comments powered by Talkyard.