Updated:
Program Arduino with AVR-GCC
Arduino is both an IDE and a hardware platform. The IDE is written in Java and uses a language called Processing
that is a very thin software layer on top of C plus pre-packaged libraries for an easy access to UART port, SPI
port, etc.
This is a good idea because it simplifies ATmega8/168 software development for newbies but, by other hand, this scheme has a few disadvantages compared with pure (and raw) C programs:
- C lets a more accurate time and execution control. No hidden code, "What You Type Is What Is Executed" (tm)
- C lets simple and direct access to hardware and interrupts
- C lets you make ports to other MCUs apart than Arduino (ATmega168)
In this page I compile and upload a simple program written in pure C (with avr-libc) without Arduino IDE. Only a
terminal, a text editor and the AVR-GCC toolchain (avr-as, avr-gcc, make, avrdude, etc) are needed.
Blinking LED Example
Let's start with a trivial example: a led blinking on Arduino pin 13 (actually, blinks all bits of PORTB). Create a folder for your project and copy the following example program in a file called blink.c
/* Hi Emacs, this is -*- c-mode -*- */ #include <avr/io.h> #include <util/delay.h> int main (void) { unsigned char counter; /* set PORTB for output*/ DDRB = 0xFF; while (1) { /* set PORTB.2 high */ PORTB = 0xFF; /* wait (10 * 120000) cycles = wait 1200000 cycles */ counter = 0; while (counter != 50) { /* wait (30000 x 4) cycles = wait 120000 cycles */ _delay_loop_2(30000); counter++; } /* set PORTB.2 low */ PORTB = 0x00; /* wait (10 * 120000) cycles = wait 1200000 cycles */ counter = 0; while (counter != 50) { /* wait (30000 x 4) cycles = wait 120000 cycles */ _delay_loop_2(30000); counter++; } } return 1; }
Compile and upload
After connecting Arduino board to USB port, Linux 2.6 should automaticaly load FTDI driver (ftdi_sio.ko
)
$ dmesg ... usb 3-2: FTDI USB Serial Device converter now attached to ttyUSB0 usbcore: registered new interface driver ftdi_sio drivers/usb/serial/ftdi_sio.c: v1.4.3:USB FTDI Serial Converters Driver
The toolchain (compiler/linker/assembler, standard C library and a programming utility) are contained in this three packages:
The manual of the c library is at /usr/share/doc/avr-libc/avr-libc-user-manual/index.html
It is useful to examine the "Hello world!" project explained at file:///usr/share/doc/avr-libc/avr-libc-user-manual/group__demo__project.html
.
At the end of this page there is a Makefile you can customize to your own needs. Change program's name to blink
and compile it:
this generates blink.hex
, the firmware's image to upload. There is basically two methods for upload it
to Arduino
- using ICSP (In-Circuit Serial Programming)
- using a bootloader stored in program (flash) memory, consumes about 2KB of program memory, see last section of this page for information about how to burn it if you've a "blank" device.
The second option is not strictly required. In fact, it has no decisive advantages respect to the first option except that you may only need one USB cable instead of two (USB for communication and Parallel for uploading).
Upload with a Bootloader
In case there is a bootloader already burned on AVR's program memory, flash blink.hex
using the
bootloader. Be sure that the fuse BOOTRST=0, if not, bootloader won't execute after reset (see below about fuse
bits)
Upload without a Bootloader with Parallel Port programmer
If you don't want to use a bootloader, burn directly blink.hex
with the parallel programmer. Be sure
that the fuse BOOTRST=1, if not, the program won't execute after reset (see below about fuse bits)
use -p m8
in case you are using atmega8 (not atmega168).
Upload without a Bootloader with AVR ISP mkII programmer from AVR Studio
To use this method, you'll need to purchase a mkII programmer (30€ aprox) and connect it to Arduino using its ICSP
connector. In AVR Studio IDE, program Arduino is a completely graphical process. Click on Tool -> Program
AVR -> Connect...
. Select AVR ISP mkII programmer, USB link, and select as flash image the generated
*.hex file. Click on Program
button et voilà!
Note that...
- Now, when you write software, pin numbers are different from those that we used to use in Arduino IDE
- You access ports and other hardware using AVR-GCC notation, PORTB, PORTD, etc see the ATmega8/168 datasheet for details about the name of SFRs (Special Function Registers). A few atmega8's SFR names differ from those in atmega168/328p
- If you use another part (atmega8, atmega168, atmega328, etc) modify the
MCU
variable in the Makefile. - Advice in case you encounter this problem: recently arduino ships with atmega328p with is identical to atmega168 but with more memory. The current avr-libc library (01-01-2009) doesn't support well atmega328. In particular, the utility for serial port setup doesn't work:
#define BAUD 19200 #include <util/setbaud.h> UBRR0H = UBRRH_VALUE; UBRR0L = UBRRL_VALUE; #if USE_2X UCSR0A |= (1 << U2X0); #else UCSR0A &= ~(1 << U2X0); #endif
you should use instead:
#define BAUD_RATE 19200 UBRR0L = (uint8_t)(F_CPU/(BAUD_RATE*16L)-1); UBRR0H = (F_CPU/(BAUD_RATE*16L)-1) >> 8; UCSR0B = (1<<RXEN0) | (1<<TXEN0); UCSR0C = (1<<UCSZ00) | (1<<UCSZ01); /* Enable internal pull-up resistor on pin D0 (RX), in order to supress line noise */ DDRD &= ~_BV(PIND0); PORTD |= _BV(PIND0);
Burn a bootloader using ICSP
This section is only relevant if you have a "blank" device, with no bootloader burned in its flash memory. The part that comes with Arduino already has a bootloader so you can skip this section. An easy check to see whether bootloader is installed or not is that, if installed, a led at pin 13 must blink 3 times after reset.
What is a bootloader?
A bootloader is a small program which resides in a special AVR memory section (the bootloader section), its basic
mission is to receive new firmware uploaded thought serial port<ref>Or USB port, or I2C port, or SPI port,
etc. It depends on the bootloader type</ref> and store it in AVR's flash memory (program memory). Each
bootloader is AVR device specific, talks a specific protocol and uses a concrete serial port baud rate. All these
configuration parameters must match the ones used with the host programming utility: avrdude.
This utility can talk many types of protocols and supports many types of links (serial, parallel, usb...).
File | Description |
---|---|
ATmegaBOOT_168_ng.hex | Bootloader for AVR ATmega168 @16MHz, protocol stk500v1, 19200-8N1 serial port. |
The following instructions burn the bootloader on the AVR device. I've written them with Linux in mind but
they works also on Windows (checked!) replacing /dev/parport0
with LPT1
and installing giveio.sys to give
to user-space processes direct access to parallel port under Windows 2000/XP.
- (to do: explain howto compile a bootloader image instead of provide an already compiled one)
- connect the Parallel
Programmer (
dapa
) to parallel port (/dev/parport0
) and the ICSP<ref>ICSP (In-Circuit Serial Programming) makes reference the mode in which microcontroller is programmed, it no means that the cable must be serial</ref> port of Arduino, also to power (USB or external)$ # write the following fuse bits: efuse=0x00, hfuse=0xdd, lfuse=0xff, see below$ # write the following fuse bits: lock=0x3f (unlock boot section)$ avrdude -c dapa -p m168 -P /dev/parport0 115000 -U flash:w:ATmegaBOOT_168_ng.hex$ # write the following fuse bits: lock=0x0f (lock boot section)
Explanation of above commands: prior to burn the image, it changes the ATmega88/168 fuse bits to setup the
following configuration: external crystal oscillator, disable clock dividers, maximum size for bootloader section,
etc. And then, flashes ATmegaBOOT_168_ng.hex
in AVR. Please, refer to the manual to get more
information about this (very important) fuse bits.
To have access to parallel port, your user must be part of lp
group, edit /etc/group
and quit session to let changes take effect.
Read fuse bits
The last command is only to make sure that the cables are well connected. The device's signature of ATmega168 is 0x1E, 0x94, 0x06.