Developing in C for the ATmega328P: Build

4 minute read

Where I describe the process of developing code for the Arduino Uno(AVR ATmega328P), specifically, the build process.

Introduction (from Edit)

The middle three steps compile/link/locate are typically called the build process, which can simplify the five steps to three:

  1. Edit
  2. Build
  3. Upload

For a detailed and very worthwhile description of the three steps in Build, I highly recommend this page, Compiling, Link and Locating: Barr Group.

2. Build (compile/link/locate)

The image is a typical start-up build sequence, I need to cd three times, to get into the specific folder. Once there, I use make clean to ensure I have a clean start (source code and makefile only) then I execute a make flash.

Important for a successful build

  1. The source file must be named main.c. If not, you might see “undefined reference to ‘main’”

  2. You must be in the same folder as the makefile and the main.c file to execute the make command. If not, you will see “make: No targets specified and no makefile found. Stop.”

  3. You must use one of the target make commands (see Make Commands below). If not, you will see “make: ** No rule to make target ‘dlash’. Stop.”*. In this case, I mis-spelled flash to dlash.

Typical Build Screen

Typical Build Screen

Large Version to see detail

Compile Error: “expected identifier”

This error was identified in this video.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
 make flash                                                                                                                                     2avr-gcc -Og -ggdb -std=gnu99 -Wall -Wundef -Werror -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums  -ffunction-sections -fdata-sections  -DF_CPU=16000000UL   -DBAUD=9600UL -DSOFT_RESET=0 -I.  -I../../../Library -mmcu=atmega328p -c -o main.o main.c
main.c:1:1: error: expected identifier or '(' before '/' token
    1 | / Serial I/O test
      | ^
In file included from /opt/homebrew/Cellar/avr-gcc@9/9.4.0/lib/avr-gcc/9/gcc/avr/9.4.0/include/stdint.h:9,
                 from /opt/homebrew/Cellar/avr-gcc@9/9.4.0/avr/include/inttypes.h:37,
                 from /opt/homebrew/Cellar/avr-gcc@9/9.4.0/avr/include/stdio.h:44,
                 from main.c:4:
/opt/homebrew/Cellar/avr-gcc@9/9.4.0/avr/include/stdint.h:163:9: error: unknown type name 'int8_t'
  163 | typedef int8_t   int_least8_t;
      |         ^~~~~~
/opt/homebrew/Cellar/avr-gcc@9/9.4.0/avr/include/stdint.h:217:9: error: unknown type name 'int8_t'
  217 | typedef int8_t int_fast8_t;
      |         ^~~~~~
make: *** [main.o] Error 1

Originally, I thought I hadn’t included the AVR-GCC header for integer types called stdint.h, however, in the video its clear that is not the solution. Upon better inspection, I looked at the first error (line 3) and realized the first line in the program, which was meant to be a comment, was missing a “/”. Once the line was made back into a comment, the compile was error free. The errors in lines 10 and 13 were incorrect and were not errors!.

Simple Solution

Always look at the first error before attempting to solve the later errors. Its not unusual for the first error to cause false positive errors, later on.

Compile Error: “implicit declaration”

The error in this video is an “implicit declaration”, it happens quite frequently as it is caused by not adding the header file for a function used in the program.

Simple Explanation

The easiest way to to solve the implicit declaration error, is to include the correct header file. For a vast majority of the AVR_C functions, it will be the name of the function followed by a .h. For example:

make flash                                                                          
avr-gcc -Og -ggdb -std=gnu99 -Wall -Wundef -Werror -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums  -ffunction-sections -fdata-sections  -DF_CPU=16000000UL   -DBAUD=9600UL -DSOFT_RESET=0 -I.  -I../../../Library -mmcu=atmega328p -c -o main.o main.c
main.c: In function 'main':
main.c:20:9: error: implicit declaration of function 'digitalWrite' [-Werror=implicit-function-declaration]
   20 |         digitalWrite(LED, HIGH);
      |         ^~~~~~~~~~~~
cc1: all warnings being treated as errors
make: *** [main.o] Error 1

In the code editor, you see the following headers included:

#include "pinMode.h"
#include "delay.h"

And if you add a third header for the digitalWrite function:

#include "digitalWrite.h"

You will have these results:

make flash                                                                          
avr-gcc -Og -ggdb -std=gnu99 -Wall -Wundef -Werror -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums  -ffunction-sections -fdata-sections  -DF_CPU=16000000UL   -DBAUD=9600UL -DSOFT_RESET=0 -I.  -I../../../Library -mmcu=atmega328p -c -o main.o main.c
avr-gcc -Wl,-Map,main.map  -Wl,--gc-sections  -Wl,-u,vfprintf -lprintf_flt -lm   -mmcu=atmega328p main.o ../../../Library/analogRead.o ../../../Library/analogWrite.o ../../../Library/button.o ../../../Library/delay.o ../../../Library/digitalRead.o ../../../Library/digitalWrite.o ../../../Library/pinMode.o ../../../Library/sysclock.o ../../../Library/tinymt32.o ../../../Library/tone.o ../../../Library/uart.o ../../../Library/unolib.o  -o main.elf
avr-objcopy -j .text -j .data -O ihex main.elf main.hex
avrdude -c Arduino -p atmega328p -F -V -P /dev/cu.usbmodem3101 -b 115200 -U flash:w:main.hex

avrdude: AVR device initialized and ready to accept instructions

Reading | ################################################## | 100% 0.00s

avrdude: Device signature = 0x1e950f (probably m328p)
avrdude: NOTE: "flash" memory has been specified, an erase cycle will be performed
         To disable this feature, specify the -D option.
avrdude: erasing chip
avrdude: reading input file "main.hex"
avrdude: input file main.hex auto detected as Intel Hex
avrdude: writing flash (4028 bytes):

Writing | ################################################## | 100% 0.65s

avrdude: 4028 bytes of flash written

avrdude done.  Thank you.

Make Commands

The Makefile in each folder has a variety of options which can be quite useful. Here are a few of them:

# Same as Verify in Arduino IDE, simply compiles the main.c program
make
# Same as Upload in Arduino IDE, compiles, links and uploads the code to the UNO
make flash
# Create a an assembly listing of the code complete with C source code
make disassembly
# Show the size of the code created in great detail
make size
# Remove all files except source files (main.c and Makefile)
make all_clean
# Remove all the current object files in the Library 
make LIB_clean

Next Steps

Developing in C for the ATmega328P: Upload

Comments powered by Talkyard.