Assembly LanguageThere are two big problems with assembly language:
| Assembler
Object Code
| Linker
Machine Code binary programs
| Boot loader
Processor Hardware
Higher level programming language (C, C++, etc),.c
,.cpp
, and.h
files
| Compiler (target architecture specific) avr-gcc, avr-g++
Assembly Language,.s
files
| Assembler
Object Code,.o
files
| Library builder,
Object Libraries,.a
files
| Linker, takes.o
and.a
files
Executables, portable.elf
files
| Boot converter (takes.elf
{.a})
Machine Code Binary,.hex
files
| Boot loader (avrdude)
Bits in the memory of the processor
main programThe Arduino environment takes this one step further, by abstracting the main program into two parts: setup and loop, and supplying a collection of Arduino core functionality (like delay, Serial, pinMode, etc).
libm
libc
Processor hardware
setup(), loop()So, let's take a look at the steps in the tool chain.
Arduino core
main program
libm
libc
Processor hardware
Blink.cpp
Blink.s
Blink.o
, which is binary with some special symbols. Look at the output of avr-nm Blink.o
to see the various symbols.
Blink.elf
file Executable and Linkable Format that is a standard file format for executables, object code, and dumps. Look at the output of avr-nm Blink.elf
to see the resulting fixed location of symbols in memory.
Blink.hex
file, which can be loaded by the avrdude program into the arduino. arduino-ua/mkfiles/Makefile-templatewhich you copy over to your Makefile in the directory where you are doing code development.
# Template makefile for arduino-ua toolchain |
# Version 2.0 - 2012-11-07 |
|
# Remember to `make clean` before `make upload to a different board type |
# Actually, when you resume development its a good idea to start with a |
# make clean. |
|
# This is needed only if you have a main .ino file from a use of the IDE. |
# Normally you will only have .cpp files. |
TARGET = |
|
# Two common board types, mega2560 and uno. |
# Either set your board type here or supply it on the make command as in |
# `make upload BOARD_TAG=uno` |
BOARD_TAG = mega2560 |
|
# Identify and of the extra libraries that are not in the core collection |
# here. If you don't have any, leave it undefined. |
# For example: |
# ARDUINO_LIBS = SPI Adafruit_GFX Adafruit_ST7735 \ |
# Adafruit_SD Adafruit_SD/utility SD/utility UAUtils |
ARDUINO_LIBS = UAUtils |
|
# if there is a ARDUINO_UA_ROOT environment variable, it defines the |
# root of the arduino_ua install. If not, we assume it is $(HOME) |
ifndef ARDUINO_UA_ROOT |
ARDUINO_UA_ROOT=$(HOME) |
endif |
|
# now set up all the toolchain defaults for the arduino-us install |
include $(ARDUINO_UA_ROOT)/arduino-ua/mkfiles/ArduinoUA.mk |
|
# This is magic to define the names MEGA or UNO in C/C++ files. on the |
# basis of the board type. |
BOARD_DEFINE := $(shell echo $(BOARD_TAG) | tr 'a-z' 'A-Z' | tr -d [0-9]) |
|
# You can also add extra symbols to be defined, like DEBUG |
DEFINITIONS = $(BOARD_DEFINE) |
|
# this puts the -D arguments into the format rquired by the compiler. |
DEFINES := ${DEFINITIONS:%=-D%} |
|
# Define your compiler flags. Remember to `+=` the rule. |
#CFLAGS += -Wall -Werror -std=c99 |
#CXXFLAGS += -Wall -Werror |
CPPFLAGS += $(DEFINES) |
|
# Override the default optimization levels here |
# For example, set to optimize level 0 to get the mimimum amount |
# CPP_OPTIMIZE = -O0 |
# C_OPTIMIZE = -O0 |
# LD_OPTIMIZE = -O0 |
|
# Sometimes it is a good idea to see what code is being generated by the |
# compiler. Set GENERATE_ASM to 1 to do this, and you will get .s files |
# in the build-cli directory. To do this only on demand, you can say |
# make GENERATE_ASM=1 |
# So for example if you only want to look at the assembly for the main |
# program, compile everything, touch the code, and do a make with the |
# GENERATE_ADM set in the command. |
# GENERATE_ASM = 1 |
hello.c
that you want to compile and run. code/Makefiles/hello.c #include <stdio.h> |
int main(int argc, char* argv[]){ |
printf("Bonjour tout le monde\n"); |
} |
hello
, you can simply do: $ make hello
The make
program is a very powerful build-manager, with many default rules, so that if you ask it to make something in particular, like hello
it will look in the current directory for a source file hello.c
and attempt to compile it. To see what make will do, without actually performing any actions, you can use the -n
option and try $ make -n hello
If you tried the earlier make command, you will probably get output that looks like this: make: `hello` is up to date.
This is because hello.c
has not changed, and make will in general not attempt to build something for which the building blocks have not changed. Make's notion of "not changed" is very simple. If a component, like hello.c
is older than the target thing being built, that is, hello
, then it assume that the taget is up to date. hello.c
, or simply fake a change by to it by touching its time stamp as with $ touch hello.c
Now when you do $ make -n hello
the output (under ubuntu linux) is cc hello.c -o hello
Which indicates that the C compiler named cc
is being used for compilation. cc
may not be what we want. Suppose we always want to use gcc
. Then we need to create our own custom Makefile
that contains the single line that sets the variable CC
to have the value gcc
CC = gcc
Note, tab characters are special in Makefiles, and are used to define "recipies", so everything else (including the variable definition above) should start without a tab. the output (under any linux) is$ touch hello.c
$ make -n hello
gcc hello.c -o hello
construct.c
that depends on two modules count and thing. #include <stdio.h> |
#include "thing.h" |
#include "count.h" |
|
#ifndef NUM_THINGS |
#define NUM_THINGS 10 |
#endif |
|
int main(int argc, char* argv[]) { |
int j; |
|
printf("Making some things\n"); |
for (j=0; j < NUM_THINGS; j++) { |
int thing = make_thing(); |
printf("Made thing %d\n", thing); |
} |
|
printf("We made %d things\n", get_count()); |
|
} |
/* shared global counter */ |
static int count = 0; |
|
int get_count() { |
return count; |
} |
|
int inc_count() { |
count++; |
return count; |
} |
/* shared global counter */ |
#ifndef _COUNT_H_ |
#define _COUNT_H |
#else |
extern int get_count(); |
extern int inc_count(); |
#endif |
/* thing factory */ |
#include "count.h" |
|
int make_thing() { |
return inc_count(); |
} |
|
/* thing factory */ |
#ifndef _THING_H_ |
#define _THING_H |
#else |
extern int make_thing(void); |
#endif |
$ make construct
we get an error. $ gcc construct.c -o construct
/tmp/ccfV1GKk.o: In function `main':
construct.c:(.text+0x20): undefined reference to `make_thing'
construct.c:(.text+0x4a): undefined reference to `get_count'
collect2: ld returned 1 exit status
make: *** [construct] Error 1
The problem is that make has no idea that construct requires two other object modules: thing.o and count.o
in order to build the final executable. construct : thing.o count.o
Then we get: $ make construct
gcc construct.c thing.o count.o -o construct
Which produces the executable. $ touch construct.c count.c
$ make construct
gcc -c -o count.o count.c
gcc construct.c thing.o count.o -o construct
Now, thing.c
depends on count.c
, because changing the interface to count will potentially affect thing (and construct). But if we change count.c
make does not know that thing needs to be recompiled. $ make construct
gcc -c -o count.o count.c
gcc construct.c thing.o count.o -o construct
So we should add the other functional dependencies thing.o : count.o
Also the *.h
files define interface dependencies, and so should also be mentioned, but in the context of the *.o
files generated that depend on them: thing.o : thing.h
count.o : count.h
.o
object file for construct, then you need a dependency like this: construct.o : count.h thing.h
IMPORTANT NOTE: Do not add a dependency like this: program.c : foo.h
because if foo.h
is changed, it means to make will want to remake program.c
, and unless you have a way of generating C source files, you have a problem since make potentially removes all targets. CC = gcc |
construct : thing.o count.o |
thing.o : count.o |
|
count.o : count.h |
thing.o : thing.h count.h |
24. The AVR Tool Chain Tangible Computing / Version 3.20 2013-03-25 |