Chapter 6. Life Without the Arduino IDE

The Arduino hardware actually isn’t all that special; it is just a very basic development board based on the Atmel AVR devices. It is the Arduino IDE and bootloader firmware that make it easier for nonprogrammers to work with it and get things running. It is, however, possible to completely forgo the Arduino IDE. It is a convenient application that takes care of a lot of the messy details of the software build process for the programmer, but those who want to work from the command line with just a text editor can do so without ever using an IDE.

In this chapter we will look at some examples of alternative ways to build programs for an Arduino, and how to use the AVR-GCC toolchain from the command line, without any assistance other than a makefile. We will also see how assembly language can be used “down on the metal” to wring the last bit of performance from an AVR MCU.

Just as there is more than one way to create executable code for an Arduino, there is more than one way to upload software into an AVR. In this chapter we will look at some of the ways to get the job done that don’t involve the Arduino IDE.

IDE Alternatives

The Arduino IDE isn’t the only way to develop and load programs for an AVR MCU on an Arduino board. One Arduino programming alternative is the PlatformIO tool, which runs under Linux, Mac OS X, and Windows. It is a Python-based code builder and library manager that is executed from the command line. Another Python tool for building Arduino programs is the Ino tool; it works with Linux and Mac OS, but does not currently run in a Windows environment.

PlatformIO

PlatformIO is a Python-based command-line tool that supports over 100 different target microcontrollers. It is largely platform independent, requiring only Python 2.6 or 2.7 to run on Windows, Mac OS X, or Linux (I recommend installing Python 2.7). For more information, visit the PlatformIO website.

Based on the type of microcontroller specified, PlatformIO will determine what toolchain components are necessary and then call them in the correct order to complete the compilation, linking, and target upload operations. The boards that PlatformIO supports include the Trinket series from Adafruit, all of the Arduino AVR boards, the BitWizard Raspduino (mentioned in Chapter 1), the Digispark boards from Digistump, the Engduino, the LowPowerLab Mote boards, the Microduino, the Sanguino AVR boards, and the AVR boards from SparkFun. It also supports various ARM-based products such as boards using the Atmel SAM MCU, the STM32 MCU, the LPC from NXP, the Nordic nRF51, as well as the TI MSP430 series and more.

Installing PlatformIO on a Linux or Mac system is straightforward. You can use the command line, download an installation script, or use a Python pip tool. You may need to install the cURL data transfer tool first, but this is a common utility and on a Linux system it can be obtained directly from a package repository.

Figure 6-1 shows the console output after successfully downloading and installing the PlatformIO packages.

Once it’s installed, you can find out what it can do. If you type in platformio boards you will be presented with a long list of currently supported boards.

PlatformIO uses the notion of projects to keep things neat and tidy. Each project starts out with a configuration file and two subdirectories, src and lib. The configuration file must be edited before it can be used. It is a conventional ASCII KVP (key/value pair) INI-type file. You can find detailed documentation on the “Project Configuration File” reference page.

I recommend creating a subdirectory for your PlatformIO projects separate from the sketchbook directory used by the Arduino IDE. In my case, I called it platformio (not very creative, but it works for me). To create a new project, enter platformio init. Figure 6-2 shows the console output when a project is created.

PlatformIO has many capabilities, including predefined frameworks for various board types, a library manager, and the ability to integrate with an IDE or IDE-like editor such as the Arduino (along with Eclipse, Energia, Qt Creator, Sublime Text, Vim, and Visual Studio).

aian 0601
Figure 6-1. PlatformIO successful installation console output
aian 0602
Figure 6-2. Initializing a new PlatformIO project

Ino

Ino is a simple command-line build tool that takes a makefile-based approach to compilation for Arduino targets. Like PlatformIO it is built using Python, but unlike PlatformIO it is specifically intended for use with Arduino boards. Ino supports *.ino and *.pde sketch files as well as *.c and *.cpp sources, and it claims to support all the boards supported by the Arduino IDE. Note that the current version of Ino only works with Linux and Mac OS X host platforms. Ino will work with Python 2.6 or 2.7.

You can download and install Ino either by downloading a compressed TAR file, cloning it from GitHub, or using the Python pip or easy_install tools. I used pip to install Ino and its various components. After installation, running the command ino -h displayed the output shown in Figure 6-3 on the console window.

aian 0603
Figure 6-3. The help output from the Ino tool

Ino creates and uses makefiles, but these are transparent to the user. Like PlatformIO, it uses a directory-based project scheme, and when a project is initialized Ino will create two subdirectories: src and lib. It also creates a minimal template sketch (sketch.ino) in the src directory, just as the latest version of the Arduino IDE does. It is up to you to fill in the blanks and provide the rest of the necessary files.

More information about Ino is available at the official website; the tool can be downloaded from the Python Package Index.

The AVR Toolchain

The primary means of converting a source file with C or C++ source code into a binary object that can then be incorporated into a finished executable AVR program is the AVR-GCC compiler and its suite of utilities. These are collectively referred to as the “toolchain.” As previously mentioned, the primary role of the Arduino IDE is to wrap a user-friendly shell around these tools and hide the messy details as much as possible. PlatformIO and Ino also hide the toolchain behind Python scripts. But it’s still there, regardless, and the compiler, linker, assembler, and other tools are available if you want to build your AVR code using a makefile from the command line, or if you just want to perform each step manually.

Note

The Arduino IDE installation package (in whatever form works for your OS) will take care of installing the AVR-GCC toolchain for you. On a Linux system the Arduino IDE requires the AVR-GCC toolchain and its associated components, so the package manager will install it at the time the Arduino IDE is installed. The main reason for installing the toolchain in addition to what the Arduino IDE provides is to be able to use the latest versions of the tools. On a Windows system the Arduino IDE, tools, and libraries will be placed in a separate directory apart from where something like the WinAVR suite will usually place its toolchain components, so you could have both available if you wanted to do so.

Table 6-1 lists the AVR tools found on a Linux system after installing the Arduino distribution package. The same basic set of programs will be found on a Windows or Apple system after installing the Arduino IDE. Methods for obtaining and installing the GNU AVR tools on various host systems are covered in “Installing the Toolchain”.

Not all of the tools listed in Table 6-1 are necessary to build executable programs for an AVR chip on an Arduino board. With the exception of AVRDUDE, they are AVR versions of existing GNU tools, with similar or identical functionality.

The important tools in the toolchain, from the perspective of getting something compiled for an Arduino (or any AVR MCU), are avr-gcc, avr-g++, avr-ar, avr-as, avr-ld, avr-objcopy, avr-ranlib, and AVRDUDE.

Table 6-1. AVR cross-compilation tools
Tool Description

avr-addr2line

Converts addresses into filenames and line numbers

avr-ar

Creates object code archives, and modifies or extracts code

avr-as

The portable GNU assembler for the AVR

avr-c++filt

Demangles C++ symbols

avr-gcc

The GCC compiler backend to produce AVR object code

avr-g++

The G++ compiler backend to produce AVR object code

avr-ld

The GNU linker for AVR object code

avr-nm

Lists symbols embedded in object files

avr-objcopy

Copies and translates object files

avr-objdump

Displays information from object files

avr-ranlib

Generates an index for a library archive

avr-readelf

Displays information about ELF files

