AVR - How to program an AVR chip in Linux

I don't have the time for a full explanation, but I can give you cookbook-style the commands I use on my Linux box to program AVRs:

Preparations

  • On Ubuntu, make sure several required packages are installed: sudo apt-get install avr-libc avrdude binutils-avr gcc-avr srecord optionally throw in gdb-avr simulavr for debug and simulation.
  • I started to create a directory in which all my ATtiny projects find a home: mkdir ~/attiny: cd ~/attiny
  • For each project I create a dedicated subfolder (and I don't mind long names): mkdir waveShare4digit8segmentDisplay; cd waveShare4digit8segmentDisplay

Create source

  • Edit the source file with your favorite text editor: vi project.cpp

Settings

The commands below heavily rely on environment variables, to keep maintenance easy.

  • The base name of the files used/created: src=project
  • Common compiler flags: cflags="-g -DF_CPU=${avrFreq} -Wall -Os - Werror -Wextra"

The variables below may need to be changed depending on the exact programmer you use. Refer to the man pages for details.

  • baud=19200 The baudrate your programmer communicates at with the PC:
  • programmerDev=/dev/ttyUSB003 The device name where your programmer is located. Check dmesg output for details.
  • programmerType=avrisp This may be different for your exact programmer.

The variables below depend on the exact controller you want to program:

  • avrType=attiny2313 Check avrdude -c $programmerType for supported devices.
  • avrFreq=1000000 Check the controller's datasheet for default clock.

Compile

  • First step is to create an object file: avr-gcc ${cflags) -mmcu=${avrType) -Wa,-ahlmns=${src).lst -c -o ${src).o ${src).cpp
  • Second step is to create an ELF file: avr-gcc ${cflags) -mmcu=${avrType) -o ${src).elf ${src).o
  • Third step is to create an Intel Hex file, this is the file that is actually sent to the programmer: avr-objcopy -j .text -j .data -O ihex ${src).elf ${src).flash.hex

Programming

  • Final step is to program the device: avrdude -p${avrType} -c${programmerType} -P${programmerDev} -b${baud} -v -U flash:w:${src}.flash.hex

Makefile

As an alternative to remembering the commands, I cooked up a makefile to my personal liking, you can save it under the name Makefile (mind the capital M). It works as follows:

  • make makefile Edit the makefile;
  • make edit Edit the source file;
  • make flash Program the device's flash memory;
  • make help List other commands.

Here is the makefile:

baud=19200
src=project
avrType=attiny2313
avrFreq=4000000 # 4MHz for accurate baudrate timing
programmerDev=/dev/ttyUSB003
programmerType=arduino

cflags=-g -DF_CPU=$(avrFreq) -Wall -Os -Werror -Wextra

memoryTypes=calibration eeprom efuse flash fuse hfuse lfuse lock signature application apptable boot prodsig usersig

.PHONY: backup clean disassemble dumpelf edit eeprom elf flash fuses help hex makefile object program

help:
    @echo 'backup       Read all known memory types from controller and write it into a file. Available memory types: $(memoryTypes)'
    @echo 'clean        Delete automatically created files.'
    @echo 'disassemble  Compile source code, then disassemble object file to mnemonics.'
    @echo 'dumpelf      Dump the contents of the .elf file. Useful for information purposes only.'
    @echo 'edit     Edit the .cpp source file.'
    @echo 'eeprom       Extract EEPROM data from .elf file and program the device with it.'
    @echo 'elf      Create $(src).elf'
    @echo 'flash        Program $(src).hex to controller flash memory.'
    @echo 'fuses        Extract FUSES data from .elf file and program the device with it.'
    @echo 'help     Show this text.'
    @echo 'hex      Create all hex files for flash, eeprom and fuses.'
    @echo 'object       Create $(src).o'
    @echo 'program      Do all programming to controller.'

edit:
    vi $(src).cpp

makefile:
    vi Makefile

#all: object elf hex

clean: 
    rm $(src).elf $(src).eeprom.hex $(src).fuses.hex $(src).lfuse.hex $(src).hfuse.hex $(src).efuse.hex $(src).flash.hex $(src).o
    date

object:
    avr-gcc $(cflags) -mmcu=$(avrType) -Wa,-ahlmns=$(src).lst -c -o $(src).o $(src).cpp 

elf: object
    avr-gcc $(cflags) -mmcu=$(avrType) -o $(src).elf $(src).o
    chmod a-x $(src).elf 2>&1

hex:    elf
    avr-objcopy -j .text -j .data -O ihex $(src).elf $(src).flash.hex
    avr-objcopy -j .eeprom --set-section-flags=.eeprom="alloc,load" --change-section-lma .eeprom=0 -O ihex $(src).elf $(src).eeprom.hex
    avr-objcopy -j .fuse -O ihex $(src).elf $(src).fuses.hex --change-section-lma .fuse=0
    srec_cat $(src).fuses.hex -Intel -crop 0x00 0x01 -offset  0x00 -O $(src).lfuse.hex -Intel
    srec_cat $(src).fuses.hex -Intel -crop 0x01 0x02 -offset -0x01 -O $(src).hfuse.hex -Intel
    srec_cat $(src).fuses.hex -Intel -crop 0x02 0x03 -offset -0x02 -O $(src).efuse.hex -Intel

disassemble: elf
    avr-objdump -s -j .fuse $(src).elf
    avr-objdump -C -d $(src).elf 2>&1

eeprom: hex
    #avrdude -p$(avrType) -c$(programmerType) -P$(programmerDev) -b$(baud) -v -U eeprom:w:$(src).eeprom.hex
    date

fuses: hex
    avrdude -p$(avrType) -c$(programmerType) -P$(programmerDev) -b$(baud) -v -U lfuse:w:$(src).lfuse.hex
    #avrdude -p$(avrType) -c$(programmerType) -P$(programmerDev) -b$(baud) -v -U hfuse:w:$(src).hfuse.hex
    #avrdude -p$(avrType) -c$(programmerType) -P$(programmerDev) -b$(baud) -v -U efuse:w:$(src).efuse.hex
    date

dumpelf: elf
    avr-objdump -s -h $(src).elf

program: flash eeprom fuses

flash: hex
    avrdude -p$(avrType) -c$(programmerType) -P$(programmerDev) -b$(baud) -v -U flash:w:$(src).flash.hex
    date

backup:
    @for memory in $(memoryTypes); do \
        avrdude -p $(avrType) -c$(programmerType) -P$(programmerDev) -b$(baud) -v -U $$memory:r:./$(avrType).$$memory.hex:i; \
    done

It may be seem necessary to run avrdude as root, if that happens it justifies a question in its own. It can be solved with udev but requires a bit specific information from how the programmer is recognized by the operating system.

Hello World

Let me throw in an 'Hello World' that makes a controller pin 2 (PB3) (eg. ATtiny13, ATtiny45, ATtiny85) toggle at 1Hz. Attach an LED and series resistor to the pin and the LED should start to blink.

  • make edit

i

#include <avr/io.h>
#include <util/delay.h>

int main(void)
{
  DDRB = 0x08;

  while (1) {
    PORTB = 0x00; _delay_ms(500);
    PORTB = 0x08; _delay_ms(500);
  }
}

<ESC>:wq

  • make flash

Done.


You can use the AVR GNU tools as standalone packages in linux. These include avr-gcc, avr-binutils, and avr-libc. This is what is referred to as the toolchain.

Once you have built a hex file and you are wishing to flash it onto your chip, you can use avrdude.

All of these are freely and readily available on Linux and not too difficult to configure to work together.

LadyAda has a solid step by step tutorial on the whole process.