avr-size

Lists object file section sizes and total size

avr-strings

Prints strings of printable characters in binary files

avr-strip

Discards symbols from AVR object files

AVRDUDE

The driver program for various AVR MCU programmers

The heart of the toolchain is the compiler, which is called avr-gcc, or avr-g++ for C++ sources. These are versions of the GNU compiler that have been tailored specifically for use with C or C++ source code and Atmel’s line of AVR microcontrollers. They’re similar to the full version of gcc, with some of the compiler’s options preset to make them more convenient to use for AVR MCU cross-compiling.

If you happen to look in the directory where the toolchain components have been installed, you might see the following:

avr-c++
avr-cpp
avr-g++
avr-gcc
avr-gcc-4.5.3

These are all just variants of the GNU compiler. The avr-c++ and avr-g++ files are identical. The avr-cpp, avr-gcc, and avr-gcc-4.5.3 files are also identical. If you’re curious, you can get the version information by typing avr-gcc -v and avr-g++ -v.

After the source code is compiled, the linker (avr-ld) combines all the binary modules into a single executable. The source modules must have already been compiled into object files before they can be processed by the linker.

Compiling and linking isn’t the end of the process, however, because the binary executable image file created by the linker must be converted into a so-called Intel Hex file, which contains an ASCII representation of the binary code in the form of ASCII hex characters. Then it can be uploaded to the target board, where the bootloader will translate the ASCII hex back into binary values and write them into the flash memory on the microcontroller.

Other members of the GNU toolchain, such as ar, ranlib, nm, ld, and strip, also have AVR versions. Figure 6-4 shows a diagram of how the compiler, linker, converter, and uploader all work in sequence to get a program into a compiled form, link in necessary functions from library modules, and then transfer the finished program to an AVR target.

The object files shown in Figure 6-4 might be the compiled code from other modules in your project, or they may be from code supplied with a sensor or other accessory, or they could be runtime AVR-GCC support code such as the main() function that the Arduino IDE supplies. The library objects shown could be actual binary libraries such as avr-libc or other libraries created with avr-ar and avr-ranlib, or they might be object files created when an Arduino-style library is compiled prior to linking.

Note

One component not mentioned yet is the runtime AVR-GCC support library, avr-libc, which provides many of the same functions found in a standard C runtime AVR-GCC library. It also includes support functions specific to AVR microcontrollers. avr-libc is described in detail in “avr-libc”.

Installing the Toolchain

Although the toolchain components are typically installed for you when you install the Arduino IDE using a package manager, the Windows installer, or the Mac OS X ZIP file, you can install these components individually if you don’t plan on using the Arduino IDE.

At a minimum, you will need the following packages (these are Linux package names):

  • avr-lib

  • avrdude

  • avrdude-doc

  • binutils-avr

  • gcc-avr

  • gdb-avr

aian 0604
Figure 6-4. The AVR-GCC toolchain

If you are using Windows, then you may also want to consider installing other Unix/Linux-compatible tools such as grep, fgrep, ls, and diff. These are already present on Linux and Mac OS X platforms. Although not specifically described here, it is possible to install the AVR GNU toolchain on systems running Solaris or BSD Unix (FreeBSD, for example), or something even more off the beaten path. You just need a really good reason, a lot of patience, and possibly a significant amount of skill in working with source code packages. In general, it is much easier to stick with systems that already have ready-made installation packages available.

Windows installation

As previously mentioned, the Arduino package comes with the necessary compiler and binutils programs. Just install the Arduino IDE, and it will install the toolchain components it needs in the same directory where the main executable code for the IDE is located.

The Atmel Corporation provides precompiled binary installation packages, basic documentation, and the source code for AVR gcc and binutils. You can access the installation packages and other resources from the Atmel website. Source code packages are also available on the website.

Another easy way to install the AVR-GCC toolchain on a Windows system is with the package WinAVR. The install script creates a directory (it’s C:WinAVR-20100110 on my machine), and the binary executables, libraries, and include files are placed there. There is also a good collection of documentation in both PDF and HTML formats in C:WinAVR-20100110doc. Be forewarned that WinAVR has not been updated since about 2010, so some of the toolchain components are a bit stale, but they’re still usable for most projects. The Atmel version appears to be more recent.

A collection of GNU utilities precompiled for Windows can be found at GnuWin32. This is a large collection of system tools and utilities, but it doesn’t include the gcc/g++ compilers or the binutils packages. You can obtain those from the Atmel or WinAVR download locations.

You can find more packages and tutorials on Google by searching for “Windows avr binutils.” Those who like to use the Eclipse editor/IDE environment and want to try it for AVR software development might want to check out the “AVR Eclipse Environment on Windows” tutorial on Protostack.com.

Linux installation

On most Linux systems the installation of the GNU gcc, binutils, and other AVR-related packages is just a matter of selecting packages using a software package manager. You can have both the “normal” GCC toolchain components and the AVR toolchain components installed at the same time because the AVR versions of the compiler and binutils tools will have a prefix of “avr-”. I wouldn’t recommend attempting to build avr-gcc or any of the other members of the toolchain from source unless you have a very compelling reason to do so, and you happen to be very comfortable working with large, complex source code packages.

Mac OS X installation

Because Mac OS X is based on a BSD Unix foundation, a lot of what can be said about Linux can also apply to Mac OS X. The good folks at Adafruit have created a helpful guide to installing the AVR GNU toolchain in a Mac OS X environment. That page’s link to the Mac package is obsolete, but see the next paragraph for the current link.

The CrossPack development environment from Object Development contains all of the toolchain components needed to develop AVR software in a Mac OS X environment, and it doesn’t require Xcode to compile. You can download it from the Object Development website. Note that this is just the toolchain; it doesn’t provide a GUI IDE or an editor, so you’ll need to install those yourself.

A version of the Eclipse IDE is available for Mac OS X with the AVR toolchain, and of course there is a version of the Arduino IDE available for the Mac OS X platform.

make

For small programs using the toolchain programs from the command line may be fine, but when things start to expand it is handy to have some way to automate the process. The tool of choice for this in a command-line environment is make.

make is a type of interpreter, and it processes what are called makefiles. The language used by make is not a general-purpose programming language, but rather a set of actions, rules, and relationship definitions. You can think of it as a form of a script, and it is often referred to as a macro language because the statements use replacement and substitution to build commands for other tools. make does more than this, however, since it can also detect source file changes and track dependencies between source files (for example, if A depends on B and C, and B changes, then B will be recompiled and A will also need to be recompiled to incorporate those changes). make was initially created by Richard Stallman, Roland McGrath, and Paul D. Smith.

Many IDEs for large-scale code development utilize make as the “backend” for compilation. The PlatformIO and Ino tools also use make, but they do it in a way that hides what is going on, and then clean up afterward. There are also tools available that automate the process of creating input for the make tool, and if you have ever used the configure tool to build a software package, then you’ve seen this type of utility in action.

The basic idea behind make is managing large sets of program source files. It can determine which source files have changed, and what other source files may need to be recompiled if they depend on the files that have changed. The make utility can invoke compilers, linkers, automatic documentation generators, and even other makefiles (for situations where the source code may be distributed over multiple subdirectories). make can also detect when a tool such as avr-gcc or avr-ld has encountered an error.

A good place to start in order to get an idea of how make is used is looking at the makefiles found in existing projects. Describing all of the capabilities of make is far beyond the scope of this book (the official GNU manual is over 200 pages long), and there are many books available that cover make and its applications in detail (see Appendix D). You can download the official user’s manual for make in PDF format from the GNU website.

avr-gcc

GCC is an acronym for GNU Compiler Collection. The GCC is based on the concept of utilizing different frontend symbolic processors for specific languages, and then passing the resulting intermediate code to a backend for a specific target platform. The avr-gcc cross-compiler is built from the GCC source and preconfigured to generate object code specifically for the AVR family of microcontrollers. The GCC can generate object code for many different types of processors. These include the Intel CPUs found in PCs, SPARC RISC CPUs, the 68000 family of large-scale microprocessors, the MSP430 MCUs from Texas Instruments, various ARM-based MCUs, and many others.

avr-gcc and avr-g++ accept a number of command-line arguments, also called switches. These define things like optimization, compilation mode, pass-through arguments for the linker, paths for include files, and the target processor. Although there are literally hundreds of command-line switches, only a few are necessary to successfully compile code for a specific target processor.

Compilation may involve up to four steps: preprocessing, compilation, assembly, and linking. These always occur in this order. The preprocessor expands all include statements (#include) by loading the text in the named file into the current source. Normally #include statements should only be used to include so-called header files, like stdio.h or stdlib.h, and not source files. (Although that’s generally considered to be a bad idea, I’ve seen it done.) The preprocessor also strips out any comments and interprets any conditional preprocessor statements, such as #ifdef, #else, and #endif. The output of the preprocessor is a squeaky-clean source file containing pure source code and nothing else. You can use the -E switch to stop the process after the preprocessor is finished and examine the stripped source code.

The GCC is basically a translator for a particular C-like language such as traditional or ANSI C, C++, Objective-C, or Objective-C++. The output from the compiler is an intermediate assembly language file. Usually this would be the input into the assembler (avr-as), which in turn will generate an object file. The intermediate assembly language file is deleted after the assembler executes. It is possible to stop the process just before the assembler is run and examine the assembly language output using the -S switch.

The -c switch is used to create an object file without invoking the linker. This is used in makefiles where all the source files are compiled first and then linked in a single step. The compiler also has switches for optimization, warnings (it can generate a lot of warnings), and path specification so that include files can be located. The -o switch specifies the name of a compiled executable image; the default is usually a.out.

For more information refer to the GCC manual pages, or you can download a user manual in PDF, PostScript, or HTML format from the GCC online documentation. GCC is not a simple utility, and the number of available options borders on overwhelming. Fortunately, you don’t need to use all of them to create working executable code.

binutils

The GNU binutils are a collection of programs that handle the various tasks involved with converting the output of the compiler into something that a processor can execute. Table 6-2 lists the contents of the binutils-avr package for a Linux system. This suite of tools contains everything needed to assemble, link, convert, and process binary executable files into a form suitable for a target AVR microcontroller. Manuals for the binutils tools, and for most all other GNU software as well, can be found on the official web page at GNU Manuals Online.

Table 6-2. AVR binutils collection
Tool Description

avr-addr2line

Converts addresses into filenames and line numbers

avr-ar

Creates object code archives, and modifies or extracts code

avr-as

The portable GNU assembler for the AVR

avr-c++filt

Demangles C++ symbols

avr-ld

The GNU linker for AVR object code

avr-nm

Lists symbols embedded in object files

avr-objcopy

Copies and translates object files

avr-objdump

Displays information from object files

avr-ranlib

Generates index for a library archive

avr-readelf

Displays information about ELF files

avr-size

Lists object file section sizes and total size

avr-strings

Prints strings of printable characters in binary files

avr-strip

Discards symbols from AVR object files

The essential support utilities needed to build programs for an AVR MCU are avr-ar, avr-as, avr-ld, avr-objcopy, and avr-ranlib, but the other components in the binutils suite may or may not be of use to you in your software development efforts. The main applications are:

avr-ar

avr-ar is used to create binary object code archives, or static libraries. It can also be used to modify an existing library or extract code from a library. A binary library file (usually with a .a extension) is a collection of binary code modules (i.e., object modules) with a master index (created using avr-ranlib, described momentarily). An object code library is referred to as a “static” library because any component that is used in another program is incorporated into and becomes a permanent, or static, part of the final executable object. If you’re curious, a dynamic shared library is a different sort of thing, and since these aren’t typically used with AVR devices (or any small microcontroller, for that matter) they are not covered in this book.

avr-as

avr-as is the portable GNU assembler for the AVR family of MCUs. Although it is often used in conjunction with the GCC, it can also be used as a standalone assembler (as discussed in “AVR Assembly Language”). There are other assemblers available for AVR microcontrollers, and these are discussed in “AVR Assembly Language” as well, but only avr-as is intended to be used with the gcc/g++ compilers.

avr-ld

avr-ld is typically the last step in the process of creating an executable binary object. The primary function of the linker is to combine two or more object files, resolve any address references between them, and relocate data as necessary.

When an executable is built from multiple object files, each of the object files may contain a reference to a function or data that does not exist within a particular object, but does exist in another object. The AVR version of libc, discussed next, is an example of this type of situation. For instance, a program may refer to something like atoi() (ASCII-to-integer), but not include the source for atoi() within itself. When the program is compiled into an object file (a .o file) the compiler will leave a hole, so to speak, in the binary code that refers to atoi(). The linker detects this empty location, finds the address of the atoi() function in a library (i.e., avr-libc.a), writes the external address into the code, and then includes the object code for the atoi() function in the final binary executable image.

avr-objcopy

The avr-objcopy utility copies the contents of an object file to another format, typically a so-called Intel-format ASCII hex file suitable for uploading to an AVR MCU using the Arduino bootloader. avr-objcopy can also generate a type of ASCII hex file called an S-record, which is commonly used with Motorola (Freescale) MCUs.

The Intel hex format file might look something like the following, which shows the beginning and end lines of the Arduino bootloader for an ATmega168 or ATmega328 MCU:

:107800000C94343C0C94513C0C94513C0C94513CE1
:107810000C94513C0C94513C0C94513C0C94513CB4
:107820000C94513C0C94513C0C94513C0C94513CA4
:107830000C94513C0C94513C0C94513C0C94513C94
:107840000C94513C0C94513C0C94513C0C94513C84
:107850000C94513C0C94513C0C94513C0C94513C74
:107860000C94513C0C94513C11241FBECFEFD8E036
<em>...more data here...</em>
:107F700009F0C7CF103011F00296E5CF112480919F
:107F8000C00085FFB9CEBCCE8EE10E94C73CA2CD19
:0C7F900085E90E94C73C9ECDF894FFCF0D
:027F9C00800063
:040000030000780081
:00000001FF

Many lines have been omitted from the middle part of the listing for the sake of brevity, but you can find the original file, and others, in the directory /usr/share/arduino/hardware/arduino/bootloaders on a Linux system, or in C:Program FilesArduinohardwarearduinoavrootloaders on a Windows system.

In this listing, each line contains a start code (the : character), a byte count (which for all but the last four lines in our example is hex 10, or 16), the location where the code is to be written in the MCU’s flash memory (the bootloader can alter this if need be), a record type code, the actual code written as up to 32 ASCII hex characters (2 characters per byte, for 16 bytes), and an end-of-line checksum. You can learn more about the Intel hex file format at Wikipedia, although it is seldom necessary to examine a hex file directly.

To convert a binary executable to a hex file you could use objcopy like so:

avr-objcopy -O ihex execpgm execpgm.hex

objcopy, like almost all GNU tools, is capable of much more and has a plethora of command-line options, most of which you will probably never find a use for. The online manual for objcopy can be found at Sourceware.org.

avr-ranlib

avr-ranlib generates an index for inclusion into a binary object archive file. This helps to speed up the linking process, because this is what the linker will use to locate the address of an object needed to fill in a link “hole” in another object file. If the index is not available, then the linker will have to scan through the library file, object by object, looking for a suitable match.

avr-libc

avr-libc is an AVR version of the C/C++ runtime library. Together with avr-gcc and avr-binutils it forms the core of the GNU toolchain for AVR microcontrollers.

External libraries supplied with the AVR toolchain, such as arv-libc, should be in a standard location, which on a Linux system would be something like /usr/lib/avr/lib/ or /usr/local/avr/lib, depending on how avr-gcc was built and how your system is configured. External libraries can be in any directory, actually, just so long as the linker can find them.

avr-libc is the one critical component not provided with avr-gcc and binutils. It is a standard C/C++ library that contains AVR equivalents of the same functions found in a regular (i.e., GNU libc) standard C library, with some limitations related to the capabilities of AVR MCUs (limited available memory, for example).

Table 6-3 lists the include files available with avr-libc. If you are experienced with C or C++ programming on a full-size system, then most of these will look familiar to you.

Table 6-3. Common include files provided by avr-libc
Filename Description

alloca.h

Allocates space in the stack frame of the caller

assert.h

Tests an expression for false result

ctype.h

Character conversion macros and ctype macros

errno.h

Defines system error codes

inttypes.h

Integer type conversions

math.h

Basic math functions

setjmp.h

Defines nonlocal goto methods setjmp() and longjmp()

stdint.h

Defines standard integer types

stdio.h

Standard I/O facilities

stdlib.h

General utilities

string.h

String operations and utilities

Some of the include files supplied with avr-libc are unique to the AVR target; these are listed in Table 6-4. These include files are located in the /usr/lib/avr/include/avr directory (on a Linux system). Some define functions and constants for things like boot management, time delays, EEPROM access, fuse settings, and port pin functions. Others define the interrupts and I/O mappings for specific processor types.

Table 6-4. AVR-specific include files provided by avr-libc

boot.h

io90pwm316.h

iom169pa.h

iom32u2.h

iomx8.h

iotn45.h

iox128a3.h

builtins.h

io90pwm3b.h

iom169p.h

iom32u4.h

iomxx0_1.h

iotn461a.h

iox128d3.h

common.h

io90pwm81.h

iom16a.h

iom32u6.h

iomxx4.h

iotn461.h

iox16a4.h

cpufunc.h

io90pwmx.h

iom16.h

iom406.h

iomxxhva.h

iotn48.h

iox16d4.h

crc16.h

io90scr100.h

iom16hva2.h

iom48.h

iotn10.h

iotn4.h

iox192a3.h

delay.h

ioa6289.h

iom16hva.h

iom48p.h

iotn11.h

iotn5.h

iox192d3.h

eeprom.h

ioat94k.h

iom16hvb.h

iom640.h

iotn12.h

iotn84a.h

iox256a3b.h

fuse.h

iocan128.h

iom16hvbrevb.h

iom644.h

iotn13a.h

iotn84.h

iox256a3.h

interrupt.h

iocan32.h

iom16m1.h

iom644pa.h

iotn13.h

iotn85.h

iox256d3.h

io1200.h

iocan64.h

iom16u2.h

iom644p.h

iotn15.h

iotn861a.h

iox32a4.h

io2313.h

iocanxx.h

iom16u4.h

iom6450.h

iotn167.h

iotn861.h

iox32d4.h

io2323.h

io.h

iom2560.h

iom645.h

iotn20.h

iotn87.h

iox64a1.h

io2333.h

iom103.h

iom2561.h

iom6490.h

iotn22.h

iotn88.h

iox64a1u.h

io2343.h

iom1280.h

iom3000.h

iom649.h

iotn2313a.h

iotn9.h

iox64a3.h

io43u32x.h

iom1281.h

iom323.h

iom649p.h

iotn2313.h

iotnx4.h

iox64d3.h

io43u35x.h

iom1284p.h

iom324.h

iom64c1.h

iotn24a.h

iotnx5.h

lock.h

io4414.h

iom128.h

iom324pa.h

iom64.h

iotn24.h

iotnx61.h

parity.h

io4433.h

iom128rfa1.h

iom3250.h

iom64hve.h

iotn25.h

iousb1286.h

pgmspace.h

io4434.h

iom161.h

iom325.h

iom64m1.h

iotn261a.h

iousb1287.h

portpins.h

io76c711.h

iom162.h

iom328p.h

iom8515.h

iotn261.h

iousb162.h

power.h

io8515.h

iom163.h

iom3290.h

iom8535.h

iotn26.h

iousb646.h

sfr_defs.h

io8534.h

iom164.h

iom329.h

iom88.h

iotn28.h

iousb647.h

signal.h

io8535.h

iom165.h

iom32c1.h

iom88pa.h

iotn40.h

iousb82.h

signature.h

io86r401.h

iom165p.h

iom32.h

iom88p.h

iotn4313.h

iousbxx2.h

sleep.h

io90pwm1.h

iom168.h

iom32hvb.h

iom8.h

iotn43u.h

iousbxx6_7.h

version.h

io90pwm216.h

iom168p.h

iom32hvbrevb.h

iom8hva.h

iotn44a.h

iox128a1.h

wdt.h

avr-libc also includes a number of utility and compatibility include files, as shown in Table 6-5. The files delay.h, crc16.h, and parity.h in the avr/ directory actually point to include files in the util/ directory, so if you include, say, <avr/parity.h> in your code it will actually use <util/parity.h>.

Table 6-5. Utility and compatibility include files provided by avr-libc
Filenames Description

util/atomic.h

Atomically and nonatomically executed code blocks

util/crc16.h

CRC computations

util/delay.h

Convenience functions for busy-wait delay loops

util/delay_basic.h

Basic busy-wait delay loops

util/parity.h

Parity bit generation

util/setbaud.h

Helper macros for baud rate calculations

util/twi.h

TWI bit mask definitions

compat/deprecated.h

Deprecated items

compat/ina90.h

Compatibility with IAR EWB 3.x

For information on using avr-libc refer to the user manual, and don’t forget to check the include files themselves for notes regarding applications and limitations. Bear in mind that things like malloc() and printf(), while they can be used with an AVR MCU, have limitations in the memory-constrained environment of the AVR. Math is another issue, since the AVR MCUs do not have floating-point math functions. They support integer and fixed-point math, but floating-point math must be done using a floating-point processor simulation. This is slow, so avoid it if at all possible.

The avr-libc home page can be found at http://www.nongnu.org/avr-libc/.

Note

A bzip2 compressed version of the PDF user documentation is located at http://bit.ly/avr-libc-manual. The documentation for avr-libc also covers the AVR toolchain.

Building C or C++ Programs from Scratch

If you want to build your own software without using the Arduino IDE and its standard runtime AVR-GCC and libraries, you can definitely do that. It is, however, often easier to get something up and running using the Arduino tools, and then move on to rewriting the parts that need optimization or customization.

Compiling with avr-gcc or avr-g++

What avr-gcc or avr-g++ does with the source code internally is not something you would normally be concerned about, but you can, of course, read more about the internals of the GCC tools at GNU.org.

The commands gcc and g++ actually do more than just compile source code. If you use a command like this:

avr-gcc -mmcu avr5 -o test test_src.c -L../avrlibs -lruntime

gcc will compile test_src.c for the ATmega32U4, call avr-ld to link it to file called libruntime.a located in ../avrlibs, and then put the final result into a binary executable image called test.

If you just want the compiler to compile something into an object file for linking at a later step, you can use the -c (compile only) switch. This is common in makefiles where multiple source files are compiled first, and then linked into an executable file (possibly with external libraries as well) as a final step. The command for this is simple:

avr-gcc -mmcu avr5 -c test_src.c

avr-gcc also supports a suite of switches for things like optimization, warnings, and alternate paths for include (header) files.

Multiple Source Files and make

When working with multiple source files, the make utility is essential. You can write your own makefile (which is what I typically do), or use a tool like arduino-mk. This is a predefined makefile that incorporates the necessary logic to build Arduino sketches using the components of the AVR-GCC toolchain.

You can download arduino-mk from GitHub. For some Linux platforms it is available as an installable package. If you do install it from a package manager, note that it will most likely put the main file, arduino-mk, in the same directory where the Arduino IDE installed its components. The main website is at Hardware Fun, and you can download a ZIP archive from GitHub. Once arduino-mk is installed, you will probably need to install some additional support functions and define some shell environment global variables. Refer to the documentation on the Hardware Fun website for details.

If you are not familiar with the language of makefiles, then using them might involve a steep learning curve, but I think it’s well worth the effort if you want to move beyond an IDE and into the realm of hardcore embedded systems development. Not every MCU has an IDE available for it, and not every IDE can do all of the things that can be accomplished when you have direct control over the build process.

We can reuse the concepts from Example 5-5 to create a simple makefile such as the one shown next. In this case we are supplying our own main() function in the source file main.cpp, the equivalent of the setup() and loop() functions in alarmloop.c, global variables in globals.c, and the #define statements in the file defs.h. Our makefile looks like this:

CC = avr-gcc
LD = avr-ld
OC = avr-objcopy

SRCS   = main.c alarmloop.c globals.c
HDRS   = main.h globals.h defs.h
OBJS   = main.o alarmloop.o globals.o
EXEC   = alarm
TARGET = alarm.hex

$(TARGET): $(OBJS)
	$(LD) -o $(EXEC) $(OBJS)
	$(OC) -O ihex $(EXEC) $(TARGET)

main.o: main.c $(HDRS)
	$(CC) -mmcu avr5 -Wall -c main.c

alarmloop.o: alarmloop.c $(HDRS)
	$(CC) -mmcu avr5 -Wall -c alarmloop.c

globals.o: globals.c
	$(CC) -mmcu avr5 -Wall -c globals.c

The directory for this simple project would contain, at a minimum:

Makefile
main.c
main.h
alarmloop.c
alarmloop.h
globals.c
globals.h

Just typing make will start the process. make looks in the current directory for a file named Makefile (note the capital M, that’s required) and will load and process it if found. You can use another name for the makefile if you wish by telling make what to look for, like so:

make -f mymake

Our sample makefile has several features that are worth covering briefly. First is the declaration of alias names, such as CC, LD, and SRCS. These are expanded when encountered further on in the file, and save on repetitive typing as well as perform substitutions with conditional statements.

Another thing to note is the rules. In this case I’ve used explicit rules in the form of:

target: dependencies
 	action
	

The action is indented using a tab character. make requires this as part of its syntax, so be careful not to use spaces here. Also note that there can be as many actions under a rule as you like, so long as each is on its own line and has a leading tab.

When make evaluates a rule it looks at the dependencies to determine what is necessary to build the target. In the case of main.o, the rule specifies main.c and the other include (header) files, which are defined by the macro $(HDRS). If the target does not yet exist then it will always be built, but if it does exist then make will check to see if any of the dependencies have changed by examining the time and date stamps of the files. Only if there has been a change will the target be recompiled. In the case of $(TARGET) the rule states that the two subsequent actions (linking and object conversion) must occur if any of the source objects have changed.

So, for example, if you edit alarmloop.c to change something and then run make, the first thing it will do is recompile alarmloop.c to create a new alarmloop.o. It will then rebuild $(TARGET) since the alarmloop.o object has now changed. If the include file alarmloop.h has changed, then it will recompile both main.c and alarmloop.c before rebuilding $(TARGET).

Creating a makefile can be a bit of a chore, but once it’s done you generally don’t need to modify it very often, except perhaps to add a new source module or change a compiler or linker switch setting.

AVR Assembly Language

For those who really want to wring out every last bit of performance from an AVR device, there is assembly language programming. This is definitely not for the faint of heart, but it allows you to take full control of what the MCU does and when it does it. In some situations this level of control may be necessary to get the best possible performance with the least amount of program memory. For some of the AVR MCUs, such as the ATtiny series, assembly language is really the only way to go, as a C or C++ program may easily compile down into something that will be too large to fit comfortably in the limited amount of memory.

This section provides a very, very high-level overview of AVR assembly language programming. As stated in the preface, this book is not a programming tutorial; it is a hardware reference and guide to sources of more detailed information. The intent of this section is to give you a sense of what is involved with assembly language programming so that you can decide if it is something you want to pursue. To do the subject justice would require another, much larger, book. Fortunately there are already such books available, and you can also download a lot of useful information from various websites, such as those listed in “AVR Assembly Language Resources”.

Because assembly language is closer to the hardware than any other language, it is sometimes referred to as machine language. Actually, machine language is comprised of the binary codes for various instructions; assembly language is a human-readable form of the machine language. The assembler’s job is to translate from human readable to machine readable, and also add some handy features like macros, symbolic names, conditional assembly, and the ability to refer to functions in external libraries.

In assembly language we can write something like MOV R6, R7, which copies the contents of register R7 into register R6, and it will be translated by the assembler into a binary form (machine language) that the MCU can execute. The names used for the various operations carried out by the MCU’s process are called mnemonics, and the parameters that follow a mnemonic (if any) are called operands.

Programming in assembly language is the art of providing the detailed step-by-step instructions necessary for a CPU to do something useful. A language like C takes care of the details so the programmer doesn’t have to decide which registers to use and what status bits to check. With assembly language there is nothing between the programmer and the fundamental logic of the processor, and even the simplest operation must be explicitly described.

The AVR Programming Model

Internally, an AVR MCU consists of an AVR core, flash and EEPROM memory, and a suite of peripheral functions. The core contains an instruction register and decoder, a program counter, a small amount of RAM, various status bits, 32 general-purpose registers, and an Arithmetic Logic Unit (ALU). Figure 6-5 shows how the various components are organized inside an AVR MCU. Refer to Chapter 2 and Chapter 3 for additional details on the internal functions of AVR MCUs.

aian 0201
Figure 6-5. AVR CPU block diagram

Instructions operate on registers and memory, in that data can be copied from one register to another; two registers can be compared, swapped, added, subtracted, multiplied, and divided (to name a few of the operations); and data can be read from flash memory, EEPROM, or RAM. Peripheral functions are controlled and accessed via registers as well, but these are not part of the core set of 32 general-purpose registers. The three primary characteristics of an MCU, memory, instructions, and registers, are described here:

Memory organization

As mentioned in Chapter 2, the Atmel AVR MCU uses what is called a Harvard architecture. In a Harvard architecture the program code is stored in read-only (flash) memory and modifiable data (variables) are stored in a separate memory space (the RAM in the AVR core). Other microprocessors may use an alternate scheme called the Von Neumann architecture, in which programs and data share the same memory space.

In an AVR MCU the general-purpose registers and the I/O registers used by the peripheral functions are technically part of the read/write memory space. Figure 6-6 shows how memory is laid out in the AVR MCU.

Figure 6-6 is intentionally generic. The assembler directives CSEG, DSEG, and ESEG (used by the Atmel assembler) refer to the code, data, and EEPROM memory spaces, respectively. The highest addresses for the CSEG, DSEG, and ESEG memory spaces will vary from one AVR type to another. Note that space for an optional bootloader is reserved at the end of the CSEG space.

Instruction processing

An AVR MCU utilizes what is called a single-level pipeline to fetch, decode, and execute instructions from flash memory. While one instruction is being executed the next instruction is being prefetched from memory, and it will be ready to be decoded and executed as soon as the current instruction completes. This feature, and the RISC nature of the AVR core, is what allows an AVR MCU to execute most instructions in a single clock cycle.

Registers

Many of the AVR instructions set or clear bits in an 8-bit status register (SREG). Each bit has a specific purpose, as shown in Table 6-6, and the true or false (1 or 0) state of certain bits is checked by instructions such as BREQ (Branch if Equal) or BRNE (Branch if Not Equal) following a CP (Compare) instruction, which can alter the Z, C, N, V, H, and S status bits.

The 32 general-purpose registers are organized as 8-bit registers, with the final three pairs (26-27, 28-29, and 30-31) available as 16-bit index (indirect address) registers referred to as the X, Y, and Z pointers, respectively.

A limited number of instructions can operate on two 8-bit registers as a 16-bit register pair. The lower-numbered register of the pair holds the least significant bits. The least significant register must also be even numbered, so the pair of r0:r1 is valid, but r1:r2 is not.

Table 6-6. AVR SREG status bits
Bit Symbol Function

0

C

Carry flag

1

Z

Zero flag

2

N

Negative flag

3

V

Two’s complement overflow indicator

4

S

For signed tests

5

H

Half carry flag

6

T

Transfer bit used by BLD and BST instructions

7

I

Global interrupt enable/disable flag

In addition to the status register, the general-purpose registers, and the I/O registers, the AVR has a program counter (PC) register and a stack pointer (SP) register. These are modified by certain instructions (a jump will modify the PC, for example) or a subroutine call, which utilizes both the PC and SP registers. For example, when a CALL instruction is encountered the current value of the PC is adjusted to point to the instruction following the CALL, then it’s pushed onto the stack. The PC is then loaded with the start address of the subroutine. When the subroutine returns via a RET instruction, the saved PC is “popped” from the stack and the program resumes at the location immediately following the CALL instruction.

There are, of course, many other details about the inner workings of AVR MCUs and AVR assembly language that have not been covered here. If you want to explore assembly language programming with an AVR, then reading a good book or two is highly recommended. Appendix D contains some suggestions, and the links already mentioned are also very useful.

Creating AVR Assembly Language Programs

Depending on the assembler, single-line comments may begin with a semicolon (;), a hash or pound (#) symbol, or some other character. The GNU assembler (avr-as) supports multiline comments with an opening and closing /* and */, like those found in C code. Refer to your assembler’s documentation for specifics.

aian 0606
Figure 6-6. AVR memory layout

Comments in assembly language are very important. Say, for example, you have some assembly language that looks like this:

LOOP:
LDI		R16, 0X00
OUT		PORTB, R16
LDI		R16, 0XFF
OUT		PORTB, R16
RJMP 	LOOP

What is it doing? In this case it’s fairly easy to see that all it does is load register 16 (R16) with either the value of 0 or 255 (0xFF) and then write that value to PORTB. The pins for PORTB will toggle on and off as fast as the MCU can execute the loop.

We can improve on this and add in the rest of the missing bits for things like program origin and port initialization. The result looks like this:

; set power-up/reset vector
.ORG 0X0000
RJMP MAIN

; code entry point
.ORG 0X0100
MAIN:
LDI		R16, 0XFF	; load R16 with 0b11111111
OUT		DDRB, R16	; set port B data direction register

; endless loop - toggles port B pins on and off
LOOP:
LDI		R16, 0X00	; load R16 with zero
OUT		PORTB, R16	; write to port B
LDI		R16, 0XFF	; now load it with 0xFF
OUT		PORTB, R16	; and write it to port B
RJMP 	LOOP        ; jump back and do it again

I’ve gone bit overboard with the comments, but in assembly language it is not uncommon to see a comment on almost every line. Bear in mind that even something as simple as the common “hello world” program requires many more assembly language statements than it does C or C++ statements to achieve the same result, and some of those assembler statements may not be intuitively obvious.

Most assemblers provide a set of predefined keywords called directives. The previous example has one, the .ORG directive. Table 6-7 lists a few more useful directives. These keywords are called directives because they direct the assembler to make specific associations or perform certain actions.

Table 6-7. AVR assembler directives
Directive Operation

BYTE

Reserve one or more bytes as a variable

CSEG

Use the code segment

CSEGSIZE

Configure code segment memory size

DB

Define a constant byte or bytes

DEF

Define a symbolic name for a register

DEVICE

Define which device to assemble for (the target)

DSEG

Use the data segment

DW

Define a constant word or words (16-bit values)

ENDM, ENDMACRO

End of a macro definition

EQU

Assign a symbol to an expression

ESEG

Use EEPROM segment

EXIT

Exit from file

INCLUDE

Read and include source from another file

LIST

Enable list file generation

LISTMAC

Enable macro expansion in list file

MACRO

Start of a macro definition

NOLIST

Disable list file generation

ORG

Set program origin

SET

Assign a symbol to an expression

The following code fragments show how some of the directives can be used.

; Disable listing generation when including the external files.
; This helps keep the listing output neat and uncluttered.

.NOLIST
.INCLUDE "macrodefs.inc"
.INCLUDE "mcutype.inc"
.LIST

; Define a macro to do something useful

.MACRO	SUBI16			; Define macro
	SUBI	@1,low(@0)	; Subtract low byte
	SBCI	@2,high(@0)	; Subtract high byte
.ENDMACRO				; End macro definition

SUBI16 0x2200,R16,R17	; Subtract 0x2200 from R17:R16

AVR Assembly Language Resources

There are many good sources of information and useful tutorials available online if you want to delve deeper into AVR assembly:

  • The AVR Assembler Site has a wealth of information, all neatly organized into various categories.

  • AVRbeginners.net is a very slick website with lots of details on the inner workings of AVR MCUs and some assembly language examples.

  • Atmel has the online reference AVR Assembler, and a description of Atmel’s assembler can be found in the AVR Assembler User Guide. It’s not the same as the avr-as assembler, but the general principles are similar.

  • The avr-as assembler is described in the documentation found at GNU Manuals Online, and the avr-libc documentation also has a brief overview.

  • Gerhard Schmidt has created a website with a lot of useful information, and it’s available in both English and German. The material is also available as a PDF file.

  • You can download an assembly language summary from the Johns Hopkins University website.

This is just the tip of the iceberg, so to speak. For some book suggestions, refer to Appendix D.

Uploading AVR Executable Code

Compiling or assembling code into an executable file is only half the process. The other half involves uploading the executable code into an AVR MCU for execution. There are several ways to do this, including via the Arduino bootloader, the ICSP interface found on many Arduino boards (at least, the relevant pins are usually available), and a JTAG interface.

In-System Programming

Atmel application note AVR910 describes the In-System Programming interface used on AVR MCUs. This is what is called the ICSP interface on Arduino boards. It is basically an extension to the SPI interface of the AVR MCU. This function (serial I/O) was shown as a separate functional block in Figure 6-5 because it has the ability to communicate directly with the flash and EEPROM memory in the MCU.

Figure 6-7 shows the pinout for the primary ICSP connector on an Arduino Uno (R2). There is a second ICSP connector on the board, which is used for the AVR MCU that handles the USB interface. It is wired the same, but there really isn’t a reason to use it unless you need to reprogram that MCU as well (and perhaps lose USB functionality). Note that there is also a 10-pin connector format defined by Atmel, but it is not used with most Arduino-type boards. The 10-pin connector has the same signals as the 6-pin connector, and more ground connections to take up the additional 4 pins.

aian 0607
Figure 6-7. The ICSP connector on an Arduino Uno R2

In “binutils” the avr-objcopy tool was described, with a focus on converting an executable binary image to an ASCII hex file. The reason for converting the binary image to a hex file is the ability of the Intel hex format to define record segments, addresses, and the end of the file, and then send the data over a communication link that might have issues with pure binary. The AVR MCU uses binary data to program the flash memory, but if you look at the AVR910 application note you can see that whatever is doing the programming has a lot of control over the process. The hex file is used by a programming device, not by the AVR MCU.

Programming with the Bootloader

In the case of an Arduino board, the programming device is the bootloader in flash. This allows the MCU to program itself, at the cost of losing some of the flash memory space to hold the bootloader. Without the bootloader it falls on the programming device to deal with final data placement (target addresses), MCU configuration bits (fuses), and other low-level details. (For more details on the fuse bits used in the AVR MCUs, refer to “Fuse Bits”).

It is important to note that the Arduino bootloader firmware uses the AVR’s serial interface pins (RxD and TxD on pins D0 and D1, respectively), so if you want to attach something like an RS-232 converter you can use that to program the AVR, or you can use a USB-to-serial adapter like the SparkFun module shown in Figure 6-8. On Arduino boards that use an FTDI FT232L, an ATmega8, or an ATmega16U2 for the USB interface, a quick look at the schematic will show that the interface chip or MCU is using the D0 and D1 serial pins through 1K resistors, and the DTR signal is used to generate a reset of the primary AVR MCU.

aian 0608
Figure 6-8. SparkFun USB-to-serial adapter

You can still use the D0 and D1 pins, provided that they are isolated correctly. The partial schematic in Figure 6-9 shows how an FTDI FT232L is typically connected to an AVR MCU in an Arduino. Note that this does not apply to the Leonardo and other boards with the ATmega32U4, which has a built-in USB interface.

Uploading Without the Bootloader

If you really need to use the maximum available space in the AVR flash for a program, or you don’t want to use the D0 and D1 pins in the standard Arduino way to upload a program, then you can load a compiled program directly using the ICSP interface. This approach works with a “fresh” ATmega MCU directly from Atmel, as well as with an Arduino board.

To upload without the Arduino IDE requires direct interaction with the AVRDUDE utility (described in “AVRDUDE”). Typically this would also involve the use of makefiles or some other technique to produce compiled code.

If you want the convenience of letting the Arduino IDE handle the compilation chores for you, then you can go that route as well. The Arduino IDE supports direct AVR device programming by allowing you to select a programming device via a menu item under Tools on the main menu. An upload can then be started using the “Upload Using Programmer” function in the File drop-down menu. If you have overwritten the bootloader firmware and then decide to go back to using the bootloader in the conventional manner, then it will need to be reloaded as described in “Replacing the Bootloader”.

aian 0609
Figure 6-9. Arduino USB interface using an FTDI converter IC

Of course, if memory space is not an issue and the Arduino bootloader has already been installed, then there is no need to remove the bootloader. The ICSP interface works fine with or without the bootloader. If you elect to use the ICSP interface, you can simply ignore the bootloader.

Life without the bootloader requires a special programming device. Atmel tools such as the AVRISP MKII and the new Atmel-ICE are the gold standards because of their capabilities and compatibility. Sadly, Atmel has discontinued the AVRISP MKII in favor of the Atmel-ICE, but there are many compatible devices currently available. The AVRISP MKII did not support JTAG.

However, since the ISP interface is essentially an SPI serial port you can use a variety of devices to get the job done, including another Arduino board (more on this shortly). Some readily available programming devices include the USBtinyISP from Adafruit (Figure 6-10) and the Pocket AVR Programmer from SparkFun (Figure 6-11).

The USBtinyISP programmer is a kit, but it is relatively easy to assemble. You can read more about it on the Adafruit website. The Pocket AVR Programmer comes preassembled, and you can find more information about it at SparkFun.

aian 0610
Figure 6-10. The USBtinyISP from Adafruit (assembled)
aian 0611
Figure 6-11. The Pocket AVR Programmer from SparkFun

In addition to loading software onto an AVR, a programmer can also be used to read registers, examine memory, and set fuse bits. The ability to set fuse bits is a compelling reason to have some type of ISP device available. For more information about fuse bits, see “Fuse Bits”.

JTAG

JTAG, an acronym for Joint Test Action Group, is a low-level interface designed to provide access to debugging facilities incorporated into an MCU or other logic device. The formal definition is found in the IEEE document Standard Test Access Port and Boundary-Scan Architecture, IEEE Standard 1149.1-1990. The latest version of this and other standards can be obtained from the IEEE.

Not all AVR MCUs have JTAG support. As far as I can tell from the Atmel selection guides and datasheets, the XMEGA series devices have it, but the 8-bit Mega series parts (like those used in Arduino boards) do not. But since there are many types of AVR MCUs available, it is entirely possible that there are some XMEGA parts without JTAG, and some Mega parts that have it.

In most cases, though, you don’t really need the advanced features of JTAG. It’s nice when you want to step through the code with a debugger, or examine register contents on the fly, but it is often the case that just looking at the pins with an oscilloscope or logic analyzer will provide plenty of information.

As for accessing the internal functions in an AVR, you can use something like USBtinyISP to set the internal AVR fuse bits or load the EEPROM. So unless you have a real need for a JTAG tool, you can probably skip the expense.

AVRDUDE

An AVR programmer is good to have, but it needs something that can provide it with the data to be uploaded into the AVR MCU. That something is called AVRDUDE.

AVRDUDE, or the AVR Download UploaDEr, is a utility program for uploading and downloading code and data from the memory spaces of an AVR MCU. Figure 6-12 shows the output AVRDUDE will generate when executed without any arguments.

AVRDUDE can also program the on-board EEPROM memory, and the fuse and lock bits as well. The tool runs in either command-line or interactive mode. The command-line mode is useful when incorporating AVRDUDE into a script or a makefile, while the interactive mode can be used to poke around in the MCU’s memory, modify individual bytes in the EEPROM, or fiddle around with the fuse or lock bits.

AVRDUDE supports a variety of programming devices, including the Atmel STK500, the AVRISP and AVRISP MKII, serial bit-bangers, and a parallel port interface. A manual is available in PDF format.

The Arduino IDE uses AVRDUDE to handle the upload process, and you can see what the command line looks like by enabling the upload output from the Preferences dialog. Here is what it looks like when uploading the simple intrusion alarm sketch from Chapter 5 (note I have wrapped the line in order to fit it on the page):

/usr/share/arduino/hardware/tools/avrdude
 -C/usr/share/arduino/hardware/tools/avrdude.conf
 -v -v -v -v -patmega328p -carduino -P/dev/ttyUSB0 -b57600 -D
 -Uflash:w:/tmp/build2510643905912671503.tmp/simple_alarm.cpp.hex:i
aian 0612
Figure 6-12. AVRDUDE help output

The -p, -c, -P, -D, and -U switches are the key things to note here. These and some of the other available switches are described in Table 6-8. The multiple -v switches just tell AVRDUDE to be as verbose as possible.

Table 6-8. AVRDUDE command-line switches
Switch Function Description

-p

Processor ID

Identifies the part connected to the programmer. In our case, the Arduino board being programmed does indeed have an ATmega328p—it’s a Duemilanove.

-c

Programmer ID

Specifies the programmer to use. In this case, the Arduino bootloader is the programmer.

-C

Configuration

Specifies a configuration file to use.

-P

Port name

Identifies the port to which the programmer is attached. Since this is a Linux system, the pseudo serial port at /dev/ttyUSB is used.

-b

Port baud rate

Overrides the default baud rate.

-D

Auto-erase

Disables the flash auto-erase.

-U

Upload

Upload specification.

Note that the -U switch in the command line is comprised of multiple parts. It defines the memory target (flash), the mode (write), and a source file containing the hex form of the executable image. The final i at the end of the argument string specifies that the hex source is in Intel format.

Using an Arduino as an ISP

By loading a utility program onto an Arduino, you can make it become an ISP for another Arduino. You can upload a new bootloader using this technique. The Arduino website provides simple directions.

Essentially all that is necessary is a sketch supplied with the Arduino IDE, and two Arduino boards. Figure 6-13 shows how the boards are connected. In this case a Duemilanove is acting as the ISP device for an Uno R3. It can also work with boards that don’t have the same pinouts for the SPI signals, like the Leonardo, but you will need to make adjustments for that. Refer to the Arduino documentation for details.

Bootloader Operation

The purpose of the bootloader is to accept a program for the AVR from the host development system. The microcontrollers installed on Arduino boards come with the bootloader preinstalled, and for most applications there is seldom any need to remove or reload the bootloader software. Unless there is a compelling reason to try to reclaim the few kilobytes of memory (depending on the processor type and the vintage) that the bootloader consumes, the easiest approach is just to leave it alone and take advantage of it.

The operation of the Arduino bootloader is similar to that of any other flash memory–equipped microcontroller. The primary objective is to get the user-supplied program into on-board memory and then transfer control to the new program for execution. When an Arduino is powered on, it begins to execute the bootloader. The bootloader then checks for new incoming program data on the USB interface, and if nothing is detected after some small amount of time, the program loaded into the main section of flash memory is executed.

On newer versions of the Arduino this check for incoming data also occurs even while the AVR processor is running a previously stored program, so that when a new program upload is detected the existing program code is interrupted and the bootloader is given full control of the processor. On older Arduino boards the reset button must be pressed as soon as the IDE starts to upload the compiled program in order to detect the upload. You may need to do this a few times to get the timing just right. Note that some Arduino-compatible boards also behave this way.

aian 0613
Figure 6-13. Using an Arduino as a programmer for another Arduino

Once the bootloader has determined that the incoming data is a valid program upload, it then unlocks the on-board flash memory and begins reading in the new program and writing it to the flash memory (but not in the location where the bootloader itself resides). Once the upload is complete the memory is once again locked so that it cannot be accidentally modified, and an interrupt is generated that will cause the processor to be directed (or vectored, as it is sometimes called) to the starting address of the new program.

Current versions of the Arduino bootloader can read data at 19,200 baud (19.2 Kbaud), or about 1,900 bytes per second. Older versions of the bootloader listened for incoming data at 9,600 baud, and you can alter the behavior of the Arduino IDE to accommodate this if necessary. At 19.2K it can still take many seconds to transfer a large program sketch to the target processor.

The bootloader source code is available from Arduino.cc, and it’s worth reading if you are really curious about how it works. If you do peruse it you will notice that the section that manipulates the flash memory is written in AVR assembly language. At this point the code is “down on the metal” and interacting with the microcontroller’s internal control registers, and assembly is the appropriate language for this task. The bootloader source code also gives some insight into what goes on at the lowest levels in a microcontroller. There is a lot happening behind the scenes when a program is loaded and executed.

Replacing the Bootloader

Should you have the need (or the desire) to install a new bootloader on the AVR processor in an Arduino, you will need a device to program the microcontroller’s flash memory directly via the ICSP port like the ones shown in Figures 6-10 and 6-11, or you can use the Arduino-to-Arduino trick described in “Using an Arduino as an ISP”.

The bootloader resides in a particular region of the flash memory in an AVR available for just this purpose, and the available size ranges from 256 bytes to 4 KB, depending on the AVR type. The Arduino IDE supports bootloader uploading via a programming device like those shown earlier. Under Tools on the main IDE menu you must select the type of programmer you have, compile the bootloader, and then upload it to the AVR microcontroller. Refer to the Arduino’s built-in help and to the main Arduino website for details.

Summary

This chapter has been a whirlwind tour through multiple topics, from the components of the AVR toolchain to the make utility, assembly language programming, and finally the nitty-gritty of AVR bootloaders. Even if you never use any of the tools or techniques covered here, it is still useful to know something about what is going on under the hood of the Arduino IDE. It also serves as a glimpse into what working with embedded systems was like before the Arduino IDE came along.

If you want to explore any of the topics presented here in more detail be sure to avail yourself of the references given, and don’t forget to look at Appendix D for even more sources of information. Embedded microcontroller devices are key components of modern civilization, and for every computer you can see there are many, many more that you cannot, hidden in TV remote controls, microwave ovens, your stereo system and DVD player, traffic light controls, your automobile, and even the keyboard for the computer sitting on your desk. At one time all these little devices were programmed using the techniques covered briefly in this chapter, and many of them are still programmed with these same methods today.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.145.12.156